summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes/2.14.2.txt35
-rw-r--r--Documentation/RelNotes/2.15.0.txt266
-rw-r--r--Documentation/config.txt37
-rw-r--r--Documentation/diff-config.txt11
-rw-r--r--Documentation/diff-options.txt51
-rw-r--r--Documentation/git-branch.txt10
-rw-r--r--Documentation/git-for-each-ref.txt15
-rw-r--r--Documentation/git-format-patch.txt4
-rw-r--r--Documentation/git-grep.txt7
-rw-r--r--Documentation/git-interpret-trailers.txt60
-rw-r--r--Documentation/git-merge.txt13
-rw-r--r--Documentation/git-pack-objects.txt17
-rw-r--r--Documentation/git-push.txt4
-rw-r--r--Documentation/git-send-pack.txt4
-rw-r--r--Documentation/git-tag.txt7
-rw-r--r--Documentation/git.txt3
-rw-r--r--Documentation/gitattributes.txt69
-rw-r--r--Documentation/githooks.txt4
-rw-r--r--Documentation/pretty-formats.txt23
-rw-r--r--Documentation/technical/api-builtin.txt73
-rw-r--r--Documentation/technical/api-config.txt4
-rw-r--r--Documentation/technical/api-sub-process.txt59
-rw-r--r--Documentation/technical/api-tree-walking.txt6
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile4
l---------RelNotes2
-rw-r--r--apply.c68
-rw-r--r--apply.h1
-rw-r--r--archive.c2
-rw-r--r--attr.c12
-rw-r--r--bisect.c18
-rw-r--r--branch.c16
-rw-r--r--builtin.h100
-rw-r--r--builtin/add.c7
-rw-r--r--builtin/am.c45
-rw-r--r--builtin/blame.c5
-rw-r--r--builtin/branch.c46
-rw-r--r--builtin/cat-file.c9
-rw-r--r--builtin/checkout.c6
-rw-r--r--builtin/clean.c22
-rw-r--r--builtin/clone.c4
-rw-r--r--builtin/commit-tree.c4
-rw-r--r--builtin/commit.c34
-rw-r--r--builtin/count-objects.c1
-rw-r--r--builtin/describe.c9
-rw-r--r--builtin/diff-files.c1
-rw-r--r--builtin/diff-index.c1
-rw-r--r--builtin/diff-tree.c1
-rw-r--r--builtin/diff.c2
-rw-r--r--builtin/difftool.c39
-rw-r--r--builtin/fetch.c32
-rw-r--r--builtin/fmt-merge-msg.c3
-rw-r--r--builtin/for-each-ref.c27
-rw-r--r--builtin/fsck.c74
-rw-r--r--builtin/gc.c1
-rw-r--r--builtin/grep.c408
-rw-r--r--builtin/hash-object.c12
-rw-r--r--builtin/index-pack.c1
-rw-r--r--builtin/interpret-trailers.c107
-rw-r--r--builtin/log.c33
-rw-r--r--builtin/ls-files.c11
-rw-r--r--builtin/merge-tree.c16
-rw-r--r--builtin/merge.c30
-rw-r--r--builtin/mv.c3
-rw-r--r--builtin/notes.c2
-rw-r--r--builtin/pack-objects.c8
-rw-r--r--builtin/pack-redundant.c1
-rw-r--r--builtin/prune-packed.c4
-rw-r--r--builtin/prune.c2
-rw-r--r--builtin/pull.c4
-rw-r--r--builtin/push.c2
-rw-r--r--builtin/read-tree.c2
-rw-r--r--builtin/receive-pack.c7
-rw-r--r--builtin/remote.c2
-rw-r--r--builtin/replace.c6
-rw-r--r--builtin/reset.c17
-rw-r--r--builtin/rev-list.c9
-rw-r--r--builtin/rev-parse.c8
-rw-r--r--builtin/revert.c2
-rw-r--r--builtin/rm.c3
-rw-r--r--builtin/send-pack.c2
-rw-r--r--builtin/show-branch.c10
-rw-r--r--builtin/submodule--helper.c127
-rw-r--r--builtin/tag.c66
-rw-r--r--builtin/unpack-file.c12
-rw-r--r--builtin/update-index.c2
-rw-r--r--builtin/update-ref.c69
-rw-r--r--builtin/verify-tag.c21
-rw-r--r--bulk-checkin.c1
-rw-r--r--cache-tree.c5
-rw-r--r--cache.h192
-rw-r--r--color.c8
-rw-r--r--color.h2
-rw-r--r--commit.c61
-rw-r--r--commit.h9
-rw-r--r--compat/bswap.h38
-rw-r--r--compat/win32/syslog.c2
-rw-r--r--config.c80
-rw-r--r--config.h17
-rw-r--r--connected.c1
-rw-r--r--contrib/coccinelle/array.cocci17
-rwxr-xr-xcontrib/contacts/git-contacts2
-rwxr-xr-xcontrib/rerere-train.sh54
-rw-r--r--convert.c236
-rw-r--r--convert.h29
-rw-r--r--credential-cache.c2
-rw-r--r--diff.c1354
-rw-r--r--diff.h39
-rw-r--r--diffcore-rename.c4
-rw-r--r--dir.c50
-rw-r--r--entry.c132
-rw-r--r--fast-import.c1
-rw-r--r--fetch-pack.c1
-rw-r--r--fsck.c2
-rw-r--r--git-compat-util.h10
-rw-r--r--git-gui/Makefile2
-rw-r--r--git-gui/lib/choose_repository.tcl17
-rwxr-xr-xgit-merge-octopus.sh2
-rwxr-xr-xgit-merge-one-file.sh8
-rwxr-xr-xgit-merge-resolve.sh2
-rw-r--r--git-rebase--am.sh4
-rw-r--r--git-rebase--interactive.sh7
-rwxr-xr-xgit-rebase.sh13
-rwxr-xr-xgit-stash.sh24
-rwxr-xr-xgit-submodule.sh7
-rw-r--r--git.c20
-rwxr-xr-xgitweb/gitweb.perl5
-rw-r--r--grep.c15
-rw-r--r--grep.h1
-rw-r--r--http-backend.c1
-rw-r--r--http-push.c1
-rw-r--r--http-walker.c1
-rw-r--r--http.c15
-rw-r--r--mailmap.c6
-rw-r--r--name-hash.c22
-rw-r--r--notes-merge.c5
-rw-r--r--notes.c4
-rw-r--r--object.c1
-rw-r--r--object.h2
-rw-r--r--outgoing/packfile.h0
-rw-r--r--pack-bitmap.c1
-rw-r--r--pack-check.c1
-rw-r--r--packfile.c1896
-rw-r--r--packfile.h138
-rw-r--r--pager.c2
-rw-r--r--patch-ids.c14
-rw-r--r--path.c1
-rw-r--r--perl/Git.pm2
-rw-r--r--perl/Git/SVN.pm2
-rw-r--r--pkt-line.c19
-rw-r--r--pkt-line.h2
-rw-r--r--pretty.c53
-rw-r--r--progress.c15
-rw-r--r--progress.h3
-rw-r--r--reachable.c1
-rw-r--r--read-cache.c75
-rw-r--r--ref-filter.c108
-rw-r--r--ref-filter.h30
-rw-r--r--reflog-walk.c152
-rw-r--r--reflog-walk.h7
-rw-r--r--refs.c53
-rw-r--r--refs/files-backend.c647
-rw-r--r--refs/packed-backend.c885
-rw-r--r--refs/packed-backend.h25
-rw-r--r--refs/refs-internal.h16
-rw-r--r--remote.c20
-rw-r--r--remote.h2
-rw-r--r--repository.c8
-rw-r--r--repository.h8
-rw-r--r--rerere.c26
-rw-r--r--revision.c77
-rw-r--r--revision.h7
-rw-r--r--run-command.c4
-rw-r--r--sequencer.c85
-rw-r--r--server-info.c1
-rw-r--r--setup.c12
-rw-r--r--sha1-lookup.c216
-rw-r--r--sha1_file.c1976
-rw-r--r--sha1_name.c241
-rw-r--r--shallow.c4
-rw-r--r--strbuf.c1
-rw-r--r--strbuf.h2
-rw-r--r--streaming.c1
-rw-r--r--string-list.c8
-rw-r--r--sub-process.c111
-rw-r--r--sub-process.h55
-rw-r--r--submodule-config.c131
-rw-r--r--submodule-config.h15
-rw-r--r--submodule.c389
-rw-r--r--submodule.h25
-rw-r--r--t/helper/test-hashmap.c34
-rw-r--r--t/helper/test-path-utils.c18
-rw-r--r--t/helper/test-submodule-config.c17
-rw-r--r--t/helper/test-write-cache.c23
-rwxr-xr-xt/lib-gpg.sh1
-rwxr-xr-xt/perf/p0007-write-cache.sh29
-rwxr-xr-xt/t0001-init.sh30
-rwxr-xr-xt/t0021-conversion.sh180
-rw-r--r--t/t0021/rot13-filter.pl214
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh67
-rwxr-xr-xt/t1200-tutorial.sh268
-rwxr-xr-xt/t1408-packed-refs.sh42
-rwxr-xr-xt/t1411-reflog-show.sh10
-rwxr-xr-xt/t1414-reflog-walk.sh135
-rwxr-xr-xt/t1450-fsck.sh16
-rwxr-xr-xt/t3200-branch.sh51
-rwxr-xr-xt/t3203-branch-output.sh31
-rwxr-xr-xt/t3210-pack-refs.sh42
-rwxr-xr-xt/t3418-rebase-continue.sh85
-rwxr-xr-xt/t3504-cherry-pick-rerere.sh92
-rwxr-xr-xt/t3700-add.sh1
-rwxr-xr-xt/t3903-stash.sh34
-rwxr-xr-xt/t3905-stash-include-untracked.sh17
-rwxr-xr-xt/t4015-diff-whitespace.sh559
-rwxr-xr-xt/t4027-diff-submodule.sh67
-rwxr-xr-xt/t4062-diff-pickaxe.sh4
-rwxr-xr-xt/t4124-apply-ws-rule.sh33
-rwxr-xr-xt/t4150-am.sh83
-rwxr-xr-xt/t4200-rerere.sh57
-rwxr-xr-xt/t4202-log.sh6
-rwxr-xr-xt/t4205-log-pretty-formats.sh51
-rwxr-xr-xt/t4207-log-decoration-colors.sh22
-rwxr-xr-xt/t5308-pack-detect-duplicates.sh11
-rwxr-xr-xt/t5520-pull.sh4
-rwxr-xr-xt/t5526-fetch-submodules.sh8
-rwxr-xr-xt/t5531-deep-submodule-push.sh25
-rwxr-xr-xt/t5534-push-signed.sh7
-rwxr-xr-xt/t6006-rev-list-format.sh129
-rwxr-xr-xt/t6018-rev-list-glob.sh20
-rwxr-xr-xt/t6040-tracking-info.sh20
-rwxr-xr-xt/t6300-for-each-ref.sh39
-rwxr-xr-xt/t7004-tag.sh27
-rwxr-xr-xt/t7006-pager.sh80
-rwxr-xr-xt/t7301-clean-interactive.sh10
-rwxr-xr-xt/t7400-submodule-basic.sh16
-rwxr-xr-xt/t7411-submodule-config.sh87
-rwxr-xr-xt/t7513-interpret-trailers.sh142
-rwxr-xr-xt/t7600-merge.sh15
-rwxr-xr-xt/t7614-merge-signoff.sh69
-rwxr-xr-xt/t7810-grep.sh5
-rwxr-xr-xt/t8008-blame-formats.sh30
-rw-r--r--t/test-lib-functions.sh1
-rw-r--r--t/test-lib.sh4
-rw-r--r--tag.c10
-rw-r--r--tag.h2
-rwxr-xr-xtemplates/hooks--prepare-commit-msg.sample32
-rw-r--r--trailer.c228
-rw-r--r--trailer.h70
-rw-r--r--transport-helper.c2
-rw-r--r--tree-diff.c4
-rw-r--r--tree-walk.c9
-rw-r--r--tree-walk.h2
-rw-r--r--unpack-trees.c92
-rw-r--r--vcs-svn/fast_export.c41
-rw-r--r--vcs-svn/fast_export.h3
-rw-r--r--vcs-svn/repo_tree.c48
-rw-r--r--vcs-svn/repo_tree.h23
-rw-r--r--vcs-svn/svndump.c33
258 files changed, 9859 insertions, 6304 deletions
diff --git a/Documentation/RelNotes/2.14.2.txt b/Documentation/RelNotes/2.14.2.txt
new file mode 100644
index 0000000000..5517afcf59
--- /dev/null
+++ b/Documentation/RelNotes/2.14.2.txt
@@ -0,0 +1,35 @@
+Git v2.14.2 Release Notes
+=========================
+
+Fixes since v2.14.1
+-------------------
+
+ * Because recent Git for Windows do come with a real msgfmt, the
+ build procedure for git-gui has been updated to use it instead of a
+ hand-rolled substitute.
+
+ * "%C(color name)" in the pretty print format always produced ANSI
+ color escape codes, which was an early design mistake. They now
+ honor the configuration (e.g. "color.ui = never") and also tty-ness
+ of the output medium.
+
+ * The http.{sslkey,sslCert} configuration variables are to be
+ interpreted as a pathname that honors "~[username]/" prefix, but
+ weren't, which has been fixed.
+
+ * Numerous bugs in walking of reflogs via "log -g" and friends have
+ been fixed.
+
+ * "git commit" when seeing an totally empty message said "you did not
+ edit the message", which is clearly wrong. The message has been
+ corrected.
+
+ * When a directory is not readable, "gitweb" fails to build the
+ project list. Work this around by skipping such a directory.
+
+ * A recently added test for the "credential-cache" helper revealed
+ that EOF detection done around the time the connection to the cache
+ daemon is torn down were flaky. This was fixed by reacting to
+ ECONNRESET and behaving as if we got an EOF.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.15.0.txt b/Documentation/RelNotes/2.15.0.txt
new file mode 100644
index 0000000000..cb630724c7
--- /dev/null
+++ b/Documentation/RelNotes/2.15.0.txt
@@ -0,0 +1,266 @@
+Git 2.15 Release Notes
+======================
+
+Backward compatibility notes and other notable changes.
+
+ * Use of an empty string as a pathspec element that is used for
+ 'everything matches' is still warned and Git asks users to use a
+ more explicit '.' for that instead. The hope is that existing
+ users will not mind this change, and eventually the warning can be
+ turned into a hard error, upgrading the deprecation into removal of
+ this (mis)feature. That is now scheduled to happen in the upcoming
+ release.
+
+ * Git now avoids blindly falling back to ".git" when the setup
+ sequence said we are _not_ in Git repository. A corner case that
+ happens to work right now may be broken by a call to die("BUG").
+ We've tried hard to locate such cases and fixed them, but there
+ might still be cases that need to be addressed--bug reports are
+ greatly appreciated.
+
+
+Updates since v2.14
+-------------------
+
+UI, Workflows & Features
+
+ * An example that is now obsolete has been removed from a sample hook,
+ and an old example in it that added a sign-off manually has been
+ improved to use the interpret-trailers command.
+
+ * The advice message given when "git rebase" stops for conflicting
+ changes has been improved.
+
+ * The "rerere-train" script (in contrib/) learned the "--overwrite"
+ option to allow overwriting existing recorded resolutions.
+
+ * "git contacts" (in contrib/) now lists the address on the
+ "Reported-by:" trailer to its output, in addition to those on
+ S-o-b: and other trailers, to make it easier to notify (and thank)
+ the original bug reporter.
+
+ * "git rebase", especially when it is run by mistake and ends up
+ trying to replay many changes, spent long time in silence. The
+ command has been taught to show progress report when it spends
+ long time preparing these many changes to replay (which would give
+ the user a chance to abort with ^C).
+
+ * "git merge" learned a "--signoff" option to add the Signed-off-by:
+ trailer with the committer's name.
+
+ * "git diff" learned to optionally paint new lines that are the same
+ as deleted lines elsewhere differently from genuinely new lines.
+
+ * "git interpret-trailers" learned to take the trailer specifications
+ from the command line that overrides the configured values.
+
+ * "git interpret-trailers" has been taught a "--parse" and a few
+ other options to make it easier for scripts to grab existing
+ trailer lines from a commit log message.
+
+ * "gitweb" shows a link to visit the 'raw' contents of blbos in the
+ history overview page.
+
+ * "[gc] rerereResolved = 5.days" used to be invalid, as the variable
+ is defined to take an integer counting the number of days. It now
+ is allowed.
+
+ * The code to acquire a lock on a reference (e.g. while accepting a
+ push from a client) used to immediately fail when the reference is
+ already locked---now it waits for a very short while and retries,
+ which can make it succeed if the lock holder was holding it during
+ a read-only operation.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Conversion from uchar[20] to struct object_id continues.
+
+ * Start using selected c99 constructs in small, stable and
+ essentialpart of the system to catch people who care about
+ older compilers that do not grok them.
+
+ * The filter-process interface learned to allow a process with long
+ latency give a "delayed" response.
+
+ * Many uses of comparision callback function the hashmap API uses
+ cast the callback function type when registering it to
+ hashmap_init(), which defeats the compile time type checking when
+ the callback interface changes (e.g. gaining more parameters).
+ The callback implementations have been updated to take "void *"
+ pointers and cast them to the type they expect instead.
+
+ * Because recent Git for Windows do come with a real msgfmt, the
+ build procedure for git-gui has been updated to use it instead of a
+ hand-rolled substitute.
+
+ * "git grep --recurse-submodules" has been reworked to give a more
+ consistent output across submodule boundary (and do its thing
+ without having to fork a separate process).
+
+ * A helper function to read a single whole line into strbuf
+ mistakenly triggered OOM error at EOF under certain conditions,
+ which has been fixed.
+ (merge 642956cf45 rs/strbuf-getwholeline-fix later to maint).
+
+ * The "ref-store" code reorganization continues.
+
+ * "git commit" used to discard the index and re-read from the filesystem
+ just in case the pre-commit hook has updated it in the middle; this
+ has been optimized out when we know we do not run the pre-commit hook.
+ (merge 680ee550d7 kw/commit-keep-index-when-pre-commit-is-not-run later to maint).
+
+ * Updates to the HTTP layer we made recently unconditionally used
+ features of libCurl without checking the existence of them, causing
+ compilation errors, which has been fixed. Also migrate the code to
+ check feature macros, not version numbers, to cope better with
+ libCurl that vendor ships with backported features.
+
+ * The API to start showing progress meter after a short delay has
+ been simplified.
+ (merge 8aade107dd jc/simplify-progress later to maint).
+
+ * Code clean-up to avoid mixing values read from the .gitmodules file
+ and values read from the .git/config file.
+
+ * We used to spend more than necessary cycles allocating and freeing
+ piece of memory while writing each index entry out. This has been
+ optimized.
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.14
+-----------------
+
+ * "%C(color name)" in the pretty print format always produced ANSI
+ color escape codes, which was an early design mistake. They now
+ honor the configuration (e.g. "color.ui = never") and also tty-ness
+ of the output medium.
+
+ * The http.{sslkey,sslCert} configuration variables are to be
+ interpreted as a pathname that honors "~[username]/" prefix, but
+ weren't, which has been fixed.
+
+ * Numerous bugs in walking of reflogs via "log -g" and friends have
+ been fixed.
+
+ * "git commit" when seeing an totally empty message said "you did not
+ edit the message", which is clearly wrong. The message has been
+ corrected.
+
+ * When a directory is not readable, "gitweb" fails to build the
+ project list. Work this around by skipping such a directory.
+
+ * Some versions of GnuPG fails to kill gpg-agent it auto-spawned
+ and such a left-over agent can interfere with a test. Work it
+ around by attempting to kill one before starting a new test.
+ (merge 29ff1f8f74 st/lib-gpg-kill-stray-agent later to maint).
+
+ * A recently added test for the "credential-cache" helper revealed
+ that EOF detection done around the time the connection to the cache
+ daemon is torn down were flaky. This was fixed by reacting to
+ ECONNRESET and behaving as if we got an EOF.
+
+ * "git log --tag=no-such-tag" showed log starting from HEAD, which
+ has been fixed---it now shows nothing.
+ (merge 5d34d1ac06 jk/rev-list-empty-input later to maint).
+
+ * The "tag.pager" configuration variable was useless for those who
+ actually create tag objects, as it interfered with the use of an
+ editor. A new mechanism has been introduced for commands to enable
+ pager depending on what operation is being carried out to fix this,
+ and then "git tag -l" is made to run pager by default.
+ (merge 595d59e2b5 ma/pager-per-subcommand-action later to maint).
+
+ * "git push --recurse-submodules $there HEAD:$target" was not
+ propagated down to the submodules, but now it is.
+ (merge c7be7201a7 bw/push-options-recursively-to-submodules later to maint).
+
+ * Commands like "git rebase" accepted the --rerere-autoupdate option
+ from the command line, but did not always use it. This has been
+ fixed.
+ (merge f826fb799e pw/sequence-rerere-autoupdate later to maint).
+
+ * "git clone --recurse-submodules --quiet" did not pass the quiet
+ option down to submodules.
+ (merge 03c004c581 bw/clone-recursive-quiet later to maint).
+
+ * Test portability fix for OBSD.
+ (merge bed67874e2 rs/obsd-getcwd-workaround later to maint).
+ (merge 4c7fda8fc1 rs/t4062-obsd later to maint).
+
+ * Portability fix for OBSD.
+ (merge 29c2eda80b rs/in-obsd-basename-dirname-take-const later to maint).
+
+ * "git am -s" has been taught that some input may end with a trailer
+ block that is not Signed-off-by: and it should refrain from adding
+ an extra blank line before adding a new sign-off in such a case.
+ (merge 735285b403 pw/am-signoff later to maint).
+
+ * "git svn" used with "--localtime" option did not compute the tz
+ offset for the timestamp in question and instead always used the
+ current time, which has been corrected.
+ (merge 1adc4b9a58 ur/svn-local-zone later to maint).
+
+ * Memory leak in an error codepath has been plugged.
+ (merge 83cd6f9017 rs/fsck-obj-leakfix later to maint).
+ (merge 896dca3ab7 rs/unpack-entry-leakfix later to maint).
+ (merge 149d8cbb2e rs/win32-syslog-leakfix later to maint).
+
+ * "git stash -u" used the contents of the committed version of the
+ ".gitignore" file to decide which paths are ignored, even when the
+ file has local changes. The command has been taught to instead use
+ the locally modified contents.
+
+ * bash 4.4 or newer gave a warning on NUL byte in command
+ substitution done in "git stash"; this has been squelched.
+ (merge 5fc92f8828 kd/stash-with-bash-4.4 later to maint).
+
+ * "git grep -L" and "git grep --quiet -L" reported different exit
+ codes; this has been corrected.
+ (merge e1f68c66d5 as/grep-quiet-no-match-exit-code-fix later to maint).
+
+ * When handshake with a subprocess filter notices that the process
+ asked for an unknown capability, Git did not report what program
+ the offending subprocess was running. This has been corrected.
+ (merge d3ba566342 cc/subprocess-handshake-missing-capabilities later to maint).
+
+ * "git apply" that is used as a better "patch -p1" failed to apply a
+ taken from a file with CRLF line endings to a file with CRLF line
+ endings. The root cause was because it misused convert_to_git()
+ that tried to do "safe-crlf" processing by looking at the index
+ entry at the same path, which is a nonsense---in that mode, "apply"
+ is not working on the data in (or derived from) the index at all.
+ This has been fixed.
+ (merge c24f3abace tb/apply-with-crlf later to maint).
+
+ * Killing "git merge --edit" before the editor returns control left
+ the repository in a state with MERGE_MSG but without MERGE_HEAD,
+ which incorrectly tells the subsequent "git commit" that there was
+ a squash merge in progress. This has been fixed.
+ (merge 9d89b35526 mg/killed-merge later to maint).
+
+ * Other minor doc, test and build updates and code cleanups.
+ (merge dff2813391 ab/ref-filter-no-contains later to maint).
+ (merge f094b89a4d ma/parse-maybe-bool later to maint).
+ (merge 974ce8078c mf/no-dashed-subcommands later to maint).
+ (merge f81935cc4d jc/perl-git-comment-typofix later to maint).
+ (merge 57ea241ef0 rs/t3700-clean-leftover later to maint).
+ (merge 39b00fa4d4 jk/drop-sha1-entry-pos later to maint).
+ (merge 0b006014c8 jk/hashcmp-memcmp later to maint).
+ (merge 1e22a9917b rj/add-chmod-error-message later to maint).
+ (merge 881529c846 rs/apply-lose-prefix-length later to maint).
+ (merge 6355a76802 rs/find-pack-entry-bisection later to maint).
+ (merge de3ce210ed rs/merge-microcleanup later to maint).
+ (merge 7f0a02be2f ah/doc-empty-string-is-false later to maint).
+ (merge 70ec6bd63b rs/t1002-do-not-use-sum later to maint).
+ (merge 2456990dfd sb/sha1-file-cleanup later to maint).
+ (merge 2aac933c62 hv/t5526-andand-chain-fix later to maint).
+ (merge c8d0c4fe9b sb/submodule-parallel-update later to maint).
+ (merge 794b7e1674 mg/format-ref-doc-fix later to maint).
+ (merge 24da8a26a9 rs/commit-h-single-parent-cleanup later to maint).
+ (merge 4e36907fa3 jk/doc-the-this later to maint).
+ (merge 15d1d0951e bc/vcs-svn-cleanup later to maint).
+ (merge b8f43b120b jn/vcs-svn-cleanup later to maint).
+ (merge 4a4becfb23 jt/doc-pack-objects-fix later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index d5c9c4cab6..dc4e3f58a2 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -216,15 +216,15 @@ boolean::
synonyms are accepted for 'true' and 'false'; these are all
case-insensitive.
- true;; Boolean true can be spelled as `yes`, `on`, `true`,
- or `1`. Also, a variable defined without `= <value>`
+ true;; Boolean true literals are `yes`, `on`, `true`,
+ and `1`. Also, a variable defined without `= <value>`
is taken as true.
- false;; Boolean false can be spelled as `no`, `off`,
- `false`, or `0`.
+ false;; Boolean false literals are `no`, `off`, `false`,
+ `0` and the empty string.
+
When converting value to the canonical form using `--bool` type
-specifier; 'git config' will ensure that the output is "true" or
+specifier, 'git config' will ensure that the output is "true" or
"false" (spelled in lowercase).
integer::
@@ -776,6 +776,12 @@ core.commentChar::
If set to "auto", `git-commit` would select a character that is not
the beginning character of any line in existing commit messages.
+core.filesRefLockTimeout::
+ The length of time, in milliseconds, to retry when trying to
+ lock an individual reference. Value 0 means not to retry at
+ all; -1 means to try indefinitely. Default is 100 (i.e.,
+ retry for 100ms).
+
core.packedRefsTimeout::
The length of time, in milliseconds, to retry when trying to
lock the `packed-refs` file. Value 0 means not to retry at
@@ -1077,14 +1083,25 @@ This does not affect linkgit:git-format-patch[1] or the
'git-diff-{asterisk}' plumbing commands. Can be overridden on the
command line with the `--color[=<when>]` option.
+diff.colorMoved::
+ If set to either a valid `<mode>` or a true value, moved lines
+ in a diff are colored differently, for details of valid modes
+ see '--color-moved' in linkgit:git-diff[1]. If simply set to
+ true the default color mode will be used. When set to false,
+ moved lines are not colored.
+
color.diff.<slot>::
Use customized color for diff colorization. `<slot>` specifies
which part of the patch to use the specified color, and is one
of `context` (context text - `plain` is a historical synonym),
`meta` (metainformation), `frag`
(hunk header), 'func' (function in hunk header), `old` (removed lines),
- `new` (added lines), `commit` (commit headers), or `whitespace`
- (highlighting whitespace errors).
+ `new` (added lines), `commit` (commit headers), `whitespace`
+ (highlighting whitespace errors), `oldMoved` (deleted lines),
+ `newMoved` (added lines), `oldMovedDimmed`, `oldMovedAlternative`,
+ `oldMovedAlternativeDimmed`, `newMovedDimmed`, `newMovedAlternative`
+ and `newMovedAlternativeDimmed` (See the '<mode>'
+ setting of '--color-moved' in linkgit:git-diff[1] for details).
color.decorate.<slot>::
Use customized color for 'git log --decorate' output. `<slot>` is one
@@ -1553,11 +1570,13 @@ gc.<pattern>.reflogExpireUnreachable::
gc.rerereResolved::
Records of conflicted merge you resolved earlier are
kept for this many days when 'git rerere gc' is run.
+ You can also use more human-readable "1.month.ago", etc.
The default is 60 days. See linkgit:git-rerere[1].
gc.rerereUnresolved::
Records of conflicted merge you have not resolved are
kept for this many days when 'git rerere gc' is run.
+ You can also use more human-readable "1.month.ago", etc.
The default is 15 days. See linkgit:git-rerere[1].
gitcvs.commitMsgAnnotation::
@@ -2912,8 +2931,8 @@ sendemail.smtpsslcertpath::
sendemail.<identity>.*::
Identity-specific versions of the 'sendemail.*' parameters
- found below, taking precedence over those when the this
- identity is selected, through command-line or
+ found below, taking precedence over those when this
+ identity is selected, through either the command-line or
`sendemail.identity`.
sendemail.aliasesFile::
diff --git a/Documentation/diff-config.txt b/Documentation/diff-config.txt
index cbce8ec638..5ca942ab5e 100644
--- a/Documentation/diff-config.txt
+++ b/Documentation/diff-config.txt
@@ -200,7 +200,10 @@ diff.algorithm::
+
diff.wsErrorHighlight::
- A comma separated list of `old`, `new`, `context`, that
- specifies how whitespace errors on lines are highlighted
- with `color.diff.whitespace`. Can be overridden by the
- command line option `--ws-error-highlight=<kind>`
+ Highlight whitespace errors in the `context`, `old` or `new`
+ lines of the diff. Multiple values are separated by comma,
+ `none` resets previous values, `default` reset the list to
+ `new` and `all` is a shorthand for `old,new,context`. The
+ whitespace errors are colored with `color.diff.whitespace`.
+ The command line option `--ws-error-highlight=<kind>`
+ overrides this setting.
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 43d18a4c5c..a88c76741e 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -231,6 +231,40 @@ ifdef::git-diff[]
endif::git-diff[]
It is the same as `--color=never`.
+--color-moved[=<mode>]::
+ Moved lines of code are colored differently.
+ifdef::git-diff[]
+ It can be changed by the `diff.colorMoved` configuration setting.
+endif::git-diff[]
+ The <mode> defaults to 'no' if the option is not given
+ and to 'zebra' if the option with no mode is given.
+ The mode must be one of:
++
+--
+no::
+ Moved lines are not highlighted.
+default::
+ Is a synonym for `zebra`. This may change to a more sensible mode
+ in the future.
+plain::
+ Any line that is added in one location and was removed
+ in another location will be colored with 'color.diff.newMoved'.
+ Similarly 'color.diff.oldMoved' will be used for removed lines
+ that are added somewhere else in the diff. This mode picks up any
+ moved line, but it is not very useful in a review to determine
+ if a block of code was moved without permutation.
+zebra::
+ Blocks of moved text of at least 20 alphanumeric characters
+ are detected greedily. The detected blocks are
+ painted using either the 'color.diff.{old,new}Moved' color or
+ 'color.diff.{old,new}MovedAlternative'. The change between
+ the two colors indicates that a new block was detected.
+dimmed_zebra::
+ Similar to 'zebra', but additional dimming of uninteresting parts
+ of moved code is performed. The bordering lines of two adjacent
+ blocks are considered interesting, the rest is uninteresting.
+--
+
--word-diff[=<mode>]::
Show a word diff, using the <mode> to delimit changed words.
By default, words are delimited by whitespace; see
@@ -300,15 +334,14 @@ ifndef::git-format-patch[]
with --exit-code.
--ws-error-highlight=<kind>::
- Highlight whitespace errors on lines specified by <kind>
- in the color specified by `color.diff.whitespace`. <kind>
- is a comma separated list of `old`, `new`, `context`. When
- this option is not given, only whitespace errors in `new`
- lines are highlighted. E.g. `--ws-error-highlight=new,old`
- highlights whitespace errors on both deleted and added lines.
- `all` can be used as a short-hand for `old,new,context`.
- The `diff.wsErrorHighlight` configuration variable can be
- used to specify the default behaviour.
+ Highlight whitespace errors in the `context`, `old` or `new`
+ lines of the diff. Multiple values are separated by comma,
+ `none` resets previous values, `default` reset the list to
+ `new` and `all` is a shorthand for `old,new,context`. When
+ this option is not given, and the configuration variable
+ `diff.wsErrorHighlight` is not set, only whitespace errors in
+ `new` lines are highlighted. The whitespace errors are colored
+ whith `color.diff.whitespace`.
endif::git-format-patch[]
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 81bd0a7b77..e292737b9c 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -195,10 +195,8 @@ start-point is either a local or remote-tracking branch.
branch.autoSetupMerge configuration variable is true.
--set-upstream::
- If specified branch does not exist yet or if `--force` has been
- given, acts exactly like `--track`. Otherwise sets up configuration
- like `--track` would when creating the branch, except that where
- branch points to is not changed.
+ As this option had confusing syntax, it is no longer supported.
+ Please use `--track` or `--set-upstream-to` instead.
-u <upstream>::
--set-upstream-to=<upstream>::
@@ -267,8 +265,8 @@ start-point is either a local or remote-tracking branch.
Only list branches of the given object.
--format <format>::
- A string that interpolates `%(fieldname)` from the object
- pointed at by a ref being shown. The format is the same as
+ A string that interpolates `%(fieldname)` from a branch ref being shown
+ and the object it points at. The format is the same as
that of linkgit:git-for-each-ref[1].
Examples
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 03e187a105..bb370c9c7b 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -38,11 +38,12 @@ OPTIONS
key.
<format>::
- A string that interpolates `%(fieldname)` from the
- object pointed at by a ref being shown. If `fieldname`
+ A string that interpolates `%(fieldname)` from a ref being shown
+ and the object it points at. If `fieldname`
is prefixed with an asterisk (`*`) and the ref points
- at a tag object, the value for the field in the object
- tag refers is used. When unspecified, defaults to
+ at a tag object, use the value for the field in the object
+ which the tag object refers to (instead of the field in the tag object).
+ When unspecified, `<format>` defaults to
`%(objectname) SPC %(objecttype) TAB %(refname)`.
It also interpolates `%%` to `%`, and `%xx` where `xx`
are hex digits interpolates to character with hex code
@@ -156,8 +157,10 @@ HEAD::
otherwise.
color::
- Change output color. Followed by `:<colorname>`, where names
- are described in `color.branch.*`.
+ Change output color. Followed by `:<colorname>`, where color
+ names are described under Values in the "CONFIGURATION FILE"
+ section of linkgit:git-config[1]. For example,
+ `%(color:bold red)`.
align::
Left-, middle-, or right-align the content between
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index c890328b02..6cbe462a77 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -23,6 +23,7 @@ SYNOPSIS
[(--reroll-count|-v) <n>]
[--to=<email>] [--cc=<email>]
[--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
+ [--progress]
[<common diff options>]
[ <since> | <revision range> ]
@@ -283,6 +284,9 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
range are always formatted as creation patches, independently
of this flag.
+--progress::
+ Show progress reports on stderr as patches are generated.
+
CONFIGURATION
-------------
You can specify extra mail header lines to be added to each message,
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index 5033483db4..720c7850e2 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -95,13 +95,6 @@ OPTIONS
<tree> option the prefix of all submodule output will be the name of
the parent project's <tree> object.
---parent-basename <basename>::
- For internal use only. In order to produce uniform output with the
- --recurse-submodules option, this option can be used to provide the
- basename of a parent's <tree> object to a submodule so the submodule
- can prefix its output with the parent's name rather than the SHA1 of
- the submodule.
-
-a::
--text::
Process binary files as if they were text.
diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 31cdeaecdf..9dd19a1dd9 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -3,24 +3,27 @@ git-interpret-trailers(1)
NAME
----
-git-interpret-trailers - help add structured information into commit messages
+git-interpret-trailers - add or parse structured information in commit messages
SYNOPSIS
--------
[verse]
-'git interpret-trailers' [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
+'git interpret-trailers' [options] [(--trailer <token>[(=|:)<value>])...] [<file>...]
+'git interpret-trailers' [options] [--parse] [<file>...]
DESCRIPTION
-----------
-Help adding 'trailers' lines, that look similar to RFC 822 e-mail
+Help parsing or adding 'trailers' lines, that look similar to RFC 822 e-mail
headers, at the end of the otherwise free-form part of a commit
message.
This command reads some patches or commit messages from either the
-<file> arguments or the standard input if no <file> is specified. Then
-this command applies the arguments passed using the `--trailer`
-option, if any, to the commit message part of each input file. The
-result is emitted on the standard output.
+<file> arguments or the standard input if no <file> is specified. If
+`--parse` is specified, the output consists of the parsed trailers.
+
+Otherwise, this command applies the arguments passed using the
+`--trailer` option, if any, to the commit message part of each input
+file. The result is emitted on the standard output.
Some configuration variables control the way the `--trailer` arguments
are applied to each commit message and the way any existing trailer in
@@ -80,6 +83,45 @@ OPTIONS
trailer to the input messages. See the description of this
command.
+--where <placement>::
+--no-where::
+ Specify where all new trailers will be added. A setting
+ provided with '--where' overrides all configuration variables
+ and applies to all '--trailer' options until the next occurrence of
+ '--where' or '--no-where'.
+
+--if-exists <action>::
+--no-if-exists::
+ Specify what action will be performed when there is already at
+ least one trailer with the same <token> in the message. A setting
+ provided with '--if-exists' overrides all configuration variables
+ and applies to all '--trailer' options until the next occurrence of
+ '--if-exists' or '--no-if-exists'.
+
+--if-missing <action>::
+--no-if-missing::
+ Specify what action will be performed when there is no other
+ trailer with the same <token> in the message. A setting
+ provided with '--if-missing' overrides all configuration variables
+ and applies to all '--trailer' options until the next occurrence of
+ '--if-missing' or '--no-if-missing'.
+
+--only-trailers::
+ Output only the trailers, not any other parts of the input.
+
+--only-input::
+ Output only trailers that exist in the input; do not add any
+ from the command-line or by following configured `trailer.*`
+ rules.
+
+--unfold::
+ Remove any whitespace-continuation in trailers, so that each
+ trailer appears on a line by itself with its full content.
+
+--parse::
+ A convenience alias for `--only-trailers --only-input
+ --unfold`.
+
CONFIGURATION VARIABLES
-----------------------
@@ -170,8 +212,8 @@ trailer.<token>.where::
configuration variable and it overrides what is specified by
that option for trailers with the specified <token>.
-trailer.<token>.ifexist::
- This option takes the same values as the 'trailer.ifexist'
+trailer.<token>.ifexists::
+ This option takes the same values as the 'trailer.ifexists'
configuration variable and it overrides what is specified by
that option for trailers with the specified <token>.
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 04fdd8cf08..615e6bacde 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -64,6 +64,14 @@ OPTIONS
-------
include::merge-options.txt[]
+--signoff::
+ Add Signed-off-by line by the committer at the end of the commit
+ log message. The meaning of a signoff depends on the project,
+ but it typically certifies that committer has
+ the rights to submit this work under the same license and
+ agrees to a Developer Certificate of Origin
+ (see http://developercertificate.org/ for more information).
+
-S[<keyid>]::
--gpg-sign[=<keyid>]::
GPG-sign the resulting merge commit. The `keyid` argument is
@@ -280,7 +288,10 @@ After seeing a conflict, you can do two things:
* Resolve the conflicts. Git will mark the conflicts in
the working tree. Edit the files into shape and
- 'git add' them to the index. Use 'git commit' to seal the deal.
+ 'git add' them to the index. Use 'git commit' or
+ 'git merge --continue' to seal the deal. The latter command
+ checks whether there is a (interrupted) merge in progress
+ before calling 'git commit'.
You can work through the conflict with a number of tools:
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 8973510a41..473a16135a 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -18,8 +18,9 @@ SYNOPSIS
DESCRIPTION
-----------
-Reads list of objects from the standard input, and writes a packed
-archive with specified base-name, or to the standard output.
+Reads list of objects from the standard input, and writes either one or
+more packed archives with the specified base-name to disk, or a packed
+archive to the standard output.
A packed archive is an efficient way to transfer a set of objects
between two repositories as well as an access efficient archival
@@ -47,9 +48,9 @@ transport by their peers.
OPTIONS
-------
base-name::
- Write into a pair of files (.pack and .idx), using
+ Write into pairs of files (.pack and .idx), using
<base-name> to determine the name of the created file.
- When this option is used, the two files are written in
+ When this option is used, the two files in a pair are written in
<base-name>-<SHA-1>.{pack,idx} files. <SHA-1> is a hash
based on the pack content and is written to the standard
output of the command.
@@ -108,9 +109,13 @@ base-name::
is taken from the `pack.windowMemory` configuration variable.
--max-pack-size=<n>::
- Maximum size of each output pack file. The size can be suffixed with
+ In unusual scenarios, you may not be able to create files
+ larger than a certain size on your filesystem, and this option
+ can be used to tell the command to split the output packfile
+ into multiple independent packfiles, each not larger than the
+ given size. The size can be suffixed with
"k", "m", or "g". The minimum size allowed is limited to 1 MiB.
- If specified, multiple packfiles may be created, which also
+ This option
prevents the creation of a bitmap index.
The default is unlimited, unless the config variable
`pack.packSizeLimit` is set.
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 0a639664fd..3e76e99f38 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -12,7 +12,7 @@ SYNOPSIS
'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
[-u | --set-upstream] [--push-option=<string>]
- [--[no-]signed|--sign=(true|false|if-asked)]
+ [--[no-]signed|--signed=(true|false|if-asked)]
[--force-with-lease[=<refname>[:<expect>]]]
[--no-verify] [<repository> [<refspec>...]]
@@ -141,7 +141,7 @@ already exists on the remote side.
information, see `push.followTags` in linkgit:git-config[1].
--[no-]signed::
---sign=(true|false|if-asked)::
+--signed=(true|false|if-asked)::
GPG-sign the push request to update refs on the receiving
side, to allow it to be checked by the hooks and/or be
logged. If `false` or `--no-signed`, no signing will be
diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt
index 966abb0df8..f51c64939b 100644
--- a/Documentation/git-send-pack.txt
+++ b/Documentation/git-send-pack.txt
@@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>]
[--verbose] [--thin] [--atomic]
- [--[no-]signed|--sign=(true|false|if-asked)]
+ [--[no-]signed|--signed=(true|false|if-asked)]
[<host>:]<directory> [<ref>...]
DESCRIPTION
@@ -71,7 +71,7 @@ be in a separate packet, and the list must end with a flush packet.
refs.
--[no-]signed::
---sign=(true|false|if-asked)::
+--signed=(true|false|if-asked)::
GPG-sign the push request to update refs on the receiving
side, to allow it to be checked by the hooks and/or be
logged. If `false` or `--no-signed`, no signing will be
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 1eb15afa1c..543fb425ee 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -188,8 +188,8 @@ This option is only applicable when listing tags without annotation lines.
Defaults to HEAD.
<format>::
- A string that interpolates `%(fieldname)` from the object
- pointed at by a ref being shown. The format is the same as
+ A string that interpolates `%(fieldname)` from a tag ref being shown
+ and the object it points at. The format is the same as
that of linkgit:git-for-each-ref[1]. When unspecified,
defaults to `%(refname:strip=2)`.
@@ -205,6 +205,9 @@ it in the repository configuration as follows:
signingKey = <gpg-keyid>
-------------------------------------
+`pager.tag` is only respected when listing tags, i.e., when `-l` is
+used or implied. The default is to use a pager.
+See linkgit:git-config[1].
DISCUSSION
----------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 7dd5e03280..6e3a6767e5 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -75,7 +75,8 @@ example the following invocations are equivalent:
Note that omitting the `=` in `git -c foo.bar ...` is allowed and sets
`foo.bar` to the boolean true value (just like `[foo]bar` would in a
config file). Including the equals but with an empty value (like `git -c
-foo.bar= ...`) sets `foo.bar` to the empty string.
+foo.bar= ...`) sets `foo.bar` to the empty string which ` git config
+--bool` will convert to `false`.
--exec-path[=<path>]::
Path to wherever your core Git programs are installed.
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 2a2d7e2a4d..c4f2be2542 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -425,8 +425,8 @@ packet: git< capability=clean
packet: git< capability=smudge
packet: git< 0000
------------------------
-Supported filter capabilities in version 2 are "clean" and
-"smudge".
+Supported filter capabilities in version 2 are "clean", "smudge",
+and "delay".
Afterwards Git sends a list of "key=value" pairs terminated with
a flush packet. The list will contain at least the filter command
@@ -512,12 +512,73 @@ the protocol then Git will stop the filter process and restart it
with the next file that needs to be processed. Depending on the
`filter.<driver>.required` flag Git will interpret that as error.
-After the filter has processed a blob it is expected to wait for
-the next "key=value" list containing a command. Git will close
+After the filter has processed a command it is expected to wait for
+a "key=value" list containing the next command. Git will close
the command pipe on exit. The filter is expected to detect EOF
and exit gracefully on its own. Git will wait until the filter
process has stopped.
+Delay
+^^^^^
+
+If the filter supports the "delay" capability, then Git can send the
+flag "can-delay" after the filter command and pathname. This flag
+denotes that the filter can delay filtering the current blob (e.g. to
+compensate network latencies) by responding with no content but with
+the status "delayed" and a flush packet.
+------------------------
+packet: git> command=smudge
+packet: git> pathname=path/testfile.dat
+packet: git> can-delay=1
+packet: git> 0000
+packet: git> CONTENT
+packet: git> 0000
+packet: git< status=delayed
+packet: git< 0000
+------------------------
+
+If the filter supports the "delay" capability then it must support the
+"list_available_blobs" command. If Git sends this command, then the
+filter is expected to return a list of pathnames representing blobs
+that have been delayed earlier and are now available.
+The list must be terminated with a flush packet followed
+by a "success" status that is also terminated with a flush packet. If
+no blobs for the delayed paths are available, yet, then the filter is
+expected to block the response until at least one blob becomes
+available. The filter can tell Git that it has no more delayed blobs
+by sending an empty list. As soon as the filter responds with an empty
+list, Git stops asking. All blobs that Git has not received at this
+point are considered missing and will result in an error.
+
+------------------------
+packet: git> command=list_available_blobs
+packet: git> 0000
+packet: git< pathname=path/testfile.dat
+packet: git< pathname=path/otherfile.dat
+packet: git< 0000
+packet: git< status=success
+packet: git< 0000
+------------------------
+
+After Git received the pathnames, it will request the corresponding
+blobs again. These requests contain a pathname and an empty content
+section. The filter is expected to respond with the smudged content
+in the usual way as explained above.
+------------------------
+packet: git> command=smudge
+packet: git> pathname=path/testfile.dat
+packet: git> 0000
+packet: git> 0000 # empty content!
+packet: git< status=success
+packet: git< 0000
+packet: git< SMUDGED_CONTENT
+packet: git< 0000
+packet: git< 0000 # empty list, keep "status=success" unchanged!
+------------------------
+
+Example
+^^^^^^^
+
A long running filter demo implementation can be found in
`contrib/long-running-filter/example.pl` located in the Git
core repository. If you develop your own long running filter
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index b2514f4d44..623ed1a138 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -121,8 +121,8 @@ it is not suppressed by the `--no-verify` option. A non-zero exit
means a failure of the hook and aborts the commit. It should not
be used as replacement for pre-commit hook.
-The sample `prepare-commit-msg` hook that comes with Git comments
-out the `Conflicts:` part of a merge's commit message.
+The sample `prepare-commit-msg` hook that comes with Git removes the
+help message found in the commented portion of the commit template.
commit-msg
~~~~~~~~~~
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 4d6dac5770..d433d50f81 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -173,13 +173,17 @@ endif::git-rev-list[]
- '%Cblue': switch color to blue
- '%Creset': reset color
- '%C(...)': color specification, as described under Values in the
- "CONFIGURATION FILE" section of linkgit:git-config[1];
- adding `auto,` at the beginning (e.g. `%C(auto,red)`) will emit
- color only when colors are enabled for log output (by `color.diff`,
- `color.ui`, or `--color`, and respecting the `auto` settings of the
- former if we are going to a terminal). `auto` alone (i.e.
- `%C(auto)`) will turn on auto coloring on the next placeholders
- until the color is switched again.
+ "CONFIGURATION FILE" section of linkgit:git-config[1].
+ By default, colors are shown only when enabled for log output (by
+ `color.diff`, `color.ui`, or `--color`, and respecting the `auto`
+ settings of the former if we are going to a terminal). `%C(auto,...)`
+ is accepted as a historical synonym for the default (e.g.,
+ `%C(auto,red)`). Specifying `%C(always,...) will show the colors
+ even when color is not otherwise enabled (though consider
+ just using `--color=always` to enable color for the whole output,
+ including this format and anything else git might color). `auto`
+ alone (i.e. `%C(auto)`) will turn on auto coloring on the next
+ placeholders until the color is switched again.
- '%m': left (`<`), right (`>`) or boundary (`-`) mark
- '%n': newline
- '%%': a raw '%'
@@ -201,7 +205,10 @@ endif::git-rev-list[]
- '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'
respectively, but padding both sides (i.e. the text is centered)
- %(trailers): display the trailers of the body as interpreted by
- linkgit:git-interpret-trailers[1]
+ linkgit:git-interpret-trailers[1]. If the `:only` option is given,
+ omit non-trailer lines from the trailer block. If the `:unfold`
+ option is given, behave as if interpret-trailer's `--unfold` option
+ was given. E.g., `%(trailers:only:unfold)` to do both.
NOTE: Some placeholders may depend on other options given to the
revision traversal engine. For example, the `%g*` reflog options will
diff --git a/Documentation/technical/api-builtin.txt b/Documentation/technical/api-builtin.txt
deleted file mode 100644
index 22a39b9299..0000000000
--- a/Documentation/technical/api-builtin.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-builtin API
-===========
-
-Adding a new built-in
----------------------
-
-There are 4 things to do to add a built-in command implementation to
-Git:
-
-. Define the implementation of the built-in command `foo` with
- signature:
-
- int cmd_foo(int argc, const char **argv, const char *prefix);
-
-. Add the external declaration for the function to `builtin.h`.
-
-. Add the command to the `commands[]` table defined in `git.c`.
- The entry should look like:
-
- { "foo", cmd_foo, <options> },
-+
-where options is the bitwise-or of:
-
-`RUN_SETUP`::
- If there is not a Git directory to work on, abort. If there
- is a work tree, chdir to the top of it if the command was
- invoked in a subdirectory. If there is no work tree, no
- chdir() is done.
-
-`RUN_SETUP_GENTLY`::
- If there is a Git directory, chdir as per RUN_SETUP, otherwise,
- don't chdir anywhere.
-
-`USE_PAGER`::
-
- If the standard output is connected to a tty, spawn a pager and
- feed our output to it.
-
-`NEED_WORK_TREE`::
-
- Make sure there is a work tree, i.e. the command cannot act
- on bare repositories.
- This only makes sense when `RUN_SETUP` is also set.
-
-. Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`.
-
-Additionally, if `foo` is a new command, there are 3 more things to do:
-
-. Add tests to `t/` directory.
-
-. Write documentation in `Documentation/git-foo.txt`.
-
-. Add an entry for `git-foo` to `command-list.txt`.
-
-. Add an entry for `/git-foo` to `.gitignore`.
-
-
-How a built-in is called
-------------------------
-
-The implementation `cmd_foo()` takes three parameters, `argc`, `argv,
-and `prefix`. The first two are similar to what `main()` of a
-standalone command would be called with.
-
-When `RUN_SETUP` is specified in the `commands[]` table, and when you
-were started from a subdirectory of the work tree, `cmd_foo()` is called
-after chdir(2) to the top of the work tree, and `prefix` gets the path
-to the subdirectory the command started from. This allows you to
-convert a user-supplied pathname (typically relative to that directory)
-to a pathname relative to the top of the work tree.
-
-The return value from `cmd_foo()` becomes the exit status of the
-command.
diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt
index 20741f345e..7a83a3a6e2 100644
--- a/Documentation/technical/api-config.txt
+++ b/Documentation/technical/api-config.txt
@@ -187,6 +187,10 @@ Same as `git_config_bool`, except that integers are returned as-is, and
an `is_bool` flag is unset.
`git_config_maybe_bool`::
+Deprecated. Use `git_parse_maybe_bool` instead. They are exactly the
+same, except this function takes an unused argument `name`.
+
+`git_parse_maybe_bool`::
Same as `git_config_bool`, except that it returns -1 on error rather
than dying.
diff --git a/Documentation/technical/api-sub-process.txt b/Documentation/technical/api-sub-process.txt
deleted file mode 100644
index 793508cf3e..0000000000
--- a/Documentation/technical/api-sub-process.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-sub-process API
-===============
-
-The sub-process API makes it possible to run background sub-processes
-for the entire lifetime of a Git invocation. If Git needs to communicate
-with an external process multiple times, then this can reduces the process
-invocation overhead. Git and the sub-process communicate through stdin and
-stdout.
-
-The sub-processes are kept in a hashmap by command name and looked up
-via the subprocess_find_entry function. If an existing instance can not
-be found then a new process should be created and started. When the
-parent git command terminates, all sub-processes are also terminated.
-
-This API is based on the run-command API.
-
-Data structures
----------------
-
-* `struct subprocess_entry`
-
-The sub-process structure. Members should not be accessed directly.
-
-Types
------
-
-'int(*subprocess_start_fn)(struct subprocess_entry *entry)'::
-
- User-supplied function to initialize the sub-process. This is
- typically used to negotiate the interface version and capabilities.
-
-
-Functions
----------
-
-`cmd2process_cmp`::
-
- Function to test two subprocess hashmap entries for equality.
-
-`subprocess_start`::
-
- Start a subprocess and add it to the subprocess hashmap.
-
-`subprocess_stop`::
-
- Kill a subprocess and remove it from the subprocess hashmap.
-
-`subprocess_find_entry`::
-
- Find a subprocess in the subprocess hashmap.
-
-`subprocess_get_child_process`::
-
- Get the underlying `struct child_process` from a subprocess.
-
-`subprocess_read_status`::
-
- Helper function to read packets looking for the last "status=<foo>"
- key/value pair.
diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt
index 14af37c3f1..bde18622a8 100644
--- a/Documentation/technical/api-tree-walking.txt
+++ b/Documentation/technical/api-tree-walking.txt
@@ -55,9 +55,9 @@ Initializing
`fill_tree_descriptor`::
- Initialize a `tree_desc` and decode its first entry given the sha1 of
- a tree. Returns the `buffer` member if the sha1 is a valid tree
- identifier and NULL otherwise.
+ Initialize a `tree_desc` and decode its first entry given the
+ object ID of a tree. Returns the `buffer` member if the latter
+ is a valid tree identifier and NULL otherwise.
`setup_traverse_info`::
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 2388ba13ca..75beb2e775 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.14.1
+DEF_VER=v2.14.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 461c845d33..f2bb7f2f63 100644
--- a/Makefile
+++ b/Makefile
@@ -655,6 +655,7 @@ TEST_PROGRAMS_NEED_X += test-parse-options
TEST_PROGRAMS_NEED_X += test-path-utils
TEST_PROGRAMS_NEED_X += test-prio-queue
TEST_PROGRAMS_NEED_X += test-read-cache
+TEST_PROGRAMS_NEED_X += test-write-cache
TEST_PROGRAMS_NEED_X += test-ref-store
TEST_PROGRAMS_NEED_X += test-regex
TEST_PROGRAMS_NEED_X += test-revision-walking
@@ -816,6 +817,7 @@ LIB_OBJS += notes-merge.o
LIB_OBJS += notes-utils.o
LIB_OBJS += object.o
LIB_OBJS += oidset.o
+LIB_OBJS += packfile.o
LIB_OBJS += pack-bitmap.o
LIB_OBJS += pack-bitmap-write.o
LIB_OBJS += pack-check.o
@@ -842,6 +844,7 @@ LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
LIB_OBJS += refs/files-backend.o
LIB_OBJS += refs/iterator.o
+LIB_OBJS += refs/packed-backend.o
LIB_OBJS += refs/ref-cache.o
LIB_OBJS += ref-filter.o
LIB_OBJS += remote.o
@@ -2037,7 +2040,6 @@ XDIFF_OBJS += xdiff/xhistogram.o
VCSSVN_OBJS += vcs-svn/line_buffer.o
VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/repo_tree.o
VCSSVN_OBJS += vcs-svn/fast_export.o
VCSSVN_OBJS += vcs-svn/svndiff.o
VCSSVN_OBJS += vcs-svn/svndump.o
diff --git a/RelNotes b/RelNotes
index 86e33ed682..80ae7a3110 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.14.1.txt \ No newline at end of file
+Documentation/RelNotes/2.15.0.txt \ No newline at end of file
diff --git a/apply.c b/apply.c
index f2d599141d..86666217d4 100644
--- a/apply.c
+++ b/apply.c
@@ -80,7 +80,6 @@ int init_apply_state(struct apply_state *state,
{
memset(state, 0, sizeof(*state));
state->prefix = prefix;
- state->prefix_length = state->prefix ? strlen(state->prefix) : 0;
state->lock_file = lock_file;
state->newfd = -1;
state->apply = 1;
@@ -220,6 +219,7 @@ struct patch {
unsigned int recount:1;
unsigned int conflicted_threeway:1;
unsigned int direct_to_threeway:1;
+ unsigned int crlf_in_old:1;
struct fragment *fragments;
char *result;
size_t resultsize;
@@ -786,11 +786,11 @@ static int guess_p_value(struct apply_state *state, const char *nameline)
* Does it begin with "a/$our-prefix" and such? Then this is
* very likely to apply to our directory.
*/
- if (!strncmp(name, state->prefix, state->prefix_length))
+ if (starts_with(name, state->prefix))
val = count_slashes(state->prefix);
else {
cp++;
- if (!strncmp(cp, state->prefix, state->prefix_length))
+ if (starts_with(cp, state->prefix))
val = count_slashes(state->prefix) + 1;
}
}
@@ -1663,6 +1663,19 @@ static void check_whitespace(struct apply_state *state,
}
/*
+ * Check if the patch has context lines with CRLF or
+ * the patch wants to remove lines with CRLF.
+ */
+static void check_old_for_crlf(struct patch *patch, const char *line, int len)
+{
+ if (len >= 2 && line[len-1] == '\n' && line[len-2] == '\r') {
+ patch->ws_rule |= WS_CR_AT_EOL;
+ patch->crlf_in_old = 1;
+ }
+}
+
+
+/*
* Parse a unified diff. Note that this really needs to parse each
* fragment separately, since the only way to know the difference
* between a "---" that is part of a patch, and a "---" that starts
@@ -1712,11 +1725,14 @@ static int parse_fragment(struct apply_state *state,
if (!deleted && !added)
leading++;
trailing++;
+ check_old_for_crlf(patch, line, len);
if (!state->apply_in_reverse &&
state->ws_error_action == correct_ws_error)
check_whitespace(state, line, len, patch->ws_rule);
break;
case '-':
+ if (!state->apply_in_reverse)
+ check_old_for_crlf(patch, line, len);
if (state->apply_in_reverse &&
state->ws_error_action != nowarn_ws_error)
check_whitespace(state, line, len, patch->ws_rule);
@@ -1725,6 +1741,8 @@ static int parse_fragment(struct apply_state *state,
trailing = 0;
break;
case '+':
+ if (state->apply_in_reverse)
+ check_old_for_crlf(patch, line, len);
if (!state->apply_in_reverse &&
state->ws_error_action != nowarn_ws_error)
check_whitespace(state, line, len, patch->ws_rule);
@@ -2089,10 +2107,9 @@ static int use_patch(struct apply_state *state, struct patch *p)
int i;
/* Paths outside are not touched regardless of "--include" */
- if (0 < state->prefix_length) {
- int pathlen = strlen(pathname);
- if (pathlen <= state->prefix_length ||
- memcmp(state->prefix, pathname, state->prefix_length))
+ if (state->prefix && *state->prefix) {
+ const char *rest;
+ if (!skip_prefix(pathname, state->prefix, &rest) || !*rest)
return 0;
}
@@ -2268,8 +2285,11 @@ static void show_stats(struct apply_state *state, struct patch *patch)
add, pluses, del, minuses);
}
-static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
+static int read_old_data(struct stat *st, struct patch *patch,
+ const char *path, struct strbuf *buf)
{
+ enum safe_crlf safe_crlf = patch->crlf_in_old ?
+ SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
switch (st->st_mode & S_IFMT) {
case S_IFLNK:
if (strbuf_readlink(buf, path, st->st_size) < 0)
@@ -2278,7 +2298,15 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
case S_IFREG:
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
return error(_("unable to open or read %s"), path);
- convert_to_git(&the_index, path, buf->buf, buf->len, buf, 0);
+ /*
+ * "git apply" without "--index/--cached" should never look
+ * at the index; the target file may not have been added to
+ * the index yet, and we may not even be in any Git repository.
+ * Pass NULL to convert_to_git() to stress this; the function
+ * should never look at the index when explicit crlf option
+ * is given.
+ */
+ convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
return 0;
default:
return -1;
@@ -2809,13 +2837,10 @@ static void update_image(struct apply_state *state,
img->line_allocated = img->line;
}
if (preimage_limit != postimage->nr)
- memmove(img->line + applied_pos + postimage->nr,
- img->line + applied_pos + preimage_limit,
- (img->nr - (applied_pos + preimage_limit)) *
- sizeof(*img->line));
- memcpy(img->line + applied_pos,
- postimage->line,
- postimage->nr * sizeof(*img->line));
+ MOVE_ARRAY(img->line + applied_pos + postimage->nr,
+ img->line + applied_pos + preimage_limit,
+ img->nr - (applied_pos + preimage_limit));
+ COPY_ARRAY(img->line + applied_pos, postimage->line, postimage->nr);
if (!state->allow_overlap)
for (i = 0; i < postimage->nr; i++)
img->line[applied_pos + i].flag |= LINE_PATCHED;
@@ -3384,6 +3409,7 @@ static int load_patch_target(struct apply_state *state,
struct strbuf *buf,
const struct cache_entry *ce,
struct stat *st,
+ struct patch *patch,
const char *name,
unsigned expected_mode)
{
@@ -3399,7 +3425,7 @@ static int load_patch_target(struct apply_state *state,
} else if (has_symlink_leading_path(name, strlen(name))) {
return error(_("reading from '%s' beyond a symbolic link"), name);
} else {
- if (read_old_data(st, name, buf))
+ if (read_old_data(st, patch, name, buf))
return error(_("failed to read %s"), name);
}
}
@@ -3432,7 +3458,7 @@ static int load_preimage(struct apply_state *state,
/* We have a patched copy in memory; use that. */
strbuf_add(&buf, previous->result, previous->resultsize);
} else {
- status = load_patch_target(state, &buf, ce, st,
+ status = load_patch_target(state, &buf, ce, st, patch,
patch->old_name, patch->old_mode);
if (status < 0)
return status;
@@ -3520,7 +3546,7 @@ static int load_current(struct apply_state *state,
if (verify_index_match(ce, &st))
return error(_("%s: does not match index"), name);
- status = load_patch_target(state, &buf, ce, &st, name, mode);
+ status = load_patch_target(state, &buf, ce, &st, patch, name, mode);
if (status < 0)
return status;
else if (status)
@@ -3551,7 +3577,7 @@ static int try_threeway(struct apply_state *state,
/* Preimage the patch was prepared for */
if (patch->is_new)
write_sha1_file("", 0, blob_type, pre_oid.hash);
- else if (get_sha1(patch->old_sha1_prefix, pre_oid.hash) ||
+ else if (get_oid(patch->old_sha1_prefix, &pre_oid) ||
read_blob_object(&buf, &pre_oid, patch->old_mode))
return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
@@ -4075,7 +4101,7 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list)
else
return error(_("sha1 information is lacking or "
"useless for submodule %s"), name);
- } else if (!get_sha1_blob(patch->old_sha1_prefix, oid.hash)) {
+ } else if (!get_oid_blob(patch->old_sha1_prefix, &oid)) {
; /* ok */
} else if (!patch->lines_added && !patch->lines_deleted) {
/* mode-only change: update the current */
diff --git a/apply.h b/apply.h
index b3d6783d55..d9b3957703 100644
--- a/apply.h
+++ b/apply.h
@@ -35,7 +35,6 @@ enum apply_verbosity {
struct apply_state {
const char *prefix;
- int prefix_length;
/* These are lock_file related */
struct lock_file *lock_file;
diff --git a/archive.c b/archive.c
index 2ad7e6cb90..1ab8d3a1d7 100644
--- a/archive.c
+++ b/archive.c
@@ -391,7 +391,7 @@ static void parse_treeish_arg(const char **argv,
free(ref);
}
- if (get_sha1(name, oid.hash))
+ if (get_oid(name, &oid))
die("Not a valid object name");
commit = lookup_commit_reference_gently(&oid, 1);
diff --git a/attr.c b/attr.c
index 56961f0236..2f49151736 100644
--- a/attr.c
+++ b/attr.c
@@ -76,18 +76,20 @@ struct attr_hash_entry {
};
/* attr_hashmap comparison function */
-static int attr_hash_entry_cmp(void *unused_cmp_data,
- const struct attr_hash_entry *a,
- const struct attr_hash_entry *b,
- void *unused_keydata)
+static int attr_hash_entry_cmp(const void *unused_cmp_data,
+ const void *entry,
+ const void *entry_or_key,
+ const void *unused_keydata)
{
+ const struct attr_hash_entry *a = entry;
+ const struct attr_hash_entry *b = entry_or_key;
return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);
}
/* Initialize an 'attr_hashmap' object */
static void attr_hashmap_init(struct attr_hashmap *map)
{
- hashmap_init(&map->map, (hashmap_cmp_fn) attr_hash_entry_cmp, NULL, 0);
+ hashmap_init(&map->map, attr_hash_entry_cmp, NULL, 0);
}
/*
diff --git a/bisect.c b/bisect.c
index a9fd9fbc61..2549eaf7b1 100644
--- a/bisect.c
+++ b/bisect.c
@@ -680,16 +680,16 @@ static int is_expected_rev(const struct object_id *oid)
return res;
}
-static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
+static int bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
{
char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
- memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
- update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ memcpy(bisect_rev_hex, oid_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
+ update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev->hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
argv_checkout[2] = bisect_rev_hex;
if (no_checkout) {
- update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ update_ref(NULL, "BISECT_HEAD", bisect_rev->hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
} else {
int res;
res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
@@ -796,7 +796,7 @@ static void check_merge_bases(int no_checkout)
handle_skipped_merge_base(mb);
} else {
printf(_("Bisecting: a merge base must be tested\n"));
- exit(bisect_checkout(mb->hash, no_checkout));
+ exit(bisect_checkout(mb, no_checkout));
}
}
@@ -939,7 +939,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
struct rev_info revs;
struct commit_list *tried;
int reaches = 0, all = 0, nr, steps;
- const unsigned char *bisect_rev;
+ struct object_id *bisect_rev;
char *steps_msg;
read_bisect_terms(&term_bad, &term_good);
@@ -977,11 +977,11 @@ int bisect_next_all(const char *prefix, int no_checkout)
exit(4);
}
- bisect_rev = revs.commits->item->object.oid.hash;
+ bisect_rev = &revs.commits->item->object.oid;
- if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
+ if (!oidcmp(bisect_rev, current_bad_oid)) {
exit_if_skipped_commits(tried, current_bad_oid);
- printf("%s is the first %s commit\n", sha1_to_hex(bisect_rev),
+ printf("%s is the first %s commit\n", oid_to_hex(bisect_rev),
term_bad);
show_diff_tree(prefix, revs.commits->item);
/* This means the bisection process succeeded. */
diff --git a/branch.c b/branch.c
index 36541d05cd..ce1f8bb58e 100644
--- a/branch.c
+++ b/branch.c
@@ -90,24 +90,24 @@ int install_branch_config(int flag, const char *local, const char *origin, const
if (shortname) {
if (origin)
printf_ln(rebasing ?
- _("Branch %s set up to track remote branch %s from %s by rebasing.") :
- _("Branch %s set up to track remote branch %s from %s."),
+ _("Branch '%s' set up to track remote branch '%s' from '%s' by rebasing.") :
+ _("Branch '%s' set up to track remote branch '%s' from '%s'."),
local, shortname, origin);
else
printf_ln(rebasing ?
- _("Branch %s set up to track local branch %s by rebasing.") :
- _("Branch %s set up to track local branch %s."),
+ _("Branch '%s' set up to track local branch '%s' by rebasing.") :
+ _("Branch '%s' set up to track local branch '%s'."),
local, shortname);
} else {
if (origin)
printf_ln(rebasing ?
- _("Branch %s set up to track remote ref %s by rebasing.") :
- _("Branch %s set up to track remote ref %s."),
+ _("Branch '%s' set up to track remote ref '%s' by rebasing.") :
+ _("Branch '%s' set up to track remote ref '%s'."),
local, remote);
else
printf_ln(rebasing ?
- _("Branch %s set up to track local ref %s by rebasing.") :
- _("Branch %s set up to track local ref %s."),
+ _("Branch '%s' set up to track local ref '%s' by rebasing.") :
+ _("Branch '%s' set up to track local ref '%s'."),
local, remote);
}
}
diff --git a/builtin.h b/builtin.h
index 498ac80d07..42378f3aa4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -6,6 +6,94 @@
#include "cache.h"
#include "commit.h"
+/*
+ * builtin API
+ * ===========
+ *
+ * Adding a new built-in
+ * ---------------------
+ *
+ * There are 4 things to do to add a built-in command implementation to
+ * Git:
+ *
+ * . Define the implementation of the built-in command `foo` with
+ * signature:
+ *
+ * int cmd_foo(int argc, const char **argv, const char *prefix);
+ *
+ * . Add the external declaration for the function to `builtin.h`.
+ *
+ * . Add the command to the `commands[]` table defined in `git.c`.
+ * The entry should look like:
+ *
+ * { "foo", cmd_foo, <options> },
+ *
+ * where options is the bitwise-or of:
+ *
+ * `RUN_SETUP`:
+ * If there is not a Git directory to work on, abort. If there
+ * is a work tree, chdir to the top of it if the command was
+ * invoked in a subdirectory. If there is no work tree, no
+ * chdir() is done.
+ *
+ * `RUN_SETUP_GENTLY`:
+ * If there is a Git directory, chdir as per RUN_SETUP, otherwise,
+ * don't chdir anywhere.
+ *
+ * `USE_PAGER`:
+ *
+ * If the standard output is connected to a tty, spawn a pager and
+ * feed our output to it.
+ *
+ * `NEED_WORK_TREE`:
+ *
+ * Make sure there is a work tree, i.e. the command cannot act
+ * on bare repositories.
+ * This only makes sense when `RUN_SETUP` is also set.
+ *
+ * `SUPPORT_SUPER_PREFIX`:
+ *
+ * The built-in supports `--super-prefix`.
+ *
+ * `DELAY_PAGER_CONFIG`:
+ *
+ * If RUN_SETUP or RUN_SETUP_GENTLY is set, git.c normally handles
+ * the `pager.<cmd>`-configuration. If this flag is used, git.c
+ * will skip that step, instead allowing the built-in to make a
+ * more informed decision, e.g., by ignoring `pager.<cmd>` for
+ * certain subcommands.
+ *
+ * . Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`.
+ *
+ * Additionally, if `foo` is a new command, there are 4 more things to do:
+ *
+ * . Add tests to `t/` directory.
+ *
+ * . Write documentation in `Documentation/git-foo.txt`.
+ *
+ * . Add an entry for `git-foo` to `command-list.txt`.
+ *
+ * . Add an entry for `/git-foo` to `.gitignore`.
+ *
+ *
+ * How a built-in is called
+ * ------------------------
+ *
+ * The implementation `cmd_foo()` takes three parameters, `argc`, `argv,
+ * and `prefix`. The first two are similar to what `main()` of a
+ * standalone command would be called with.
+ *
+ * When `RUN_SETUP` is specified in the `commands[]` table, and when you
+ * were started from a subdirectory of the work tree, `cmd_foo()` is called
+ * after chdir(2) to the top of the work tree, and `prefix` gets the path
+ * to the subdirectory the command started from. This allows you to
+ * convert a user-supplied pathname (typically relative to that directory)
+ * to a pathname relative to the top of the work tree.
+ *
+ * The return value from `cmd_foo()` becomes the exit status of the
+ * command.
+ */
+
#define DEFAULT_MERGE_LOG_LEN 20
extern const char git_usage_string[];
@@ -25,6 +113,18 @@ struct fmt_merge_msg_opts {
extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
struct fmt_merge_msg_opts *);
+/**
+ * If a built-in has DELAY_PAGER_CONFIG set, the built-in should call this early
+ * when it wishes to respect the `pager.foo`-config. The `cmd` is the name of
+ * the built-in, e.g., "foo". If a paging-choice has already been setup, this
+ * does nothing. The default in `def` should be 0 for "pager off", 1 for "pager
+ * on" or -1 for "punt".
+ *
+ * You should most likely use a default of 0 or 1. "Punt" (-1) could be useful
+ * to be able to fall back to some historical compatibility name.
+ */
+extern void setup_auto_pager(const char *cmd, int def);
+
extern int is_builtin(const char *s);
extern int cmd_add(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index e888fb8c5f..c20548e4f5 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -32,7 +32,7 @@ struct update_callback_data {
int add_errors;
};
-static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
+static void chmod_pathspec(struct pathspec *pathspec, char flip)
{
int i;
@@ -42,8 +42,8 @@ static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
if (pathspec && !ce_path_match(ce, pathspec, NULL))
continue;
- if (chmod_cache_entry(ce, force_mode) < 0)
- fprintf(stderr, "cannot chmod '%s'", ce->name);
+ if (chmod_cache_entry(ce, flip) < 0)
+ fprintf(stderr, "cannot chmod %cx '%s'\n", flip, ce->name);
}
}
@@ -116,6 +116,7 @@ int add_files_to_cache(const char *prefix,
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = update_callback;
rev.diffopt.format_callback_data = &data;
+ rev.diffopt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
return !!data.add_errors;
diff --git a/builtin/am.c b/builtin/am.c
index c973bd96dc..c369dd1dce 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -31,6 +31,7 @@
#include "mailinfo.h"
#include "apply.h"
#include "string-list.h"
+#include "packfile.h"
/**
* Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -431,6 +432,14 @@ static void am_load(struct am_state *state)
read_state_file(&sb, state, "utf8", 1);
state->utf8 = !strcmp(sb.buf, "t");
+ if (file_exists(am_path(state, "rerere-autoupdate"))) {
+ read_state_file(&sb, state, "rerere-autoupdate", 1);
+ state->allow_rerere_autoupdate = strcmp(sb.buf, "t") ?
+ RERERE_NOAUTOUPDATE : RERERE_AUTOUPDATE;
+ } else {
+ state->allow_rerere_autoupdate = 0;
+ }
+
read_state_file(&sb, state, "keep", 1);
if (!strcmp(sb.buf, "t"))
state->keep = KEEP_TRUE;
@@ -1003,6 +1012,10 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
write_state_bool(state, "sign", state->signoff);
write_state_bool(state, "utf8", state->utf8);
+ if (state->allow_rerere_autoupdate)
+ write_state_bool(state, "rerere-autoupdate",
+ state->allow_rerere_autoupdate == RERERE_AUTOUPDATE);
+
switch (state->keep) {
case KEEP_FALSE:
str = "f";
@@ -1131,7 +1144,7 @@ static int index_has_changes(struct strbuf *sb)
struct object_id head;
int i;
- if (!get_sha1_tree("HEAD", head.hash)) {
+ if (!get_oid_tree("HEAD", &head)) {
struct diff_options opt;
diff_setup(&opt);
@@ -1181,34 +1194,10 @@ static void NORETURN die_user_resolve(const struct am_state *state)
*/
static void am_append_signoff(struct am_state *state)
{
- char *cp;
- struct strbuf mine = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
-
- /* our sign-off */
- strbuf_addf(&mine, "\n%s%s\n",
- sign_off_header,
- fmt_name(getenv("GIT_COMMITTER_NAME"),
- getenv("GIT_COMMITTER_EMAIL")));
-
- /* Does sb end with it already? */
- if (mine.len < sb.len &&
- !strcmp(mine.buf, sb.buf + sb.len - mine.len))
- goto exit; /* no need to duplicate */
-
- /* Does it have any Signed-off-by: in the text */
- for (cp = sb.buf;
- cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
- cp = strchr(cp, '\n')) {
- if (sb.buf == cp || cp[-1] == '\n')
- break;
- }
-
- strbuf_addstr(&sb, mine.buf + !!cp);
-exit:
- strbuf_release(&mine);
+ append_signoff(&sb, 0, 0);
state->msg = strbuf_detach(&sb, &state->msg_len);
}
@@ -1432,7 +1421,7 @@ static void write_index_patch(const struct am_state *state)
struct rev_info rev_info;
FILE *fp;
- if (!get_sha1_tree("HEAD", head.hash))
+ if (!get_oid_tree("HEAD", &head))
tree = lookup_tree(&head);
else
tree = lookup_tree(&empty_tree_oid);
@@ -1661,7 +1650,7 @@ static void do_commit(const struct am_state *state)
if (write_cache_as_tree(tree.hash, 0, NULL))
die(_("git write-tree failed to write a tree"));
- if (!get_sha1_commit("HEAD", parent.hash)) {
+ if (!get_oid_commit("HEAD", &parent)) {
old_oid = &parent;
commit_list_insert(lookup_commit(&parent), &parents);
} else {
diff --git a/builtin/blame.c b/builtin/blame.c
index bda1a78726..67adaef4d8 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -488,7 +488,7 @@ static int read_ancestry(const char *graft_file)
return -1;
while (!strbuf_getwholeline(&buf, fp, '\n')) {
/* The format is just "Commit Parent1 Parent2 ...\n" */
- struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
+ struct commit_graft *graft = read_graft_line(&buf);
if (graft)
register_commit_graft(graft, 0);
}
@@ -925,8 +925,7 @@ parse_done:
sb.found_guilty_entry = &found_guilty_entry;
sb.found_guilty_entry_data = &pi;
if (show_progress)
- pi.progress = start_progress_delay(_("Blaming lines"),
- sb.num_lines, 50, 1);
+ pi.progress = start_delayed_progress(_("Blaming lines"), sb.num_lines);
assign_blame(&sb, opt);
diff --git a/builtin/branch.c b/builtin/branch.c
index 8a0595e115..355f9ef5da 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -92,7 +92,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
return config_error_nonbool(var);
return color_parse(value, branch_colors[slot]);
}
- return git_color_default_config(var, value, cb);
+ return git_default_config(var, value, cb);
}
static const char *branch_get_color(enum color_branch ix)
@@ -383,7 +383,7 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
return strbuf_detach(&fmt, NULL);
}
-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format)
{
int i;
struct ref_array array;
@@ -407,14 +407,17 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
if (filter->verbose)
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
- if (!format)
- format = to_free = build_format(filter, maxwidth, remote_prefix);
- verify_ref_format(format);
+ if (!format->format)
+ format->format = to_free = build_format(filter, maxwidth, remote_prefix);
+ format->use_color = branch_use_color;
+
+ if (verify_ref_format(format))
+ die(_("unable to parse format string"));
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
- format_ref_array_item(array.items[i], format, 0, &out);
+ format_ref_array_item(array.items[i], format, &out);
if (column_active(colopts)) {
assert(!filter->verbose && "--column and --verbose are incompatible");
/* format to a string_list to let print_columns() do its job */
@@ -549,7 +552,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
struct ref_filter filter;
int icase = 0;
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
- const char *format = NULL;
+ struct ref_format format = REF_FORMAT_INIT;
struct option options[] = {
OPT_GROUP(N_("Generic options")),
@@ -558,8 +561,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT__QUIET(&quiet, N_("suppress informational messages")),
OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"),
BRANCH_TRACK_EXPLICIT),
- OPT_SET_INT( 0, "set-upstream", &track, N_("change upstream info"),
- BRANCH_TRACK_OVERRIDE),
+ { OPTION_SET_INT, 0, "set-upstream", &track, NULL, N_("do not use"),
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, BRANCH_TRACK_OVERRIDE },
OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("Unset the upstream info")),
OPT__COLOR(&branch_use_color, N_("use colored output")),
@@ -593,7 +596,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
N_("print only branches of the object"), 0, parse_opt_object_name
},
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
- OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
+ OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_END(),
};
@@ -667,7 +670,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!sorting)
sorting = ref_default_sorting();
sorting->ignore_case = icase;
- print_ref_list(&filter, sorting, format);
+ print_ref_list(&filter, sorting, &format);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
return 0;
@@ -756,8 +759,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
} else if (argc > 0 && argc <= 2) {
struct branch *branch = branch_get(argv[0]);
- int branch_existed = 0, remote_tracking = 0;
- struct strbuf buf = STRBUF_INIT;
if (!strcmp(argv[0], "HEAD"))
die(_("it does not make sense to create 'HEAD' manually"));
@@ -769,28 +770,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
if (track == BRANCH_TRACK_OVERRIDE)
- fprintf(stderr, _("The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to\n"));
-
- strbuf_addf(&buf, "refs/remotes/%s", branch->name);
- remote_tracking = ref_exists(buf.buf);
- strbuf_release(&buf);
+ die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
- branch_existed = ref_exists(branch->refname);
create_branch(argv[0], (argc == 2) ? argv[1] : head,
force, reflog, 0, quiet, track);
- /*
- * We only show the instructions if the user gave us
- * one branch which doesn't exist locally, but is the
- * name of a remote-tracking branch.
- */
- if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
- !branch_existed && remote_tracking) {
- fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
- fprintf(stderr, " git branch -d %s\n", branch->name);
- fprintf(stderr, " git branch --set-upstream-to %s\n", branch->name);
- }
-
} else
usage_with_options(builtin_branch_usage, options);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 96b786e489..4ccbfaac31 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -12,6 +12,7 @@
#include "streaming.h"
#include "tree-walk.h"
#include "sha1-array.h"
+#include "packfile.h"
struct batch_options {
int enabled;
@@ -63,8 +64,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (unknown_type)
flags |= OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
- if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
- oid.hash, &obj_context))
+ if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH,
+ &oid, &obj_context))
die("Not a valid object name %s", obj_name);
if (!path)
@@ -361,10 +362,10 @@ static void batch_one_object(const char *obj_name, struct batch_options *opt,
struct expand_data *data)
{
struct object_context ctx;
- int flags = opt->follow_symlinks ? GET_SHA1_FOLLOW_SYMLINKS : 0;
+ int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
enum follow_symlinks_result result;
- result = get_sha1_with_context(obj_name, flags, data->oid.hash, &ctx);
+ result = get_oid_with_context(obj_name, flags, &data->oid, &ctx);
if (result != FOUND) {
switch (result) {
case MISSING_OBJECT:
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 9661e1bcba..5c202b7af5 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -358,6 +358,8 @@ static int checkout_paths(const struct checkout_opts *opts,
state.force = 1;
state.refresh_cache = 1;
state.istate = &the_index;
+
+ enable_delayed_checkout(&state);
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
if (ce->ce_flags & CE_MATCHED) {
@@ -372,6 +374,7 @@ static int checkout_paths(const struct checkout_opts *opts,
pos = skip_same_name(ce, pos) - 1;
}
}
+ errs |= finish_delayed_checkout(&state);
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
@@ -858,7 +861,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
}
if (starts_with(var, "submodule."))
- return submodule_config(var, value, NULL);
+ return git_default_submodule_config(var, value, NULL);
return git_xmerge_config(var, value, NULL);
}
@@ -1179,7 +1182,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.prefix = prefix;
opts.show_progress = -1;
- gitmodules_config();
git_config(git_checkout_config, &opts);
opts.track = BRANCH_TRACK_UNSPECIFIED;
diff --git a/builtin/clean.c b/builtin/clean.c
index 057fc97fe4..21a7a32994 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -33,15 +33,6 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
static const char *msg_warn_remove_failed = N_("failed to remove %s");
-static int clean_use_color = -1;
-static char clean_colors[][COLOR_MAXLEN] = {
- GIT_COLOR_RESET,
- GIT_COLOR_NORMAL, /* PLAIN */
- GIT_COLOR_BOLD_BLUE, /* PROMPT */
- GIT_COLOR_BOLD, /* HEADER */
- GIT_COLOR_BOLD_RED, /* HELP */
- GIT_COLOR_BOLD_RED, /* ERROR */
-};
enum color_clean {
CLEAN_COLOR_RESET = 0,
CLEAN_COLOR_PLAIN = 1,
@@ -51,6 +42,16 @@ enum color_clean {
CLEAN_COLOR_ERROR = 5
};
+static int clean_use_color = -1;
+static char clean_colors[][COLOR_MAXLEN] = {
+ [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
+ [CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD,
+ [CLEAN_COLOR_HELP] = GIT_COLOR_BOLD_RED,
+ [CLEAN_COLOR_PLAIN] = GIT_COLOR_NORMAL,
+ [CLEAN_COLOR_PROMPT] = GIT_COLOR_BOLD_BLUE,
+ [CLEAN_COLOR_RESET] = GIT_COLOR_RESET,
+};
+
#define MENU_OPTS_SINGLETON 01
#define MENU_OPTS_IMMEDIATE 02
#define MENU_OPTS_LIST_ONLY 04
@@ -125,8 +126,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
return 0;
}
- /* inspect the color.ui config variable and others */
- return git_color_default_config(var, value, cb);
+ return git_default_config(var, value, cb);
}
static const char *clean_get_color(enum color_clean ix)
diff --git a/builtin/clone.c b/builtin/clone.c
index 08b5cc433c..8d11b570a1 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -25,6 +25,7 @@
#include "remote.h"
#include "run-command.h"
#include "connected.h"
+#include "packfile.h"
/*
* Overall FIXMEs:
@@ -768,6 +769,9 @@ static int checkout(int submodule_progress)
if (submodule_progress)
argv_array_push(&args, "--progress");
+ if (option_verbosity < 0)
+ argv_array_push(&args, "--quiet");
+
err = run_command_v_opt(args.argv, RUN_GIT_CMD);
argv_array_clear(&args);
}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index a4a923d7c0..19e898fa4e 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -56,7 +56,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
struct object_id oid;
if (argc <= ++i)
usage(commit_tree_usage);
- if (get_sha1_commit(argv[i], oid.hash))
+ if (get_oid_commit(argv[i], &oid))
die("Not a valid object name %s", argv[i]);
assert_sha1_type(oid.hash, OBJ_COMMIT);
new_parent(lookup_commit(&oid), &parents);
@@ -106,7 +106,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
continue;
}
- if (get_sha1_tree(arg, tree_oid.hash))
+ if (get_oid_tree(arg, &tree_oid))
die("Not a valid object name %s", arg);
if (got_tree)
die("Cannot give more than one trees");
diff --git a/builtin/commit.c b/builtin/commit.c
index 8e93802511..b3b04f5dd3 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -195,7 +195,6 @@ static void determine_whence(struct wt_status *s)
static void status_init_config(struct wt_status *s, config_fn_t fn)
{
wt_status_prepare(s);
- gitmodules_config();
git_config(fn, s);
determine_whence(s);
init_diff_ui_defaults();
@@ -510,7 +509,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
s->index_file = index_file;
s->fp = fp;
s->nowarn = nowarn;
- s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0;
+ s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
if (!s->is_initial)
hashcpy(s->sha1_commit, oid.hash);
s->status_format = status_format;
@@ -891,7 +890,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (amend)
parent = "HEAD^1";
- if (get_sha1(parent, oid.hash)) {
+ if (get_oid(parent, &oid)) {
int i, ita_nr = 0;
for (i = 0; i < active_nr; i++)
@@ -940,13 +939,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 0;
}
- /*
- * Re-read the index as pre-commit hook could have updated it,
- * and write it out as a tree. We must do this before we invoke
- * the editor and after we invoke run_status above.
- */
- discard_cache();
+ if (!no_verify && find_hook("pre-commit")) {
+ /*
+ * Re-read the index as pre-commit hook could have updated it,
+ * and write it out as a tree. We must do this before we invoke
+ * the editor and after we invoke run_status above.
+ */
+ discard_cache();
+ }
read_cache_from(index_file);
+
if (update_main_cache_tree(0)) {
error(_("Error building trees"));
return 0;
@@ -1387,7 +1389,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
fd = hold_locked_index(&index_lock, 0);
- s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0;
+ s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
if (!s.is_initial)
hashcpy(s.sha1_commit, oid.hash);
@@ -1657,7 +1659,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
s.colopts = 0;
- if (get_sha1("HEAD", oid.hash))
+ if (get_oid("HEAD", &oid))
current_head = NULL;
else {
current_head = lookup_commit_or_die(&oid, "HEAD");
@@ -1739,17 +1741,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (verbose || /* Truncate the message just before the diff, if any. */
cleanup_mode == CLEANUP_SCISSORS)
strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-
if (cleanup_mode != CLEANUP_NONE)
strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
- if (template_untouched(&sb) && !allow_empty_message) {
+
+ if (message_is_empty(&sb) && !allow_empty_message) {
rollback_index_files();
- fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
+ fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
exit(1);
}
- if (message_is_empty(&sb) && !allow_empty_message) {
+ if (template_untouched(&sb) && !allow_empty_message) {
rollback_index_files();
- fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
+ fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
exit(1);
}
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index 1d82e61f2a..33343818c8 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -10,6 +10,7 @@
#include "builtin.h"
#include "parse-options.h"
#include "quote.h"
+#include "packfile.h"
static unsigned long garbage;
static off_t size_garbage;
diff --git a/builtin/describe.c b/builtin/describe.c
index 89ea1cdd60..9c13c6817b 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -55,10 +55,13 @@ static const char *prio_names[] = {
};
static int commit_name_cmp(const void *unused_cmp_data,
- const struct commit_name *cn1,
- const struct commit_name *cn2,
+ const void *entry,
+ const void *entry_or_key,
const void *peeled)
{
+ const struct commit_name *cn1 = entry;
+ const struct commit_name *cn2 = entry_or_key;
+
return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled);
}
@@ -503,7 +506,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
return cmd_name_rev(args.argc, args.argv, prefix);
}
- hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, NULL, 0);
+ hashmap_init(&names, commit_name_cmp, NULL, 0);
for_each_rawref(get_name, NULL);
if (!names.size && !always)
die(_("No names found, cannot describe anything."));
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 17bf84d18f..e88493ffe5 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -26,7 +26,6 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
init_revisions(&rev, prefix);
- gitmodules_config();
rev.abbrev = 0;
precompose_argv(argc, argv);
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 185e6f9b58..9d772f8f27 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -23,7 +23,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
init_revisions(&rev, prefix);
- gitmodules_config();
rev.abbrev = 0;
precompose_argv(argc, argv);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 31d2cb4107..d66499909e 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -110,7 +110,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
init_revisions(opt, prefix);
- gitmodules_config();
opt->abbrev = 0;
opt->diff = 1;
opt->disable_stdin = 1;
diff --git a/builtin/diff.c b/builtin/diff.c
index 7cde6abbcf..7e3ebcea38 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -315,8 +315,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
no_index = DIFF_NO_INDEX_IMPLICIT;
}
- if (!no_index)
- gitmodules_config();
init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
precompose_argv(argc, argv);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index a1a26ba891..b2d3ba7539 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -111,7 +111,7 @@ static int use_wt_file(const char *workdir, const char *name,
int fd = open(buf.buf, O_RDONLY);
if (fd >= 0 &&
- !index_fd(wt_oid.hash, fd, &st, OBJ_BLOB, name, 0)) {
+ !index_fd(&wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
if (is_null_oid(oid)) {
oidcpy(oid, &wt_oid);
use = 1;
@@ -131,10 +131,12 @@ struct working_tree_entry {
};
static int working_tree_entry_cmp(const void *unused_cmp_data,
- struct working_tree_entry *a,
- struct working_tree_entry *b,
- void *unused_keydata)
+ const void *entry,
+ const void *entry_or_key,
+ const void *unused_keydata)
{
+ const struct working_tree_entry *a = entry;
+ const struct working_tree_entry *b = entry_or_key;
return strcmp(a->path, b->path);
}
@@ -149,9 +151,13 @@ struct pair_entry {
};
static int pair_cmp(const void *unused_cmp_data,
- struct pair_entry *a, struct pair_entry *b,
- void *unused_keydata)
+ const void *entry,
+ const void *entry_or_key,
+ const void *unused_keydata)
{
+ const struct pair_entry *a = entry;
+ const struct pair_entry *b = entry_or_key;
+
return strcmp(a->path, b->path);
}
@@ -179,9 +185,13 @@ struct path_entry {
};
static int path_entry_cmp(const void *unused_cmp_data,
- struct path_entry *a, struct path_entry *b,
- void *key)
+ const void *entry,
+ const void *entry_or_key,
+ const void *key)
{
+ const struct path_entry *a = entry;
+ const struct path_entry *b = entry_or_key;
+
return strcmp(a->path, key ? key : b->path);
}
@@ -372,10 +382,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
rdir_len = rdir.len;
wtdir_len = wtdir.len;
- hashmap_init(&working_tree_dups,
- (hashmap_cmp_fn)working_tree_entry_cmp, NULL, 0);
- hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, NULL, 0);
- hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, NULL, 0);
+ hashmap_init(&working_tree_dups, working_tree_entry_cmp, NULL, 0);
+ hashmap_init(&submodules, pair_cmp, NULL, 0);
+ hashmap_init(&symlinks2, pair_cmp, NULL, 0);
child.no_stdin = 1;
child.git_cmd = 1;
@@ -585,10 +594,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* in the common case of --symlinks and the difftool updating
* files through the symlink.
*/
- hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp,
- NULL, wtindex.cache_nr);
- hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp,
- NULL, wtindex.cache_nr);
+ hashmap_init(&wt_modified, path_entry_cmp, NULL, wtindex.cache_nr);
+ hashmap_init(&tmp_modified, path_entry_cmp, NULL, wtindex.cache_nr);
for (i = 0; i < wtindex.cache_nr; i++) {
struct hashmap_entry dummy;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c87e59f3b1..225c734924 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -17,6 +17,7 @@
#include "connected.h"
#include "argv-array.h"
#include "utf8.h"
+#include "packfile.h"
static const char * const builtin_fetch_usage[] = {
N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@ -39,7 +40,7 @@ static int prune = -1; /* unspecified */
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
static int progress = -1;
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_children = -1;
+static int max_children = 1;
static enum transport_family family;
static const char *depth;
static const char *deepen_since;
@@ -68,9 +69,30 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
recurse_submodules = r;
}
+ if (!strcmp(k, "submodule.fetchjobs")) {
+ max_children = parse_submodule_fetchjobs(k, v);
+ return 0;
+ } else if (!strcmp(k, "fetch.recursesubmodules")) {
+ recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+ return 0;
+ }
+
return git_default_config(k, v, cb);
}
+static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "submodule.fetchjobs")) {
+ max_children = parse_submodule_fetchjobs(var, value);
+ return 0;
+ } else if (!strcmp(var, "fetch.recursesubmodules")) {
+ recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
+ return 0;
+ }
+
+ return 0;
+}
+
static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
{
ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
@@ -1311,6 +1333,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc; i++)
strbuf_addf(&default_rla, " %s", argv[i]);
+ config_from_gitmodules(gitmodules_fetch_config, NULL);
git_config(git_fetch_config, NULL);
argc = parse_options(argc, argv, prefix,
@@ -1338,12 +1361,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (depth || deepen_since || deepen_not.nr)
deepen = 1;
- if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
- set_config_fetch_recurse_submodules(recurse_submodules_default);
- gitmodules_config();
- git_config(submodule_config, NULL);
- }
-
if (all) {
if (argc == 1)
die(_("fetch --all does not take a repository argument"));
@@ -1383,6 +1400,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
result = fetch_populated_submodules(&options,
submodule_prefix,
recurse_submodules,
+ recurse_submodules_default,
verbosity < 0,
max_children);
argv_array_clear(&options);
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 10cbb43416..e99b5ddbf9 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -408,7 +408,8 @@ static void shortlog(const char *name,
}
static void fmt_merge_msg_title(struct strbuf *out,
- const char *current_branch) {
+ const char *current_branch)
+{
int i = 0;
char *sep = "";
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 52be99cbac..5d7c921a77 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -17,25 +17,25 @@ static char const * const for_each_ref_usage[] = {
int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
int i;
- const char *format = "%(objectname) %(objecttype)\t%(refname)";
struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
- int maxcount = 0, quote_style = 0, icase = 0;
+ int maxcount = 0, icase = 0;
struct ref_array array;
struct ref_filter filter;
+ struct ref_format format = REF_FORMAT_INIT;
struct option opts[] = {
- OPT_BIT('s', "shell", &quote_style,
+ OPT_BIT('s', "shell", &format.quote_style,
N_("quote placeholders suitably for shells"), QUOTE_SHELL),
- OPT_BIT('p', "perl", &quote_style,
+ OPT_BIT('p', "perl", &format.quote_style,
N_("quote placeholders suitably for perl"), QUOTE_PERL),
- OPT_BIT(0 , "python", &quote_style,
+ OPT_BIT(0 , "python", &format.quote_style,
N_("quote placeholders suitably for python"), QUOTE_PYTHON),
- OPT_BIT(0 , "tcl", &quote_style,
+ OPT_BIT(0 , "tcl", &format.quote_style,
N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
OPT_GROUP(""),
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
- OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
+ OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
N_("field name to sort on"), &parse_opt_ref_sorting),
OPT_CALLBACK(0, "points-at", &filter.points_at,
@@ -52,16 +52,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
memset(&array, 0, sizeof(array));
memset(&filter, 0, sizeof(filter));
+ format.format = "%(objectname) %(objecttype)\t%(refname)";
+
+ git_config(git_default_config, NULL);
+
parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
if (maxcount < 0) {
error("invalid --count argument: `%d'", maxcount);
usage_with_options(for_each_ref_usage, opts);
}
- if (HAS_MULTI_BITS(quote_style)) {
+ if (HAS_MULTI_BITS(format.quote_style)) {
error("more than one quoting style?");
usage_with_options(for_each_ref_usage, opts);
}
- if (verify_ref_format(format))
+ if (verify_ref_format(&format))
usage_with_options(for_each_ref_usage, opts);
if (!sorting)
@@ -69,9 +73,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
sorting->ignore_case = icase;
filter.ignore_case = icase;
- /* for warn_ambiguous_refs */
- git_config(git_default_config, NULL);
-
filter.name_patterns = argv;
filter.match_as_path = 1;
filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
@@ -80,7 +81,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (!maxcount || array.nr < maxcount)
maxcount = array.nr;
for (i = 0; i < maxcount; i++)
- show_ref_array_item(array.items[i], format, quote_style);
+ show_ref_array_item(array.items[i], &format);
ref_array_clear(&array);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 99dea7adf6..1e4c471b41 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -15,10 +15,13 @@
#include "progress.h"
#include "streaming.h"
#include "decorate.h"
+#include "packfile.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
#define HAS_OBJ 0x0004
+/* This flag is set if something points to this object. */
+#define USED 0x0008
static int show_root;
static int show_tags;
@@ -168,18 +171,7 @@ static void mark_object_reachable(struct object *obj)
static int traverse_one_object(struct object *obj)
{
- int result;
- struct tree *tree = NULL;
-
- if (obj->type == OBJ_TREE) {
- tree = (struct tree *)obj;
- if (parse_tree(tree) < 0)
- return 1; /* error already displayed */
- }
- result = fsck_walk(obj, obj, &fsck_walk_options);
- if (tree)
- free_tree_buffer(tree);
- return result;
+ return fsck_walk(obj, obj, &fsck_walk_options);
}
static int traverse_reachable(void)
@@ -188,7 +180,7 @@ static int traverse_reachable(void)
unsigned int nr = 0;
int result = 0;
if (show_progress)
- progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
+ progress = start_delayed_progress(_("Checking connectivity"), 0);
while (pending.nr) {
struct object_array_entry *entry;
struct object *obj;
@@ -206,7 +198,7 @@ static int mark_used(struct object *obj, int type, void *data, struct fsck_optio
{
if (!obj)
return 1;
- obj->used = 1;
+ obj->flags |= USED;
return 0;
}
@@ -255,7 +247,7 @@ static void check_unreachable_object(struct object *obj)
}
/*
- * "!used" means that nothing at all points to it, including
+ * "!USED" means that nothing at all points to it, including
* other unreachable objects. In other words, it's the "tip"
* of some set of unreachable objects, usually a commit that
* got dropped.
@@ -266,7 +258,7 @@ static void check_unreachable_object(struct object *obj)
* deleted a branch by mistake, this is a prime candidate to
* start looking at, for example.
*/
- if (!obj->used) {
+ if (!(obj->flags & USED)) {
if (show_dangling)
printf("dangling %s %s\n", printable_type(obj),
describe_object(obj));
@@ -335,6 +327,8 @@ static void check_connectivity(void)
static int fsck_obj(struct object *obj)
{
+ int err;
+
if (obj->flags & SEEN)
return 0;
obj->flags |= SEEN;
@@ -345,20 +339,13 @@ static int fsck_obj(struct object *obj)
if (fsck_walk(obj, NULL, &fsck_obj_options))
objerror(obj, "broken links");
- if (fsck_object(obj, NULL, 0, &fsck_obj_options))
- return -1;
-
- if (obj->type == OBJ_TREE) {
- struct tree *item = (struct tree *) obj;
-
- free_tree_buffer(item);
- }
+ err = fsck_object(obj, NULL, 0, &fsck_obj_options);
+ if (err)
+ goto out;
if (obj->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *) obj;
- free_commit_buffer(commit);
-
if (!commit->parents && show_root)
printf("root %s\n", describe_object(&commit->object));
}
@@ -374,7 +361,12 @@ static int fsck_obj(struct object *obj)
}
}
- return 0;
+out:
+ if (obj->type == OBJ_TREE)
+ free_tree_buffer((struct tree *)obj);
+ if (obj->type == OBJ_COMMIT)
+ free_commit_buffer((struct commit *)obj);
+ return err;
}
static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
@@ -390,7 +382,8 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
errors_found |= ERROR_OBJECT;
return error("%s: object corrupt or missing", oid_to_hex(oid));
}
- obj->flags = HAS_OBJ;
+ obj->flags &= ~(REACHABLE | SEEN);
+ obj->flags |= HAS_OBJ;
return fsck_obj(obj);
}
@@ -408,7 +401,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
add_decoration(fsck_walk_options.object_names,
obj,
xstrfmt("%s@{%"PRItime"}", refname, timestamp));
- obj->used = 1;
+ obj->flags |= USED;
mark_object_reachable(obj);
} else {
error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
@@ -456,7 +449,7 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
errors_found |= ERROR_REFS;
}
default_refs++;
- obj->used = 1;
+ obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(refname));
@@ -524,7 +517,8 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
return 0; /* keep checking other objects */
}
- obj->flags = HAS_OBJ;
+ obj->flags &= ~(REACHABLE | SEEN);
+ obj->flags |= HAS_OBJ;
if (fsck_obj(obj))
errors_found |= ERROR_OBJECT;
return 0;
@@ -606,7 +600,7 @@ static int fsck_cache_tree(struct cache_tree *it)
errors_found |= ERROR_REFS;
return 1;
}
- obj->used = 1;
+ obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(":"));
@@ -667,7 +661,7 @@ static struct option fsck_opts[] = {
int cmd_fsck(int argc, const char **argv, const char *prefix)
{
- int i, heads;
+ int i;
struct alternate_object_database *alt;
errors_found = 0;
@@ -735,25 +729,23 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
}
- heads = 0;
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
- unsigned char sha1[20];
- if (!get_sha1(arg, sha1)) {
- struct object *obj = lookup_object(sha1);
+ struct object_id oid;
+ if (!get_oid(arg, &oid)) {
+ struct object *obj = lookup_object(oid.hash);
if (!obj || !(obj->flags & HAS_OBJ)) {
- error("%s: object missing", sha1_to_hex(sha1));
+ error("%s: object missing", oid_to_hex(&oid));
errors_found |= ERROR_OBJECT;
continue;
}
- obj->used = 1;
+ obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(arg));
mark_object_reachable(obj);
- heads++;
continue;
}
error("invalid parameter: expected sha1, got '%s'", arg);
@@ -785,7 +777,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
if (!blob)
continue;
obj = &blob->object;
- obj->used = 1;
+ obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj,
diff --git a/builtin/gc.c b/builtin/gc.c
index e6b84475ae..3c78fcb9b1 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -19,6 +19,7 @@
#include "sigchain.h"
#include "argv-array.h"
#include "commit.h"
+#include "packfile.h"
#define FAILED_RUN "failed to run %s"
diff --git a/builtin/grep.c b/builtin/grep.c
index 7e79eb1a75..19e23946ac 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -28,13 +28,7 @@ static char const * const grep_usage[] = {
NULL
};
-static const char *super_prefix;
static int recurse_submodules;
-static struct argv_array submodule_options = ARGV_ARRAY_INIT;
-static const char *parent_basename;
-
-static int grep_submodule_launch(struct grep_opt *opt,
- const struct grep_source *gs);
#define GREP_NUM_THREADS_DEFAULT 8
static int num_threads;
@@ -186,10 +180,7 @@ static void *run(void *arg)
break;
opt->output_priv = w;
- if (w->source.type == GREP_SOURCE_SUBMODULE)
- hit |= grep_submodule_launch(opt, &w->source);
- else
- hit |= grep_source(opt, &w->source);
+ hit |= grep_source(opt, &w->source);
grep_source_clear_data(&w->source);
work_done(w);
}
@@ -284,7 +275,7 @@ static int wait_all(void)
static int grep_cmd_config(const char *var, const char *value, void *cb)
{
int st = grep_config(var, value, cb);
- if (git_color_default_config(var, value, cb) < 0)
+ if (git_default_config(var, value, cb) < 0)
st = -1;
if (!strcmp(var, "grep.threads")) {
@@ -327,21 +318,13 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
{
struct strbuf pathbuf = STRBUF_INIT;
- if (super_prefix) {
- strbuf_add(&pathbuf, filename, tree_name_len);
- strbuf_addstr(&pathbuf, super_prefix);
- strbuf_addstr(&pathbuf, filename + tree_name_len);
+ if (opt->relative && opt->prefix_length) {
+ quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
+ strbuf_insert(&pathbuf, 0, filename, tree_name_len);
} else {
strbuf_addstr(&pathbuf, filename);
}
- if (opt->relative && opt->prefix_length) {
- char *name = strbuf_detach(&pathbuf, NULL);
- quote_path_relative(name + tree_name_len, opt->prefix, &pathbuf);
- strbuf_insert(&pathbuf, 0, name, tree_name_len);
- free(name);
- }
-
#ifndef NO_PTHREADS
if (num_threads) {
add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid);
@@ -366,15 +349,10 @@ static int grep_file(struct grep_opt *opt, const char *filename)
{
struct strbuf buf = STRBUF_INIT;
- if (super_prefix)
- strbuf_addstr(&buf, super_prefix);
- strbuf_addstr(&buf, filename);
-
- if (opt->relative && opt->prefix_length) {
- char *name = strbuf_detach(&buf, NULL);
- quote_path_relative(name, opt->prefix, &buf);
- free(name);
- }
+ if (opt->relative && opt->prefix_length)
+ quote_path_relative(filename, opt->prefix, &buf);
+ else
+ strbuf_addstr(&buf, filename);
#ifndef NO_PTHREADS
if (num_threads) {
@@ -421,284 +399,89 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
exit(status);
}
-static void compile_submodule_options(const struct grep_opt *opt,
- const char **argv,
- int cached, int untracked,
- int opt_exclude, int use_index,
- int pattern_type_arg)
-{
- struct grep_pat *pattern;
-
- if (recurse_submodules)
- argv_array_push(&submodule_options, "--recurse-submodules");
-
- if (cached)
- argv_array_push(&submodule_options, "--cached");
- if (!use_index)
- argv_array_push(&submodule_options, "--no-index");
- if (untracked)
- argv_array_push(&submodule_options, "--untracked");
- if (opt_exclude > 0)
- argv_array_push(&submodule_options, "--exclude-standard");
-
- if (opt->invert)
- argv_array_push(&submodule_options, "-v");
- if (opt->ignore_case)
- argv_array_push(&submodule_options, "-i");
- if (opt->word_regexp)
- argv_array_push(&submodule_options, "-w");
- switch (opt->binary) {
- case GREP_BINARY_NOMATCH:
- argv_array_push(&submodule_options, "-I");
- break;
- case GREP_BINARY_TEXT:
- argv_array_push(&submodule_options, "-a");
- break;
- default:
- break;
- }
- if (opt->allow_textconv)
- argv_array_push(&submodule_options, "--textconv");
- if (opt->max_depth != -1)
- argv_array_pushf(&submodule_options, "--max-depth=%d",
- opt->max_depth);
- if (opt->linenum)
- argv_array_push(&submodule_options, "-n");
- if (!opt->pathname)
- argv_array_push(&submodule_options, "-h");
- if (!opt->relative)
- argv_array_push(&submodule_options, "--full-name");
- if (opt->name_only)
- argv_array_push(&submodule_options, "-l");
- if (opt->unmatch_name_only)
- argv_array_push(&submodule_options, "-L");
- if (opt->null_following_name)
- argv_array_push(&submodule_options, "-z");
- if (opt->count)
- argv_array_push(&submodule_options, "-c");
- if (opt->file_break)
- argv_array_push(&submodule_options, "--break");
- if (opt->heading)
- argv_array_push(&submodule_options, "--heading");
- if (opt->pre_context)
- argv_array_pushf(&submodule_options, "--before-context=%d",
- opt->pre_context);
- if (opt->post_context)
- argv_array_pushf(&submodule_options, "--after-context=%d",
- opt->post_context);
- if (opt->funcname)
- argv_array_push(&submodule_options, "-p");
- if (opt->funcbody)
- argv_array_push(&submodule_options, "-W");
- if (opt->all_match)
- argv_array_push(&submodule_options, "--all-match");
- if (opt->debug)
- argv_array_push(&submodule_options, "--debug");
- if (opt->status_only)
- argv_array_push(&submodule_options, "-q");
-
- switch (pattern_type_arg) {
- case GREP_PATTERN_TYPE_BRE:
- argv_array_push(&submodule_options, "-G");
- break;
- case GREP_PATTERN_TYPE_ERE:
- argv_array_push(&submodule_options, "-E");
- break;
- case GREP_PATTERN_TYPE_FIXED:
- argv_array_push(&submodule_options, "-F");
- break;
- case GREP_PATTERN_TYPE_PCRE:
- argv_array_push(&submodule_options, "-P");
- break;
- case GREP_PATTERN_TYPE_UNSPECIFIED:
- break;
- default:
- die("BUG: Added a new grep pattern type without updating switch statement");
- }
-
- for (pattern = opt->pattern_list; pattern != NULL;
- pattern = pattern->next) {
- switch (pattern->token) {
- case GREP_PATTERN:
- argv_array_pushf(&submodule_options, "-e%s",
- pattern->pattern);
- break;
- case GREP_AND:
- case GREP_OPEN_PAREN:
- case GREP_CLOSE_PAREN:
- case GREP_NOT:
- case GREP_OR:
- argv_array_push(&submodule_options, pattern->pattern);
- break;
- /* BODY and HEAD are not used by git-grep */
- case GREP_PATTERN_BODY:
- case GREP_PATTERN_HEAD:
- break;
- }
- }
-
- /*
- * Limit number of threads for child process to use.
- * This is to prevent potential fork-bomb behavior of git-grep as each
- * submodule process has its own thread pool.
- */
- argv_array_pushf(&submodule_options, "--threads=%d",
- DIV_ROUND_UP(num_threads, 2));
-
- /* Add Pathspecs */
- argv_array_push(&submodule_options, "--");
- for (; *argv; argv++)
- argv_array_push(&submodule_options, *argv);
-}
+static int grep_cache(struct grep_opt *opt, struct repository *repo,
+ const struct pathspec *pathspec, int cached);
+static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
+ struct tree_desc *tree, struct strbuf *base, int tn_len,
+ int check_attr, struct repository *repo);
-/*
- * Launch child process to grep contents of a submodule
- */
-static int grep_submodule_launch(struct grep_opt *opt,
- const struct grep_source *gs)
+static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
+ const struct pathspec *pathspec,
+ const struct object_id *oid,
+ const char *filename, const char *path)
{
- struct child_process cp = CHILD_PROCESS_INIT;
- int status, i;
- const char *end_of_base;
- const char *name;
- struct strbuf child_output = STRBUF_INIT;
-
- end_of_base = strchr(gs->name, ':');
- if (gs->identifier && end_of_base)
- name = end_of_base + 1;
- else
- name = gs->name;
+ struct repository submodule;
+ int hit;
- prepare_submodule_repo_env(&cp.env_array);
- argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
+ if (!is_submodule_active(superproject, path))
+ return 0;
- if (opt->relative && opt->prefix_length)
- argv_array_pushf(&cp.env_array, "%s=%s",
- GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
- opt->prefix);
+ if (repo_submodule_init(&submodule, superproject, path))
+ return 0;
- /* Add super prefix */
- argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
- super_prefix ? super_prefix : "",
- name);
- argv_array_push(&cp.args, "grep");
+ repo_read_gitmodules(&submodule);
/*
- * Add basename of parent project
- * When performing grep on a tree object the filename is prefixed
- * with the object's name: 'tree-name:filename'. In order to
- * provide uniformity of output we want to pass the name of the
- * parent project's object name to the submodule so the submodule can
- * prefix its output with the parent's name and not its own OID.
+ * NEEDSWORK: This adds the submodule's object directory to the list of
+ * alternates for the single in-memory object store. This has some bad
+ * consequences for memory (processed objects will never be freed) and
+ * performance (this increases the number of pack files git has to pay
+ * attention to, to the sum of the number of pack files in all the
+ * repositories processed so far). This can be removed once the object
+ * store is no longer global and instead is a member of the repository
+ * object.
*/
- if (gs->identifier && end_of_base)
- argv_array_pushf(&cp.args, "--parent-basename=%.*s",
- (int) (end_of_base - gs->name),
- gs->name);
+ add_to_alternates_memory(submodule.objectdir);
- /* Add options */
- for (i = 0; i < submodule_options.argc; i++) {
- /*
- * If there is a tree identifier for the submodule, add the
- * rev after adding the submodule options but before the
- * pathspecs. To do this we listen for the '--' and insert the
- * oid before pushing the '--' onto the child process argv
- * array.
- */
- if (gs->identifier &&
- !strcmp("--", submodule_options.argv[i])) {
- argv_array_push(&cp.args, oid_to_hex(gs->identifier));
- }
+ if (oid) {
+ struct object *object;
+ struct tree_desc tree;
+ void *data;
+ unsigned long size;
+ struct strbuf base = STRBUF_INIT;
- argv_array_push(&cp.args, submodule_options.argv[i]);
- }
+ object = parse_object_or_die(oid, oid_to_hex(oid));
- cp.git_cmd = 1;
- cp.dir = gs->path;
+ grep_read_lock();
+ data = read_object_with_reference(object->oid.hash, tree_type,
+ &size, NULL);
+ grep_read_unlock();
- /*
- * Capture output to output buffer and check the return code from the
- * child process. A '0' indicates a hit, a '1' indicates no hit and
- * anything else is an error.
- */
- status = capture_command(&cp, &child_output, 0);
- if (status && (status != 1)) {
- /* flush the buffer */
- write_or_die(1, child_output.buf, child_output.len);
- die("process for submodule '%s' failed with exit code: %d",
- gs->name, status);
- }
+ if (!data)
+ die(_("unable to read tree (%s)"), oid_to_hex(&object->oid));
- opt->output(opt, child_output.buf, child_output.len);
- strbuf_release(&child_output);
- /* invert the return code to make a hit equal to 1 */
- return !status;
-}
+ strbuf_addstr(&base, filename);
+ strbuf_addch(&base, '/');
-/*
- * Prep grep structures for a submodule grep
- * oid: the oid of the submodule or NULL if using the working tree
- * filename: name of the submodule including tree name of parent
- * path: location of the submodule
- */
-static int grep_submodule(struct grep_opt *opt, const struct object_id *oid,
- const char *filename, const char *path)
-{
- if (!is_submodule_active(the_repository, path))
- return 0;
- if (!is_submodule_populated_gently(path, NULL)) {
- /*
- * If searching history, check for the presence of the
- * submodule's gitdir before skipping the submodule.
- */
- if (oid) {
- const struct submodule *sub =
- submodule_from_path(null_sha1, path);
- if (sub)
- path = git_path("modules/%s", sub->name);
-
- if (!(is_directory(path) && is_git_directory(path)))
- return 0;
- } else {
- return 0;
- }
+ init_tree_desc(&tree, data, size);
+ hit = grep_tree(opt, pathspec, &tree, &base, base.len,
+ object->type == OBJ_COMMIT, &submodule);
+ strbuf_release(&base);
+ free(data);
+ } else {
+ hit = grep_cache(opt, &submodule, pathspec, 1);
}
-#ifndef NO_PTHREADS
- if (num_threads) {
- add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, oid);
- return 0;
- } else
-#endif
- {
- struct grep_source gs;
- int hit;
-
- grep_source_init(&gs, GREP_SOURCE_SUBMODULE,
- filename, path, oid);
- hit = grep_submodule_launch(opt, &gs);
-
- grep_source_clear(&gs);
- return hit;
- }
+ repo_clear(&submodule);
+ return hit;
}
-static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
- int cached)
+static int grep_cache(struct grep_opt *opt, struct repository *repo,
+ const struct pathspec *pathspec, int cached)
{
int hit = 0;
int nr;
struct strbuf name = STRBUF_INIT;
int name_base_len = 0;
- if (super_prefix) {
- name_base_len = strlen(super_prefix);
- strbuf_addstr(&name, super_prefix);
+ if (repo->submodule_prefix) {
+ name_base_len = strlen(repo->submodule_prefix);
+ strbuf_addstr(&name, repo->submodule_prefix);
}
- read_cache();
+ repo_read_index(repo);
- for (nr = 0; nr < active_nr; nr++) {
- const struct cache_entry *ce = active_cache[nr];
+ for (nr = 0; nr < repo->index->cache_nr; nr++) {
+ const struct cache_entry *ce = repo->index->cache[nr];
strbuf_setlen(&name, name_base_len);
strbuf_addstr(&name, ce->name);
@@ -715,14 +498,14 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
ce_skip_worktree(ce)) {
if (ce_stage(ce) || ce_intent_to_add(ce))
continue;
- hit |= grep_oid(opt, &ce->oid, ce->name,
- 0, ce->name);
+ hit |= grep_oid(opt, &ce->oid, name.buf,
+ 0, name.buf);
} else {
- hit |= grep_file(opt, ce->name);
+ hit |= grep_file(opt, name.buf);
}
} else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
submodule_path_match(pathspec, name.buf, NULL)) {
- hit |= grep_submodule(opt, NULL, ce->name, ce->name);
+ hit |= grep_submodule(opt, repo, pathspec, NULL, ce->name, ce->name);
} else {
continue;
}
@@ -730,8 +513,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
if (ce_stage(ce)) {
do {
nr++;
- } while (nr < active_nr &&
- !strcmp(ce->name, active_cache[nr]->name));
+ } while (nr < repo->index->cache_nr &&
+ !strcmp(ce->name, repo->index->cache[nr]->name));
nr--; /* compensate for loop control */
}
if (hit && opt->status_only)
@@ -744,7 +527,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
struct tree_desc *tree, struct strbuf *base, int tn_len,
- int check_attr)
+ int check_attr, struct repository *repo)
{
int hit = 0;
enum interesting match = entry_not_interesting;
@@ -752,8 +535,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
int old_baselen = base->len;
struct strbuf name = STRBUF_INIT;
int name_base_len = 0;
- if (super_prefix) {
- strbuf_addstr(&name, super_prefix);
+ if (repo->submodule_prefix) {
+ strbuf_addstr(&name, repo->submodule_prefix);
name_base_len = name.len;
}
@@ -791,11 +574,11 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
strbuf_addch(base, '/');
init_tree_desc(&sub, data, size);
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
- check_attr);
+ check_attr, repo);
free(data);
} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
- hit |= grep_submodule(opt, entry.oid, base->buf,
- base->buf + tn_len);
+ hit |= grep_submodule(opt, repo, pathspec, entry.oid,
+ base->buf, base->buf + tn_len);
}
strbuf_setlen(base, old_baselen);
@@ -809,7 +592,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
}
static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
- struct object *obj, const char *name, const char *path)
+ struct object *obj, const char *name, const char *path,
+ struct repository *repo)
{
if (obj->type == OBJ_BLOB)
return grep_oid(opt, &obj->oid, name, 0, path);
@@ -828,10 +612,6 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
if (!data)
die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
- /* Use parent's name as base when recursing submodules */
- if (recurse_submodules && parent_basename)
- name = parent_basename;
-
len = name ? strlen(name) : 0;
strbuf_init(&base, PATH_MAX + len + 1);
if (len) {
@@ -840,7 +620,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
}
init_tree_desc(&tree, data, size);
hit = grep_tree(opt, pathspec, &tree, &base, base.len,
- obj->type == OBJ_COMMIT);
+ obj->type == OBJ_COMMIT, repo);
strbuf_release(&base);
free(data);
return hit;
@@ -849,6 +629,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
}
static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
+ struct repository *repo,
const struct object_array *list)
{
unsigned int i;
@@ -862,9 +643,10 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
/* load the gitmodules file for this rev */
if (recurse_submodules) {
submodule_free();
- gitmodules_config_sha1(real_obj->oid.hash);
+ gitmodules_config_oid(&real_obj->oid);
}
- if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) {
+ if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path,
+ repo)) {
hit = 1;
if (opt->status_only)
break;
@@ -1005,9 +787,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
N_("ignore files specified via '.gitignore'"), 1),
OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
N_("recursively search in each submodule")),
- OPT_STRING(0, "parent-basename", &parent_basename,
- N_("basename"),
- N_("prepend parent project's basename to output")),
OPT_GROUP(""),
OPT_BOOL('v', "invert-match", &opt.invert,
N_("show non-matching lines")),
@@ -1112,7 +891,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
init_grep_defaults();
git_config(grep_cmd_config, NULL);
grep_init(&opt, prefix);
- super_prefix = get_super_prefix();
/*
* If there is no -- then the paths must exist in the working
@@ -1205,8 +983,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
break;
}
- if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH,
- oid.hash, &oc)) {
+ if (get_oid_with_context(arg, GET_OID_RECORD_PATH,
+ &oid, &oc)) {
if (seen_dashdash)
die(_("unable to resolve revision: %s"), arg);
break;
@@ -1270,13 +1048,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
}
#endif
- if (recurse_submodules) {
- gitmodules_config();
- compile_submodule_options(&opt, argv + i, cached, untracked,
- opt_exclude, use_index,
- pattern_type_arg);
- }
-
if (show_in_pager && (cached || list.nr))
die(_("--open-files-in-pager only works on the worktree"));
@@ -1318,11 +1089,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!cached)
setup_work_tree();
- hit = grep_cache(&opt, &pathspec, cached);
+ hit = grep_cache(&opt, the_repository, &pathspec, cached);
} else {
if (cached)
die(_("both --cached and trees are given."));
- hit = grep_objects(&opt, &pathspec, &list);
+
+ hit = grep_objects(&opt, &pathspec, the_repository, &list);
}
if (num_threads)
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index d04baf999a..c532ff9320 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -16,7 +16,7 @@
* needs to bypass the data conversion performed by, and the type
* limitation imposed by, index_fd() and its callees.
*/
-static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigned flags)
+static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags)
{
struct strbuf buf = STRBUF_INIT;
int ret;
@@ -24,7 +24,7 @@ static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigne
if (strbuf_read(&buf, fd, 4096) < 0)
ret = -1;
else
- ret = hash_sha1_file_literally(buf.buf, buf.len, type, sha1, flags);
+ ret = hash_sha1_file_literally(buf.buf, buf.len, type, oid, flags);
strbuf_release(&buf);
return ret;
}
@@ -33,16 +33,16 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
int literally)
{
struct stat st;
- unsigned char sha1[20];
+ struct object_id oid;
if (fstat(fd, &st) < 0 ||
(literally
- ? hash_literally(sha1, fd, type, flags)
- : index_fd(sha1, fd, &st, type_from_string(type), path, flags)))
+ ? hash_literally(&oid, fd, type, flags)
+ : index_fd(&oid, fd, &st, type_from_string(type), path, flags)))
die((flags & HASH_WRITE_OBJECT)
? "Unable to add %s to database"
: "Unable to hash %s", path);
- printf("%s\n", sha1_to_hex(sha1));
+ printf("%s\n", oid_to_hex(&oid));
maybe_flush_or_die(stdout, "hash to stdout");
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 26828c1d82..f2be145e12 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -12,6 +12,7 @@
#include "exec_cmd.h"
#include "streaming.h"
#include "thread-utils.h"
+#include "packfile.h"
static const char index_pack_usage[] =
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 175f14797b..b742539d4d 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -16,34 +16,119 @@ static const char * const git_interpret_trailers_usage[] = {
NULL
};
+static enum trailer_where where;
+static enum trailer_if_exists if_exists;
+static enum trailer_if_missing if_missing;
+
+static int option_parse_where(const struct option *opt,
+ const char *arg, int unset)
+{
+ return trailer_set_where(&where, arg);
+}
+
+static int option_parse_if_exists(const struct option *opt,
+ const char *arg, int unset)
+{
+ return trailer_set_if_exists(&if_exists, arg);
+}
+
+static int option_parse_if_missing(const struct option *opt,
+ const char *arg, int unset)
+{
+ return trailer_set_if_missing(&if_missing, arg);
+}
+
+static void new_trailers_clear(struct list_head *trailers)
+{
+ struct list_head *pos, *tmp;
+ struct new_trailer_item *item;
+
+ list_for_each_safe(pos, tmp, trailers) {
+ item = list_entry(pos, struct new_trailer_item, list);
+ list_del(pos);
+ free(item);
+ }
+}
+
+static int option_parse_trailer(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct list_head *trailers = opt->value;
+ struct new_trailer_item *item;
+
+ if (unset) {
+ new_trailers_clear(trailers);
+ return 0;
+ }
+
+ if (!arg)
+ return -1;
+
+ item = xmalloc(sizeof(*item));
+ item->text = arg;
+ item->where = where;
+ item->if_exists = if_exists;
+ item->if_missing = if_missing;
+ list_add_tail(&item->list, trailers);
+ return 0;
+}
+
+static int parse_opt_parse(const struct option *opt, const char *arg,
+ int unset)
+{
+ struct process_trailer_options *v = opt->value;
+ v->only_trailers = 1;
+ v->only_input = 1;
+ v->unfold = 1;
+ return 0;
+}
+
int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
{
- int in_place = 0;
- int trim_empty = 0;
- struct string_list trailers = STRING_LIST_INIT_NODUP;
+ struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+ LIST_HEAD(trailers);
struct option options[] = {
- OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
- OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
- OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
- N_("trailer(s) to add")),
+ OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
+ OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
+
+ OPT_CALLBACK(0, "where", NULL, N_("action"),
+ N_("where to place the new trailer"), option_parse_where),
+ OPT_CALLBACK(0, "if-exists", NULL, N_("action"),
+ N_("action if trailer already exists"), option_parse_if_exists),
+ OPT_CALLBACK(0, "if-missing", NULL, N_("action"),
+ N_("action if trailer is missing"), option_parse_if_missing),
+
+ OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
+ OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")),
+ OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
+ { OPTION_CALLBACK, 0, "parse", &opts, NULL, N_("set parsing options"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse },
+ OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
+ N_("trailer(s) to add"), option_parse_trailer),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options,
git_interpret_trailers_usage, 0);
+ if (opts.only_input && !list_empty(&trailers))
+ usage_msg_opt(
+ _("--trailer with --only-input does not make sense"),
+ git_interpret_trailers_usage,
+ options);
+
if (argc) {
int i;
for (i = 0; i < argc; i++)
- process_trailers(argv[i], in_place, trim_empty, &trailers);
+ process_trailers(argv[i], &opts, &trailers);
} else {
- if (in_place)
+ if (opts.in_place)
die(_("no input file given for in-place editing"));
- process_trailers(NULL, in_place, trim_empty, &trailers);
+ process_trailers(NULL, &opts, &trailers);
}
- string_list_clear(&trailers, 0);
+ new_trailers_clear(&trailers);
return 0;
}
diff --git a/builtin/log.c b/builtin/log.c
index c6362cf92e..f8cccbc964 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -27,6 +27,7 @@
#include "version.h"
#include "mailmap.h"
#include "gpg-interface.h"
+#include "progress.h"
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
@@ -58,9 +59,9 @@ static int auto_decoration_style(void)
return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
}
-static int parse_decoration_style(const char *var, const char *value)
+static int parse_decoration_style(const char *value)
{
- switch (git_config_maybe_bool(var, value)) {
+ switch (git_parse_maybe_bool(value)) {
case 1:
return DECORATE_SHORT_REFS;
case 0:
@@ -82,7 +83,7 @@ static int decorate_callback(const struct option *opt, const char *arg, int unse
if (unset)
decoration_style = 0;
else if (arg)
- decoration_style = parse_decoration_style("command line", arg);
+ decoration_style = parse_decoration_style(arg);
else
decoration_style = DECORATE_SHORT_REFS;
@@ -372,11 +373,14 @@ static int cmd_log_walk(struct rev_info *rev)
*/
rev->max_count++;
if (!rev->reflog_info) {
- /* we allow cycles in reflog ancestry */
+ /*
+ * We may show a given commit multiple times when
+ * walking the reflogs.
+ */
free_commit_buffer(commit);
+ free_commit_list(commit->parents);
+ commit->parents = NULL;
}
- free_commit_list(commit->parents);
- commit->parents = NULL;
if (saved_nrl < rev->diffopt.needed_rename_limit)
saved_nrl = rev->diffopt.needed_rename_limit;
if (rev->diffopt.degraded_cc_to_c)
@@ -409,7 +413,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "log.date"))
return git_config_string(&default_date_mode, var, value);
if (!strcmp(var, "log.decorate")) {
- decoration_style = parse_decoration_style(var, value);
+ decoration_style = parse_decoration_style(value);
if (decoration_style < 0)
decoration_style = 0; /* maybe warn? */
return 0;
@@ -484,8 +488,8 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
!DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
return stream_blob_to_fd(1, oid, NULL, 0);
- if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
- oidc.hash, &obj_context))
+ if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH,
+ &oidc, &obj_context))
die(_("Not a valid object name %s"), obj_name);
if (!obj_context.path ||
!textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
@@ -821,7 +825,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
return 0;
}
if (!strcmp(var, "format.from")) {
- int b = git_config_maybe_bool(var, value);
+ int b = git_parse_maybe_bool(value);
free(from);
if (b < 0)
from = xstrdup(value);
@@ -1419,6 +1423,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
char *branch_name = NULL;
char *base_commit = NULL;
struct base_tree_info bases;
+ int show_progress = 0;
+ struct progress *progress = NULL;
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
@@ -1490,6 +1496,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
OPT_FILENAME(0, "signature-file", &signature_file,
N_("add a signature from a file")),
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
+ OPT_BOOL(0, "progress", &show_progress,
+ N_("show progress while generating patches")),
OPT_END()
};
@@ -1749,8 +1757,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
start_number--;
}
rev.add_signoff = do_signoff;
+
+ if (show_progress)
+ progress = start_delayed_progress(_("Generating patches"), total);
while (0 <= --nr) {
int shown;
+ display_progress(progress, total - nr);
commit = list[nr];
rev.nr = total - nr + (start_number - 1);
/* Make the second and subsequent mails replies to the first */
@@ -1815,6 +1827,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!use_stdout)
fclose(rev.diffopt.file);
}
+ stop_progress(&progress);
free(list);
free(branch_name);
string_list_clear(&extra_to, 0);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index b8514a0029..e1339e6d17 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -19,6 +19,7 @@
#include "pathspec.h"
#include "run-command.h"
#include "submodule.h"
+#include "submodule-config.h"
static int abbrev;
static int show_deleted;
@@ -210,8 +211,6 @@ static void show_submodule(struct repository *superproject,
if (repo_read_index(&submodule) < 0)
die("index file corrupt");
- repo_read_gitmodules(&submodule);
-
show_files(&submodule, dir);
repo_clear(&submodule);
@@ -362,7 +361,7 @@ static void prune_index(struct index_state *istate,
int pos;
unsigned int first, last;
- if (!prefix)
+ if (!prefix || !istate->cache_nr)
return;
pos = index_name_pos(istate, prefix, prefixlen);
if (pos < 0)
@@ -378,8 +377,7 @@ static void prune_index(struct index_state *istate,
}
last = next;
}
- memmove(istate->cache, istate->cache + pos,
- (last - pos) * sizeof(struct cache_entry *));
+ MOVE_ARRAY(istate->cache, istate->cache + pos, last - pos);
istate->cache_nr = last - pos;
}
@@ -610,9 +608,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (require_work_tree && !is_inside_work_tree())
setup_work_tree();
- if (recurse_submodules)
- repo_read_gitmodules(the_repository);
-
if (recurse_submodules &&
(show_stage || show_deleted || show_others || show_unmerged ||
show_killed || show_modified || show_resolve_undo || with_tree))
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index bad6735c76..d01ddecf66 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -213,11 +213,11 @@ static void unresolved_directory(const struct traverse_info *info,
newbase = traverse_path(info, p);
-#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid->hash : NULL)
- buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0));
- buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1));
- buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2));
-#undef ENTRY_SHA1
+#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid : NULL)
+ buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
+ buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
+ buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
+#undef ENTRY_OID
merge_trees(t, newbase);
@@ -347,12 +347,12 @@ static void merge_trees(struct tree_desc t[3], const char *base)
static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
{
- unsigned char sha1[20];
+ struct object_id oid;
void *buf;
- if (get_sha1(rev, sha1))
+ if (get_oid(rev, &oid))
die("unknown rev %s", rev);
- buf = fill_tree_descriptor(desc, sha1);
+ buf = fill_tree_descriptor(desc, &oid);
if (!buf)
die("%s is not a tree", rev);
return buf;
diff --git a/builtin/merge.c b/builtin/merge.c
index 900bafdb45..7df3fe3927 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -32,6 +32,7 @@
#include "gpg-interface.h"
#include "sequencer.h"
#include "string-list.h"
+#include "packfile.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -70,6 +71,7 @@ static int continue_current_merge;
static int allow_unrelated_histories;
static int show_progress = -1;
static int default_to_upstream = 1;
+static int signoff;
static const char *sign_commit;
static struct strategy all_strategy[] = {
@@ -233,6 +235,7 @@ static struct option builtin_merge_options[] = {
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
+ OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
OPT_END()
};
@@ -537,7 +540,7 @@ static void parse_branch_merge_options(char *bmo)
die(_("Bad branch.%s.mergeoptions string: %s"), branch,
split_cmdline_strerror(argc));
REALLOC_ARRAY(argv, argc + 2);
- memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+ MOVE_ARRAY(argv + 1, argv, argc + 1);
argc++;
argv[0] = "branch.*.mergeoptions";
parse_options(argc, argv, NULL, builtin_merge_options,
@@ -566,7 +569,7 @@ static int git_merge_config(const char *k, const char *v, void *cb)
else if (!strcmp(k, "merge.renormalize"))
option_renormalize = git_config_bool(k, v);
else if (!strcmp(k, "merge.ff")) {
- int boolval = git_config_maybe_bool(k, v);
+ int boolval = git_parse_maybe_bool(v);
if (0 <= boolval) {
fast_forward = boolval ? FF_ALLOW : FF_NO;
} else if (v && !strcmp(v, "only")) {
@@ -756,13 +759,19 @@ N_("Please enter a commit message to explain why this merge is necessary,\n"
"Lines starting with '%c' will be ignored, and an empty message aborts\n"
"the commit.\n");
+static void write_merge_heads(struct commit_list *);
static void prepare_to_commit(struct commit_list *remoteheads)
{
struct strbuf msg = STRBUF_INIT;
strbuf_addbuf(&msg, &merge_msg);
strbuf_addch(&msg, '\n');
+ if (squash)
+ BUG("the control must not reach here under --squash");
if (0 < option_edit)
strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
+ 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);
if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
git_path_merge_msg(), "merge", NULL))
@@ -904,7 +913,7 @@ static int setup_with_upstream(const char ***argv)
return i;
}
-static void write_merge_state(struct commit_list *remoteheads)
+static void write_merge_heads(struct commit_list *remoteheads)
{
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
@@ -920,8 +929,6 @@ static void write_merge_state(struct commit_list *remoteheads)
strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
}
write_file_buf(git_path_merge_head(), buf.buf, buf.len);
- strbuf_addch(&merge_msg, '\n');
- write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
strbuf_reset(&buf);
if (fast_forward == FF_NO)
@@ -929,6 +936,13 @@ static void write_merge_state(struct commit_list *remoteheads)
write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
}
+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);
+}
+
static int default_edit_option(void)
{
static const char name[] = "GIT_MERGE_AUTOEDIT";
@@ -940,7 +954,7 @@ static int default_edit_option(void)
return 0;
if (e) {
- int v = git_config_maybe_bool(name, e);
+ int v = git_parse_maybe_bool(e);
if (v < 0)
die(_("Bad value '%s' in environment '%s'"), e, name);
return v;
@@ -1117,8 +1131,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* current branch.
*/
branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
- if (branch && starts_with(branch, "refs/heads/"))
- branch += 11;
+ if (branch)
+ skip_prefix(branch, "refs/heads/", &branch);
if (!branch || is_null_oid(&head_oid))
head_commit = NULL;
else
diff --git a/builtin/mv.c b/builtin/mv.c
index dcf6736b5b..ffdd5f01a1 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -81,7 +81,7 @@ static void prepare_move_submodule(const char *src, int first,
struct strbuf submodule_dotgit = STRBUF_INIT;
if (!S_ISGITLINK(active_cache[first]->ce_mode))
die(_("Directory %s is in index and no submodule?"), src);
- if (!is_staging_gitmodules_ok())
+ if (!is_staging_gitmodules_ok(&the_index))
die(_("Please stage your changes to .gitmodules or stash them to proceed"));
strbuf_addf(&submodule_dotgit, "%s/.git", src);
*submodule_gitfile = read_gitfile(submodule_dotgit.buf);
@@ -131,7 +131,6 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
- gitmodules_config();
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, builtin_mv_options,
diff --git a/builtin/notes.c b/builtin/notes.c
index 77573cf1ea..4303848e04 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -456,7 +456,7 @@ static int add(int argc, const char **argv, const char *prefix)
oid_to_hex(&object));
}
- prepare_note_data(&object, &d, note->hash);
+ prepare_note_data(&object, &d, note ? note->hash : NULL);
if (d.buf.len || allow_empty) {
write_note_data(&d, new_note.hash);
if (add_note(t, &object, &new_note, combine_notes_overwrite))
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index f4a8441fe9..82ad6e0c81 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -25,6 +25,7 @@
#include "sha1-array.h"
#include "argv-array.h"
#include "mru.h"
+#include "packfile.h"
static const char *pack_usage[] = {
N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
@@ -1289,7 +1290,7 @@ static int done_pbase_path_pos(unsigned hash)
static int check_pbase_path(unsigned hash)
{
- int pos = (!done_pbase_paths) ? -1 : done_pbase_path_pos(hash);
+ int pos = done_pbase_path_pos(hash);
if (0 <= pos)
return 1;
pos = -pos - 1;
@@ -1298,9 +1299,8 @@ static int check_pbase_path(unsigned hash)
done_pbase_paths_alloc);
done_pbase_paths_num++;
if (pos < done_pbase_paths_num)
- memmove(done_pbase_paths + pos + 1,
- done_pbase_paths + pos,
- (done_pbase_paths_num - pos - 1) * sizeof(unsigned));
+ MOVE_ARRAY(done_pbase_paths + pos + 1, done_pbase_paths + pos,
+ done_pbase_paths_num - pos - 1);
done_pbase_paths[pos] = hash;
return 0;
}
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index cb1df1c761..aaa8136322 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -7,6 +7,7 @@
*/
#include "builtin.h"
+#include "packfile.h"
#define BLKSIZE 512
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index ac978ad401..419238171d 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -2,6 +2,7 @@
#include "cache.h"
#include "progress.h"
#include "parse-options.h"
+#include "packfile.h"
static const char * const prune_packed_usage[] = {
N_("git prune-packed [-n | --dry-run] [-q | --quiet]"),
@@ -37,8 +38,7 @@ static int prune_object(const struct object_id *oid, const char *path,
void prune_packed_objects(int opts)
{
if (opts & PRUNE_PACKED_VERBOSE)
- progress = start_progress_delay(_("Removing duplicate objects"),
- 256, 95, 2);
+ progress = start_delayed_progress(_("Removing duplicate objects"), 256);
for_each_loose_file_in_objdir(get_object_directory(),
prune_object, NULL, prune_subdir, &opts);
diff --git a/builtin/prune.c b/builtin/prune.c
index c378690545..cddabf26a9 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -138,7 +138,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
if (show_progress == -1)
show_progress = isatty(2);
if (show_progress)
- progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
+ progress = start_delayed_progress(_("Checking connectivity"), 0);
mark_reachable_objects(&revs, 1, expire, progress);
stop_progress(&progress);
diff --git a/builtin/pull.c b/builtin/pull.c
index 9b86e519b1..7fe281414e 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -39,7 +39,7 @@ enum rebase_type {
static enum rebase_type parse_config_rebase(const char *key, const char *value,
int fatal)
{
- int v = git_config_maybe_bool("pull.rebase", value);
+ int v = git_parse_maybe_bool(value);
if (!v)
return REBASE_FALSE;
@@ -274,7 +274,7 @@ static const char *config_get_ff(void)
if (git_config_get_value("pull.ff", &value))
return NULL;
- switch (git_config_maybe_bool("pull.ff", value)) {
+ switch (git_parse_maybe_bool(value)) {
case 0:
return "--no-ff";
case 1:
diff --git a/builtin/push.c b/builtin/push.c
index 03846e8379..2ac8104229 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -481,7 +481,7 @@ static int git_push_config(const char *k, const char *v, void *cb)
} else if (!strcmp(k, "push.gpgsign")) {
const char *value;
if (!git_config_get_value("push.gpgsign", &value)) {
- switch (git_config_maybe_bool("push.gpgsign", value)) {
+ switch (git_parse_maybe_bool(value)) {
case 0:
set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
break;
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index d5f618d086..bf87a2710b 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -164,8 +164,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
argc = parse_options(argc, argv, unused_prefix, read_tree_options,
read_tree_usage, 0);
- load_submodule_cache();
-
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
prefix_set = opts.prefix ? 1 : 0;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index cabdc55e09..52c63ebfdc 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -23,6 +23,7 @@
#include "fsck.h"
#include "tmp-objdir.h"
#include "oidset.h"
+#include "packfile.h"
static const char * const receive_pack_usage[] = {
N_("git receive-pack <git-dir>"),
@@ -919,9 +920,9 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
*/
static int head_has_history(void)
{
- unsigned char sha1[20];
+ struct object_id oid;
- return !get_sha1("HEAD", sha1);
+ return !get_oid("HEAD", &oid);
}
static const char *push_to_deploy(unsigned char *sha1,
@@ -1138,7 +1139,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
if (ref_transaction_delete(transaction,
namespaced_name,
- old_oid->hash,
+ old_oid ? old_oid->hash : NULL,
0, "push", &err)) {
rp_error("%s", err.buf);
strbuf_release(&err);
diff --git a/builtin/remote.c b/builtin/remote.c
index 6273c0c23c..a995ea86c1 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -301,7 +301,7 @@ static int config_read_branches(const char *key, const char *value, void *cb)
}
string_list_append(&info->merge, xstrdup(value));
} else {
- int v = git_config_maybe_bool(orig_key, value);
+ int v = git_parse_maybe_bool(value);
if (v >= 0)
info->rebase = v;
else if (!strcmp(value, "preserve"))
diff --git a/builtin/replace.c b/builtin/replace.c
index fba336a68a..3e71a77152 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -50,7 +50,7 @@ static int show_reference(const char *refname, const struct object_id *oid,
struct object_id object;
enum object_type obj_type, repl_type;
- if (get_sha1(refname, object.hash))
+ if (get_oid(refname, &object))
return error("Failed to resolve '%s' as a valid ref.", refname);
obj_type = sha1_object_info(object.hash, NULL);
@@ -269,7 +269,7 @@ static void import_object(struct object_id *oid, enum object_type type,
if (fstat(fd, &st) < 0)
die_errno("unable to fstat %s", filename);
- if (index_fd(oid->hash, fd, &st, type, NULL, flags) < 0)
+ if (index_fd(oid, fd, &st, type, NULL, flags) < 0)
die("unable to write object to database");
/* index_fd close()s fd for us */
}
@@ -365,7 +365,7 @@ static void check_one_mergetag(struct commit *commit,
/* iterate over new parents */
for (i = 1; i < mergetag_data->argc; i++) {
struct object_id oid;
- if (get_sha1(mergetag_data->argv[i], oid.hash) < 0)
+ if (get_oid(mergetag_data->argv[i], &oid) < 0)
die(_("Not a valid object name: '%s'"), mergetag_data->argv[i]);
if (!oidcmp(&tag->tagged->oid, &oid))
return; /* found */
diff --git a/builtin/reset.c b/builtin/reset.c
index 7aeaea2737..d72c7d1c96 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -75,13 +75,13 @@ static int reset_index(const struct object_id *oid, int reset_type, int quiet)
struct object_id head_oid;
if (get_oid("HEAD", &head_oid))
return error(_("You do not have a valid HEAD."));
- if (!fill_tree_descriptor(desc, head_oid.hash))
+ if (!fill_tree_descriptor(desc, &head_oid))
return error(_("Failed to find tree of HEAD."));
nr++;
opts.fn = twoway_merge;
}
- if (!fill_tree_descriptor(desc + nr - 1, oid->hash))
+ if (!fill_tree_descriptor(desc + nr - 1, oid))
return error(_("Failed to find tree of %s."), oid_to_hex(oid));
if (unpack_trees(nr, desc, &opts))
return -1;
@@ -156,6 +156,7 @@ static int read_from_tree(const struct pathspec *pathspec,
opt.output_format = DIFF_FORMAT_CALLBACK;
opt.format_callback = update_index_from_diff;
opt.format_callback_data = &intent_to_add;
+ opt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
if (do_diff_cache(tree_oid, &opt))
return 1;
@@ -219,8 +220,8 @@ static void parse_args(struct pathspec *pathspec,
* has to be unambiguous. If there is a single argument, it
* can not be a tree
*/
- else if ((!argv[1] && !get_sha1_committish(argv[0], unused.hash)) ||
- (argv[1] && !get_sha1_treeish(argv[0], unused.hash))) {
+ else if ((!argv[1] && !get_oid_committish(argv[0], &unused)) ||
+ (argv[1] && !get_oid_treeish(argv[0], &unused))) {
/*
* Ok, argv[0] looks like a commit/tree; it should not
* be a filename.
@@ -308,15 +309,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH);
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
- load_submodule_cache();
-
- unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", oid.hash);
+ unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
if (unborn) {
/* reset on unborn branch: treat as reset to empty tree */
hashcpy(oid.hash, EMPTY_TREE_SHA1_BIN);
} else if (!pathspec.nr) {
struct commit *commit;
- if (get_sha1_committish(rev, oid.hash))
+ if (get_oid_committish(rev, &oid))
die(_("Failed to resolve '%s' as a valid revision."), rev);
commit = lookup_commit_reference(&oid);
if (!commit)
@@ -324,7 +323,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
oidcpy(&oid, &commit->object.oid);
} else {
struct tree *tree;
- if (get_sha1_treeish(rev, oid.hash))
+ if (get_oid_treeish(rev, &oid))
die(_("Failed to resolve '%s' as a valid tree."), rev);
tree = parse_tree_indirect(&oid);
if (!tree)
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 95d84d5cda..c1c74d4a79 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -11,6 +11,7 @@
#include "graph.h"
#include "bisect.h"
#include "progress.h"
+#include "reflog-walk.h"
static const char rev_list_usage[] =
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -122,6 +123,7 @@ static void show_commit(struct commit *commit, void *data)
ctx.date_mode_explicit = revs->date_mode_explicit;
ctx.fmt = revs->commit_format;
ctx.output_encoding = get_log_output_encoding();
+ ctx.color = revs->diffopt.use_color;
pretty_print_commit(&ctx, commit, &buf);
if (buf.len) {
if (revs->commit_format != CMIT_FMT_ONELINE)
@@ -348,9 +350,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
/* Only --header was specified */
revs.commit_format = CMIT_FMT_RAW;
- if ((!revs.commits &&
+ if ((!revs.commits && reflog_walk_empty(revs.reflog_info) &&
(!(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
- !revs.pending.nr)) ||
+ !revs.pending.nr) &&
+ !revs.rev_input_given) ||
revs.diff)
usage(rev_list_usage);
@@ -364,7 +367,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
revs.limited = 1;
if (show_progress)
- progress = start_progress_delay(show_progress, 0, 0, 2);
+ progress = start_delayed_progress(show_progress, 0);
if (use_bitmap_index && !revs.prune) {
if (revs.count && !revs.left_right && !revs.cherry_mark) {
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index c78b7b33d6..2bd28d3c08 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -274,7 +274,7 @@ static int try_difference(const char *arg)
return 0;
}
- if (!get_sha1_committish(this, oid.hash) && !get_sha1_committish(next, end.hash)) {
+ if (!get_oid_committish(this, &oid) && !get_oid_committish(next, &end)) {
show_rev(NORMAL, &end, next);
show_rev(symmetric ? NORMAL : REVERSED, &oid, this);
if (symmetric) {
@@ -328,7 +328,7 @@ static int try_parent_shorthands(const char *arg)
return 0;
*dotdot = 0;
- if (get_sha1_committish(arg, oid.hash)) {
+ if (get_oid_committish(arg, &oid)) {
*dotdot = '^';
return 0;
}
@@ -702,7 +702,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
quiet = 1;
- flags |= GET_SHA1_QUIETLY;
+ flags |= GET_OID_QUIETLY;
continue;
}
if (opt_with_value(arg, "--short", &arg)) {
@@ -911,7 +911,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
name++;
type = REVERSED;
}
- if (!get_sha1_with_context(name, flags, oid.hash, &unused)) {
+ if (!get_oid_with_context(name, flags, &oid, &unused)) {
if (verify)
revs_count++;
else
diff --git a/builtin/revert.c b/builtin/revert.c
index 16028b9ea8..b9d927eb09 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -155,6 +155,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
"--strategy-option", opts->xopts ? 1 : 0,
"-x", opts->record_origin,
"--ff", opts->allow_ff,
+ "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
+ "--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
NULL);
}
diff --git a/builtin/rm.c b/builtin/rm.c
index 52826d1379..d91451fea1 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -255,7 +255,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
struct pathspec pathspec;
char *seen;
- gitmodules_config();
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, builtin_rm_options,
@@ -286,7 +285,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
list.entry[list.nr].name = xstrdup(ce->name);
list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
if (list.entry[list.nr++].is_submodule &&
- !is_staging_gitmodules_ok())
+ !is_staging_gitmodules_ok(&the_index))
die (_("Please stage your changes to .gitmodules or stash them to proceed"));
}
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 633e0c3cdd..fc4f0bb5fb 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -105,7 +105,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)
if (!strcmp(k, "push.gpgsign")) {
const char *value;
if (!git_config_get_value("push.gpgsign", &value)) {
- switch (git_config_maybe_bool("push.gpgsign", value)) {
+ switch (git_parse_maybe_bool(value)) {
case 0:
args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
break;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 7073a3eb97..84547d6fba 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -393,7 +393,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid,
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
+ if (get_oid(refname + ofs, &tmp) || oidcmp(&tmp, oid))
ofs = 5;
return append_ref(refname + ofs, oid, 0);
}
@@ -408,7 +408,7 @@ static int append_remote_ref(const char *refname, const struct object_id *oid,
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
+ if (get_oid(refname + ofs, &tmp) || oidcmp(&tmp, oid))
ofs = 5;
return append_ref(refname + ofs, oid, 0);
}
@@ -514,7 +514,7 @@ static int show_independent(struct commit **rev,
static void append_one_rev(const char *av)
{
struct object_id revkey;
- if (!get_sha1(av, revkey.hash)) {
+ if (!get_oid(av, &revkey)) {
append_ref(av, &revkey, 0);
return;
}
@@ -554,7 +554,7 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
return 0;
}
- return git_color_default_config(var, value, cb);
+ return git_default_config(var, value, cb);
}
static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
@@ -808,7 +808,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
die(Q_("cannot handle more than %d rev.",
"cannot handle more than %d revs.",
MAX_REVS), MAX_REVS);
- if (get_sha1(ref_name[num_rev], revkey.hash))
+ if (get_oid(ref_name[num_rev], &revkey))
die(_("'%s' is not a valid ref."), ref_name[num_rev]);
commit = lookup_commit_reference(&revkey);
if (!commit)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6abdad3294..818fe74f0a 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -275,8 +275,6 @@ static void module_list_active(struct module_list *list)
int i;
struct module_list active_modules = MODULE_LIST_INIT;
- gitmodules_config();
-
for (i = 0; i < list->nr; i++) {
const struct cache_entry *ce = list->entries[i];
@@ -337,9 +335,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
struct strbuf sb = STRBUF_INIT;
char *upd = NULL, *url = NULL, *displaypath;
- /* Only loads from .gitmodules, no overlay with .git/config */
- gitmodules_config();
-
if (prefix && get_super_prefix())
die("BUG: cannot have prefix and superprefix");
else if (prefix)
@@ -350,7 +345,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
} else
displaypath = xstrdup(path);
- sub = submodule_from_path(null_sha1, path);
+ sub = submodule_from_path(&null_oid, path);
if (!sub)
die(_("No url found for submodule path '%s' in .gitmodules"),
@@ -475,8 +470,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
if (argc != 2)
usage(_("git submodule--helper name <path>"));
- gitmodules_config();
- sub = submodule_from_path(null_sha1, argv[1]);
+ sub = submodule_from_path(&null_oid, argv[1]);
if (!sub)
die(_("no submodule mapping found in .gitmodules for path '%s'"),
@@ -780,6 +774,10 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
struct strbuf *out)
{
const struct submodule *sub = NULL;
+ const char *url = NULL;
+ const char *update_string;
+ enum submodule_update_type update_type;
+ char *key;
struct strbuf displaypath_sb = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
const char *displaypath = NULL;
@@ -795,7 +793,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
goto cleanup;
}
- sub = submodule_from_path(null_sha1, ce->name);
+ sub = submodule_from_path(&null_oid, ce->name);
if (suc->recursive_prefix)
displaypath = relative_path(suc->recursive_prefix,
@@ -808,9 +806,17 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
goto cleanup;
}
+ key = xstrfmt("submodule.%s.update", sub->name);
+ if (!repo_config_get_string_const(the_repository, key, &update_string)) {
+ update_type = parse_submodule_update_type(update_string);
+ } else {
+ update_type = sub->update_strategy.type;
+ }
+ free(key);
+
if (suc->update.type == SM_UPDATE_NONE
|| (suc->update.type == SM_UPDATE_UNSPECIFIED
- && sub->update_strategy.type == SM_UPDATE_NONE)) {
+ && update_type == SM_UPDATE_NONE)) {
strbuf_addf(out, _("Skipping submodule '%s'"), displaypath);
strbuf_addch(out, '\n');
goto cleanup;
@@ -823,6 +829,11 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
}
strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.url", sub->name);
+ if (repo_config_get_string_const(the_repository, sb.buf, &url))
+ url = sub->url;
+
+ strbuf_reset(&sb);
strbuf_addf(&sb, "%s/.git", ce->name);
needs_cloning = !file_exists(sb.buf);
@@ -851,7 +862,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
argv_array_push(&child->args, "--depth=1");
argv_array_pushl(&child->args, "--path", sub->path, NULL);
argv_array_pushl(&child->args, "--name", sub->name, NULL);
- argv_array_pushl(&child->args, "--url", sub->url, NULL);
+ argv_array_pushl(&child->args, "--url", url, NULL);
if (suc->references.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &suc->references)
@@ -930,7 +941,7 @@ static int update_clone_task_finished(int result,
const struct cache_entry *ce;
struct submodule_update_clone *suc = suc_cb;
- int *idxP = *(int**)idx_task_cb;
+ int *idxP = idx_task_cb;
int idx = *idxP;
free(idxP);
@@ -960,10 +971,19 @@ static int update_clone_task_finished(int result,
return 0;
}
+static int gitmodules_update_clone_config(const char *var, const char *value,
+ void *cb)
+{
+ int *max_jobs = cb;
+ if (!strcmp(var, "submodule.fetchjobs"))
+ *max_jobs = parse_submodule_fetchjobs(var, value);
+ return 0;
+}
+
static int update_clone(int argc, const char **argv, const char *prefix)
{
const char *update = NULL;
- int max_jobs = -1;
+ int max_jobs = 1;
struct string_list_item *item;
struct pathspec pathspec;
struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
@@ -1000,6 +1020,9 @@ static int update_clone(int argc, const char **argv, const char *prefix)
};
suc.prefix = prefix;
+ config_from_gitmodules(gitmodules_update_clone_config, &max_jobs);
+ git_config(gitmodules_update_clone_config, &max_jobs);
+
argc = parse_options(argc, argv, prefix, module_update_clone_options,
git_submodule_helper_usage, 0);
@@ -1013,13 +1036,6 @@ static int update_clone(int argc, const char **argv, const char *prefix)
if (pathspec.nr)
suc.warn_if_uninitialized = 1;
- /* Overlay the parsed .gitmodules file with .git/config */
- gitmodules_config();
- git_config(submodule_config, NULL);
-
- if (max_jobs < 0)
- max_jobs = parallel_submodules();
-
run_processes_parallel(max_jobs,
update_clone_get_next_task,
update_clone_start_failure,
@@ -1057,17 +1073,22 @@ static int resolve_relative_path(int argc, const char **argv, const char *prefix
static const char *remote_submodule_branch(const char *path)
{
const struct submodule *sub;
- gitmodules_config();
- git_config(submodule_config, NULL);
+ const char *branch = NULL;
+ char *key;
- sub = submodule_from_path(null_sha1, path);
+ sub = submodule_from_path(&null_oid, path);
if (!sub)
return NULL;
- if (!sub->branch)
+ key = xstrfmt("submodule.%s.branch", sub->name);
+ if (repo_config_get_string_const(the_repository, key, &branch))
+ branch = sub->branch;
+ free(key);
+
+ if (!branch)
return "master";
- if (!strcmp(sub->branch, ".")) {
+ if (!strcmp(branch, ".")) {
unsigned char sha1[20];
const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
@@ -1085,7 +1106,7 @@ static const char *remote_submodule_branch(const char *path)
return refname;
}
- return sub->branch;
+ return branch;
}
static int resolve_remote_submodule_branch(int argc, const char **argv,
@@ -1108,9 +1129,28 @@ static int resolve_remote_submodule_branch(int argc, const char **argv,
static int push_check(int argc, const char **argv, const char *prefix)
{
struct remote *remote;
+ const char *superproject_head;
+ char *head;
+ int detached_head = 0;
+ struct object_id head_oid;
+
+ if (argc < 3)
+ die("submodule--helper push-check requires at least 2 arguments");
- if (argc < 2)
- die("submodule--helper push-check requires at least 1 argument");
+ /*
+ * superproject's resolved head ref.
+ * if HEAD then the superproject is in a detached head state, otherwise
+ * it will be the resolved head ref.
+ */
+ superproject_head = argv[1];
+ argv++;
+ argc--;
+ /* Get the submodule's head ref and determine if it is detached */
+ head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+ if (!head)
+ die(_("Failed to resolve HEAD as a valid ref."));
+ if (!strcmp(head, "HEAD"))
+ detached_head = 1;
/*
* The remote must be configured.
@@ -1133,18 +1173,30 @@ static int push_check(int argc, const char **argv, const char *prefix)
if (rs->pattern || rs->matching)
continue;
- /*
- * LHS must match a single ref
- * NEEDSWORK: add logic to special case 'HEAD' once
- * working with submodules in a detached head state
- * ceases to be the norm.
- */
- if (count_refspec_match(rs->src, local_refs, NULL) != 1)
+ /* LHS must match a single ref */
+ switch (count_refspec_match(rs->src, local_refs, NULL)) {
+ case 1:
+ break;
+ case 0:
+ /*
+ * If LHS matches 'HEAD' then we need to ensure
+ * that it matches the same named branch
+ * checked out in the superproject.
+ */
+ if (!strcmp(rs->src, "HEAD")) {
+ if (!detached_head &&
+ !strcmp(head, superproject_head))
+ break;
+ die("HEAD does not match the named branch in the superproject");
+ }
+ default:
die("src refspec '%s' must name a ref",
rs->src);
+ }
}
free_refspec(refspec_nr, refspec);
}
+ free(head);
return 0;
}
@@ -1173,9 +1225,6 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, embed_gitdir_options,
git_submodule_helper_usage, 0);
- gitmodules_config();
- git_config(submodule_config, NULL);
-
if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
return 1;
@@ -1191,8 +1240,6 @@ static int is_active(int argc, const char **argv, const char *prefix)
if (argc != 2)
die("submodule--helper is-active takes exactly 1 argument");
- gitmodules_config();
-
return !is_submodule_active(the_repository, argv[1]);
}
diff --git a/builtin/tag.c b/builtin/tag.c
index 01154ea8dc..c627794181 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -32,7 +32,8 @@ static const char * const git_tag_usage[] = {
static unsigned int colopts;
static int force_sign_annotate;
-static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
+ struct ref_format *format)
{
struct ref_array array;
char *to_free = NULL;
@@ -43,23 +44,24 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
if (filter->lines == -1)
filter->lines = 0;
- if (!format) {
+ if (!format->format) {
if (filter->lines) {
to_free = xstrfmt("%s %%(contents:lines=%d)",
"%(align:15)%(refname:lstrip=2)%(end)",
filter->lines);
- format = to_free;
+ format->format = to_free;
} else
- format = "%(refname:lstrip=2)";
+ format->format = "%(refname:lstrip=2)";
}
- verify_ref_format(format);
+ if (verify_ref_format(format))
+ die(_("unable to parse format string"));
filter->with_commit_tag_algo = 1;
filter_refs(&array, filter, FILTER_REFS_TAGS);
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++)
- show_ref_array_item(array.items[i], format, 0);
+ show_ref_array_item(array.items[i], format);
ref_array_clear(&array);
free(to_free);
@@ -105,17 +107,17 @@ static int verify_tag(const char *name, const char *ref,
const struct object_id *oid, const void *cb_data)
{
int flags;
- const char *fmt_pretty = cb_data;
+ const struct ref_format *format = cb_data;
flags = GPG_VERIFY_VERBOSE;
- if (fmt_pretty)
+ if (format->format)
flags = GPG_VERIFY_OMIT_STATUS;
- if (gpg_verify_tag(oid->hash, name, flags))
+ if (gpg_verify_tag(oid, name, flags))
return -1;
- if (fmt_pretty)
- pretty_print_ref(name, oid->hash, fmt_pretty);
+ if (format->format)
+ pretty_print_ref(name, oid->hash, format);
return 0;
}
@@ -134,30 +136,6 @@ static const char tag_template_nocleanup[] =
"Lines starting with '%c' will be kept; you may remove them"
" yourself if you want to.\n");
-/* Parse arg given and add it the ref_sorting array */
-static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
-{
- struct ref_sorting *s;
- int len;
-
- s = xcalloc(1, sizeof(*s));
- s->next = *sorting_tail;
- *sorting_tail = s;
-
- if (*arg == '-') {
- s->reverse = 1;
- arg++;
- }
- if (skip_prefix(arg, "version:", &arg) ||
- skip_prefix(arg, "v:", &arg))
- s->version = 1;
-
- len = strlen(arg);
- s->atom = parse_ref_filter_atom(arg, arg+len);
-
- return 0;
-}
-
static int git_tag_config(const char *var, const char *value, void *cb)
{
int status;
@@ -166,7 +144,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "tag.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_sorting_string(value, sorting_tail);
+ parse_ref_sorting(sorting_tail, value);
return 0;
}
@@ -392,7 +370,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct strbuf err = STRBUF_INIT;
struct ref_filter filter;
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
- const char *format = NULL;
+ struct ref_format format = REF_FORMAT_INIT;
int icase = 0;
struct option options[] = {
OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
@@ -431,7 +409,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
parse_opt_object_name, (intptr_t) "HEAD"
},
- OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
+ OPT_STRING( 0 , "format", &format.format, N_("format"),
+ N_("format to use for the output")),
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_END()
};
@@ -461,6 +440,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
cmdmode = 'l';
}
+ if (cmdmode == 'l')
+ setup_auto_pager("tag", 1);
+
if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
@@ -483,7 +465,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
run_column_filter(colopts, &copts);
}
filter.name_patterns = argv;
- ret = list_tags(&filter, sorting, format);
+ ret = list_tags(&filter, sorting, &format);
if (column_active(colopts))
stop_column_filter();
return ret;
@@ -501,9 +483,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (cmdmode == 'd')
return for_each_tag_name(argv, delete_tag, NULL);
if (cmdmode == 'v') {
- if (format)
- verify_ref_format(format);
- return for_each_tag_name(argv, verify_tag, format);
+ if (format.format && verify_ref_format(&format))
+ usage_with_options(git_tag_usage, options);
+ return for_each_tag_name(argv, verify_tag, &format);
}
if (msg.given || msgfile) {
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index 73f1334191..281ca1db6c 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -1,7 +1,7 @@
#include "builtin.h"
#include "config.h"
-static char *create_temp_file(unsigned char *sha1)
+static char *create_temp_file(struct object_id *oid)
{
static char path[50];
void *buf;
@@ -9,9 +9,9 @@ static char *create_temp_file(unsigned char *sha1)
unsigned long size;
int fd;
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf || type != OBJ_BLOB)
- die("unable to read blob object %s", sha1_to_hex(sha1));
+ die("unable to read blob object %s", oid_to_hex(oid));
xsnprintf(path, sizeof(path), ".merge_file_XXXXXX");
fd = xmkstemp(path);
@@ -23,15 +23,15 @@ static char *create_temp_file(unsigned char *sha1)
int cmd_unpack_file(int argc, const char **argv, const char *prefix)
{
- unsigned char sha1[20];
+ struct object_id oid;
if (argc != 2 || !strcmp(argv[1], "-h"))
usage("git unpack-file <sha1>");
- if (get_sha1(argv[1], sha1))
+ if (get_oid(argv[1], &oid))
die("Not a valid object name %s", argv[1]);
git_config(git_default_config, NULL);
- puts(create_temp_file(sha1));
+ puts(create_temp_file(&oid));
return 0;
}
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 56721cf03d..d562f2ec69 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -280,7 +280,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
fill_stat_cache_info(ce, st);
ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
- if (index_path(ce->oid.hash, path, st,
+ if (index_path(&ce->oid, path, st,
info_only ? 0 : HASH_WRITE_OBJECT)) {
free(ce);
return -1;
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 40ccfc193b..6b90c5dead 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -94,10 +94,10 @@ static char *parse_refname(struct strbuf *input, const char **next)
* provided but cannot be converted to a SHA-1, die. flags can
* include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY.
*/
-static int parse_next_sha1(struct strbuf *input, const char **next,
- unsigned char *sha1,
- const char *command, const char *refname,
- int flags)
+static int parse_next_oid(struct strbuf *input, const char **next,
+ struct object_id *oid,
+ const char *command, const char *refname,
+ int flags)
{
struct strbuf arg = STRBUF_INIT;
int ret = 0;
@@ -115,11 +115,11 @@ static int parse_next_sha1(struct strbuf *input, const char **next,
(*next)++;
*next = parse_arg(*next, &arg);
if (arg.len) {
- if (get_sha1(arg.buf, sha1))
+ if (get_oid(arg.buf, oid))
goto invalid;
} else {
/* Without -z, an empty value means all zeros: */
- hashclr(sha1);
+ oidclr(oid);
}
} else {
/* With -z, read the next NUL-terminated line */
@@ -133,13 +133,13 @@ static int parse_next_sha1(struct strbuf *input, const char **next,
*next += arg.len;
if (arg.len) {
- if (get_sha1(arg.buf, sha1))
+ if (get_oid(arg.buf, oid))
goto invalid;
} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
/* With -z, treat an empty value as all zeros: */
warning("%s %s: missing <newvalue>, treating as zero",
command, refname);
- hashclr(sha1);
+ oidclr(oid);
} else {
/*
* With -z, an empty non-required value means
@@ -182,26 +182,25 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
{
struct strbuf err = STRBUF_INIT;
char *refname;
- unsigned char new_sha1[20];
- unsigned char old_sha1[20];
+ struct object_id new_oid, old_oid;
int have_old;
refname = parse_refname(input, &next);
if (!refname)
die("update: missing <ref>");
- if (parse_next_sha1(input, &next, new_sha1, "update", refname,
- PARSE_SHA1_ALLOW_EMPTY))
+ if (parse_next_oid(input, &next, &new_oid, "update", refname,
+ PARSE_SHA1_ALLOW_EMPTY))
die("update %s: missing <newvalue>", refname);
- have_old = !parse_next_sha1(input, &next, old_sha1, "update", refname,
- PARSE_SHA1_OLD);
+ have_old = !parse_next_oid(input, &next, &old_oid, "update", refname,
+ PARSE_SHA1_OLD);
if (*next != line_termination)
die("update %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname,
- new_sha1, have_old ? old_sha1 : NULL,
+ new_oid.hash, have_old ? old_oid.hash : NULL,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@@ -218,22 +217,22 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
{
struct strbuf err = STRBUF_INIT;
char *refname;
- unsigned char new_sha1[20];
+ struct object_id new_oid;
refname = parse_refname(input, &next);
if (!refname)
die("create: missing <ref>");
- if (parse_next_sha1(input, &next, new_sha1, "create", refname, 0))
+ if (parse_next_oid(input, &next, &new_oid, "create", refname, 0))
die("create %s: missing <newvalue>", refname);
- if (is_null_sha1(new_sha1))
+ if (is_null_oid(&new_oid))
die("create %s: zero <newvalue>", refname);
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
- if (ref_transaction_create(transaction, refname, new_sha1,
+ if (ref_transaction_create(transaction, refname, new_oid.hash,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@@ -250,18 +249,18 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
{
struct strbuf err = STRBUF_INIT;
char *refname;
- unsigned char old_sha1[20];
+ struct object_id old_oid;
int have_old;
refname = parse_refname(input, &next);
if (!refname)
die("delete: missing <ref>");
- if (parse_next_sha1(input, &next, old_sha1, "delete", refname,
- PARSE_SHA1_OLD)) {
+ if (parse_next_oid(input, &next, &old_oid, "delete", refname,
+ PARSE_SHA1_OLD)) {
have_old = 0;
} else {
- if (is_null_sha1(old_sha1))
+ if (is_null_oid(&old_oid))
die("delete %s: zero <oldvalue>", refname);
have_old = 1;
}
@@ -270,7 +269,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
die("delete %s: extra input: %s", refname, next);
if (ref_transaction_delete(transaction, refname,
- have_old ? old_sha1 : NULL,
+ have_old ? old_oid.hash : NULL,
update_flags, msg, &err))
die("%s", err.buf);
@@ -286,20 +285,20 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
{
struct strbuf err = STRBUF_INIT;
char *refname;
- unsigned char old_sha1[20];
+ struct object_id old_oid;
refname = parse_refname(input, &next);
if (!refname)
die("verify: missing <ref>");
- if (parse_next_sha1(input, &next, old_sha1, "verify", refname,
- PARSE_SHA1_OLD))
- hashclr(old_sha1);
+ if (parse_next_oid(input, &next, &old_oid, "verify", refname,
+ PARSE_SHA1_OLD))
+ oidclr(&old_oid);
if (*next != line_termination)
die("verify %s: extra input: %s", refname, next);
- if (ref_transaction_verify(transaction, refname, old_sha1,
+ if (ref_transaction_verify(transaction, refname, old_oid.hash,
update_flags, &err))
die("%s", err.buf);
@@ -355,7 +354,7 @@ static void update_refs_stdin(struct ref_transaction *transaction)
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
const char *refname, *oldval;
- unsigned char sha1[20], oldsha1[20];
+ struct object_id oid, oldoid;
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
unsigned int flags = 0;
int create_reflog = 0;
@@ -412,7 +411,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
refname = argv[0];
value = argv[1];
oldval = argv[2];
- if (get_sha1(value, sha1))
+ if (get_oid(value, &oid))
die("%s: not a valid SHA1", value);
}
@@ -422,8 +421,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
* The empty string implies that the reference
* must not already exist:
*/
- hashclr(oldsha1);
- else if (get_sha1(oldval, oldsha1))
+ oidclr(&oldoid);
+ else if (get_oid(oldval, &oldoid))
die("%s: not a valid old SHA1", oldval);
}
@@ -435,10 +434,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
* NULL_SHA1 as "don't care" here:
*/
return delete_ref(msg, refname,
- (oldval && !is_null_sha1(oldsha1)) ? oldsha1 : NULL,
+ (oldval && !is_null_oid(&oldoid)) ? oldoid.hash : NULL,
flags);
else
- return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
+ return update_ref(msg, refname, oid.hash, oldval ? oldoid.hash : NULL,
flags | create_reflog_flag,
UPDATE_REFS_DIE_ON_ERR);
}
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index f9a5f7535a..ad7b79fa5c 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -32,11 +32,11 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
{
int i = 1, verbose = 0, had_error = 0;
unsigned flags = 0;
- char *fmt_pretty = NULL;
+ struct ref_format format = REF_FORMAT_INIT;
const struct option verify_tag_options[] = {
OPT__VERBOSE(&verbose, N_("print tag contents")),
OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW),
- OPT_STRING( 0 , "format", &fmt_pretty, N_("format"), N_("format to use for the output")),
+ OPT_STRING(0, "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_END()
};
@@ -50,26 +50,29 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
if (verbose)
flags |= GPG_VERIFY_VERBOSE;
- if (fmt_pretty) {
- verify_ref_format(fmt_pretty);
+ if (format.format) {
+ if (verify_ref_format(&format))
+ usage_with_options(verify_tag_usage,
+ verify_tag_options);
flags |= GPG_VERIFY_OMIT_STATUS;
}
while (i < argc) {
- unsigned char sha1[20];
+ struct object_id oid;
const char *name = argv[i++];
- if (get_sha1(name, sha1)) {
+
+ if (get_oid(name, &oid)) {
had_error = !!error("tag '%s' not found.", name);
continue;
}
- if (gpg_verify_tag(sha1, name, flags)) {
+ if (gpg_verify_tag(&oid, name, flags)) {
had_error = 1;
continue;
}
- if (fmt_pretty)
- pretty_print_ref(name, sha1, fmt_pretty);
+ if (format.format)
+ pretty_print_ref(name, oid.hash, &format);
}
return had_error;
}
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 5be7ce5c73..9a1f6c49ab 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -6,6 +6,7 @@
#include "csum-file.h"
#include "pack.h"
#include "strbuf.h"
+#include "packfile.h"
static struct bulk_checkin_state {
unsigned plugged:1;
diff --git a/cache-tree.c b/cache-tree.c
index ec23d8c03d..2440d1dc89 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -131,9 +131,8 @@ static int do_invalidate_path(struct cache_tree *it, const char *path)
* move 4 and 5 up one place (2 entries)
* 2 = 6 - 3 - 1 = subtree_nr - pos - 1
*/
- memmove(it->down+pos, it->down+pos+1,
- sizeof(struct cache_tree_sub *) *
- (it->subtree_nr - pos - 1));
+ MOVE_ARRAY(it->down + pos, it->down + pos + 1,
+ it->subtree_nr - pos - 1);
it->subtree_nr--;
}
return 1;
diff --git a/cache.h b/cache.h
index a09a5017fb..a916bc79e3 100644
--- a/cache.h
+++ b/cache.h
@@ -417,7 +417,6 @@ static inline enum object_type object_type(unsigned int mode)
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
-#define GIT_TOPLEVEL_PREFIX_ENVIRONMENT "GIT_INTERNAL_TOPLEVEL_PREFIX"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -433,6 +432,7 @@ static inline enum object_type object_type(unsigned int mode)
#define GITATTRIBUTES_FILE ".gitattributes"
#define INFOATTRIBUTES_FILE "info/attributes"
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
+#define GITMODULES_FILE ".gitmodules"
#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
@@ -684,8 +684,8 @@ extern int ie_modified(const struct index_state *, const struct cache_entry *, s
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
-extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
-extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
+extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
/*
* Record to sd the data from st that we use to check whether a file
@@ -903,20 +903,6 @@ extern void check_repository_format(void);
extern const char *sha1_file_name(const unsigned char *sha1);
/*
- * Return the name of the (local) packfile with the specified sha1 in
- * its name. The return value is a pointer to memory that is
- * overwritten each time this function is called.
- */
-extern char *sha1_pack_name(const unsigned char *sha1);
-
-/*
- * Return the name of the (local) pack index file with the specified
- * sha1 in its name. The return value is a pointer to memory that is
- * overwritten each time this function is called.
- */
-extern char *sha1_pack_index_name(const unsigned char *sha1);
-
-/*
* Return an abbreviated sha1 unique within this repository's object database.
* The result will be at least `len` characters long, and will be NUL
* terminated.
@@ -939,14 +925,7 @@ extern const struct object_id null_oid;
static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
{
- int i;
-
- for (i = 0; i < GIT_SHA1_RAWSZ; i++, sha1++, sha2++) {
- if (*sha1 != *sha2)
- return *sha1 - *sha2;
- }
-
- return 0;
+ return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
}
static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
@@ -1199,7 +1178,7 @@ static inline const unsigned char *lookup_replace_object(const unsigned char *sh
extern int sha1_object_info(const unsigned char *, unsigned long *);
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
-extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
+extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, struct object_id *oid, unsigned flags);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
extern int force_object_loose(const unsigned char *sha1, time_t mtime);
extern int git_open_cloexec(const char *name, int flags);
@@ -1208,15 +1187,10 @@ extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
-/* global flag to enable extra checks when accessing packed objects */
-extern int do_check_packed_object_crc;
-
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
extern int finalize_object_file(const char *tmpfile, const char *filename);
-extern int has_sha1_pack(const unsigned char *sha1);
-
/*
* Open the loose object at path, check its sha1, and return the contents,
* type, and size. If the object is a blob, then "contents" may return NULL,
@@ -1252,8 +1226,6 @@ extern int has_object_file_with_flags(const struct object_id *oid, int flags);
*/
extern int has_loose_object_nonlocal(const unsigned char *sha1);
-extern int has_pack_index(const unsigned char *sha1);
-
extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
/* Helper to check and "touch" a file */
@@ -1291,38 +1263,37 @@ struct object_context {
*/
struct strbuf symlink_path;
/*
- * If GET_SHA1_RECORD_PATH is set, this will record path (if any)
+ * If GET_OID_RECORD_PATH is set, this will record path (if any)
* found when resolving the name. The caller is responsible for
* releasing the memory.
*/
char *path;
};
-#define GET_SHA1_QUIETLY 01
-#define GET_SHA1_COMMIT 02
-#define GET_SHA1_COMMITTISH 04
-#define GET_SHA1_TREE 010
-#define GET_SHA1_TREEISH 020
-#define GET_SHA1_BLOB 040
-#define GET_SHA1_FOLLOW_SYMLINKS 0100
-#define GET_SHA1_RECORD_PATH 0200
-#define GET_SHA1_ONLY_TO_DIE 04000
-
-#define GET_SHA1_DISAMBIGUATORS \
- (GET_SHA1_COMMIT | GET_SHA1_COMMITTISH | \
- GET_SHA1_TREE | GET_SHA1_TREEISH | \
- GET_SHA1_BLOB)
-
-extern int get_sha1(const char *str, unsigned char *sha1);
-extern int get_sha1_commit(const char *str, unsigned char *sha1);
-extern int get_sha1_committish(const char *str, unsigned char *sha1);
-extern int get_sha1_tree(const char *str, unsigned char *sha1);
-extern int get_sha1_treeish(const char *str, unsigned char *sha1);
-extern int get_sha1_blob(const char *str, unsigned char *sha1);
-extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
-extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc);
+#define GET_OID_QUIETLY 01
+#define GET_OID_COMMIT 02
+#define GET_OID_COMMITTISH 04
+#define GET_OID_TREE 010
+#define GET_OID_TREEISH 020
+#define GET_OID_BLOB 040
+#define GET_OID_FOLLOW_SYMLINKS 0100
+#define GET_OID_RECORD_PATH 0200
+#define GET_OID_ONLY_TO_DIE 04000
+
+#define GET_OID_DISAMBIGUATORS \
+ (GET_OID_COMMIT | GET_OID_COMMITTISH | \
+ GET_OID_TREE | GET_OID_TREEISH | \
+ GET_OID_BLOB)
extern int get_oid(const char *str, struct object_id *oid);
+extern int get_oid_commit(const char *str, struct object_id *oid);
+extern int get_oid_committish(const char *str, struct object_id *oid);
+extern int get_oid_tree(const char *str, struct object_id *oid);
+extern int get_oid_treeish(const char *str, struct object_id *oid);
+extern int get_oid_blob(const char *str, struct object_id *oid);
+extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
+extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
+
typedef int each_abbrev_fn(const struct object_id *oid, void *);
extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
@@ -1500,6 +1471,7 @@ struct checkout {
struct index_state *istate;
const char *base_dir;
int base_dir_len;
+ struct delayed_checkout *delayed_checkout;
unsigned force:1,
quiet:1,
not_new:1,
@@ -1509,6 +1481,8 @@ struct checkout {
#define TEMPORARY_FILENAME_LENGTH 25
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
+extern void enable_delayed_checkout(struct checkout *state);
+extern int finish_delayed_checkout(struct checkout *state);
struct cache_def {
struct strbuf path;
@@ -1549,7 +1523,6 @@ extern struct alternate_object_database {
char path[FLEX_ARRAY];
} *alt_odb_list;
extern void prepare_alt_odb(void);
-extern void read_info_alternates(const char * relative_base, int depth);
extern char *compute_alternate_path(const char *path, struct strbuf *err);
typedef int alt_odb_fn(struct alternate_object_database *, void *);
extern int foreach_alt_odb(alt_odb_fn, void*);
@@ -1625,29 +1598,6 @@ struct pack_entry {
struct packed_git *p;
};
-extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
-
-/* A hook to report invalid files in pack directory */
-#define PACKDIR_FILE_PACK 1
-#define PACKDIR_FILE_IDX 2
-#define PACKDIR_FILE_GARBAGE 4
-extern void (*report_garbage)(unsigned seen_bits, const char *path);
-
-extern void prepare_packed_git(void);
-extern void reprepare_packed_git(void);
-extern void install_packed_git(struct packed_git *pack);
-
-/*
- * Give a rough count of objects in the repository. This sacrifices accuracy
- * for speed.
- */
-unsigned long approximate_object_count(void);
-
-extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
- struct packed_git *packs);
-
-extern void pack_report(void);
-
/*
* Create a temporary file rooted in the object database directory, or
* die on failure. The filename is taken from "pattern", which should have the
@@ -1657,15 +1607,6 @@ extern void pack_report(void);
extern int odb_mkstemp(struct strbuf *template, const char *pattern);
/*
- * 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
- * any existing contents. A pointer to buf->buf is returned as a convenience.
- *
- * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
- */
-extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
-
-/*
* Create a pack .keep file named "name" (which should generally be the output
* of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
* error.
@@ -1673,67 +1614,6 @@ extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const
extern int odb_pack_keep(const char *name);
/*
- * mmap the index file for the specified packfile (if it is not
- * already mmapped). Return 0 on success.
- */
-extern int open_pack_index(struct packed_git *);
-
-/*
- * munmap the index file for the specified packfile (if it is
- * currently mmapped).
- */
-extern void close_pack_index(struct packed_git *);
-
-extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
-extern void close_pack_windows(struct packed_git *);
-extern void close_all_packs(void);
-extern void unuse_pack(struct pack_window **);
-extern void clear_delta_base_cache(void);
-extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
-
-/*
- * Make sure that a pointer access into an mmap'd index file is within bounds,
- * and can provide at least 8 bytes of data.
- *
- * Note that this is only necessary for variable-length segments of the file
- * (like the 64-bit extended offset table), as we compare the size to the
- * fixed-length parts when we open the file.
- */
-extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
-
-/*
- * Return the SHA-1 of the nth object within the specified packfile.
- * Open the index if it is not already open. The return value points
- * at the SHA-1 within the mmapped index. Return NULL if there is an
- * error.
- */
-extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
-/*
- * Like nth_packed_object_sha1, but write the data into the object specified by
- * the the first argument. Returns the first argument on success, and NULL on
- * error.
- */
-extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
-
-/*
- * Return the offset of the nth object within the specified packfile.
- * The index must already be opened.
- */
-extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
-
-/*
- * If the object named sha1 is present in the specified packfile,
- * return its offset within the packfile; otherwise, return 0.
- */
-extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
-
-extern int is_pack_valid(struct packed_git *);
-extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
-extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
-extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
-extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
-
-/*
* Iterate over the files in the loose-object parts of the object
* directory "path", triggering the following callbacks:
*
@@ -1782,17 +1662,12 @@ int for_each_loose_file_in_objdir_buf(struct strbuf *path,
void *data);
/*
- * Iterate over loose and packed objects in both the local
+ * Iterate over loose objects in both the local
* repository and any alternates repositories (unless the
* LOCAL_ONLY flag is set).
*/
#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
-typedef int each_packed_object_fn(const struct object_id *oid,
- struct packed_git *pack,
- uint32_t pos,
- void *data);
extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
-extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags);
struct object_info {
/* Request */
@@ -1842,7 +1717,6 @@ struct object_info {
/* Do not retry packed storage after checking packed and loose storage */
#define OBJECT_INFO_QUICK 8
extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
-extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
/* Dumb servers support */
extern int update_server_info(int);
@@ -1961,6 +1835,8 @@ void shift_tree_by(const struct object_id *, const struct object_id *, struct ob
#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
#define WS_TAB_WIDTH_MASK 077
+/* All WS_* -- when extended, adapt diff.c emit_symbol */
+#define WS_RULE_MASK 07777
extern unsigned whitespace_rule_cfg;
extern unsigned whitespace_rule(const char *);
extern unsigned parse_whitespace_rule(const char *);
diff --git a/color.c b/color.c
index 31b6207a00..7aa8b076f0 100644
--- a/color.c
+++ b/color.c
@@ -361,14 +361,6 @@ int git_color_config(const char *var, const char *value, void *cb)
return 0;
}
-int git_color_default_config(const char *var, const char *value, void *cb)
-{
- if (git_color_config(var, value, cb) < 0)
- return -1;
-
- return git_default_config(var, value, cb);
-}
-
void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
{
if (*color)
diff --git a/color.h b/color.h
index 90627650fc..fd2b688dfb 100644
--- a/color.h
+++ b/color.h
@@ -42,6 +42,8 @@ struct strbuf;
#define GIT_COLOR_BG_BLUE "\033[44m"
#define GIT_COLOR_BG_MAGENTA "\033[45m"
#define GIT_COLOR_BG_CYAN "\033[46m"
+#define GIT_COLOR_FAINT "\033[2m"
+#define GIT_COLOR_FAINT_ITALIC "\033[2;3m"
/* A special value meaning "no color selected" */
#define GIT_COLOR_NIL "NIL"
diff --git a/commit.c b/commit.c
index cbfd689939..17a93d1e64 100644
--- a/commit.c
+++ b/commit.c
@@ -59,7 +59,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
struct object_id oid;
struct commit *commit;
- if (get_sha1_committish(name, oid.hash))
+ if (get_oid_committish(name, &oid))
return NULL;
commit = lookup_commit_reference(&oid);
if (parse_commit(commit))
@@ -134,35 +134,41 @@ int register_commit_graft(struct commit_graft *graft, int ignore_dups)
return 0;
}
-struct commit_graft *read_graft_line(char *buf, int len)
+struct commit_graft *read_graft_line(struct strbuf *line)
{
/* The format is just "Commit Parent1 Parent2 ...\n" */
- int i;
+ int i, phase;
+ const char *tail = NULL;
struct commit_graft *graft = NULL;
- const int entry_size = GIT_SHA1_HEXSZ + 1;
+ struct object_id dummy_oid, *oid;
- while (len && isspace(buf[len-1]))
- buf[--len] = '\0';
- if (buf[0] == '#' || buf[0] == '\0')
+ strbuf_rtrim(line);
+ if (!line->len || line->buf[0] == '#')
return NULL;
- if ((len + 1) % entry_size)
- goto bad_graft_data;
- i = (len + 1) / entry_size - 1;
- graft = xmalloc(st_add(sizeof(*graft), st_mult(GIT_SHA1_RAWSZ, i)));
- graft->nr_parent = i;
- if (get_oid_hex(buf, &graft->oid))
- goto bad_graft_data;
- for (i = GIT_SHA1_HEXSZ; i < len; i += entry_size) {
- if (buf[i] != ' ')
- goto bad_graft_data;
- if (get_sha1_hex(buf + i + 1, graft->parent[i/entry_size].hash))
+ /*
+ * phase 0 verifies line, counts hashes in line and allocates graft
+ * phase 1 fills graft
+ */
+ for (phase = 0; phase < 2; phase++) {
+ oid = graft ? &graft->oid : &dummy_oid;
+ if (parse_oid_hex(line->buf, oid, &tail))
goto bad_graft_data;
+ for (i = 0; *tail != '\0'; i++) {
+ oid = graft ? &graft->parent[i] : &dummy_oid;
+ if (!isspace(*tail++) || parse_oid_hex(tail, oid, &tail))
+ goto bad_graft_data;
+ }
+ if (!graft) {
+ graft = xmalloc(st_add(sizeof(*graft),
+ st_mult(sizeof(struct object_id), i)));
+ graft->nr_parent = i;
+ }
}
return graft;
bad_graft_data:
- error("bad graft data: %s", buf);
- free(graft);
+ error("bad graft data: %s", line->buf);
+ assert(!graft);
return NULL;
}
@@ -174,7 +180,7 @@ static int read_graft_file(const char *graft_file)
return -1;
while (!strbuf_getwholeline(&buf, fp, '\n')) {
/* The format is just "Commit Parent1 Parent2 ...\n" */
- struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
+ struct commit_graft *graft = read_graft_line(&buf);
if (!graft)
continue;
if (register_commit_graft(graft, 1))
@@ -199,11 +205,11 @@ static void prepare_commit_graft(void)
commit_graft_prepared = 1;
}
-struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
+struct commit_graft *lookup_commit_graft(const struct object_id *oid)
{
int pos;
prepare_commit_graft();
- pos = commit_graft_pos(sha1);
+ pos = commit_graft_pos(oid->hash);
if (pos < 0)
return NULL;
return commit_graft[pos];
@@ -223,9 +229,8 @@ int unregister_shallow(const struct object_id *oid)
if (pos < 0)
return -1;
if (pos + 1 < commit_graft_nr)
- memmove(commit_graft + pos, commit_graft + pos + 1,
- sizeof(struct commit_graft *)
- * (commit_graft_nr - pos - 1));
+ MOVE_ARRAY(commit_graft + pos, commit_graft + pos + 1,
+ commit_graft_nr - pos - 1);
commit_graft_nr--;
return 0;
}
@@ -335,7 +340,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.hash);
+ graft = lookup_commit_graft(&item->object.oid);
while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
struct commit *new_parent;
@@ -1587,7 +1592,7 @@ struct commit *get_merge_parent(const char *name)
struct object *obj;
struct commit *commit;
struct object_id oid;
- if (get_sha1(name, oid.hash))
+ if (get_oid(name, &oid))
return NULL;
obj = parse_object(&oid);
commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
diff --git a/commit.h b/commit.h
index 4127c298cb..6d769590f2 100644
--- a/commit.h
+++ b/commit.h
@@ -247,9 +247,9 @@ struct commit_graft {
};
typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
-struct commit_graft *read_graft_line(char *buf, int len);
+struct commit_graft *read_graft_line(struct strbuf *line);
int register_commit_graft(struct commit_graft *, int);
-struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
+struct commit_graft *lookup_commit_graft(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);
@@ -313,11 +313,6 @@ extern int interactive_add(int argc, const char **argv, const char *prefix, int
extern int run_add_interactive(const char *revision, const char *patch_mode,
const struct pathspec *pathspec);
-static inline int single_parent(struct commit *commit)
-{
- return commit->parents && !commit->parents->next;
-}
-
struct commit_list *reduce_heads(struct commit_list *heads);
struct commit_extra_header {
diff --git a/compat/bswap.h b/compat/bswap.h
index d47c003544..7d063e9e40 100644
--- a/compat/bswap.h
+++ b/compat/bswap.h
@@ -162,19 +162,29 @@ static inline uint64_t git_bswap64(uint64_t x)
#else
-#define get_be16(p) ( \
- (*((unsigned char *)(p) + 0) << 8) | \
- (*((unsigned char *)(p) + 1) << 0) )
-#define get_be32(p) ( \
- (*((unsigned char *)(p) + 0) << 24) | \
- (*((unsigned char *)(p) + 1) << 16) | \
- (*((unsigned char *)(p) + 2) << 8) | \
- (*((unsigned char *)(p) + 3) << 0) )
-#define put_be32(p, v) do { \
- unsigned int __v = (v); \
- *((unsigned char *)(p) + 0) = __v >> 24; \
- *((unsigned char *)(p) + 1) = __v >> 16; \
- *((unsigned char *)(p) + 2) = __v >> 8; \
- *((unsigned char *)(p) + 3) = __v >> 0; } while (0)
+static inline uint16_t get_be16(const void *ptr)
+{
+ const unsigned char *p = ptr;
+ return (uint16_t)p[0] << 8 |
+ (uint16_t)p[1] << 0;
+}
+
+static inline uint32_t get_be32(const void *ptr)
+{
+ const unsigned char *p = ptr;
+ return (uint32_t)p[0] << 24 |
+ (uint32_t)p[1] << 16 |
+ (uint32_t)p[2] << 8 |
+ (uint32_t)p[3] << 0;
+}
+
+static inline void put_be32(void *ptr, uint32_t value)
+{
+ unsigned char *p = ptr;
+ p[0] = value >> 24;
+ p[1] = value >> 16;
+ p[2] = value >> 8;
+ p[3] = value >> 0;
+}
#endif
diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c
index 6c7c9b6053..161978d720 100644
--- a/compat/win32/syslog.c
+++ b/compat/win32/syslog.c
@@ -43,8 +43,10 @@ void syslog(int priority, const char *fmt, ...)
va_end(ap);
while ((pos = strstr(str, "%1")) != NULL) {
+ char *oldstr = str;
str = realloc(str, st_add(++str_len, 1));
if (!str) {
+ free(oldstr);
warning_errno("realloc failed");
return;
}
diff --git a/config.c b/config.c
index 231f9a750b..d0d8ce823a 100644
--- a/config.c
+++ b/config.c
@@ -16,6 +16,7 @@
#include "string-list.h"
#include "utf8.h"
#include "dir.h"
+#include "color.h"
struct config_source {
struct config_source *prev;
@@ -928,7 +929,7 @@ ssize_t git_config_ssize_t(const char *name, const char *value)
return ret;
}
-int git_parse_maybe_bool(const char *value)
+static int git_parse_maybe_bool_text(const char *value)
{
if (!value)
return 1;
@@ -945,9 +946,9 @@ int git_parse_maybe_bool(const char *value)
return -1;
}
-int git_config_maybe_bool(const char *name, const char *value)
+int git_parse_maybe_bool(const char *value)
{
- int v = git_parse_maybe_bool(value);
+ int v = git_parse_maybe_bool_text(value);
if (0 <= v)
return v;
if (git_parse_int(value, &v))
@@ -955,9 +956,14 @@ int git_config_maybe_bool(const char *name, const char *value)
return -1;
}
+int git_config_maybe_bool(const char *name, const char *value)
+{
+ return git_parse_maybe_bool(value);
+}
+
int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
{
- int v = git_parse_maybe_bool(value);
+ int v = git_parse_maybe_bool_text(value);
if (0 <= v) {
*is_bool = 1;
return v;
@@ -1350,6 +1356,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
if (starts_with(var, "advice."))
return git_default_advice_config(var, value);
+ if (git_color_config(var, value, dummy) < 0)
+ return -1;
+
if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
pager_use_color = git_config_bool(var,value);
return 0;
@@ -1460,9 +1469,9 @@ int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_typ
return do_config_from(&top, fn, data);
}
-int git_config_from_blob_sha1(config_fn_t fn,
+int git_config_from_blob_oid(config_fn_t fn,
const char *name,
- const unsigned char *sha1,
+ const struct object_id *oid,
void *data)
{
enum object_type type;
@@ -1470,7 +1479,7 @@ int git_config_from_blob_sha1(config_fn_t fn,
unsigned long size;
int ret;
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf)
return error("unable to load config blob object '%s'", name);
if (type != OBJ_BLOB) {
@@ -1488,11 +1497,11 @@ static int git_config_from_blob_ref(config_fn_t fn,
const char *name,
void *data)
{
- unsigned char sha1[20];
+ struct object_id oid;
- if (get_sha1(name, sha1) < 0)
+ if (get_oid(name, &oid) < 0)
return error("unable to resolve config blob '%s'", name);
- return git_config_from_blob_sha1(fn, name, sha1, data);
+ return git_config_from_blob_oid(fn, name, &oid, data);
}
const char *git_etc_gitconfig(void)
@@ -1715,17 +1724,19 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
}
static int config_set_element_cmp(const void *unused_cmp_data,
- const struct config_set_element *e1,
- const struct config_set_element *e2,
+ const void *entry,
+ const void *entry_or_key,
const void *unused_keydata)
{
+ const struct config_set_element *e1 = entry;
+ const struct config_set_element *e2 = entry_or_key;
+
return strcmp(e1->key, e2->key);
}
void git_configset_init(struct config_set *cs)
{
- hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp,
- NULL, 0);
+ hashmap_init(&cs->config_hash, config_set_element_cmp, NULL, 0);
cs->hash_initialized = 1;
cs->list.nr = 0;
cs->list.alloc = 0;
@@ -1846,7 +1857,7 @@ int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *de
{
const char *value;
if (!git_configset_get_value(cs, key, &value)) {
- *dest = git_config_maybe_bool(key, value);
+ *dest = git_parse_maybe_bool(value);
if (*dest == -1)
return -1;
return 0;
@@ -2053,6 +2064,23 @@ int git_config_get_pathname(const char *key, const char **dest)
return repo_config_get_pathname(the_repository, key, dest);
}
+/*
+ * Note: This function exists solely to maintain backward compatibility with
+ * 'fetch' and 'update_clone' storing configuration in '.gitmodules' and should
+ * NOT be used anywhere else.
+ *
+ * Runs the provided config function on the '.gitmodules' file found in the
+ * working directory.
+ */
+void config_from_gitmodules(config_fn_t fn, void *data)
+{
+ if (the_repository->worktree) {
+ char *file = repo_worktree_path(the_repository, GITMODULES_FILE);
+ git_config_from_file(fn, file, data);
+ free(file);
+ }
+}
+
int git_config_get_expiry(const char *key, const char **output)
{
int ret = git_config_get_string_const(key, output);
@@ -2066,6 +2094,28 @@ int git_config_get_expiry(const char *key, const char **output)
return ret;
}
+int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamp_t now)
+{
+ char *expiry_string;
+ intmax_t days;
+ timestamp_t when;
+
+ if (git_config_get_string(key, &expiry_string))
+ return 1; /* no such thing */
+
+ if (git_parse_signed(expiry_string, &days, maximum_signed_value_of_type(int))) {
+ const int scale = 86400;
+ *expiry = now - days * scale;
+ return 0;
+ }
+
+ if (!parse_expiry_date(expiry_string, &when)) {
+ *expiry = when;
+ return 0;
+ }
+ return -1; /* thing exists but cannot be parsed */
+}
+
int git_config_get_untracked_cache(void)
{
int val = -1;
diff --git a/config.h b/config.h
index 0352da117b..97471b8873 100644
--- a/config.h
+++ b/config.h
@@ -39,8 +39,8 @@ extern int git_default_config(const char *, const char *, void *);
extern int git_config_from_file(config_fn_t fn, const char *, void *);
extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
const char *name, const char *buf, size_t len, void *data);
-extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
- const unsigned char *sha1, void *data);
+extern int git_config_from_blob_oid(config_fn_t fn, const char *name,
+ const struct object_id *oid, void *data);
extern void git_config_push_parameter(const char *text);
extern int git_config_from_parameters(config_fn_t fn, void *data);
extern void read_early_config(config_fn_t cb, void *data);
@@ -187,6 +187,16 @@ extern int repo_config_get_maybe_bool(struct repository *repo,
extern int repo_config_get_pathname(struct repository *repo,
const char *key, const char **dest);
+/*
+ * Note: This function exists solely to maintain backward compatibility with
+ * 'fetch' and 'update_clone' storing configuration in '.gitmodules' and should
+ * NOT be used anywhere else.
+ *
+ * Runs the provided config function on the '.gitmodules' file found in the
+ * working directory.
+ */
+extern void config_from_gitmodules(config_fn_t fn, void *data);
+
extern int git_config_get_value(const char *key, const char **value);
extern const struct string_list *git_config_get_value_multi(const char *key);
extern void git_config_clear(void);
@@ -205,6 +215,9 @@ extern int git_config_get_max_percent_split_change(void);
/* This dies if the configured or default date is in the future */
extern int git_config_get_expiry(const char *key, const char **output);
+/* parse either "this many days" integer, or "5.days.ago" approxidate */
+extern int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
+
struct key_value_info {
const char *filename;
int linenr;
diff --git a/connected.c b/connected.c
index 136c2ac168..f416b05051 100644
--- a/connected.c
+++ b/connected.c
@@ -3,6 +3,7 @@
#include "sigchain.h"
#include "connected.h"
#include "transport.h"
+#include "packfile.h"
/*
* If we feed all the commits we want to verify to this command
diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci
index 4ba98b7eaf..c61d1ca8dc 100644
--- a/contrib/coccinelle/array.cocci
+++ b/contrib/coccinelle/array.cocci
@@ -27,6 +27,23 @@ expression n;
@@
type T;
+T *dst;
+T *src;
+expression n;
+@@
+(
+- memmove(dst, src, (n) * sizeof(*dst));
++ MOVE_ARRAY(dst, src, n);
+|
+- memmove(dst, src, (n) * sizeof(*src));
++ MOVE_ARRAY(dst, src, n);
+|
+- memmove(dst, src, (n) * sizeof(T));
++ MOVE_ARRAY(dst, src, n);
+)
+
+@@
+type T;
T *ptr;
expression n;
@@
diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts
index dbe2abf277..85ad732fc0 100755
--- a/contrib/contacts/git-contacts
+++ b/contrib/contacts/git-contacts
@@ -11,7 +11,7 @@ use IPC::Open2;
my $since = '5-years-ago';
my $min_percent = 10;
-my $labels_rx = qr/Signed-off-by|Reviewed-by|Acked-by|Cc/i;
+my $labels_rx = qr/Signed-off-by|Reviewed-by|Acked-by|Cc|Reported-by/i;
my %seen;
sub format_contact {
diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh
index 52ad9e41fb..eeee45dd34 100755
--- a/contrib/rerere-train.sh
+++ b/contrib/rerere-train.sh
@@ -3,10 +3,56 @@
# Prime rerere database from existing merge commits
me=rerere-train
-USAGE="$me rev-list-args"
+USAGE=$(cat <<-EOF
+usage: $me [--overwrite] <rev-list-args>
+
+ -h, --help show the help
+ -o, --overwrite overwrite any existing rerere cache
+EOF
+)
SUBDIRECTORY_OK=Yes
-OPTIONS_SPEC=
+
+overwrite=0
+
+while test $# -gt 0
+do
+ opt="$1"
+ case "$opt" in
+ -h|--help)
+ echo "$USAGE"
+ exit 0
+ ;;
+ -o|--overwrite)
+ overwrite=1
+ shift
+ break
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+# Overwrite or help options are not valid except as first arg
+for opt in "$@"
+do
+ case "$opt" in
+ -h|--help)
+ echo "$USAGE"
+ exit 0
+ ;;
+ -o|--overwrite)
+ echo "$USAGE"
+ exit 0
+ ;;
+ esac
+done
+
. "$(git --exec-path)/git-sh-setup"
require_work_tree
cd_to_toplevel
@@ -34,6 +80,10 @@ do
# Cleanly merges
continue
fi
+ if test $overwrite = 1
+ then
+ git rerere forget .
+ fi
if test -s "$GIT_DIR/MERGE_RR"
then
git show -s --pretty=format:"Learning from %h %s" "$commit"
diff --git a/convert.c b/convert.c
index deaf0ba7b3..c5f0b21037 100644
--- a/convert.c
+++ b/convert.c
@@ -501,6 +501,7 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le
#define CAP_CLEAN (1u<<0)
#define CAP_SMUDGE (1u<<1)
+#define CAP_DELAY (1u<<2)
struct cmd2process {
struct subprocess_entry subprocess; /* must be the first member! */
@@ -512,69 +513,49 @@ static struct hashmap subprocess_map;
static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
{
- int err;
+ static int versions[] = {2, 0};
+ static struct subprocess_capability capabilities[] = {
+ { "clean", CAP_CLEAN },
+ { "smudge", CAP_SMUDGE },
+ { "delay", CAP_DELAY },
+ { NULL, 0 }
+ };
struct cmd2process *entry = (struct cmd2process *)subprocess;
- struct string_list cap_list = STRING_LIST_INIT_NODUP;
- char *cap_buf;
- const char *cap_name;
- struct child_process *process = &subprocess->process;
- const char *cmd = subprocess->cmd;
-
- sigchain_push(SIGPIPE, SIG_IGN);
-
- err = packet_writel(process->in, "git-filter-client", "version=2", NULL);
- if (err)
- goto done;
-
- err = strcmp(packet_read_line(process->out, NULL), "git-filter-server");
- if (err) {
- error("external filter '%s' does not support filter protocol version 2", cmd);
- goto done;
- }
- err = strcmp(packet_read_line(process->out, NULL), "version=2");
- if (err)
- goto done;
- err = packet_read_line(process->out, NULL) != NULL;
- if (err)
- goto done;
-
- err = packet_writel(process->in, "capability=clean", "capability=smudge", NULL);
-
- for (;;) {
- cap_buf = packet_read_line(process->out, NULL);
- if (!cap_buf)
- break;
- string_list_split_in_place(&cap_list, cap_buf, '=', 1);
-
- if (cap_list.nr != 2 || strcmp(cap_list.items[0].string, "capability"))
- continue;
-
- cap_name = cap_list.items[1].string;
- if (!strcmp(cap_name, "clean")) {
- entry->supported_capabilities |= CAP_CLEAN;
- } else if (!strcmp(cap_name, "smudge")) {
- entry->supported_capabilities |= CAP_SMUDGE;
- } else {
- warning(
- "external filter '%s' requested unsupported filter capability '%s'",
- cmd, cap_name
- );
- }
+ return subprocess_handshake(subprocess, "git-filter", versions, NULL,
+ capabilities,
+ &entry->supported_capabilities);
+}
- string_list_clear(&cap_list, 0);
+static void handle_filter_error(const struct strbuf *filter_status,
+ struct cmd2process *entry,
+ const unsigned int wanted_capability) {
+ if (!strcmp(filter_status->buf, "error"))
+ ; /* The filter signaled a problem with the file. */
+ else if (!strcmp(filter_status->buf, "abort") && wanted_capability) {
+ /*
+ * The filter signaled a permanent problem. Don't try to filter
+ * files with the same command for the lifetime of the current
+ * Git process.
+ */
+ entry->supported_capabilities &= ~wanted_capability;
+ } else {
+ /*
+ * Something went wrong with the protocol filter.
+ * Force shutdown and restart if another blob requires filtering.
+ */
+ error("external filter '%s' failed", entry->subprocess.cmd);
+ subprocess_stop(&subprocess_map, &entry->subprocess);
+ free(entry);
}
-
-done:
- sigchain_pop(SIGPIPE);
-
- return err;
}
static int apply_multi_file_filter(const char *path, const char *src, size_t len,
int fd, struct strbuf *dst, const char *cmd,
- const unsigned int wanted_capability)
+ const unsigned int wanted_capability,
+ struct delayed_checkout *dco)
{
int err;
+ int can_delay = 0;
struct cmd2process *entry;
struct child_process *process;
struct strbuf nbuf = STRBUF_INIT;
@@ -583,8 +564,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
if (!subprocess_map_initialized) {
subprocess_map_initialized = 1;
- hashmap_init(&subprocess_map, (hashmap_cmp_fn) cmd2process_cmp,
- NULL, 0);
+ hashmap_init(&subprocess_map, cmd2process_cmp, NULL, 0);
entry = NULL;
} else {
entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd);
@@ -603,12 +583,12 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
}
process = &entry->subprocess.process;
- if (!(wanted_capability & entry->supported_capabilities))
+ if (!(entry->supported_capabilities & wanted_capability))
return 0;
- if (CAP_CLEAN & wanted_capability)
+ if (wanted_capability & CAP_CLEAN)
filter_type = "clean";
- else if (CAP_SMUDGE & wanted_capability)
+ else if (wanted_capability & CAP_SMUDGE)
filter_type = "smudge";
else
die("unexpected filter type");
@@ -630,6 +610,14 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
if (err)
goto done;
+ if ((entry->supported_capabilities & CAP_DELAY) &&
+ dco && dco->state == CE_CAN_DELAY) {
+ can_delay = 1;
+ err = packet_write_fmt_gently(process->in, "can-delay=1\n");
+ if (err)
+ goto done;
+ }
+
err = packet_flush_gently(process->in);
if (err)
goto done;
@@ -645,14 +633,73 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
if (err)
goto done;
- err = strcmp(filter_status.buf, "success");
+ if (can_delay && !strcmp(filter_status.buf, "delayed")) {
+ string_list_insert(&dco->filters, cmd);
+ string_list_insert(&dco->paths, path);
+ } else {
+ /* The filter got the blob and wants to send us a response. */
+ err = strcmp(filter_status.buf, "success");
+ if (err)
+ goto done;
+
+ err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
+ if (err)
+ goto done;
+
+ err = subprocess_read_status(process->out, &filter_status);
+ if (err)
+ goto done;
+
+ err = strcmp(filter_status.buf, "success");
+ }
+
+done:
+ sigchain_pop(SIGPIPE);
+
+ if (err)
+ handle_filter_error(&filter_status, entry, wanted_capability);
+ else
+ strbuf_swap(dst, &nbuf);
+ strbuf_release(&nbuf);
+ return !err;
+}
+
+
+int async_query_available_blobs(const char *cmd, struct string_list *available_paths)
+{
+ int err;
+ char *line;
+ struct cmd2process *entry;
+ struct child_process *process;
+ struct strbuf filter_status = STRBUF_INIT;
+
+ assert(subprocess_map_initialized);
+ entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd);
+ if (!entry) {
+ error("external filter '%s' is not available anymore although "
+ "not all paths have been filtered", cmd);
+ return 0;
+ }
+ process = &entry->subprocess.process;
+ sigchain_push(SIGPIPE, SIG_IGN);
+
+ err = packet_write_fmt_gently(
+ process->in, "command=list_available_blobs\n");
if (err)
goto done;
- err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
+ err = packet_flush_gently(process->in);
if (err)
goto done;
+ while ((line = packet_read_line(process->out, NULL))) {
+ const char *path;
+ if (skip_prefix(line, "pathname=", &path))
+ string_list_insert(available_paths, xstrdup(path));
+ else
+ ; /* ignore unknown keys */
+ }
+
err = subprocess_read_status(process->out, &filter_status);
if (err)
goto done;
@@ -662,29 +709,8 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
done:
sigchain_pop(SIGPIPE);
- if (err) {
- if (!strcmp(filter_status.buf, "error")) {
- /* The filter signaled a problem with the file. */
- } else if (!strcmp(filter_status.buf, "abort")) {
- /*
- * The filter signaled a permanent problem. Don't try to filter
- * files with the same command for the lifetime of the current
- * Git process.
- */
- entry->supported_capabilities &= ~wanted_capability;
- } else {
- /*
- * Something went wrong with the protocol filter.
- * Force shutdown and restart if another blob requires filtering.
- */
- error("external filter '%s' failed", cmd);
- subprocess_stop(&subprocess_map, &entry->subprocess);
- free(entry);
- }
- } else {
- strbuf_swap(dst, &nbuf);
- }
- strbuf_release(&nbuf);
+ if (err)
+ handle_filter_error(&filter_status, entry, 0);
return !err;
}
@@ -699,7 +725,8 @@ static struct convert_driver {
static int apply_filter(const char *path, const char *src, size_t len,
int fd, struct strbuf *dst, struct convert_driver *drv,
- const unsigned int wanted_capability)
+ const unsigned int wanted_capability,
+ struct delayed_checkout *dco)
{
const char *cmd = NULL;
@@ -709,15 +736,16 @@ static int apply_filter(const char *path, const char *src, size_t len,
if (!dst)
return 1;
- if ((CAP_CLEAN & wanted_capability) && !drv->process && drv->clean)
+ if ((wanted_capability & CAP_CLEAN) && !drv->process && drv->clean)
cmd = drv->clean;
- else if ((CAP_SMUDGE & wanted_capability) && !drv->process && drv->smudge)
+ else if ((wanted_capability & CAP_SMUDGE) && !drv->process && drv->smudge)
cmd = drv->smudge;
if (cmd && *cmd)
return apply_single_file_filter(path, src, len, fd, dst, cmd);
else if (drv->process && *drv->process)
- return apply_multi_file_filter(path, src, len, fd, dst, drv->process, wanted_capability);
+ return apply_multi_file_filter(path, src, len, fd, dst,
+ drv->process, wanted_capability, dco);
return 0;
}
@@ -1058,7 +1086,7 @@ int would_convert_to_git_filter_fd(const char *path)
if (!ca.drv->required)
return 0;
- return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN);
+ return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN, NULL);
}
const char *get_convert_attr_ascii(const char *path)
@@ -1096,7 +1124,7 @@ int convert_to_git(const struct index_state *istate,
convert_attrs(&ca, path);
- ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN);
+ ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN, NULL);
if (!ret && ca.drv && ca.drv->required)
die("%s: clean filter '%s' failed", path, ca.drv->name);
@@ -1104,10 +1132,12 @@ int convert_to_git(const struct index_state *istate,
src = dst->buf;
len = dst->len;
}
- ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
- if (ret && dst) {
- src = dst->buf;
- len = dst->len;
+ if (checksafe != SAFE_CRLF_KEEP_CRLF) {
+ ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
+ if (ret && dst) {
+ src = dst->buf;
+ len = dst->len;
+ }
}
return ret | ident_to_git(path, src, len, dst, ca.ident);
}
@@ -1122,7 +1152,7 @@ void convert_to_git_filter_fd(const struct index_state *istate,
assert(ca.drv);
assert(ca.drv->clean || ca.drv->process);
- if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN))
+ if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL))
die("%s: clean filter '%s' failed", path, ca.drv->name);
crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
@@ -1131,7 +1161,7 @@ void convert_to_git_filter_fd(const struct index_state *istate,
static int convert_to_working_tree_internal(const char *path, const char *src,
size_t len, struct strbuf *dst,
- int normalizing)
+ int normalizing, struct delayed_checkout *dco)
{
int ret = 0, ret_filter = 0;
struct conv_attrs ca;
@@ -1156,22 +1186,30 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
}
}
- ret_filter = apply_filter(path, src, len, -1, dst, ca.drv, CAP_SMUDGE);
+ ret_filter = apply_filter(
+ path, src, len, -1, dst, ca.drv, CAP_SMUDGE, dco);
if (!ret_filter && ca.drv && ca.drv->required)
die("%s: smudge filter %s failed", path, ca.drv->name);
return ret | ret_filter;
}
+int async_convert_to_working_tree(const char *path, const char *src,
+ size_t len, struct strbuf *dst,
+ void *dco)
+{
+ return convert_to_working_tree_internal(path, src, len, dst, 0, dco);
+}
+
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
{
- return convert_to_working_tree_internal(path, src, len, dst, 0);
+ return convert_to_working_tree_internal(path, src, len, dst, 0, NULL);
}
int renormalize_buffer(const struct index_state *istate, const char *path,
const char *src, size_t len, struct strbuf *dst)
{
- int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
+ int ret = convert_to_working_tree_internal(path, src, len, dst, 1, NULL);
if (ret) {
src = dst->buf;
len = dst->len;
diff --git a/convert.h b/convert.h
index cecf59d1aa..4f2da225a8 100644
--- a/convert.h
+++ b/convert.h
@@ -4,13 +4,16 @@
#ifndef CONVERT_H
#define CONVERT_H
+#include "string-list.h"
+
struct index_state;
enum safe_crlf {
SAFE_CRLF_FALSE = 0,
SAFE_CRLF_FAIL = 1,
SAFE_CRLF_WARN = 2,
- SAFE_CRLF_RENORMALIZE = 3
+ SAFE_CRLF_RENORMALIZE = 3,
+ SAFE_CRLF_KEEP_CRLF = 4
};
extern enum safe_crlf safe_crlf;
@@ -34,6 +37,26 @@ enum eol {
#endif
};
+enum ce_delay_state {
+ CE_NO_DELAY = 0,
+ CE_CAN_DELAY = 1,
+ CE_RETRY = 2
+};
+
+struct delayed_checkout {
+ /*
+ * State of the currently processed cache entry. If the state is
+ * CE_CAN_DELAY, then the filter can delay the current cache entry.
+ * If the state is CE_RETRY, then this signals the filter that the
+ * cache entry was requested before.
+ */
+ enum ce_delay_state state;
+ /* List of filter drivers that signaled delayed blobs. */
+ struct string_list filters;
+ /* List of delayed blobs identified by their path. */
+ struct string_list paths;
+};
+
extern enum eol core_eol;
extern const char *get_cached_convert_stats_ascii(const struct index_state *istate,
const char *path);
@@ -46,6 +69,10 @@ extern int convert_to_git(const struct index_state *istate,
struct strbuf *dst, enum safe_crlf checksafe);
extern int convert_to_working_tree(const char *path, const char *src,
size_t len, struct strbuf *dst);
+extern int async_convert_to_working_tree(const char *path, const char *src,
+ size_t len, struct strbuf *dst,
+ void *dco);
+extern int async_query_available_blobs(const char *cmd, struct string_list *available_paths);
extern int renormalize_buffer(const struct index_state *istate,
const char *path, const char *src, size_t len,
struct strbuf *dst);
diff --git a/credential-cache.c b/credential-cache.c
index 91550bfb0b..1cccc3a0b9 100644
--- a/credential-cache.c
+++ b/credential-cache.c
@@ -25,7 +25,7 @@ static int send_request(const char *socket, const struct strbuf *out)
int r;
r = read_in_full(fd, in, sizeof(in));
- if (r == 0)
+ if (r == 0 || (r < 0 && errno == ECONNRESET))
break;
if (r < 0)
die_errno("read error from cache daemon");
diff --git a/diff.c b/diff.c
index 85e714f6c6..3d3e553a98 100644
--- a/diff.c
+++ b/diff.c
@@ -16,10 +16,12 @@
#include "userdiff.h"
#include "submodule-config.h"
#include "submodule.h"
+#include "hashmap.h"
#include "ll-merge.h"
#include "string-list.h"
#include "argv-array.h"
#include "graph.h"
+#include "packfile.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
@@ -32,6 +34,7 @@ static int diff_indent_heuristic = 1;
static int diff_rename_limit_default = 400;
static int diff_suppress_blank_empty;
static int diff_use_color_default = -1;
+static int diff_color_moved_default;
static int diff_context_default = 3;
static int diff_interhunk_context_default;
static const char *diff_word_regex_cfg;
@@ -56,6 +59,14 @@ static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_YELLOW, /* COMMIT */
GIT_COLOR_BG_RED, /* WHITESPACE */
GIT_COLOR_NORMAL, /* FUNCINFO */
+ GIT_COLOR_BOLD_MAGENTA, /* OLD_MOVED */
+ GIT_COLOR_BOLD_BLUE, /* OLD_MOVED ALTERNATIVE */
+ GIT_COLOR_FAINT, /* OLD_MOVED_DIM */
+ GIT_COLOR_FAINT_ITALIC, /* OLD_MOVED_ALTERNATIVE_DIM */
+ GIT_COLOR_BOLD_CYAN, /* NEW_MOVED */
+ GIT_COLOR_BOLD_YELLOW, /* NEW_MOVED ALTERNATIVE */
+ GIT_COLOR_FAINT, /* NEW_MOVED_DIM */
+ GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */
};
static NORETURN void die_want_option(const char *option_name)
@@ -81,6 +92,22 @@ static int parse_diff_color_slot(const char *var)
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;
}
@@ -229,12 +256,44 @@ int git_diff_heuristic_config(const char *var, const char *value, void *cb)
return 0;
}
+static int parse_color_moved(const char *arg)
+{
+ switch (git_parse_maybe_bool(arg)) {
+ case 0:
+ return COLOR_MOVED_NO;
+ case 1:
+ return COLOR_MOVED_DEFAULT;
+ default:
+ break;
+ }
+
+ if (!strcmp(arg, "no"))
+ return COLOR_MOVED_NO;
+ else if (!strcmp(arg, "plain"))
+ return COLOR_MOVED_PLAIN;
+ else if (!strcmp(arg, "zebra"))
+ return COLOR_MOVED_ZEBRA;
+ else if (!strcmp(arg, "default"))
+ return COLOR_MOVED_DEFAULT;
+ else if (!strcmp(arg, "dimmed_zebra"))
+ return COLOR_MOVED_ZEBRA_DIM;
+ else
+ return error(_("color moved setting must be one of 'no', 'default', 'zebra', 'dimmed_zebra', 'plain'"));
+}
+
int git_diff_ui_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
diff_use_color_default = git_config_colorbool(var, value);
return 0;
}
+ if (!strcmp(var, "diff.colormoved")) {
+ int cm = parse_color_moved(value);
+ if (cm < 0)
+ return -1;
+ diff_color_moved_default = cm;
+ return 0;
+ }
if (!strcmp(var, "diff.context")) {
diff_context_default = git_config_int(var, value);
if (diff_context_default < 0)
@@ -299,9 +358,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
return 0;
}
- if (git_color_config(var, value, cb) < 0)
- return -1;
-
return git_diff_basic_config(var, value, cb);
}
@@ -346,9 +402,6 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
return 0;
}
- if (starts_with(var, "submodule."))
- return parse_submodule_config_option(var, value);
-
if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
@@ -409,8 +462,6 @@ static struct diff_tempfile {
struct tempfile tempfile;
} diff_temp[2];
-typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
-
struct emit_callback {
int color_diff;
unsigned ws_rule;
@@ -418,7 +469,6 @@ struct emit_callback {
int blank_at_eof_in_postimage;
int lno_in_preimage;
int lno_in_postimage;
- sane_truncate_fn truncate;
const char **label_path;
struct diff_words_data *diff_words;
struct diff_options *opt;
@@ -560,68 +610,735 @@ static void emit_line(struct diff_options *o, const char *set, const char *reset
emit_line_0(o, set, reset, line[0], line+1, len-1);
}
-static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
+enum diff_symbol {
+ DIFF_SYMBOL_BINARY_DIFF_HEADER,
+ DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
+ DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL,
+ DIFF_SYMBOL_BINARY_DIFF_BODY,
+ DIFF_SYMBOL_BINARY_DIFF_FOOTER,
+ DIFF_SYMBOL_STATS_SUMMARY_NO_FILES,
+ DIFF_SYMBOL_STATS_SUMMARY_ABBREV,
+ DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES,
+ DIFF_SYMBOL_STATS_LINE,
+ DIFF_SYMBOL_WORD_DIFF,
+ DIFF_SYMBOL_STAT_SEP,
+ DIFF_SYMBOL_SUMMARY,
+ DIFF_SYMBOL_SUBMODULE_ADD,
+ DIFF_SYMBOL_SUBMODULE_DEL,
+ DIFF_SYMBOL_SUBMODULE_UNTRACKED,
+ DIFF_SYMBOL_SUBMODULE_MODIFIED,
+ DIFF_SYMBOL_SUBMODULE_HEADER,
+ DIFF_SYMBOL_SUBMODULE_ERROR,
+ DIFF_SYMBOL_SUBMODULE_PIPETHROUGH,
+ DIFF_SYMBOL_REWRITE_DIFF,
+ DIFF_SYMBOL_BINARY_FILES,
+ DIFF_SYMBOL_HEADER,
+ DIFF_SYMBOL_FILEPAIR_PLUS,
+ DIFF_SYMBOL_FILEPAIR_MINUS,
+ DIFF_SYMBOL_WORDS_PORCELAIN,
+ DIFF_SYMBOL_WORDS,
+ DIFF_SYMBOL_CONTEXT,
+ DIFF_SYMBOL_CONTEXT_INCOMPLETE,
+ DIFF_SYMBOL_PLUS,
+ DIFF_SYMBOL_MINUS,
+ DIFF_SYMBOL_NO_LF_EOF,
+ DIFF_SYMBOL_CONTEXT_FRAGINFO,
+ DIFF_SYMBOL_CONTEXT_MARKER,
+ DIFF_SYMBOL_SEPARATOR
+};
+/*
+ * Flags for content lines:
+ * 0..12 are whitespace rules
+ * 13-15 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT
+ * 16 is marking if the line is blank at EOF
+ */
+#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<16)
+#define DIFF_SYMBOL_MOVED_LINE (1<<17)
+#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<18)
+#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<19)
+#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK)
+
+/*
+ * This struct is used when we need to buffer the output of the diff output.
+ *
+ * NEEDSWORK: Instead of storing a copy of the line, add an offset pointer
+ * into the pre/post image file. This pointer could be a union with the
+ * line pointer. By storing an offset into the file instead of the literal line,
+ * we can decrease the memory footprint for the buffered output. At first we
+ * may want to only have indirection for the content lines, but we could also
+ * enhance the state for emitting prefabricated lines, e.g. the similarity
+ * score line or hunk/file headers would only need to store a number or path
+ * and then the output can be constructed later on depending on state.
+ */
+struct emitted_diff_symbol {
+ const char *line;
+ int len;
+ int flags;
+ enum diff_symbol s;
+};
+#define EMITTED_DIFF_SYMBOL_INIT {NULL}
+
+struct emitted_diff_symbols {
+ struct emitted_diff_symbol *buf;
+ int nr, alloc;
+};
+#define EMITTED_DIFF_SYMBOLS_INIT {NULL, 0, 0}
+
+static void append_emitted_diff_symbol(struct diff_options *o,
+ struct emitted_diff_symbol *e)
{
- if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
- ecbdata->blank_at_eof_in_preimage &&
- ecbdata->blank_at_eof_in_postimage &&
- ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
- ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
- return 0;
- return ws_blank_line(line, len, ecbdata->ws_rule);
+ struct emitted_diff_symbol *f;
+
+ ALLOC_GROW(o->emitted_symbols->buf,
+ o->emitted_symbols->nr + 1,
+ o->emitted_symbols->alloc);
+ f = &o->emitted_symbols->buf[o->emitted_symbols->nr++];
+
+ memcpy(f, e, sizeof(struct emitted_diff_symbol));
+ f->line = e->line ? xmemdupz(e->line, e->len) : NULL;
}
-static void emit_line_checked(const char *reset,
- struct emit_callback *ecbdata,
- const char *line, int len,
- enum color_diff color,
- unsigned ws_error_highlight,
- char sign)
+struct moved_entry {
+ struct hashmap_entry ent;
+ const struct emitted_diff_symbol *es;
+ struct moved_entry *next_line;
+};
+
+static int next_byte(const char **cp, const char **endp,
+ const struct diff_options *diffopt)
+{
+ int retval;
+
+ if (*cp > *endp)
+ return -1;
+
+ if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_CHANGE)) {
+ while (*cp < *endp && isspace(**cp))
+ (*cp)++;
+ /*
+ * After skipping a couple of whitespaces, we still have to
+ * account for one space.
+ */
+ return (int)' ';
+ }
+
+ if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE)) {
+ while (*cp < *endp && isspace(**cp))
+ (*cp)++;
+ /* return the first non-ws character via the usual below */
+ }
+
+ retval = (unsigned char)(**cp);
+ (*cp)++;
+ return retval;
+}
+
+static int moved_entry_cmp(const struct diff_options *diffopt,
+ const struct moved_entry *a,
+ const struct moved_entry *b,
+ const void *keydata)
+{
+ const char *ap = a->es->line, *ae = a->es->line + a->es->len;
+ const char *bp = b->es->line, *be = b->es->line + b->es->len;
+
+ if (!(diffopt->xdl_opts & XDF_WHITESPACE_FLAGS))
+ return a->es->len != b->es->len || memcmp(ap, bp, a->es->len);
+
+ if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_AT_EOL)) {
+ while (ae > ap && isspace(*ae))
+ ae--;
+ while (be > bp && isspace(*be))
+ be--;
+ }
+
+ while (1) {
+ int ca, cb;
+ ca = next_byte(&ap, &ae, diffopt);
+ cb = next_byte(&bp, &be, diffopt);
+ if (ca != cb)
+ return 1;
+ if (ca < 0)
+ return 0;
+ }
+}
+
+static unsigned get_string_hash(struct emitted_diff_symbol *es, struct diff_options *o)
+{
+ if (o->xdl_opts & XDF_WHITESPACE_FLAGS) {
+ static struct strbuf sb = STRBUF_INIT;
+ const char *ap = es->line, *ae = es->line + es->len;
+ int c;
+
+ strbuf_reset(&sb);
+ while (ae > ap && isspace(*ae))
+ ae--;
+ while ((c = next_byte(&ap, &ae, o)) > 0)
+ strbuf_addch(&sb, c);
+
+ return memhash(sb.buf, sb.len);
+ } else {
+ return memhash(es->line, es->len);
+ }
+}
+
+static struct moved_entry *prepare_entry(struct diff_options *o,
+ int line_no)
+{
+ struct moved_entry *ret = xmalloc(sizeof(*ret));
+ struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
+
+ ret->ent.hash = get_string_hash(l, o);
+ ret->es = l;
+ ret->next_line = NULL;
+
+ return ret;
+}
+
+static void add_lines_to_move_detection(struct diff_options *o,
+ struct hashmap *add_lines,
+ struct hashmap *del_lines)
+{
+ struct moved_entry *prev_line = NULL;
+
+ int n;
+ for (n = 0; n < o->emitted_symbols->nr; n++) {
+ struct hashmap *hm;
+ struct moved_entry *key;
+
+ switch (o->emitted_symbols->buf[n].s) {
+ case DIFF_SYMBOL_PLUS:
+ hm = add_lines;
+ break;
+ case DIFF_SYMBOL_MINUS:
+ hm = del_lines;
+ break;
+ default:
+ prev_line = NULL;
+ continue;
+ }
+
+ key = prepare_entry(o, n);
+ if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
+ prev_line->next_line = key;
+
+ hashmap_add(hm, key);
+ prev_line = key;
+ }
+}
+
+static int shrink_potential_moved_blocks(struct moved_entry **pmb,
+ int pmb_nr)
+{
+ int lp, rp;
+
+ /* Shrink the set of potential block to the remaining running */
+ for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
+ while (lp < pmb_nr && pmb[lp])
+ lp++;
+ /* lp points at the first NULL now */
+
+ while (rp > -1 && !pmb[rp])
+ rp--;
+ /* rp points at the last non-NULL */
+
+ if (lp < pmb_nr && rp > -1 && lp < rp) {
+ pmb[lp] = pmb[rp];
+ pmb[rp] = NULL;
+ rp--;
+ lp++;
+ }
+ }
+
+ /* Remember the number of running sets */
+ return rp + 1;
+}
+
+/*
+ * If o->color_moved is COLOR_MOVED_PLAIN, this function does nothing.
+ *
+ * Otherwise, if the last block has fewer alphanumeric characters than
+ * COLOR_MOVED_MIN_ALNUM_COUNT, unset DIFF_SYMBOL_MOVED_LINE on all lines in
+ * that block.
+ *
+ * The last block consists of the (n - block_length)'th line up to but not
+ * including the nth line.
+ *
+ * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
+ * Think of a way to unify them.
+ */
+static void adjust_last_block(struct diff_options *o, int n, int block_length)
+{
+ int i, alnum_count = 0;
+ if (o->color_moved == COLOR_MOVED_PLAIN)
+ return;
+ for (i = 1; i < block_length + 1; i++) {
+ const char *c = o->emitted_symbols->buf[n - i].line;
+ for (; *c; c++) {
+ if (!isalnum(*c))
+ continue;
+ alnum_count++;
+ if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT)
+ return;
+ }
+ }
+ for (i = 1; i < block_length + 1; i++)
+ o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
+}
+
+/* Find blocks of moved code, delegate actual coloring decision to helper */
+static void mark_color_as_moved(struct diff_options *o,
+ struct hashmap *add_lines,
+ struct hashmap *del_lines)
+{
+ struct moved_entry **pmb = NULL; /* potentially moved blocks */
+ int pmb_nr = 0, pmb_alloc = 0;
+ int n, flipped_block = 1, block_length = 0;
+
+
+ for (n = 0; n < o->emitted_symbols->nr; n++) {
+ struct hashmap *hm = NULL;
+ struct moved_entry *key;
+ struct moved_entry *match = NULL;
+ struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+ int i;
+
+ switch (l->s) {
+ case DIFF_SYMBOL_PLUS:
+ hm = del_lines;
+ key = prepare_entry(o, n);
+ match = hashmap_get(hm, key, o);
+ free(key);
+ break;
+ case DIFF_SYMBOL_MINUS:
+ hm = add_lines;
+ key = prepare_entry(o, n);
+ match = hashmap_get(hm, key, o);
+ free(key);
+ break;
+ default:
+ flipped_block = 1;
+ }
+
+ if (!match) {
+ adjust_last_block(o, n, block_length);
+ pmb_nr = 0;
+ block_length = 0;
+ continue;
+ }
+
+ l->flags |= DIFF_SYMBOL_MOVED_LINE;
+
+ if (o->color_moved == COLOR_MOVED_PLAIN)
+ continue;
+
+ /* Check any potential block runs, advance each or nullify */
+ for (i = 0; i < pmb_nr; i++) {
+ struct moved_entry *p = pmb[i];
+ struct moved_entry *pnext = (p && p->next_line) ?
+ p->next_line : NULL;
+ if (pnext && !hm->cmpfn(o, pnext, match, NULL)) {
+ pmb[i] = p->next_line;
+ } else {
+ pmb[i] = NULL;
+ }
+ }
+
+ pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
+
+ if (pmb_nr == 0) {
+ /*
+ * The current line is the start of a new block.
+ * Setup the set of potential blocks.
+ */
+ for (; match; match = hashmap_get_next(hm, match)) {
+ ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
+ pmb[pmb_nr++] = match;
+ }
+
+ flipped_block = (flipped_block + 1) % 2;
+
+ adjust_last_block(o, n, block_length);
+ block_length = 0;
+ }
+
+ block_length++;
+
+ if (flipped_block)
+ l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
+ }
+ adjust_last_block(o, n, block_length);
+
+ free(pmb);
+}
+
+#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
+ (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
+static void dim_moved_lines(struct diff_options *o)
+{
+ int n;
+ for (n = 0; n < o->emitted_symbols->nr; n++) {
+ struct emitted_diff_symbol *prev = (n != 0) ?
+ &o->emitted_symbols->buf[n - 1] : NULL;
+ struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+ struct emitted_diff_symbol *next =
+ (n < o->emitted_symbols->nr - 1) ?
+ &o->emitted_symbols->buf[n + 1] : NULL;
+
+ /* Not a plus or minus line? */
+ if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS)
+ continue;
+
+ /* Not a moved line? */
+ if (!(l->flags & DIFF_SYMBOL_MOVED_LINE))
+ continue;
+
+ /*
+ * If prev or next are not a plus or minus line,
+ * pretend they don't exist
+ */
+ if (prev && prev->s != DIFF_SYMBOL_PLUS &&
+ prev->s != DIFF_SYMBOL_MINUS)
+ prev = NULL;
+ if (next && next->s != DIFF_SYMBOL_PLUS &&
+ next->s != DIFF_SYMBOL_MINUS)
+ next = NULL;
+
+ /* Inside a block? */
+ if ((prev &&
+ (prev->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
+ (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK)) &&
+ (next &&
+ (next->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
+ (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK))) {
+ l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
+ continue;
+ }
+
+ /* Check if we are at an interesting bound: */
+ if (prev && (prev->flags & DIFF_SYMBOL_MOVED_LINE) &&
+ (prev->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
+ (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
+ continue;
+ if (next && (next->flags & DIFF_SYMBOL_MOVED_LINE) &&
+ (next->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
+ (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
+ continue;
+
+ /*
+ * The boundary to prev and next are not interesting,
+ * so this line is not interesting as a whole
+ */
+ l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
+ }
+}
+
+static void emit_line_ws_markup(struct diff_options *o,
+ const char *set, const char *reset,
+ const char *line, int len, char sign,
+ unsigned ws_rule, int blank_at_eof)
{
- const char *set = diff_get_color(ecbdata->color_diff, color);
const char *ws = NULL;
- if (ecbdata->opt->ws_error_highlight & ws_error_highlight) {
- ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+ if (o->ws_error_highlight & ws_rule) {
+ ws = diff_get_color_opt(o, DIFF_WHITESPACE);
if (!*ws)
ws = NULL;
}
if (!ws)
- emit_line_0(ecbdata->opt, set, reset, sign, line, len);
- else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
+ emit_line_0(o, set, reset, sign, line, len);
+ else if (blank_at_eof)
/* Blank line at EOF - paint '+' as well */
- emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
+ emit_line_0(o, ws, reset, sign, line, len);
else {
/* Emit just the prefix, then the rest. */
- emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
- ws_check_emit(line, len, ecbdata->ws_rule,
- ecbdata->opt->file, set, reset, ws);
+ emit_line_0(o, set, reset, sign, "", 0);
+ ws_check_emit(line, len, ws_rule,
+ o->file, set, reset, ws);
}
}
+static void emit_diff_symbol_from_struct(struct diff_options *o,
+ struct emitted_diff_symbol *eds)
+{
+ static const char *nneof = " No newline at end of file\n";
+ const char *context, *reset, *set, *meta, *fraginfo;
+ struct strbuf sb = STRBUF_INIT;
+
+ enum diff_symbol s = eds->s;
+ const char *line = eds->line;
+ int len = eds->len;
+ unsigned flags = eds->flags;
+
+ switch (s) {
+ case DIFF_SYMBOL_NO_LF_EOF:
+ context = diff_get_color_opt(o, DIFF_CONTEXT);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ putc('\n', o->file);
+ emit_line_0(o, context, reset, '\\',
+ nneof, strlen(nneof));
+ break;
+ case DIFF_SYMBOL_SUBMODULE_HEADER:
+ case DIFF_SYMBOL_SUBMODULE_ERROR:
+ case DIFF_SYMBOL_SUBMODULE_PIPETHROUGH:
+ case DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES:
+ case DIFF_SYMBOL_SUMMARY:
+ case DIFF_SYMBOL_STATS_LINE:
+ case DIFF_SYMBOL_BINARY_DIFF_BODY:
+ case DIFF_SYMBOL_CONTEXT_FRAGINFO:
+ emit_line(o, "", "", line, len);
+ break;
+ case DIFF_SYMBOL_CONTEXT_INCOMPLETE:
+ case DIFF_SYMBOL_CONTEXT_MARKER:
+ context = diff_get_color_opt(o, DIFF_CONTEXT);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ emit_line(o, context, reset, line, len);
+ break;
+ case DIFF_SYMBOL_SEPARATOR:
+ fprintf(o->file, "%s%c",
+ diff_line_prefix(o),
+ o->line_termination);
+ break;
+ case DIFF_SYMBOL_CONTEXT:
+ set = diff_get_color_opt(o, DIFF_CONTEXT);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ emit_line_ws_markup(o, set, reset, line, len, ' ',
+ flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
+ break;
+ case DIFF_SYMBOL_PLUS:
+ switch (flags & (DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+ set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT_DIM);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT:
+ set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+ set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_DIM);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE:
+ set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED);
+ break;
+ default:
+ set = diff_get_color_opt(o, DIFF_FILE_NEW);
+ }
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ emit_line_ws_markup(o, set, reset, line, len, '+',
+ flags & DIFF_SYMBOL_CONTENT_WS_MASK,
+ flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
+ break;
+ case DIFF_SYMBOL_MINUS:
+ switch (flags & (DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+ set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT_DIM);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_ALT:
+ set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE |
+ DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+ set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_DIM);
+ break;
+ case DIFF_SYMBOL_MOVED_LINE:
+ set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED);
+ break;
+ default:
+ set = diff_get_color_opt(o, DIFF_FILE_OLD);
+ }
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ emit_line_ws_markup(o, set, reset, line, len, '-',
+ flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
+ break;
+ case DIFF_SYMBOL_WORDS_PORCELAIN:
+ context = diff_get_color_opt(o, DIFF_CONTEXT);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ emit_line(o, context, reset, line, len);
+ fputs("~\n", o->file);
+ break;
+ case DIFF_SYMBOL_WORDS:
+ context = diff_get_color_opt(o, DIFF_CONTEXT);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ /*
+ * Skip the prefix character, if any. With
+ * diff_suppress_blank_empty, there may be
+ * none.
+ */
+ if (line[0] != '\n') {
+ line++;
+ len--;
+ }
+ emit_line(o, context, reset, line, len);
+ break;
+ case DIFF_SYMBOL_FILEPAIR_PLUS:
+ meta = diff_get_color_opt(o, DIFF_METAINFO);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ fprintf(o->file, "%s%s+++ %s%s%s\n", diff_line_prefix(o), meta,
+ line, reset,
+ strchr(line, ' ') ? "\t" : "");
+ break;
+ case DIFF_SYMBOL_FILEPAIR_MINUS:
+ meta = diff_get_color_opt(o, DIFF_METAINFO);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ fprintf(o->file, "%s%s--- %s%s%s\n", diff_line_prefix(o), meta,
+ line, reset,
+ strchr(line, ' ') ? "\t" : "");
+ break;
+ case DIFF_SYMBOL_BINARY_FILES:
+ case DIFF_SYMBOL_HEADER:
+ fprintf(o->file, "%s", line);
+ break;
+ case DIFF_SYMBOL_BINARY_DIFF_HEADER:
+ fprintf(o->file, "%sGIT binary patch\n", diff_line_prefix(o));
+ break;
+ case DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA:
+ fprintf(o->file, "%sdelta %s\n", diff_line_prefix(o), line);
+ break;
+ case DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL:
+ fprintf(o->file, "%sliteral %s\n", diff_line_prefix(o), line);
+ break;
+ case DIFF_SYMBOL_BINARY_DIFF_FOOTER:
+ fputs(diff_line_prefix(o), o->file);
+ fputc('\n', o->file);
+ break;
+ case DIFF_SYMBOL_REWRITE_DIFF:
+ fraginfo = diff_get_color(o->use_color, DIFF_FRAGINFO);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ emit_line(o, fraginfo, reset, line, len);
+ break;
+ case DIFF_SYMBOL_SUBMODULE_ADD:
+ set = diff_get_color_opt(o, DIFF_FILE_NEW);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ emit_line(o, set, reset, line, len);
+ break;
+ case DIFF_SYMBOL_SUBMODULE_DEL:
+ set = diff_get_color_opt(o, DIFF_FILE_OLD);
+ reset = diff_get_color_opt(o, DIFF_RESET);
+ emit_line(o, set, reset, line, len);
+ break;
+ case DIFF_SYMBOL_SUBMODULE_UNTRACKED:
+ fprintf(o->file, "%sSubmodule %s contains untracked content\n",
+ diff_line_prefix(o), line);
+ break;
+ case DIFF_SYMBOL_SUBMODULE_MODIFIED:
+ fprintf(o->file, "%sSubmodule %s contains modified content\n",
+ diff_line_prefix(o), line);
+ break;
+ case DIFF_SYMBOL_STATS_SUMMARY_NO_FILES:
+ emit_line(o, "", "", " 0 files changed\n",
+ strlen(" 0 files changed\n"));
+ break;
+ case DIFF_SYMBOL_STATS_SUMMARY_ABBREV:
+ emit_line(o, "", "", " ...\n", strlen(" ...\n"));
+ break;
+ case DIFF_SYMBOL_WORD_DIFF:
+ fprintf(o->file, "%.*s", len, line);
+ break;
+ case DIFF_SYMBOL_STAT_SEP:
+ fputs(o->stat_sep, o->file);
+ break;
+ default:
+ die("BUG: unknown diff symbol");
+ }
+ strbuf_release(&sb);
+}
+
+static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
+ const char *line, int len, unsigned flags)
+{
+ struct emitted_diff_symbol e = {line, len, flags, s};
+
+ if (o->emitted_symbols)
+ append_emitted_diff_symbol(o, &e);
+ else
+ emit_diff_symbol_from_struct(o, &e);
+}
+
+void diff_emit_submodule_del(struct diff_options *o, const char *line)
+{
+ emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_DEL, line, strlen(line), 0);
+}
+
+void diff_emit_submodule_add(struct diff_options *o, const char *line)
+{
+ emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ADD, line, strlen(line), 0);
+}
+
+void diff_emit_submodule_untracked(struct diff_options *o, const char *path)
+{
+ emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_UNTRACKED,
+ path, strlen(path), 0);
+}
+
+void diff_emit_submodule_modified(struct diff_options *o, const char *path)
+{
+ emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_MODIFIED,
+ path, strlen(path), 0);
+}
+
+void diff_emit_submodule_header(struct diff_options *o, const char *header)
+{
+ emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_HEADER,
+ header, strlen(header), 0);
+}
+
+void diff_emit_submodule_error(struct diff_options *o, const char *err)
+{
+ emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ERROR, err, strlen(err), 0);
+}
+
+void diff_emit_submodule_pipethrough(struct diff_options *o,
+ const char *line, int len)
+{
+ emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_PIPETHROUGH, line, len, 0);
+}
+
+static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
+{
+ if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
+ ecbdata->blank_at_eof_in_preimage &&
+ ecbdata->blank_at_eof_in_postimage &&
+ ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
+ ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
+ return 0;
+ return ws_blank_line(line, len, ecbdata->ws_rule);
+}
+
static void emit_add_line(const char *reset,
struct emit_callback *ecbdata,
const char *line, int len)
{
- emit_line_checked(reset, ecbdata, line, len,
- DIFF_FILE_NEW, WSEH_NEW, '+');
+ unsigned flags = WSEH_NEW | ecbdata->ws_rule;
+ if (new_blank_line_at_eof(ecbdata, line, len))
+ flags |= DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF;
+
+ emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_PLUS, line, len, flags);
}
static void emit_del_line(const char *reset,
struct emit_callback *ecbdata,
const char *line, int len)
{
- emit_line_checked(reset, ecbdata, line, len,
- DIFF_FILE_OLD, WSEH_OLD, '-');
+ unsigned flags = WSEH_OLD | ecbdata->ws_rule;
+ emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_MINUS, line, len, flags);
}
static void emit_context_line(const char *reset,
struct emit_callback *ecbdata,
const char *line, int len)
{
- emit_line_checked(reset, ecbdata, line, len,
- DIFF_CONTEXT, WSEH_CONTEXT, ' ');
+ unsigned flags = WSEH_CONTEXT | ecbdata->ws_rule;
+ emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT, line, len, flags);
}
static void emit_hunk_header(struct emit_callback *ecbdata,
@@ -644,7 +1361,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
if (len < 10 ||
memcmp(line, atat, 2) ||
!(ep = memmem(line + 2, len - 2, atat, 2))) {
- emit_line(ecbdata->opt, context, reset, line, len);
+ emit_diff_symbol(ecbdata->opt,
+ DIFF_SYMBOL_CONTEXT_MARKER, line, len, 0);
return;
}
ep += 2; /* skip over @@ */
@@ -678,7 +1396,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
}
strbuf_add(&msgbuf, line + len, org_len - len);
- emit_line(ecbdata->opt, "", "", msgbuf.buf, msgbuf.len);
+ strbuf_complete_line(&msgbuf);
+ emit_diff_symbol(ecbdata->opt,
+ DIFF_SYMBOL_CONTEXT_FRAGINFO, msgbuf.buf, msgbuf.len, 0);
strbuf_release(&msgbuf);
}
@@ -700,17 +1420,17 @@ static void remove_tempfile(void)
}
}
-static void print_line_count(FILE *file, int count)
+static void add_line_count(struct strbuf *out, int count)
{
switch (count) {
case 0:
- fprintf(file, "0,0");
+ strbuf_addstr(out, "0,0");
break;
case 1:
- fprintf(file, "1");
+ strbuf_addstr(out, "1");
break;
default:
- fprintf(file, "1,%d", count);
+ strbuf_addf(out, "1,%d", count);
break;
}
}
@@ -719,7 +1439,6 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
int prefix, const char *data, int size)
{
const char *endp = NULL;
- static const char *nneof = " No newline at end of file\n";
const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
while (0 < size) {
@@ -737,13 +1456,8 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
size -= len;
data += len;
}
- if (!endp) {
- const char *context = diff_get_color(ecb->color_diff,
- DIFF_CONTEXT);
- putc('\n', ecb->opt->file);
- emit_line_0(ecb->opt, context, reset, '\\',
- nneof, strlen(nneof));
- }
+ if (!endp)
+ emit_diff_symbol(ecb->opt, DIFF_SYMBOL_NO_LF_EOF, NULL, 0, 0);
}
static void emit_rewrite_diff(const char *name_a,
@@ -755,16 +1469,12 @@ static void emit_rewrite_diff(const char *name_a,
struct diff_options *o)
{
int lc_a, lc_b;
- const char *name_a_tab, *name_b_tab;
- const char *metainfo = diff_get_color(o->use_color, DIFF_METAINFO);
- const char *fraginfo = diff_get_color(o->use_color, DIFF_FRAGINFO);
- const char *reset = diff_get_color(o->use_color, DIFF_RESET);
static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
const char *a_prefix, *b_prefix;
char *data_one, *data_two;
size_t size_one, size_two;
struct emit_callback ecbdata;
- const char *line_prefix = diff_line_prefix(o);
+ struct strbuf out = STRBUF_INIT;
if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
a_prefix = o->b_prefix;
@@ -776,8 +1486,6 @@ static void emit_rewrite_diff(const char *name_a,
name_a += (*name_a == '/');
name_b += (*name_b == '/');
- name_a_tab = strchr(name_a, ' ') ? "\t" : "";
- name_b_tab = strchr(name_b, ' ') ? "\t" : "";
strbuf_reset(&a_name);
strbuf_reset(&b_name);
@@ -804,18 +1512,23 @@ static void emit_rewrite_diff(const char *name_a,
lc_a = count_lines(data_one, size_one);
lc_b = count_lines(data_two, size_two);
- fprintf(o->file,
- "%s%s--- %s%s%s\n%s%s+++ %s%s%s\n%s%s@@ -",
- line_prefix, metainfo, a_name.buf, name_a_tab, reset,
- line_prefix, metainfo, b_name.buf, name_b_tab, reset,
- line_prefix, fraginfo);
+
+ emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS,
+ a_name.buf, a_name.len, 0);
+ emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS,
+ b_name.buf, b_name.len, 0);
+
+ strbuf_addstr(&out, "@@ -");
if (!o->irreversible_delete)
- print_line_count(o->file, lc_a);
+ add_line_count(&out, lc_a);
else
- fprintf(o->file, "?,?");
- fprintf(o->file, " +");
- print_line_count(o->file, lc_b);
- fprintf(o->file, " @@%s\n", reset);
+ strbuf_addstr(&out, "?,?");
+ strbuf_addstr(&out, " +");
+ add_line_count(&out, lc_b);
+ strbuf_addstr(&out, " @@\n");
+ emit_diff_symbol(o, DIFF_SYMBOL_REWRITE_DIFF, out.buf, out.len, 0);
+ strbuf_release(&out);
+
if (lc_a && !o->irreversible_delete)
emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
if (lc_b)
@@ -875,37 +1588,49 @@ struct diff_words_data {
struct diff_words_style *style;
};
-static int fn_out_diff_words_write_helper(FILE *fp,
+static int fn_out_diff_words_write_helper(struct diff_options *o,
struct diff_words_style_elem *st_el,
const char *newline,
- size_t count, const char *buf,
- const char *line_prefix)
+ size_t count, const char *buf)
{
int print = 0;
+ struct strbuf sb = STRBUF_INIT;
while (count) {
char *p = memchr(buf, '\n', count);
if (print)
- fputs(line_prefix, fp);
+ strbuf_addstr(&sb, diff_line_prefix(o));
+
if (p != buf) {
- if (st_el->color && fputs(st_el->color, fp) < 0)
- return -1;
- if (fputs(st_el->prefix, fp) < 0 ||
- fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
- fputs(st_el->suffix, fp) < 0)
- return -1;
- if (st_el->color && *st_el->color
- && fputs(GIT_COLOR_RESET, fp) < 0)
- return -1;
+ const char *reset = st_el->color && *st_el->color ?
+ GIT_COLOR_RESET : NULL;
+ if (st_el->color && *st_el->color)
+ strbuf_addstr(&sb, st_el->color);
+ strbuf_addstr(&sb, st_el->prefix);
+ strbuf_add(&sb, buf, p ? p - buf : count);
+ strbuf_addstr(&sb, st_el->suffix);
+ if (reset)
+ strbuf_addstr(&sb, reset);
}
if (!p)
- return 0;
- if (fputs(newline, fp) < 0)
- return -1;
+ goto out;
+
+ strbuf_addstr(&sb, newline);
count -= p + 1 - buf;
buf = p + 1;
print = 1;
+ if (count) {
+ emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF,
+ sb.buf, sb.len, 0);
+ strbuf_reset(&sb);
+ }
}
+
+out:
+ if (sb.len)
+ emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF,
+ sb.buf, sb.len, 0);
+ strbuf_release(&sb);
return 0;
}
@@ -987,24 +1712,20 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
fputs(line_prefix, diff_words->opt->file);
}
if (diff_words->current_plus != plus_begin) {
- fn_out_diff_words_write_helper(diff_words->opt->file,
+ fn_out_diff_words_write_helper(diff_words->opt,
&style->ctx, style->newline,
plus_begin - diff_words->current_plus,
- diff_words->current_plus, line_prefix);
- if (*(plus_begin - 1) == '\n')
- fputs(line_prefix, diff_words->opt->file);
+ diff_words->current_plus);
}
if (minus_begin != minus_end) {
- fn_out_diff_words_write_helper(diff_words->opt->file,
+ fn_out_diff_words_write_helper(diff_words->opt,
&style->old, style->newline,
- minus_end - minus_begin, minus_begin,
- line_prefix);
+ minus_end - minus_begin, minus_begin);
}
if (plus_begin != plus_end) {
- fn_out_diff_words_write_helper(diff_words->opt->file,
+ fn_out_diff_words_write_helper(diff_words->opt,
&style->new, style->newline,
- plus_end - plus_begin, plus_begin,
- line_prefix);
+ plus_end - plus_begin, plus_begin);
}
diff_words->current_plus = plus_end;
@@ -1098,11 +1819,12 @@ static void diff_words_show(struct diff_words_data *diff_words)
/* special case: only removal */
if (!diff_words->plus.text.size) {
- fputs(line_prefix, diff_words->opt->file);
- fn_out_diff_words_write_helper(diff_words->opt->file,
+ emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF,
+ line_prefix, strlen(line_prefix), 0);
+ fn_out_diff_words_write_helper(diff_words->opt,
&style->old, style->newline,
diff_words->minus.text.size,
- diff_words->minus.text.ptr, line_prefix);
+ diff_words->minus.text.ptr);
diff_words->minus.text.size = 0;
return;
}
@@ -1125,12 +1847,12 @@ static void diff_words_show(struct diff_words_data *diff_words)
if (diff_words->current_plus != diff_words->plus.text.ptr +
diff_words->plus.text.size) {
if (color_words_output_graph_prefix(diff_words))
- fputs(line_prefix, diff_words->opt->file);
- fn_out_diff_words_write_helper(diff_words->opt->file,
+ emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF,
+ line_prefix, strlen(line_prefix), 0);
+ fn_out_diff_words_write_helper(diff_words->opt,
&style->ctx, style->newline,
diff_words->plus.text.ptr + diff_words->plus.text.size
- - diff_words->current_plus, diff_words->current_plus,
- line_prefix);
+ - diff_words->current_plus, diff_words->current_plus);
}
diff_words->minus.text.size = diff_words->plus.text.size = 0;
}
@@ -1138,9 +1860,29 @@ static void diff_words_show(struct diff_words_data *diff_words)
/* In "color-words" mode, show word-diff of words accumulated in the buffer */
static void diff_words_flush(struct emit_callback *ecbdata)
{
+ struct diff_options *wo = ecbdata->diff_words->opt;
+
if (ecbdata->diff_words->minus.text.size ||
ecbdata->diff_words->plus.text.size)
diff_words_show(ecbdata->diff_words);
+
+ if (wo->emitted_symbols) {
+ struct diff_options *o = ecbdata->opt;
+ struct emitted_diff_symbols *wol = wo->emitted_symbols;
+ int i;
+
+ /*
+ * NEEDSWORK:
+ * Instead of appending each, concat all words to a line?
+ */
+ for (i = 0; i < wol->nr; i++)
+ append_emitted_diff_symbol(o, &wol->buf[i]);
+
+ for (i = 0; i < wol->nr; i++)
+ free((void *)wol->buf[i].line);
+
+ wol->nr = 0;
+ }
}
static void diff_filespec_load_driver(struct diff_filespec *one)
@@ -1176,6 +1918,11 @@ static void init_diff_words_data(struct emit_callback *ecbdata,
xcalloc(1, sizeof(struct diff_words_data));
ecbdata->diff_words->type = o->word_diff;
ecbdata->diff_words->opt = o;
+
+ if (orig_opts->emitted_symbols)
+ o->emitted_symbols =
+ xcalloc(1, sizeof(struct emitted_diff_symbols));
+
if (!o->word_regex)
o->word_regex = userdiff_word_regex(one);
if (!o->word_regex)
@@ -1210,6 +1957,7 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
{
if (ecbdata->diff_words) {
diff_words_flush(ecbdata);
+ free (ecbdata->diff_words->opt->emitted_symbols);
free (ecbdata->diff_words->opt);
free (ecbdata->diff_words->minus.text.ptr);
free (ecbdata->diff_words->minus.orig);
@@ -1246,8 +1994,6 @@ static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, u
unsigned long allot;
size_t l = len;
- if (ecb->truncate)
- return ecb->truncate(line, len);
cp = line;
allot = l;
while (0 < l) {
@@ -1276,30 +2022,25 @@ static void find_lno(const char *line, struct emit_callback *ecbdata)
static void fn_out_consume(void *priv, char *line, unsigned long len)
{
struct emit_callback *ecbdata = priv;
- const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
- const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
struct diff_options *o = ecbdata->opt;
- const char *line_prefix = diff_line_prefix(o);
o->found_changes = 1;
if (ecbdata->header) {
- fprintf(o->file, "%s", ecbdata->header->buf);
+ emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+ ecbdata->header->buf, ecbdata->header->len, 0);
strbuf_reset(ecbdata->header);
ecbdata->header = NULL;
}
if (ecbdata->label_path[0]) {
- const char *name_a_tab, *name_b_tab;
-
- name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
- name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
-
- fprintf(o->file, "%s%s--- %s%s%s\n",
- line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
- fprintf(o->file, "%s%s+++ %s%s%s\n",
- line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
+ emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS,
+ ecbdata->label_path[0],
+ strlen(ecbdata->label_path[0]), 0);
+ emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS,
+ ecbdata->label_path[1],
+ strlen(ecbdata->label_path[1]), 0);
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
@@ -1315,12 +2056,13 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
len = sane_truncate_line(ecbdata, line, len);
find_lno(line, ecbdata);
emit_hunk_header(ecbdata, line, len);
- if (line[len-1] != '\n')
- putc('\n', o->file);
return;
}
if (ecbdata->diff_words) {
+ enum diff_symbol s =
+ ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN ?
+ DIFF_SYMBOL_WORDS_PORCELAIN : DIFF_SYMBOL_WORDS;
if (line[0] == '-') {
diff_words_append(line, len,
&ecbdata->diff_words->minus);
@@ -1340,21 +2082,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
return;
}
diff_words_flush(ecbdata);
- if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
- emit_line(o, context, reset, line, len);
- fputs("~\n", o->file);
- } else {
- /*
- * Skip the prefix character, if any. With
- * diff_suppress_blank_empty, there may be
- * none.
- */
- if (line[0] != '\n') {
- line++;
- len--;
- }
- emit_line(o, context, reset, line, len);
- }
+ emit_diff_symbol(o, s, line, len, 0);
return;
}
@@ -1375,8 +2103,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
default:
/* incomplete line at the end */
ecbdata->lno_in_preimage++;
- emit_line(o, diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
- reset, line, len);
+ emit_diff_symbol(o, DIFF_SYMBOL_CONTEXT_INCOMPLETE,
+ line, len, 0);
break;
}
}
@@ -1521,20 +2249,14 @@ static int scale_linear(int it, int width, int max_change)
return 1 + (it * (width - 1) / max_change);
}
-static void show_name(FILE *file,
- const char *prefix, const char *name, int len)
-{
- fprintf(file, " %s%-*s |", prefix, len, name);
-}
-
-static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset)
+static void show_graph(struct strbuf *out, char ch, int cnt,
+ const char *set, const char *reset)
{
if (cnt <= 0)
return;
- fprintf(file, "%s", set);
- while (cnt--)
- putc(ch, file);
- fprintf(file, "%s", reset);
+ strbuf_addstr(out, set);
+ strbuf_addchars(out, ch, cnt);
+ strbuf_addstr(out, reset);
}
static void fill_print_name(struct diffstat_file *file)
@@ -1558,14 +2280,16 @@ static void fill_print_name(struct diffstat_file *file)
file->print_name = pname;
}
-int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
+static void print_stat_summary_inserts_deletes(struct diff_options *options,
+ int files, int insertions, int deletions)
{
struct strbuf sb = STRBUF_INIT;
- int ret;
if (!files) {
assert(insertions == 0 && deletions == 0);
- return fprintf(fp, "%s\n", " 0 files changed");
+ emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_NO_FILES,
+ NULL, 0, 0);
+ return;
}
strbuf_addf(&sb,
@@ -1592,9 +2316,19 @@ int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
deletions);
}
strbuf_addch(&sb, '\n');
- ret = fputs(sb.buf, fp);
+ emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES,
+ sb.buf, sb.len, 0);
strbuf_release(&sb);
- return ret;
+}
+
+void print_stat_summary(FILE *fp, int files,
+ int insertions, int deletions)
+{
+ struct diff_options o;
+ memset(&o, 0, sizeof(o));
+ o.file = fp;
+
+ print_stat_summary_inserts_deletes(&o, files, insertions, deletions);
}
static void show_stats(struct diffstat_t *data, struct diff_options *options)
@@ -1604,13 +2338,13 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
int total_files = data->nr, count;
int width, name_width, graph_width, number_width = 0, bin_width = 0;
const char *reset, *add_c, *del_c;
- const char *line_prefix = "";
int extra_shown = 0;
+ const char *line_prefix = diff_line_prefix(options);
+ struct strbuf out = STRBUF_INIT;
if (data->nr == 0)
return;
- line_prefix = diff_line_prefix(options);
count = options->stat_count ? options->stat_count : data->nr;
reset = diff_get_color_opt(options, DIFF_RESET);
@@ -1764,26 +2498,32 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
}
if (file->is_binary) {
- fprintf(options->file, "%s", line_prefix);
- show_name(options->file, prefix, name, len);
- fprintf(options->file, " %*s", number_width, "Bin");
+ strbuf_addf(&out, " %s%-*s |", prefix, len, name);
+ strbuf_addf(&out, " %*s", number_width, "Bin");
if (!added && !deleted) {
- putc('\n', options->file);
+ strbuf_addch(&out, '\n');
+ emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
+ out.buf, out.len, 0);
+ strbuf_reset(&out);
continue;
}
- fprintf(options->file, " %s%"PRIuMAX"%s",
+ strbuf_addf(&out, " %s%"PRIuMAX"%s",
del_c, deleted, reset);
- fprintf(options->file, " -> ");
- fprintf(options->file, "%s%"PRIuMAX"%s",
+ strbuf_addstr(&out, " -> ");
+ strbuf_addf(&out, "%s%"PRIuMAX"%s",
add_c, added, reset);
- fprintf(options->file, " bytes");
- fprintf(options->file, "\n");
+ strbuf_addstr(&out, " bytes\n");
+ emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
+ out.buf, out.len, 0);
+ strbuf_reset(&out);
continue;
}
else if (file->is_unmerged) {
- fprintf(options->file, "%s", line_prefix);
- show_name(options->file, prefix, name, len);
- fprintf(options->file, " Unmerged\n");
+ strbuf_addf(&out, " %s%-*s |", prefix, len, name);
+ strbuf_addstr(&out, " Unmerged\n");
+ emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
+ out.buf, out.len, 0);
+ strbuf_reset(&out);
continue;
}
@@ -1806,14 +2546,16 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
add = total - del;
}
}
- fprintf(options->file, "%s", line_prefix);
- show_name(options->file, prefix, name, len);
- fprintf(options->file, " %*"PRIuMAX"%s",
+ strbuf_addf(&out, " %s%-*s |", prefix, len, name);
+ strbuf_addf(&out, " %*"PRIuMAX"%s",
number_width, added + deleted,
added + deleted ? " " : "");
- show_graph(options->file, '+', add, add_c, reset);
- show_graph(options->file, '-', del, del_c, reset);
- fprintf(options->file, "\n");
+ show_graph(&out, '+', add, add_c, reset);
+ show_graph(&out, '-', del, del_c, reset);
+ strbuf_addch(&out, '\n');
+ emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
+ out.buf, out.len, 0);
+ strbuf_reset(&out);
}
for (i = 0; i < data->nr; i++) {
@@ -1834,11 +2576,13 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
if (i < count)
continue;
if (!extra_shown)
- fprintf(options->file, "%s ...\n", line_prefix);
+ emit_diff_symbol(options,
+ DIFF_SYMBOL_STATS_SUMMARY_ABBREV,
+ NULL, 0, 0);
extra_shown = 1;
}
- fprintf(options->file, "%s", line_prefix);
- print_stat_summary(options->file, total_files, adds, dels);
+
+ print_stat_summary_inserts_deletes(options, total_files, adds, dels);
}
static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
@@ -1850,7 +2594,7 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
for (i = 0; i < data->nr; i++) {
int added = data->files[i]->added;
- int deleted= data->files[i]->deleted;
+ int deleted = data->files[i]->deleted;
if (data->files[i]->is_unmerged ||
(!data->files[i]->is_interesting && (added + deleted == 0))) {
@@ -1860,8 +2604,7 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
dels += deleted;
}
}
- fprintf(options->file, "%s", diff_line_prefix(options));
- print_stat_summary(options->file, total_files, adds, dels);
+ print_stat_summary_inserts_deletes(options, total_files, adds, dels);
}
static void show_numstat(struct diffstat_t *data, struct diff_options *options)
@@ -2225,8 +2968,8 @@ static unsigned char *deflate_it(char *data,
return deflated;
}
-static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two,
- const char *prefix)
+static void emit_binary_diff_body(struct diff_options *o,
+ mmfile_t *one, mmfile_t *two)
{
void *cp;
void *delta;
@@ -2255,13 +2998,18 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two,
}
if (delta && delta_size < deflate_size) {
- fprintf(file, "%sdelta %lu\n", prefix, orig_size);
+ char *s = xstrfmt("%lu", orig_size);
+ emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
+ s, strlen(s), 0);
+ free(s);
free(deflated);
data = delta;
data_size = delta_size;
- }
- else {
- fprintf(file, "%sliteral %lu\n", prefix, two->size);
+ } else {
+ char *s = xstrfmt("%lu", two->size);
+ emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL,
+ s, strlen(s), 0);
+ free(s);
free(delta);
data = deflated;
data_size = deflate_size;
@@ -2270,8 +3018,9 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two,
/* emit data encoded in base85 */
cp = data;
while (data_size) {
+ int len;
int bytes = (52 < data_size) ? 52 : data_size;
- char line[70];
+ char line[71];
data_size -= bytes;
if (bytes <= 26)
line[0] = bytes + 'A' - 1;
@@ -2279,20 +3028,24 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two,
line[0] = bytes - 26 + 'a' - 1;
encode_85(line + 1, cp, bytes);
cp = (char *) cp + bytes;
- fprintf(file, "%s", prefix);
- fputs(line, file);
- fputc('\n', file);
+
+ len = strlen(line);
+ line[len++] = '\n';
+ line[len] = '\0';
+
+ emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_BODY,
+ line, len, 0);
}
- fprintf(file, "%s\n", prefix);
+ emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_FOOTER, NULL, 0, 0);
free(data);
}
-static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two,
- const char *prefix)
+static void emit_binary_diff(struct diff_options *o,
+ mmfile_t *one, mmfile_t *two)
{
- fprintf(file, "%sGIT binary patch\n", prefix);
- emit_binary_diff_body(file, one, two, prefix);
- emit_binary_diff_body(file, two, one, prefix);
+ emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER, NULL, 0, 0);
+ emit_binary_diff_body(o, one, two);
+ emit_binary_diff_body(o, two, one);
}
int diff_filespec_is_binary(struct diff_filespec *one)
@@ -2369,24 +3122,16 @@ static void builtin_diff(const char *name_a,
if (o->submodule_format == DIFF_SUBMODULE_LOG &&
(!one->mode || S_ISGITLINK(one->mode)) &&
(!two->mode || S_ISGITLINK(two->mode))) {
- const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
- const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
- show_submodule_summary(o->file, one->path ? one->path : two->path,
- line_prefix,
+ show_submodule_summary(o, one->path ? one->path : two->path,
&one->oid, &two->oid,
- two->dirty_submodule,
- meta, del, add, reset);
+ two->dirty_submodule);
return;
} else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
(!one->mode || S_ISGITLINK(one->mode)) &&
(!two->mode || S_ISGITLINK(two->mode))) {
- const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
- const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
- show_submodule_inline_diff(o->file, one->path ? one->path : two->path,
- line_prefix,
+ show_submodule_inline_diff(o, one->path ? one->path : two->path,
&one->oid, &two->oid,
- two->dirty_submodule,
- meta, del, add, reset, o);
+ two->dirty_submodule);
return;
}
@@ -2435,7 +3180,8 @@ static void builtin_diff(const char *name_a,
if (complete_rewrite &&
(textconv_one || !diff_filespec_is_binary(one)) &&
(textconv_two || !diff_filespec_is_binary(two))) {
- fprintf(o->file, "%s", header.buf);
+ emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+ header.buf, header.len, 0);
strbuf_reset(&header);
emit_rewrite_diff(name_a, name_b, one, two,
textconv_one, textconv_two, o);
@@ -2445,23 +3191,31 @@ static void builtin_diff(const char *name_a,
}
if (o->irreversible_delete && lbl[1][0] == '/') {
- fprintf(o->file, "%s", header.buf);
+ emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf,
+ header.len, 0);
strbuf_reset(&header);
goto free_ab_and_return;
} else if (!DIFF_OPT_TST(o, TEXT) &&
( (!textconv_one && diff_filespec_is_binary(one)) ||
(!textconv_two && diff_filespec_is_binary(two)) )) {
+ struct strbuf sb = STRBUF_INIT;
if (!one->data && !two->data &&
S_ISREG(one->mode) && S_ISREG(two->mode) &&
!DIFF_OPT_TST(o, BINARY)) {
if (!oidcmp(&one->oid, &two->oid)) {
if (must_show_header)
- fprintf(o->file, "%s", header.buf);
+ emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+ header.buf, header.len,
+ 0);
goto free_ab_and_return;
}
- fprintf(o->file, "%s", header.buf);
- fprintf(o->file, "%sBinary files %s and %s differ\n",
- line_prefix, lbl[0], lbl[1]);
+ emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+ header.buf, header.len, 0);
+ strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
+ diff_line_prefix(o), lbl[0], lbl[1]);
+ emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES,
+ sb.buf, sb.len, 0);
+ strbuf_release(&sb);
goto free_ab_and_return;
}
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
@@ -2470,16 +3224,21 @@ static void builtin_diff(const char *name_a,
if (mf1.size == mf2.size &&
!memcmp(mf1.ptr, mf2.ptr, mf1.size)) {
if (must_show_header)
- fprintf(o->file, "%s", header.buf);
+ emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+ header.buf, header.len, 0);
goto free_ab_and_return;
}
- fprintf(o->file, "%s", header.buf);
+ emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0);
strbuf_reset(&header);
if (DIFF_OPT_TST(o, BINARY))
- emit_binary_diff(o->file, &mf1, &mf2, line_prefix);
- else
- fprintf(o->file, "%sBinary files %s and %s differ\n",
- line_prefix, lbl[0], lbl[1]);
+ emit_binary_diff(o, &mf1, &mf2);
+ else {
+ strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
+ diff_line_prefix(o), lbl[0], lbl[1]);
+ emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES,
+ sb.buf, sb.len, 0);
+ strbuf_release(&sb);
+ }
o->found_changes = 1;
} else {
/* Crazy xdl interfaces.. */
@@ -2491,7 +3250,8 @@ static void builtin_diff(const char *name_a,
const struct userdiff_funcname *pe;
if (must_show_header) {
- fprintf(o->file, "%s", header.buf);
+ emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+ header.buf, header.len, 0);
strbuf_reset(&header);
}
@@ -3249,7 +4009,7 @@ static void diff_fill_oid_info(struct diff_filespec *one)
}
if (lstat(one->path, &st) < 0)
die_errno("stat '%s'", one->path);
- if (index_path(one->oid.hash, one->path, &st, 0))
+ if (index_path(&one->oid, one->path, &st, 0))
die("cannot hash %s", one->path);
}
}
@@ -3282,8 +4042,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
const char *other;
const char *attr_path;
- name = p->one->path;
- other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+ name = one->path;
+ other = (strcmp(name, two->path) ? two->path : NULL);
attr_path = name;
if (o->prefix_length)
strip_prefix(o->prefix_length, &name, &other);
@@ -3406,6 +4166,8 @@ void diff_setup(struct diff_options *options)
options->a_prefix = "a/";
options->b_prefix = "b/";
}
+
+ options->color_moved = diff_color_moved_default;
}
void diff_setup_done(struct diff_options *options)
@@ -3515,6 +4277,9 @@ void diff_setup_done(struct diff_options *options)
if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1)
die(_("--follow requires exactly one pathspec"));
+
+ if (!options->use_color || external_diff())
+ options->color_moved = 0;
}
static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@ -3939,7 +4704,19 @@ int diff_opt_parse(struct diff_options *options,
}
else if (!strcmp(arg, "--no-color"))
options->use_color = 0;
- else if (!strcmp(arg, "--color-words")) {
+ else if (!strcmp(arg, "--color-moved")) {
+ if (diff_color_moved_default)
+ options->color_moved = diff_color_moved_default;
+ if (options->color_moved == COLOR_MOVED_NO)
+ options->color_moved = COLOR_MOVED_DEFAULT;
+ } else if (!strcmp(arg, "--no-color-moved"))
+ options->color_moved = COLOR_MOVED_NO;
+ else if (skip_prefix(arg, "--color-moved=", &arg)) {
+ int cm = parse_color_moved(arg);
+ if (cm < 0)
+ die("bad --color-moved argument: %s", arg);
+ options->color_moved = cm;
+ } else if (!strcmp(arg, "--color-words")) {
options->use_color = 1;
options->word_diff = DIFF_WORDS_COLOR;
}
@@ -4469,67 +5246,76 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
}
}
-static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_filespec *fs)
+static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs)
{
+ struct strbuf sb = STRBUF_INIT;
if (fs->mode)
- fprintf(file, " %s mode %06o ", newdelete, fs->mode);
+ strbuf_addf(&sb, " %s mode %06o ", newdelete, fs->mode);
else
- fprintf(file, " %s ", newdelete);
- write_name_quoted(fs->path, file, '\n');
-}
+ strbuf_addf(&sb, " %s ", newdelete);
+ quote_c_style(fs->path, &sb, NULL, 0);
+ strbuf_addch(&sb, '\n');
+ emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
+ sb.buf, sb.len, 0);
+ strbuf_release(&sb);
+}
-static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name,
- const char *line_prefix)
+static void show_mode_change(struct diff_options *opt, struct diff_filepair *p,
+ int show_name)
{
if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
- fprintf(file, "%s mode change %06o => %06o%c", line_prefix, p->one->mode,
- p->two->mode, show_name ? ' ' : '\n');
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addf(&sb, " mode change %06o => %06o",
+ p->one->mode, p->two->mode);
if (show_name) {
- write_name_quoted(p->two->path, file, '\n');
+ strbuf_addch(&sb, ' ');
+ quote_c_style(p->two->path, &sb, NULL, 0);
}
+ emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
+ sb.buf, sb.len, 0);
+ strbuf_release(&sb);
}
}
-static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p,
- const char *line_prefix)
+static void show_rename_copy(struct diff_options *opt, const char *renamecopy,
+ struct diff_filepair *p)
{
+ struct strbuf sb = STRBUF_INIT;
char *names = pprint_rename(p->one->path, p->two->path);
-
- fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
+ strbuf_addf(&sb, " %s %s (%d%%)\n",
+ renamecopy, names, similarity_index(p));
free(names);
- show_mode_change(file, p, 0, line_prefix);
+ emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
+ sb.buf, sb.len, 0);
+ show_mode_change(opt, p, 0);
}
static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
{
- FILE *file = opt->file;
- const char *line_prefix = diff_line_prefix(opt);
-
switch(p->status) {
case DIFF_STATUS_DELETED:
- fputs(line_prefix, file);
- show_file_mode_name(file, "delete", p->one);
+ show_file_mode_name(opt, "delete", p->one);
break;
case DIFF_STATUS_ADDED:
- fputs(line_prefix, file);
- show_file_mode_name(file, "create", p->two);
+ show_file_mode_name(opt, "create", p->two);
break;
case DIFF_STATUS_COPIED:
- fputs(line_prefix, file);
- show_rename_copy(file, "copy", p, line_prefix);
+ show_rename_copy(opt, "copy", p);
break;
case DIFF_STATUS_RENAMED:
- fputs(line_prefix, file);
- show_rename_copy(file, "rename", p, line_prefix);
+ show_rename_copy(opt, "rename", p);
break;
default:
if (p->score) {
- fprintf(file, "%s rewrite ", line_prefix);
- write_name_quoted(p->two->path, file, ' ');
- fprintf(file, "(%d%%)\n", similarity_index(p));
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addstr(&sb, " rewrite ");
+ quote_c_style(p->two->path, &sb, NULL, 0);
+ strbuf_addf(&sb, " (%d%%)\n", similarity_index(p));
+ emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
+ sb.buf, sb.len, 0);
}
- show_mode_change(file, p, !p->score, line_prefix);
+ show_mode_change(opt, p, !p->score);
break;
}
}
@@ -4734,6 +5520,51 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
warning(_(rename_limit_advice), varname, needed);
}
+static void diff_flush_patch_all_file_pairs(struct diff_options *o)
+{
+ int i;
+ static struct emitted_diff_symbols esm = EMITTED_DIFF_SYMBOLS_INIT;
+ struct diff_queue_struct *q = &diff_queued_diff;
+
+ if (WSEH_NEW & WS_RULE_MASK)
+ die("BUG: WS rules bit mask overlaps with diff symbol flags");
+
+ if (o->color_moved)
+ o->emitted_symbols = &esm;
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ if (check_pair_status(p))
+ diff_flush_patch(p, o);
+ }
+
+ if (o->emitted_symbols) {
+ if (o->color_moved) {
+ struct hashmap add_lines, del_lines;
+
+ hashmap_init(&del_lines,
+ (hashmap_cmp_fn)moved_entry_cmp, o, 0);
+ hashmap_init(&add_lines,
+ (hashmap_cmp_fn)moved_entry_cmp, o, 0);
+
+ add_lines_to_move_detection(o, &add_lines, &del_lines);
+ mark_color_as_moved(o, &add_lines, &del_lines);
+ if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
+ dim_moved_lines(o);
+
+ hashmap_free(&add_lines, 0);
+ hashmap_free(&del_lines, 0);
+ }
+
+ for (i = 0; i < esm.nr; i++)
+ emit_diff_symbol_from_struct(o, &esm.buf[i]);
+
+ for (i = 0; i < esm.nr; i++)
+ free((void *)esm.buf[i].line);
+ }
+ esm.nr = 0;
+}
+
void diff_flush(struct diff_options *options)
{
struct diff_queue_struct *q = &diff_queued_diff;
@@ -4806,6 +5637,7 @@ void diff_flush(struct diff_options *options)
fclose(options->file);
options->file = xfopen("/dev/null", "w");
options->close_file = 1;
+ options->color_moved = 0;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
if (check_pair_status(p))
@@ -4817,20 +5649,14 @@ void diff_flush(struct diff_options *options)
if (output_format & DIFF_FORMAT_PATCH) {
if (separator) {
- fprintf(options->file, "%s%c",
- diff_line_prefix(options),
- options->line_termination);
- if (options->stat_sep) {
+ emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
+ if (options->stat_sep)
/* attach patch instead of inline */
- fputs(options->stat_sep, options->file);
- }
+ emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
+ NULL, 0, 0);
}
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- if (check_pair_status(p))
- diff_flush_patch(p, options);
- }
+ diff_flush_patch_all_file_pairs(options);
}
if (output_format & DIFF_FORMAT_CALLBACK)
diff --git a/diff.h b/diff.h
index 2d442e296f..aca150ba2e 100644
--- a/diff.h
+++ b/diff.h
@@ -148,9 +148,9 @@ struct diff_options {
int abbrev;
int ita_invisible_in_index;
/* white-space error highlighting */
-#define WSEH_NEW 1
-#define WSEH_CONTEXT 2
-#define WSEH_OLD 4
+#define WSEH_NEW (1<<12)
+#define WSEH_CONTEXT (1<<13)
+#define WSEH_OLD (1<<14)
unsigned ws_error_highlight;
const char *prefix;
int prefix_length;
@@ -186,8 +186,27 @@ struct diff_options {
void *output_prefix_data;
int diff_path_counter;
+
+ struct emitted_diff_symbols *emitted_symbols;
+ enum {
+ COLOR_MOVED_NO = 0,
+ COLOR_MOVED_PLAIN = 1,
+ COLOR_MOVED_ZEBRA = 2,
+ COLOR_MOVED_ZEBRA_DIM = 3,
+ } color_moved;
+ #define COLOR_MOVED_DEFAULT COLOR_MOVED_ZEBRA
+ #define COLOR_MOVED_MIN_ALNUM_COUNT 20
};
+void diff_emit_submodule_del(struct diff_options *o, const char *line);
+void diff_emit_submodule_add(struct diff_options *o, const char *line);
+void diff_emit_submodule_untracked(struct diff_options *o, const char *path);
+void diff_emit_submodule_modified(struct diff_options *o, const char *path);
+void diff_emit_submodule_header(struct diff_options *o, const char *header);
+void diff_emit_submodule_error(struct diff_options *o, const char *err);
+void diff_emit_submodule_pipethrough(struct diff_options *o,
+ const char *line, int len);
+
enum color_diff {
DIFF_RESET = 0,
DIFF_CONTEXT = 1,
@@ -197,7 +216,15 @@ enum color_diff {
DIFF_FILE_NEW = 5,
DIFF_COMMIT = 6,
DIFF_WHITESPACE = 7,
- DIFF_FUNCINFO = 8
+ DIFF_FUNCINFO = 8,
+ DIFF_FILE_OLD_MOVED = 9,
+ DIFF_FILE_OLD_MOVED_ALT = 10,
+ DIFF_FILE_OLD_MOVED_DIM = 11,
+ DIFF_FILE_OLD_MOVED_ALT_DIM = 12,
+ DIFF_FILE_NEW_MOVED = 13,
+ DIFF_FILE_NEW_MOVED_ALT = 14,
+ DIFF_FILE_NEW_MOVED_DIM = 15,
+ DIFF_FILE_NEW_MOVED_ALT_DIM = 16
};
const char *diff_get_color(int diff_use_color, enum color_diff ix);
#define diff_get_color_opt(o, ix) \
@@ -396,8 +423,8 @@ extern int parse_rename_score(const char **cp_p);
extern long parse_algorithm_value(const char *value);
-extern int print_stat_summary(FILE *fp, int files,
- int insertions, int deletions);
+extern void print_stat_summary(FILE *fp, int files,
+ int insertions, int deletions);
extern void setup_diff_pager(struct diff_options *);
#endif /* DIFF_H */
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 786f389498..0d8c3d2ee4 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -532,9 +532,9 @@ void diffcore_rename(struct diff_options *options)
}
if (options->show_rename_progress) {
- progress = start_progress_delay(
+ progress = start_delayed_progress(
_("Performing inexact rename detection"),
- rename_dst_nr * rename_src_nr, 50, 1);
+ rename_dst_nr * rename_src_nr);
}
mx = xcalloc(st_mult(NUM_CANDIDATE_PER_DST, num_create), sizeof(*mx));
diff --git a/dir.c b/dir.c
index ae6f5c9636..1c55dc3e36 100644
--- a/dir.c
+++ b/dir.c
@@ -2398,7 +2398,8 @@ struct ondisk_untracked_cache {
char exclude_per_dir[FLEX_ARRAY];
};
-#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, exclude_per_dir) + len + 1)
+#define ouc_offset(x) offsetof(struct ondisk_untracked_cache, x)
+#define ouc_size(len) (ouc_offset(exclude_per_dir) + len + 1)
struct write_data {
int index; /* number of written untracked_cache_dir */
@@ -2560,17 +2561,18 @@ struct read_data {
const unsigned char *end;
};
-static void stat_data_from_disk(struct stat_data *to, const struct stat_data *from)
+static void stat_data_from_disk(struct stat_data *to, const unsigned char *data)
{
- to->sd_ctime.sec = get_be32(&from->sd_ctime.sec);
- to->sd_ctime.nsec = get_be32(&from->sd_ctime.nsec);
- to->sd_mtime.sec = get_be32(&from->sd_mtime.sec);
- to->sd_mtime.nsec = get_be32(&from->sd_mtime.nsec);
- to->sd_dev = get_be32(&from->sd_dev);
- to->sd_ino = get_be32(&from->sd_ino);
- to->sd_uid = get_be32(&from->sd_uid);
- to->sd_gid = get_be32(&from->sd_gid);
- to->sd_size = get_be32(&from->sd_size);
+ memcpy(to, data, sizeof(*to));
+ to->sd_ctime.sec = ntohl(to->sd_ctime.sec);
+ to->sd_ctime.nsec = ntohl(to->sd_ctime.nsec);
+ to->sd_mtime.sec = ntohl(to->sd_mtime.sec);
+ to->sd_mtime.nsec = ntohl(to->sd_mtime.nsec);
+ to->sd_dev = ntohl(to->sd_dev);
+ to->sd_ino = ntohl(to->sd_ino);
+ to->sd_uid = ntohl(to->sd_uid);
+ to->sd_gid = ntohl(to->sd_gid);
+ to->sd_size = ntohl(to->sd_size);
}
static int read_one_dir(struct untracked_cache_dir **untracked_,
@@ -2645,7 +2647,7 @@ static void read_stat(size_t pos, void *cb)
rd->data = rd->end + 1;
return;
}
- stat_data_from_disk(&ud->stat_data, (struct stat_data *)rd->data);
+ stat_data_from_disk(&ud->stat_data, rd->data);
rd->data += sizeof(struct stat_data);
ud->valid = 1;
}
@@ -2663,22 +2665,22 @@ static void read_sha1(size_t pos, void *cb)
}
static void load_sha1_stat(struct sha1_stat *sha1_stat,
- const struct stat_data *stat,
+ const unsigned char *data,
const unsigned char *sha1)
{
- stat_data_from_disk(&sha1_stat->stat, stat);
+ stat_data_from_disk(&sha1_stat->stat, data);
hashcpy(sha1_stat->sha1, sha1);
sha1_stat->valid = 1;
}
struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
{
- const struct ondisk_untracked_cache *ouc;
struct untracked_cache *uc;
struct read_data rd;
const unsigned char *next = data, *end = (const unsigned char *)data + sz;
const char *ident;
int ident_len, len;
+ const char *exclude_per_dir;
if (sz <= 1 || end[-1] != '\0')
return NULL;
@@ -2690,21 +2692,23 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
ident = (const char *)next;
next += ident_len;
- ouc = (const struct ondisk_untracked_cache *)next;
if (next + ouc_size(0) > end)
return NULL;
uc = xcalloc(1, sizeof(*uc));
strbuf_init(&uc->ident, ident_len);
strbuf_add(&uc->ident, ident, ident_len);
- load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat,
- ouc->info_exclude_sha1);
- load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat,
- ouc->excludes_file_sha1);
- uc->dir_flags = get_be32(&ouc->dir_flags);
- uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir);
+ load_sha1_stat(&uc->ss_info_exclude,
+ next + ouc_offset(info_exclude_stat),
+ next + ouc_offset(info_exclude_sha1));
+ load_sha1_stat(&uc->ss_excludes_file,
+ next + ouc_offset(excludes_file_stat),
+ next + ouc_offset(excludes_file_sha1));
+ uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
+ exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
+ uc->exclude_per_dir = xstrdup(exclude_per_dir);
/* NUL after exclude_per_dir is covered by sizeof(*ouc) */
- next += ouc_size(strlen(ouc->exclude_per_dir));
+ next += ouc_size(strlen(exclude_per_dir));
if (next >= end)
goto done2;
diff --git a/entry.c b/entry.c
index d6b263f78e..65458f07a4 100644
--- a/entry.c
+++ b/entry.c
@@ -137,6 +137,105 @@ static int streaming_write_entry(const struct cache_entry *ce, char *path,
return result;
}
+void enable_delayed_checkout(struct checkout *state)
+{
+ if (!state->delayed_checkout) {
+ state->delayed_checkout = xmalloc(sizeof(*state->delayed_checkout));
+ state->delayed_checkout->state = CE_CAN_DELAY;
+ string_list_init(&state->delayed_checkout->filters, 0);
+ string_list_init(&state->delayed_checkout->paths, 0);
+ }
+}
+
+static int remove_available_paths(struct string_list_item *item, void *cb_data)
+{
+ struct string_list *available_paths = cb_data;
+ struct string_list_item *available;
+
+ available = string_list_lookup(available_paths, item->string);
+ if (available)
+ available->util = (void *)item->string;
+ return !available;
+}
+
+int finish_delayed_checkout(struct checkout *state)
+{
+ int errs = 0;
+ struct string_list_item *filter, *path;
+ struct delayed_checkout *dco = state->delayed_checkout;
+
+ if (!state->delayed_checkout)
+ return errs;
+
+ dco->state = CE_RETRY;
+ while (dco->filters.nr > 0) {
+ for_each_string_list_item(filter, &dco->filters) {
+ struct string_list available_paths = STRING_LIST_INIT_NODUP;
+
+ if (!async_query_available_blobs(filter->string, &available_paths)) {
+ /* Filter reported an error */
+ errs = 1;
+ filter->string = "";
+ continue;
+ }
+ if (available_paths.nr <= 0) {
+ /*
+ * Filter responded with no entries. That means
+ * the filter is done and we can remove the
+ * filter from the list (see
+ * "string_list_remove_empty_items" call below).
+ */
+ filter->string = "";
+ continue;
+ }
+
+ /*
+ * In dco->paths we store a list of all delayed paths.
+ * The filter just send us a list of available paths.
+ * Remove them from the list.
+ */
+ filter_string_list(&dco->paths, 0,
+ &remove_available_paths, &available_paths);
+
+ for_each_string_list_item(path, &available_paths) {
+ struct cache_entry* ce;
+
+ if (!path->util) {
+ error("external filter '%s' signaled that '%s' "
+ "is now available although it has not been "
+ "delayed earlier",
+ filter->string, path->string);
+ errs |= 1;
+
+ /*
+ * Do not ask the filter for available blobs,
+ * again, as the filter is likely buggy.
+ */
+ filter->string = "";
+ continue;
+ }
+ ce = index_file_exists(state->istate, path->string,
+ strlen(path->string), 0);
+ errs |= (ce ? checkout_entry(ce, state, NULL) : 1);
+ }
+ }
+ string_list_remove_empty_items(&dco->filters, 0);
+ }
+ string_list_clear(&dco->filters, 0);
+
+ /* At this point we should not have any delayed paths anymore. */
+ errs |= dco->paths.nr;
+ for_each_string_list_item(path, &dco->paths) {
+ error("'%s' was not filtered properly", path->string);
+ }
+ string_list_clear(&dco->paths, 0);
+
+ free(dco);
+ state->delayed_checkout = NULL;
+
+ return errs;
+}
+
static int write_entry(struct cache_entry *ce,
char *path, const struct checkout *state, int to_tempfile)
{
@@ -179,11 +278,34 @@ static int write_entry(struct cache_entry *ce,
/*
* Convert from git internal format to working tree format
*/
- if (ce_mode_s_ifmt == S_IFREG &&
- convert_to_working_tree(ce->name, new, size, &buf)) {
- free(new);
- new = strbuf_detach(&buf, &newsize);
- size = newsize;
+ if (ce_mode_s_ifmt == S_IFREG) {
+ struct delayed_checkout *dco = state->delayed_checkout;
+ if (dco && dco->state != CE_NO_DELAY) {
+ /* Do not send the blob in case of a retry. */
+ if (dco->state == CE_RETRY) {
+ new = NULL;
+ size = 0;
+ }
+ ret = async_convert_to_working_tree(
+ ce->name, new, size, &buf, dco);
+ if (ret && string_list_has_string(&dco->paths, ce->name)) {
+ free(new);
+ goto finish;
+ }
+ } else
+ ret = convert_to_working_tree(
+ ce->name, new, size, &buf);
+
+ if (ret) {
+ free(new);
+ new = strbuf_detach(&buf, &newsize);
+ size = newsize;
+ }
+ /*
+ * No "else" here as errors from convert are OK at this
+ * point. If the error would have been fatal (e.g.
+ * filter is required), then we would have died already.
+ */
}
fd = open_output_fd(path, ce, to_tempfile);
diff --git a/fast-import.c b/fast-import.c
index a959161b46..49516d60e6 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -167,6 +167,7 @@ Format of STDIN stream:
#include "quote.h"
#include "dir.h"
#include "run-command.h"
+#include "packfile.h"
#define PACK_ID_BITS 16
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
diff --git a/fetch-pack.c b/fetch-pack.c
index fbbc99c888..105506e9aa 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -17,6 +17,7 @@
#include "prio-queue.h"
#include "sha1-array.h"
#include "oidset.h"
+#include "packfile.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
diff --git a/fsck.c b/fsck.c
index b4204d772b..2d2d2e9432 100644
--- a/fsck.c
+++ b/fsck.c
@@ -736,7 +736,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
buffer += 41;
parent_line_count++;
}
- graft = lookup_commit_graft(commit->object.oid.hash);
+ graft = lookup_commit_graft(&commit->object.oid);
parent_count = commit_list_count(commit->parents);
if (graft) {
if (graft->nr_parent == -1 && !parent_count)
diff --git a/git-compat-util.h b/git-compat-util.h
index db9c22de76..6678b488cc 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -749,8 +749,6 @@ const char *inet_ntop(int af, const void *src, char *dst, size_t size);
extern int git_atexit(void (*handler)(void));
#endif
-extern void release_pack_memory(size_t);
-
typedef void (*try_to_free_t)(size_t);
extern try_to_free_t set_try_to_free_routine(try_to_free_t);
@@ -828,6 +826,14 @@ static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
memcpy(dst, src, st_mult(size, n));
}
+#define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \
+ BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+static inline void move_array(void *dst, const void *src, size_t n, size_t size)
+{
+ if (n)
+ memmove(dst, src, st_mult(size, n));
+}
+
/*
* These functions help you allocate structs with flex arrays, and copy
* the data directly into the array. For example, if you had:
diff --git a/git-gui/Makefile b/git-gui/Makefile
index fe30be38dc..918a8de369 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -161,7 +161,9 @@ ifeq ($(uname_S),Darwin)
endif
endif
ifneq (,$(findstring MINGW,$(uname_S)))
+ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
NO_MSGFMT=1
+endif
GITGUI_WINDOWS_WRAPPER := YesPlease
GITGUI_RELATIVE := 1
endif
diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
index 75d1da8d31..80f5a59bbb 100644
--- a/git-gui/lib/choose_repository.tcl
+++ b/git-gui/lib/choose_repository.tcl
@@ -142,6 +142,10 @@ constructor pick {} {
-label [mc "Recent Repositories"]
}
+ if {[set lenrecent [llength $sorted_recent]] < $maxrecent} {
+ set lenrecent $maxrecent
+ }
+
${NS}::label $w_body.space
${NS}::label $w_body.recentlabel \
-anchor w \
@@ -153,7 +157,7 @@ constructor pick {} {
-background [get_bg_color $w_body.recentlabel] \
-wrap none \
-width 50 \
- -height $maxrecent
+ -height $lenrecent
$w_recentlist tag conf link \
-foreground blue \
-underline 1
@@ -235,19 +239,19 @@ method _invoke_next {} {
proc _get_recentrepos {} {
set recent [list]
- foreach p [get_config gui.recentrepo] {
+ foreach p [lsort -unique [get_config gui.recentrepo]] {
if {[_is_git [file join $p .git]]} {
lappend recent $p
} else {
_unset_recentrepo $p
}
}
- return [lsort $recent]
+ return $recent
}
proc _unset_recentrepo {p} {
regsub -all -- {([()\[\]{}\.^$+*?\\])} $p {\\\1} p
- git config --global --unset gui.recentrepo "^$p\$"
+ catch {git config --global --unset-all gui.recentrepo "^$p\$"}
load_config 1
}
@@ -262,12 +266,11 @@ proc _append_recentrepos {path} {
set i [lsearch $recent $path]
if {$i >= 0} {
_unset_recentrepo $path
- set recent [lreplace $recent $i $i]
}
- lappend recent $path
git config --global --add gui.recentrepo $path
load_config 1
+ set recent [get_config gui.recentrepo]
if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} {
set maxrecent 10
@@ -275,7 +278,7 @@ proc _append_recentrepos {path} {
while {[llength $recent] > $maxrecent} {
_unset_recentrepo [lindex $recent 0]
- set recent [lrange $recent 1 end]
+ set recent [get_config gui.recentrepo]
}
}
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index bcf0d92ec2..6c390d6c22 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -100,7 +100,7 @@ do
if test $? -ne 0
then
gettextln "Simple merge did not work, trying automatic merge."
- git-merge-index -o git-merge-one-file -a ||
+ git merge-index -o git-merge-one-file -a ||
OCTOPUS_FAILURE=1
next=$(git write-tree 2>/dev/null)
fi
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index 424b034e34..9879c59395 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -115,16 +115,16 @@ case "${1:-.}${2:-.}${3:-.}" in
;;
esac
- src1=$(git-unpack-file $2)
- src2=$(git-unpack-file $3)
+ src1=$(git unpack-file $2)
+ src2=$(git unpack-file $3)
case "$1" in
'')
echo "Added $4 in both, but differently."
- orig=$(git-unpack-file e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
+ orig=$(git unpack-file e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
;;
*)
echo "Auto-merging $4"
- orig=$(git-unpack-file $1)
+ orig=$(git unpack-file $1)
;;
esac
diff --git a/git-merge-resolve.sh b/git-merge-resolve.sh
index c9da747fcf..343fe7bccd 100755
--- a/git-merge-resolve.sh
+++ b/git-merge-resolve.sh
@@ -45,7 +45,7 @@ then
exit 0
else
echo "Simple merge failed, trying Automatic merge."
- if git-merge-index -o git-merge-one-file -a
+ if git merge-index -o git-merge-one-file -a
then
exit 0
else
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
index 375239341f..6e64d40d6f 100644
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -45,7 +45,7 @@ then
# itself well to recording empty patches. fortunately, cherry-pick
# makes this easy
git cherry-pick ${gpg_sign_opt:+"$gpg_sign_opt"} --allow-empty \
- --right-only "$revisions" \
+ $allow_rerere_autoupdate --right-only "$revisions" \
${restrict_revision+^$restrict_revision}
ret=$?
else
@@ -53,6 +53,7 @@ else
git format-patch -k --stdout --full-index --cherry-pick --right-only \
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
+ $git_format_patch_opt \
"$revisions" ${restrict_revision+^$restrict_revision} \
>"$GIT_DIR/rebased-patches"
ret=$?
@@ -82,6 +83,7 @@ else
fi
git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
+ $allow_rerere_autoupdate \
${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches"
ret=$?
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 90b1fbe9cf..29b7e8824b 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -281,7 +281,7 @@ pick_one () {
test -d "$rewritten" &&
pick_one_preserving_merges "$@" && return
- output eval git cherry-pick \
+ output eval git cherry-pick $allow_rerere_autoupdate \
${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
"$strategy_args" $empty_args $ff "$@"
@@ -393,7 +393,8 @@ pick_one_preserving_merges () {
merge_args="--no-log --no-ff"
if ! do_with_author output eval \
'git merge ${gpg_sign_opt:+"$gpg_sign_opt"} \
- $merge_args $strategy_args -m "$msg_content" $new_parents'
+ $allow_rerere_autoupdate $merge_args \
+ $strategy_args -m "$msg_content" $new_parents'
then
printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
die_with_patch $sha1 "$(eval_gettext "Error redoing merge \$sha1")"
@@ -401,7 +402,7 @@ pick_one_preserving_merges () {
echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
;;
*)
- output eval git cherry-pick \
+ output eval git cherry-pick $allow_rerere_autoupdate \
${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
"$strategy_args" "$@" ||
die_with_patch $sha1 "$(eval_gettext "Could not pick \$sha1")"
diff --git a/git-rebase.sh b/git-rebase.sh
index 2cf73b88e8..ad8415e3cf 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -55,9 +55,10 @@ LF='
'
ok_to_skip_pre_rebase=
resolvemsg="
-$(gettext 'When you have resolved this problem, run "git rebase --continue".
-If you prefer to skip this patch, run "git rebase --skip" instead.
-To check out the original branch and stop rebasing, run "git rebase --abort".')
+$(gettext 'Resolve all conflicts manually, mark them as resolved with
+"git add/rm <conflicted_files>", then run "git rebase --continue".
+You can instead skip this commit: run "git rebase --skip".
+To abort and get back to the state before "git rebase", run "git rebase --abort".')
"
unset onto
unset restrict_revision
@@ -73,6 +74,7 @@ test "$(git config --bool rebase.stat)" = true && diffstat=t
autostash="$(git config --bool rebase.autostash || echo false)"
fork_point=auto
git_am_opt=
+git_format_patch_opt=
rebase_root=
force_rebase=
allow_rerere_autoupdate=
@@ -444,6 +446,11 @@ else
state_dir="$apply_dir"
fi
+if test -t 2 && test -z "$GIT_QUIET"
+then
+ git_format_patch_opt="$git_format_patch_opt --progress"
+fi
+
if test -z "$rebase_root"
then
case "$#" in
diff --git a/git-stash.sh b/git-stash.sh
index 9b6c2da7b4..8b2ce9afda 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -43,9 +43,16 @@ no_changes () {
}
untracked_files () {
+ if test "$1" = "-z"
+ then
+ shift
+ z=-z
+ else
+ z=
+ fi
excl_opt=--exclude-standard
test "$untracked" = "all" && excl_opt=
- git ls-files -o -z $excl_opt -- "$@"
+ git ls-files -o $z $excl_opt -- "$@"
}
clear_stash () {
@@ -114,7 +121,7 @@ create_stash () {
# Untracked files are stored by themselves in a parentless commit, for
# ease of unpacking later.
u_commit=$(
- untracked_files "$@" | (
+ untracked_files -z "$@" | (
GIT_INDEX_FILE="$TMPindex" &&
export GIT_INDEX_FILE &&
rm -f "$TMPindex" &&
@@ -300,6 +307,12 @@ push_stash () {
if test -z "$patch_mode"
then
+ test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
+ if test -n "$untracked"
+ then
+ git clean --force --quiet -d $CLEAN_X_OPTION -- "$@"
+ fi
+
if test $# != 0
then
git reset -q -- "$@"
@@ -309,11 +322,6 @@ push_stash () {
else
git reset --hard -q
fi
- test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
- if test -n "$untracked"
- then
- git clean --force --quiet -d $CLEAN_X_OPTION -- "$@"
- fi
if test "$keep_index" = "t" && test -n "$i_tree"
then
@@ -573,7 +581,7 @@ apply_stash () {
if test -n "$u_tree"
then
- GIT_INDEX_FILE="$TMPindex" git-read-tree "$u_tree" &&
+ GIT_INDEX_FILE="$TMPindex" git read-tree "$u_tree" &&
GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
rm -f "$TMPindex" ||
die "$(gettext "Could not restore untracked files from stash entry")"
diff --git a/git-submodule.sh b/git-submodule.sh
index e131760eec..66d1ae8ef6 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -611,7 +611,6 @@ cmd_update()
die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit
- url=$(git config submodule."$name".url)
if ! test -z "$update"
then
update_module=$update
@@ -864,7 +863,7 @@ cmd_summary() {
test $status != A && test $ignore_config = all && continue
fi
# Also show added or modified modules which are checked out
- GIT_DIR="$sm_path/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
+ GIT_DIR="$sm_path/.git" git rev-parse --git-dir >/dev/null 2>&1 &&
printf '%s\n' "$sm_path"
done
)
@@ -898,11 +897,11 @@ cmd_summary() {
missing_dst=
test $mod_src = 160000 &&
- ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
+ ! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_src^0 >/dev/null &&
missing_src=t
test $mod_dst = 160000 &&
- ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
+ ! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_dst^0 >/dev/null &&
missing_dst=t
display_name=$(git submodule--helper relative-path "$name" "$wt_prefix")
diff --git a/git.c b/git.c
index 489aab4d83..f31dca6962 100644
--- a/git.c
+++ b/git.c
@@ -33,6 +33,16 @@ static void commit_pager_choice(void) {
}
}
+void setup_auto_pager(const char *cmd, int def)
+{
+ if (use_pager != -1 || pager_in_use())
+ return;
+ use_pager = check_pager_config(cmd);
+ if (use_pager == -1)
+ use_pager = def;
+ commit_pager_choice();
+}
+
static int handle_options(const char ***argv, int *argc, int *envchanged)
{
const char **orig_argv = *argv;
@@ -283,6 +293,7 @@ static int handle_alias(int *argcp, const char ***argv)
*/
#define NEED_WORK_TREE (1<<3)
#define SUPPORT_SUPER_PREFIX (1<<4)
+#define DELAY_PAGER_CONFIG (1<<5)
struct cmd_struct {
const char *cmd;
@@ -306,7 +317,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
prefix = setup_git_directory_gently(&nongit_ok);
}
- if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
+ if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
+ !(p->option & DELAY_PAGER_CONFIG))
use_pager = check_pager_config(p->cmd);
if (use_pager == -1 && p->option & USE_PAGER)
use_pager = 1;
@@ -392,7 +404,7 @@ static struct cmd_struct commands[] = {
{ "fsck-objects", cmd_fsck, RUN_SETUP },
{ "gc", cmd_gc, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id },
- { "grep", cmd_grep, RUN_SETUP_GENTLY | SUPPORT_SUPER_PREFIX },
+ { "grep", cmd_grep, RUN_SETUP_GENTLY },
{ "hash-object", cmd_hash_object },
{ "help", cmd_help },
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
@@ -454,7 +466,7 @@ static struct cmd_struct commands[] = {
{ "stripspace", cmd_stripspace },
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX},
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
- { "tag", cmd_tag, RUN_SETUP },
+ { "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
{ "unpack-file", cmd_unpack_file, RUN_SETUP },
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
{ "update-index", cmd_update_index, RUN_SETUP },
@@ -547,7 +559,7 @@ static void execv_dashed_external(const char **argv)
if (get_super_prefix())
die("%s doesn't support --super-prefix", argv[0]);
- if (use_pager == -1)
+ if (use_pager == -1 && !is_builtin(argv[0]))
use_pager = check_pager_config(argv[0]);
commit_pager_choice();
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 3d4a8ee27c..959f04b494 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3071,6 +3071,8 @@ sub git_get_projects_list {
return if (m!^[/.]$!);
# only directories can be git repositories
return unless (-d $_);
+ # need search permission
+ return unless (-x $_);
# don't traverse too deep (Find is super slow on os x)
# $project_maxdepth excludes depth of $projectroot
if (($File::Find::name =~ tr!/!!) - $pfxdepth > $project_maxdepth) {
@@ -5965,6 +5967,9 @@ sub git_history_body {
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
if ($ftype eq 'blob') {
+ print " | " .
+ $cgi->a({-href => href(action=>"blob_plain", hash_base=>$commit, file_name=>$file_name)}, "raw");
+
my $blob_current = $file_hash;
my $blob_parent = git_get_hash_by_path($commit, $file_name);
if (defined $blob_current && defined $blob_parent &&
diff --git a/grep.c b/grep.c
index 2efec0e182..ce6a48e634 100644
--- a/grep.c
+++ b/grep.c
@@ -1821,7 +1821,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
return 0;
if (opt->status_only)
- return 0;
+ return opt->unmatch_name_only;
if (opt->unmatch_name_only) {
/* We did not see any hit, so we want to show this */
show_name(opt, gs->name);
@@ -1927,16 +1927,6 @@ void grep_source_init(struct grep_source *gs, enum grep_source_type type,
case GREP_SOURCE_FILE:
gs->identifier = xstrdup(identifier);
break;
- case GREP_SOURCE_SUBMODULE:
- if (!identifier) {
- gs->identifier = NULL;
- break;
- }
- /*
- * FALL THROUGH
- * If the identifier is non-NULL (in the submodule case) it
- * will be a SHA1 that needs to be copied.
- */
case GREP_SOURCE_OID:
gs->identifier = oiddup(identifier);
break;
@@ -1959,7 +1949,6 @@ void grep_source_clear_data(struct grep_source *gs)
switch (gs->type) {
case GREP_SOURCE_FILE:
case GREP_SOURCE_OID:
- case GREP_SOURCE_SUBMODULE:
FREE_AND_NULL(gs->buf);
gs->size = 0;
break;
@@ -2030,8 +2019,6 @@ static int grep_source_load(struct grep_source *gs)
return grep_source_load_oid(gs);
case GREP_SOURCE_BUF:
return gs->buf ? 0 : -1;
- case GREP_SOURCE_SUBMODULE:
- break;
}
die("BUG: invalid grep_source type to load");
}
diff --git a/grep.h b/grep.h
index 0c091e5104..52aecfab6e 100644
--- a/grep.h
+++ b/grep.h
@@ -193,7 +193,6 @@ struct grep_source {
GREP_SOURCE_OID,
GREP_SOURCE_FILE,
GREP_SOURCE_BUF,
- GREP_SOURCE_SUBMODULE,
} type;
void *identifier;
diff --git a/http-backend.c b/http-backend.c
index 519025d2c3..8076b1d5e5 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -9,6 +9,7 @@
#include "string-list.h"
#include "url.h"
#include "argv-array.h"
+#include "packfile.h"
static const char content_type[] = "Content-Type";
static const char content_length[] = "Content-Length";
diff --git a/http-push.c b/http-push.c
index c91f40a610..e4c9b065ce 100644
--- a/http-push.c
+++ b/http-push.c
@@ -11,6 +11,7 @@
#include "list-objects.h"
#include "sigchain.h"
#include "argv-array.h"
+#include "packfile.h"
#ifdef EXPAT_NEEDS_XMLPARSE_H
#include <xmlparse.h>
diff --git a/http-walker.c b/http-walker.c
index ee049cb13d..1ae8363de2 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -4,6 +4,7 @@
#include "http.h"
#include "list.h"
#include "transport.h"
+#include "packfile.h"
struct alt_base {
char *base;
diff --git a/http.c b/http.c
index c6c010f881..9e40a465fd 100644
--- a/http.c
+++ b/http.c
@@ -11,6 +11,7 @@
#include "pkt-line.h"
#include "gettext.h"
#include "transport.h"
+#include "packfile.h"
static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
#if LIBCURL_VERSION_NUM >= 0x070a08
@@ -91,7 +92,7 @@ static struct {
* here, too
*/
};
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
static const char *curl_deleg;
static struct {
const char *name;
@@ -272,10 +273,10 @@ static int http_options(const char *var, const char *value, void *cb)
if (!strcmp("http.sslversion", var))
return git_config_string(&ssl_version, var, value);
if (!strcmp("http.sslcert", var))
- return git_config_string(&ssl_cert, var, value);
+ return git_config_pathname(&ssl_cert, var, value);
#if LIBCURL_VERSION_NUM >= 0x070903
if (!strcmp("http.sslkey", var))
- return git_config_string(&ssl_key, var, value);
+ return git_config_pathname(&ssl_key, var, value);
#endif
#if LIBCURL_VERSION_NUM >= 0x070908
if (!strcmp("http.sslcapath", var))
@@ -352,7 +353,7 @@ static int http_options(const char *var, const char *value, void *cb)
}
if (!strcmp("http.delegation", var)) {
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
return git_config_string(&curl_deleg, var, value);
#else
warning(_("Delegation control is not supported with cURL < 7.22.0"));
@@ -677,6 +678,7 @@ void setup_curl_trace(CURL *handle)
curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
}
+#ifdef CURLPROTO_HTTP
static long get_curl_allowed_protocols(int from_user)
{
long allowed_protocols = 0;
@@ -692,6 +694,7 @@ static long get_curl_allowed_protocols(int from_user)
return allowed_protocols;
}
+#endif
static CURL *get_curl_handle(void)
{
@@ -717,7 +720,7 @@ static CURL *get_curl_handle(void)
curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
#endif
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
if (curl_deleg) {
int i;
for (i = 0; i < ARRAY_SIZE(curl_deleg_levels); i++) {
@@ -790,7 +793,7 @@ static CURL *get_curl_handle(void)
#elif LIBCURL_VERSION_NUM >= 0x071101
curl_easy_setopt(result, CURLOPT_POST301, 1);
#endif
-#if LIBCURL_VERSION_NUM >= 0x071304
+#ifdef CURLPROTO_HTTP
curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS,
get_curl_allowed_protocols(0));
curl_easy_setopt(result, CURLOPT_PROTOCOLS,
diff --git a/mailmap.c b/mailmap.c
index c1a79c100c..cb921b4db6 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -214,17 +214,17 @@ static int read_mailmap_blob(struct string_list *map,
const char *name,
char **repo_abbrev)
{
- unsigned char sha1[20];
+ struct object_id oid;
char *buf;
unsigned long size;
enum object_type type;
if (!name)
return 0;
- if (get_sha1(name, sha1) < 0)
+ if (get_oid(name, &oid) < 0)
return 0;
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid.hash, &type, &size);
if (!buf)
return error("unable to read mailmap object at %s", name);
if (type != OBJ_BLOB)
diff --git a/name-hash.c b/name-hash.c
index 0e10f3eab8..bd8dc7a6a7 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -17,10 +17,14 @@ struct dir_entry {
};
static int dir_entry_cmp(const void *unused_cmp_data,
- const struct dir_entry *e1,
- const struct dir_entry *e2,
- const char *name)
+ const void *entry,
+ const void *entry_or_key,
+ const void *keydata)
{
+ const struct dir_entry *e1 = entry;
+ const struct dir_entry *e2 = entry_or_key;
+ const char *name = keydata;
+
return e1->namelen != e2->namelen || strncasecmp(e1->name,
name ? name : e2->name, e1->namelen);
}
@@ -110,10 +114,12 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
}
static int cache_entry_cmp(const void *unused_cmp_data,
- const struct cache_entry *ce1,
- const struct cache_entry *ce2,
+ const void *entry,
+ const void *entry_or_key,
const void *remove)
{
+ const struct cache_entry *ce1 = entry;
+ const struct cache_entry *ce2 = entry_or_key;
/*
* For remove_name_hash, find the exact entry (pointer equality); for
* index_file_exists, find all entries with matching hash code and
@@ -574,10 +580,8 @@ static void lazy_init_name_hash(struct index_state *istate)
{
if (istate->name_hash_initialized)
return;
- hashmap_init(&istate->name_hash, (hashmap_cmp_fn) cache_entry_cmp,
- NULL, istate->cache_nr);
- hashmap_init(&istate->dir_hash, (hashmap_cmp_fn) dir_entry_cmp,
- NULL, istate->cache_nr);
+ hashmap_init(&istate->name_hash, cache_entry_cmp, NULL, istate->cache_nr);
+ hashmap_init(&istate->dir_hash, dir_entry_cmp, NULL, istate->cache_nr);
if (lookup_lazy_params(istate)) {
hashmap_disallow_rehash(&istate->dir_hash, 1);
diff --git a/notes-merge.c b/notes-merge.c
index 70e3fbeefb..744c685576 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -99,8 +99,7 @@ static struct notes_merge_pair *find_notes_merge_pair_pos(
else {
*occupied = 0;
if (insert_new && i < len) {
- memmove(list + i + 1, list + i,
- (len - i) * sizeof(struct notes_merge_pair));
+ MOVE_ARRAY(list + i + 1, list + i, len - i);
memset(list + i, 0, sizeof(struct notes_merge_pair));
}
}
@@ -710,7 +709,7 @@ int notes_merge_commit(struct notes_merge_options *o,
/* write file as blob, and add to partial_tree */
if (stat(path.buf, &st))
die_errno("Failed to stat '%s'", path.buf);
- if (index_path(blob_oid.hash, path.buf, &st, HASH_WRITE_OBJECT))
+ if (index_path(&blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
die("Failed to write blob object from '%s'", path.buf);
if (add_note(partial_tree, &obj_oid, &blob_oid, NULL))
die("Failed to add resolved note '%s' to notes tree",
diff --git a/notes.c b/notes.c
index 8f47c202c5..f090c88363 100644
--- a/notes.c
+++ b/notes.c
@@ -425,7 +425,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
unsigned char type;
struct leaf_node *l;
- buf = fill_tree_descriptor(&desc, subtree->val_oid.hash);
+ buf = fill_tree_descriptor(&desc, &subtree->val_oid);
if (!buf)
die("Could not read %s for notes-index",
oid_to_hex(&subtree->val_oid));
@@ -1026,7 +1026,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
t->dirty = 0;
if (flags & NOTES_INIT_EMPTY || !notes_ref ||
- get_sha1_treeish(notes_ref, object_oid.hash))
+ get_oid_treeish(notes_ref, &object_oid))
return;
if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, object_oid.hash))
die("Cannot use notes ref %s", notes_ref);
diff --git a/object.c b/object.c
index f818777412..321d7e9201 100644
--- a/object.c
+++ b/object.c
@@ -141,7 +141,6 @@ void *create_object(const unsigned char *sha1, void *o)
struct object *obj = o;
obj->parsed = 0;
- obj->used = 0;
obj->flags = 0;
hashcpy(obj->oid.hash, sha1);
diff --git a/object.h b/object.h
index 33e5cc9943..0a419ba8da 100644
--- a/object.h
+++ b/object.h
@@ -38,6 +38,7 @@ struct object_array {
* http-push.c: 16-----19
* commit.c: 16-----19
* sha1_name.c: 20
+ * builtin/fsck.c: 0--3
*/
#define FLAG_BITS 27
@@ -46,7 +47,6 @@ struct object_array {
*/
struct object {
unsigned parsed : 1;
- unsigned used : 1;
unsigned type : TYPE_BITS;
unsigned flags : FLAG_BITS;
struct object_id oid;
diff --git a/outgoing/packfile.h b/outgoing/packfile.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/outgoing/packfile.h
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 327634cd71..cb3d14ba45 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -9,6 +9,7 @@
#include "pack-bitmap.h"
#include "pack-revindex.h"
#include "pack-objects.h"
+#include "packfile.h"
/*
* An entry on the bitmap index, representing the bitmap for a given
diff --git a/pack-check.c b/pack-check.c
index e1fcb228fa..073c1fbd46 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -2,6 +2,7 @@
#include "pack.h"
#include "pack-revindex.h"
#include "progress.h"
+#include "packfile.h"
struct idx_entry {
off_t offset;
diff --git a/packfile.c b/packfile.c
new file mode 100644
index 0000000000..f86fa051c9
--- /dev/null
+++ b/packfile.c
@@ -0,0 +1,1896 @@
+#include "cache.h"
+#include "mru.h"
+#include "pack.h"
+#include "dir.h"
+#include "mergesort.h"
+#include "packfile.h"
+#include "delta.h"
+#include "list.h"
+#include "streaming.h"
+#include "sha1-lookup.h"
+
+char *odb_pack_name(struct strbuf *buf,
+ const unsigned char *sha1,
+ const char *ext)
+{
+ strbuf_reset(buf);
+ strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
+ sha1_to_hex(sha1), ext);
+ return buf->buf;
+}
+
+char *sha1_pack_name(const unsigned char *sha1)
+{
+ static struct strbuf buf = STRBUF_INIT;
+ return odb_pack_name(&buf, sha1, "pack");
+}
+
+char *sha1_pack_index_name(const unsigned char *sha1)
+{
+ static struct strbuf buf = STRBUF_INIT;
+ return odb_pack_name(&buf, sha1, "idx");
+}
+
+static unsigned int pack_used_ctr;
+static unsigned int pack_mmap_calls;
+static unsigned int peak_pack_open_windows;
+static unsigned int pack_open_windows;
+static unsigned int pack_open_fds;
+static unsigned int pack_max_fds;
+static size_t peak_pack_mapped;
+static size_t pack_mapped;
+struct packed_git *packed_git;
+
+static struct mru packed_git_mru_storage;
+struct mru *packed_git_mru = &packed_git_mru_storage;
+
+#define SZ_FMT PRIuMAX
+static inline uintmax_t sz_fmt(size_t s) { return s; }
+
+void pack_report(void)
+{
+ fprintf(stderr,
+ "pack_report: getpagesize() = %10" SZ_FMT "\n"
+ "pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n"
+ "pack_report: core.packedGitLimit = %10" SZ_FMT "\n",
+ sz_fmt(getpagesize()),
+ sz_fmt(packed_git_window_size),
+ sz_fmt(packed_git_limit));
+ fprintf(stderr,
+ "pack_report: pack_used_ctr = %10u\n"
+ "pack_report: pack_mmap_calls = %10u\n"
+ "pack_report: pack_open_windows = %10u / %10u\n"
+ "pack_report: pack_mapped = "
+ "%10" SZ_FMT " / %10" SZ_FMT "\n",
+ pack_used_ctr,
+ pack_mmap_calls,
+ pack_open_windows, peak_pack_open_windows,
+ sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped));
+}
+
+/*
+ * Open and mmap the index file at path, perform a couple of
+ * consistency checks, then record its information to p. Return 0 on
+ * success.
+ */
+static int check_packed_git_idx(const char *path, struct packed_git *p)
+{
+ void *idx_map;
+ struct pack_idx_header *hdr;
+ size_t idx_size;
+ uint32_t version, nr, i, *index;
+ int fd = git_open(path);
+ struct stat st;
+
+ if (fd < 0)
+ return -1;
+ if (fstat(fd, &st)) {
+ close(fd);
+ return -1;
+ }
+ idx_size = xsize_t(st.st_size);
+ if (idx_size < 4 * 256 + 20 + 20) {
+ close(fd);
+ return error("index file %s is too small", path);
+ }
+ idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+
+ hdr = idx_map;
+ if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
+ version = ntohl(hdr->idx_version);
+ if (version < 2 || version > 2) {
+ munmap(idx_map, idx_size);
+ return error("index file %s is version %"PRIu32
+ " and is not supported by this binary"
+ " (try upgrading GIT to a newer version)",
+ path, version);
+ }
+ } else
+ version = 1;
+
+ nr = 0;
+ index = idx_map;
+ if (version > 1)
+ index += 2; /* skip index header */
+ for (i = 0; i < 256; i++) {
+ uint32_t n = ntohl(index[i]);
+ if (n < nr) {
+ munmap(idx_map, idx_size);
+ return error("non-monotonic index %s", path);
+ }
+ nr = n;
+ }
+
+ if (version == 1) {
+ /*
+ * Total size:
+ * - 256 index entries 4 bytes each
+ * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ */
+ if (idx_size != 4*256 + nr * 24 + 20 + 20) {
+ munmap(idx_map, idx_size);
+ return error("wrong index v1 file size in %s", path);
+ }
+ } else if (version == 2) {
+ /*
+ * Minimum size:
+ * - 8 bytes of header
+ * - 256 index entries 4 bytes each
+ * - 20-byte sha1 entry * nr
+ * - 4-byte crc entry * nr
+ * - 4-byte offset entry * nr
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ * And after the 4-byte offset table might be a
+ * variable sized table containing 8-byte entries
+ * for offsets larger than 2^31.
+ */
+ unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
+ unsigned long max_size = min_size;
+ if (nr)
+ max_size += (nr - 1)*8;
+ if (idx_size < min_size || idx_size > max_size) {
+ munmap(idx_map, idx_size);
+ return error("wrong index v2 file size in %s", path);
+ }
+ if (idx_size != min_size &&
+ /*
+ * make sure we can deal with large pack offsets.
+ * 31-bit signed offset won't be enough, neither
+ * 32-bit unsigned one will be.
+ */
+ (sizeof(off_t) <= 4)) {
+ munmap(idx_map, idx_size);
+ return error("pack too large for current definition of off_t in %s", path);
+ }
+ }
+
+ p->index_version = version;
+ p->index_data = idx_map;
+ p->index_size = idx_size;
+ p->num_objects = nr;
+ return 0;
+}
+
+int open_pack_index(struct packed_git *p)
+{
+ char *idx_name;
+ size_t len;
+ int ret;
+
+ if (p->index_data)
+ return 0;
+
+ if (!strip_suffix(p->pack_name, ".pack", &len))
+ die("BUG: pack_name does not end in .pack");
+ idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name);
+ ret = check_packed_git_idx(idx_name, p);
+ free(idx_name);
+ return ret;
+}
+
+static struct packed_git *alloc_packed_git(int extra)
+{
+ struct packed_git *p = xmalloc(st_add(sizeof(*p), extra));
+ memset(p, 0, sizeof(*p));
+ p->pack_fd = -1;
+ return p;
+}
+
+struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
+{
+ const char *path = sha1_pack_name(sha1);
+ size_t alloc = st_add(strlen(path), 1);
+ struct packed_git *p = alloc_packed_git(alloc);
+
+ memcpy(p->pack_name, path, alloc); /* includes NUL */
+ hashcpy(p->sha1, sha1);
+ if (check_packed_git_idx(idx_path, p)) {
+ free(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+static void scan_windows(struct packed_git *p,
+ struct packed_git **lru_p,
+ struct pack_window **lru_w,
+ struct pack_window **lru_l)
+{
+ struct pack_window *w, *w_l;
+
+ for (w_l = NULL, w = p->windows; w; w = w->next) {
+ if (!w->inuse_cnt) {
+ if (!*lru_w || w->last_used < (*lru_w)->last_used) {
+ *lru_p = p;
+ *lru_w = w;
+ *lru_l = w_l;
+ }
+ }
+ w_l = w;
+ }
+}
+
+static int unuse_one_window(struct packed_git *current)
+{
+ struct packed_git *p, *lru_p = NULL;
+ struct pack_window *lru_w = NULL, *lru_l = NULL;
+
+ if (current)
+ scan_windows(current, &lru_p, &lru_w, &lru_l);
+ for (p = packed_git; p; p = p->next)
+ scan_windows(p, &lru_p, &lru_w, &lru_l);
+ if (lru_p) {
+ munmap(lru_w->base, lru_w->len);
+ pack_mapped -= lru_w->len;
+ if (lru_l)
+ lru_l->next = lru_w->next;
+ else
+ lru_p->windows = lru_w->next;
+ free(lru_w);
+ pack_open_windows--;
+ return 1;
+ }
+ return 0;
+}
+
+void release_pack_memory(size_t need)
+{
+ size_t cur = pack_mapped;
+ while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
+ ; /* nothing */
+}
+
+void close_pack_windows(struct packed_git *p)
+{
+ while (p->windows) {
+ struct pack_window *w = p->windows;
+
+ if (w->inuse_cnt)
+ die("pack '%s' still has open windows to it",
+ p->pack_name);
+ munmap(w->base, w->len);
+ pack_mapped -= w->len;
+ pack_open_windows--;
+ p->windows = w->next;
+ free(w);
+ }
+}
+
+static int close_pack_fd(struct packed_git *p)
+{
+ if (p->pack_fd < 0)
+ return 0;
+
+ close(p->pack_fd);
+ pack_open_fds--;
+ p->pack_fd = -1;
+
+ return 1;
+}
+
+void close_pack_index(struct packed_git *p)
+{
+ if (p->index_data) {
+ munmap((void *)p->index_data, p->index_size);
+ p->index_data = NULL;
+ }
+}
+
+static void close_pack(struct packed_git *p)
+{
+ close_pack_windows(p);
+ close_pack_fd(p);
+ close_pack_index(p);
+}
+
+void close_all_packs(void)
+{
+ struct packed_git *p;
+
+ for (p = packed_git; p; p = p->next)
+ if (p->do_not_close)
+ die("BUG: want to close pack marked 'do-not-close'");
+ else
+ close_pack(p);
+}
+
+/*
+ * The LRU pack is the one with the oldest MRU window, preferring packs
+ * with no used windows, or the oldest mtime if it has no windows allocated.
+ */
+static void find_lru_pack(struct packed_git *p, struct packed_git **lru_p, struct pack_window **mru_w, int *accept_windows_inuse)
+{
+ struct pack_window *w, *this_mru_w;
+ int has_windows_inuse = 0;
+
+ /*
+ * Reject this pack if it has windows and the previously selected
+ * one does not. If this pack does not have windows, reject
+ * it if the pack file is newer than the previously selected one.
+ */
+ if (*lru_p && !*mru_w && (p->windows || p->mtime > (*lru_p)->mtime))
+ return;
+
+ for (w = this_mru_w = p->windows; w; w = w->next) {
+ /*
+ * Reject this pack if any of its windows are in use,
+ * but the previously selected pack did not have any
+ * inuse windows. Otherwise, record that this pack
+ * has windows in use.
+ */
+ if (w->inuse_cnt) {
+ if (*accept_windows_inuse)
+ has_windows_inuse = 1;
+ else
+ return;
+ }
+
+ if (w->last_used > this_mru_w->last_used)
+ this_mru_w = w;
+
+ /*
+ * Reject this pack if it has windows that have been
+ * used more recently than the previously selected pack.
+ * If the previously selected pack had windows inuse and
+ * we have not encountered a window in this pack that is
+ * inuse, skip this check since we prefer a pack with no
+ * inuse windows to one that has inuse windows.
+ */
+ if (*mru_w && *accept_windows_inuse == has_windows_inuse &&
+ this_mru_w->last_used > (*mru_w)->last_used)
+ return;
+ }
+
+ /*
+ * Select this pack.
+ */
+ *mru_w = this_mru_w;
+ *lru_p = p;
+ *accept_windows_inuse = has_windows_inuse;
+}
+
+static int close_one_pack(void)
+{
+ struct packed_git *p, *lru_p = NULL;
+ struct pack_window *mru_w = NULL;
+ int accept_windows_inuse = 1;
+
+ for (p = packed_git; p; p = p->next) {
+ if (p->pack_fd == -1)
+ continue;
+ find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);
+ }
+
+ if (lru_p)
+ return close_pack_fd(lru_p);
+
+ return 0;
+}
+
+static unsigned int get_max_fd_limit(void)
+{
+#ifdef RLIMIT_NOFILE
+ {
+ struct rlimit lim;
+
+ if (!getrlimit(RLIMIT_NOFILE, &lim))
+ return lim.rlim_cur;
+ }
+#endif
+
+#ifdef _SC_OPEN_MAX
+ {
+ long open_max = sysconf(_SC_OPEN_MAX);
+ if (0 < open_max)
+ return open_max;
+ /*
+ * Otherwise, we got -1 for one of the two
+ * reasons:
+ *
+ * (1) sysconf() did not understand _SC_OPEN_MAX
+ * and signaled an error with -1; or
+ * (2) sysconf() said there is no limit.
+ *
+ * We _could_ clear errno before calling sysconf() to
+ * tell these two cases apart and return a huge number
+ * in the latter case to let the caller cap it to a
+ * value that is not so selfish, but letting the
+ * fallback OPEN_MAX codepath take care of these cases
+ * is a lot simpler.
+ */
+ }
+#endif
+
+#ifdef OPEN_MAX
+ return OPEN_MAX;
+#else
+ return 1; /* see the caller ;-) */
+#endif
+}
+
+/*
+ * Do not call this directly as this leaks p->pack_fd on error return;
+ * call open_packed_git() instead.
+ */
+static int open_packed_git_1(struct packed_git *p)
+{
+ struct stat st;
+ struct pack_header hdr;
+ unsigned char sha1[20];
+ unsigned char *idx_sha1;
+ long fd_flag;
+
+ if (!p->index_data && open_pack_index(p))
+ return error("packfile %s index unavailable", p->pack_name);
+
+ if (!pack_max_fds) {
+ unsigned int max_fds = get_max_fd_limit();
+
+ /* Save 3 for stdin/stdout/stderr, 22 for work */
+ if (25 < max_fds)
+ pack_max_fds = max_fds - 25;
+ else
+ pack_max_fds = 1;
+ }
+
+ while (pack_max_fds <= pack_open_fds && close_one_pack())
+ ; /* nothing */
+
+ p->pack_fd = git_open(p->pack_name);
+ if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
+ return -1;
+ pack_open_fds++;
+
+ /* If we created the struct before we had the pack we lack size. */
+ if (!p->pack_size) {
+ if (!S_ISREG(st.st_mode))
+ return error("packfile %s not a regular file", p->pack_name);
+ p->pack_size = st.st_size;
+ } else if (p->pack_size != st.st_size)
+ return error("packfile %s size changed", p->pack_name);
+
+ /* We leave these file descriptors open with sliding mmap;
+ * there is no point keeping them open across exec(), though.
+ */
+ fd_flag = fcntl(p->pack_fd, F_GETFD, 0);
+ if (fd_flag < 0)
+ return error("cannot determine file descriptor flags");
+ fd_flag |= FD_CLOEXEC;
+ if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
+ return error("cannot set FD_CLOEXEC");
+
+ /* Verify we recognize this pack file format. */
+ if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+ return error("file %s is far too short to be a packfile", p->pack_name);
+ if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
+ return error("file %s is not a GIT packfile", p->pack_name);
+ if (!pack_version_ok(hdr.hdr_version))
+ return error("packfile %s is version %"PRIu32" and not"
+ " supported (try upgrading GIT to a newer version)",
+ p->pack_name, ntohl(hdr.hdr_version));
+
+ /* Verify the pack matches its index. */
+ if (p->num_objects != ntohl(hdr.hdr_entries))
+ return error("packfile %s claims to have %"PRIu32" objects"
+ " while index indicates %"PRIu32" objects",
+ p->pack_name, ntohl(hdr.hdr_entries),
+ p->num_objects);
+ if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
+ return error("end of packfile %s is unavailable", p->pack_name);
+ if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
+ return error("packfile %s signature is unavailable", p->pack_name);
+ idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
+ if (hashcmp(sha1, idx_sha1))
+ return error("packfile %s does not match index", p->pack_name);
+ return 0;
+}
+
+static int open_packed_git(struct packed_git *p)
+{
+ if (!open_packed_git_1(p))
+ return 0;
+ close_pack_fd(p);
+ return -1;
+}
+
+static int in_window(struct pack_window *win, off_t offset)
+{
+ /* We must promise at least 20 bytes (one hash) after the
+ * offset is available from this window, otherwise the offset
+ * is not actually in this window and a different window (which
+ * has that one hash excess) must be used. This is to support
+ * the object header and delta base parsing routines below.
+ */
+ off_t win_off = win->offset;
+ return win_off <= offset
+ && (offset + 20) <= (win_off + win->len);
+}
+
+unsigned char *use_pack(struct packed_git *p,
+ struct pack_window **w_cursor,
+ off_t offset,
+ unsigned long *left)
+{
+ struct pack_window *win = *w_cursor;
+
+ /* Since packfiles end in a hash of their content and it's
+ * pointless to ask for an offset into the middle of that
+ * hash, and the in_window function above wouldn't match
+ * don't allow an offset too close to the end of the file.
+ */
+ if (!p->pack_size && p->pack_fd == -1 && open_packed_git(p))
+ die("packfile %s cannot be accessed", p->pack_name);
+ if (offset > (p->pack_size - 20))
+ die("offset beyond end of packfile (truncated pack?)");
+ if (offset < 0)
+ die(_("offset before end of packfile (broken .idx?)"));
+
+ if (!win || !in_window(win, offset)) {
+ if (win)
+ win->inuse_cnt--;
+ for (win = p->windows; win; win = win->next) {
+ if (in_window(win, offset))
+ break;
+ }
+ if (!win) {
+ size_t window_align = packed_git_window_size / 2;
+ off_t len;
+
+ if (p->pack_fd == -1 && open_packed_git(p))
+ die("packfile %s cannot be accessed", p->pack_name);
+
+ win = xcalloc(1, sizeof(*win));
+ win->offset = (offset / window_align) * window_align;
+ len = p->pack_size - win->offset;
+ if (len > packed_git_window_size)
+ len = packed_git_window_size;
+ win->len = (size_t)len;
+ pack_mapped += win->len;
+ while (packed_git_limit < pack_mapped
+ && unuse_one_window(p))
+ ; /* nothing */
+ win->base = xmmap(NULL, win->len,
+ PROT_READ, MAP_PRIVATE,
+ p->pack_fd, win->offset);
+ if (win->base == MAP_FAILED)
+ die_errno("packfile %s cannot be mapped",
+ p->pack_name);
+ if (!win->offset && win->len == p->pack_size
+ && !p->do_not_close)
+ close_pack_fd(p);
+ pack_mmap_calls++;
+ pack_open_windows++;
+ if (pack_mapped > peak_pack_mapped)
+ peak_pack_mapped = pack_mapped;
+ if (pack_open_windows > peak_pack_open_windows)
+ peak_pack_open_windows = pack_open_windows;
+ win->next = p->windows;
+ p->windows = win;
+ }
+ }
+ if (win != *w_cursor) {
+ win->last_used = pack_used_ctr++;
+ win->inuse_cnt++;
+ *w_cursor = win;
+ }
+ offset -= win->offset;
+ if (left)
+ *left = win->len - xsize_t(offset);
+ return win->base + offset;
+}
+
+void unuse_pack(struct pack_window **w_cursor)
+{
+ struct pack_window *w = *w_cursor;
+ if (w) {
+ w->inuse_cnt--;
+ *w_cursor = NULL;
+ }
+}
+
+static void try_to_free_pack_memory(size_t size)
+{
+ release_pack_memory(size);
+}
+
+struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
+{
+ static int have_set_try_to_free_routine;
+ struct stat st;
+ size_t alloc;
+ struct packed_git *p;
+
+ if (!have_set_try_to_free_routine) {
+ have_set_try_to_free_routine = 1;
+ set_try_to_free_routine(try_to_free_pack_memory);
+ }
+
+ /*
+ * Make sure a corresponding .pack file exists and that
+ * the index looks sane.
+ */
+ if (!strip_suffix_mem(path, &path_len, ".idx"))
+ return NULL;
+
+ /*
+ * ".pack" is long enough to hold any suffix we're adding (and
+ * the use xsnprintf double-checks that)
+ */
+ alloc = st_add3(path_len, strlen(".pack"), 1);
+ p = alloc_packed_git(alloc);
+ memcpy(p->pack_name, path, path_len);
+
+ xsnprintf(p->pack_name + path_len, alloc - path_len, ".keep");
+ if (!access(p->pack_name, F_OK))
+ p->pack_keep = 1;
+
+ xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
+ if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
+ free(p);
+ return NULL;
+ }
+
+ /* ok, it looks sane as far as we can check without
+ * actually mapping the pack file.
+ */
+ p->pack_size = st.st_size;
+ p->pack_local = local;
+ p->mtime = st.st_mtime;
+ if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
+ hashclr(p->sha1);
+ return p;
+}
+
+void install_packed_git(struct packed_git *pack)
+{
+ if (pack->pack_fd != -1)
+ pack_open_fds++;
+
+ pack->next = packed_git;
+ packed_git = pack;
+}
+
+void (*report_garbage)(unsigned seen_bits, const char *path);
+
+static void report_helper(const struct string_list *list,
+ int seen_bits, int first, int last)
+{
+ if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX))
+ return;
+
+ for (; first < last; first++)
+ report_garbage(seen_bits, list->items[first].string);
+}
+
+static void report_pack_garbage(struct string_list *list)
+{
+ int i, baselen = -1, first = 0, seen_bits = 0;
+
+ if (!report_garbage)
+ return;
+
+ string_list_sort(list);
+
+ for (i = 0; i < list->nr; i++) {
+ const char *path = list->items[i].string;
+ if (baselen != -1 &&
+ strncmp(path, list->items[first].string, baselen)) {
+ report_helper(list, seen_bits, first, i);
+ baselen = -1;
+ seen_bits = 0;
+ }
+ if (baselen == -1) {
+ const char *dot = strrchr(path, '.');
+ if (!dot) {
+ report_garbage(PACKDIR_FILE_GARBAGE, path);
+ continue;
+ }
+ baselen = dot - path + 1;
+ first = i;
+ }
+ if (!strcmp(path + baselen, "pack"))
+ seen_bits |= 1;
+ else if (!strcmp(path + baselen, "idx"))
+ seen_bits |= 2;
+ }
+ report_helper(list, seen_bits, first, list->nr);
+}
+
+static void prepare_packed_git_one(char *objdir, int local)
+{
+ struct strbuf path = STRBUF_INIT;
+ size_t dirnamelen;
+ DIR *dir;
+ struct dirent *de;
+ struct string_list garbage = STRING_LIST_INIT_DUP;
+
+ strbuf_addstr(&path, objdir);
+ strbuf_addstr(&path, "/pack");
+ dir = opendir(path.buf);
+ if (!dir) {
+ if (errno != ENOENT)
+ error_errno("unable to open object pack directory: %s",
+ path.buf);
+ strbuf_release(&path);
+ return;
+ }
+ strbuf_addch(&path, '/');
+ dirnamelen = path.len;
+ while ((de = readdir(dir)) != NULL) {
+ struct packed_git *p;
+ size_t base_len;
+
+ if (is_dot_or_dotdot(de->d_name))
+ continue;
+
+ strbuf_setlen(&path, dirnamelen);
+ strbuf_addstr(&path, de->d_name);
+
+ base_len = path.len;
+ if (strip_suffix_mem(path.buf, &base_len, ".idx")) {
+ /* Don't reopen a pack we already have. */
+ for (p = packed_git; p; p = p->next) {
+ size_t len;
+ if (strip_suffix(p->pack_name, ".pack", &len) &&
+ len == base_len &&
+ !memcmp(p->pack_name, path.buf, len))
+ break;
+ }
+ if (p == NULL &&
+ /*
+ * See if it really is a valid .idx file with
+ * corresponding .pack file that we can map.
+ */
+ (p = add_packed_git(path.buf, path.len, local)) != NULL)
+ install_packed_git(p);
+ }
+
+ if (!report_garbage)
+ continue;
+
+ if (ends_with(de->d_name, ".idx") ||
+ ends_with(de->d_name, ".pack") ||
+ ends_with(de->d_name, ".bitmap") ||
+ ends_with(de->d_name, ".keep"))
+ string_list_append(&garbage, path.buf);
+ else
+ report_garbage(PACKDIR_FILE_GARBAGE, path.buf);
+ }
+ closedir(dir);
+ report_pack_garbage(&garbage);
+ string_list_clear(&garbage, 0);
+ strbuf_release(&path);
+}
+
+static int approximate_object_count_valid;
+
+/*
+ * Give a fast, rough count of the number of objects in the repository. This
+ * ignores loose objects completely. If you have a lot of them, then either
+ * you should repack because your performance will be awful, or they are
+ * all unreachable objects about to be pruned, in which case they're not really
+ * interesting as a measure of repo size in the first place.
+ */
+unsigned long approximate_object_count(void)
+{
+ static unsigned long count;
+ if (!approximate_object_count_valid) {
+ struct packed_git *p;
+
+ prepare_packed_git();
+ count = 0;
+ for (p = packed_git; p; p = p->next) {
+ if (open_pack_index(p))
+ continue;
+ count += p->num_objects;
+ }
+ }
+ return count;
+}
+
+static void *get_next_packed_git(const void *p)
+{
+ return ((const struct packed_git *)p)->next;
+}
+
+static void set_next_packed_git(void *p, void *next)
+{
+ ((struct packed_git *)p)->next = next;
+}
+
+static int sort_pack(const void *a_, const void *b_)
+{
+ const struct packed_git *a = a_;
+ const struct packed_git *b = b_;
+ int st;
+
+ /*
+ * Local packs tend to contain objects specific to our
+ * variant of the project than remote ones. In addition,
+ * remote ones could be on a network mounted filesystem.
+ * Favor local ones for these reasons.
+ */
+ st = a->pack_local - b->pack_local;
+ if (st)
+ return -st;
+
+ /*
+ * Younger packs tend to contain more recent objects,
+ * and more recent objects tend to get accessed more
+ * often.
+ */
+ if (a->mtime < b->mtime)
+ return 1;
+ else if (a->mtime == b->mtime)
+ return 0;
+ return -1;
+}
+
+static void rearrange_packed_git(void)
+{
+ packed_git = llist_mergesort(packed_git, get_next_packed_git,
+ set_next_packed_git, sort_pack);
+}
+
+static void prepare_packed_git_mru(void)
+{
+ struct packed_git *p;
+
+ mru_clear(packed_git_mru);
+ for (p = packed_git; p; p = p->next)
+ mru_append(packed_git_mru, p);
+}
+
+static int prepare_packed_git_run_once = 0;
+void prepare_packed_git(void)
+{
+ struct alternate_object_database *alt;
+
+ if (prepare_packed_git_run_once)
+ return;
+ prepare_packed_git_one(get_object_directory(), 1);
+ prepare_alt_odb();
+ for (alt = alt_odb_list; alt; alt = alt->next)
+ prepare_packed_git_one(alt->path, 0);
+ rearrange_packed_git();
+ prepare_packed_git_mru();
+ prepare_packed_git_run_once = 1;
+}
+
+void reprepare_packed_git(void)
+{
+ approximate_object_count_valid = 0;
+ prepare_packed_git_run_once = 0;
+ prepare_packed_git();
+}
+
+unsigned long unpack_object_header_buffer(const unsigned char *buf,
+ unsigned long len, enum object_type *type, unsigned long *sizep)
+{
+ unsigned shift;
+ unsigned long size, c;
+ unsigned long used = 0;
+
+ c = buf[used++];
+ *type = (c >> 4) & 7;
+ size = c & 15;
+ shift = 4;
+ while (c & 0x80) {
+ if (len <= used || bitsizeof(long) <= shift) {
+ error("bad object header");
+ size = used = 0;
+ break;
+ }
+ c = buf[used++];
+ size += (c & 0x7f) << shift;
+ shift += 7;
+ }
+ *sizep = size;
+ return used;
+}
+
+unsigned long get_size_from_delta(struct packed_git *p,
+ struct pack_window **w_curs,
+ off_t curpos)
+{
+ const unsigned char *data;
+ unsigned char delta_head[20], *in;
+ git_zstream stream;
+ int st;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.next_out = delta_head;
+ stream.avail_out = sizeof(delta_head);
+
+ git_inflate_init(&stream);
+ do {
+ in = use_pack(p, w_curs, curpos, &stream.avail_in);
+ stream.next_in = in;
+ st = git_inflate(&stream, Z_FINISH);
+ curpos += stream.next_in - in;
+ } while ((st == Z_OK || st == Z_BUF_ERROR) &&
+ stream.total_out < sizeof(delta_head));
+ git_inflate_end(&stream);
+ if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) {
+ error("delta data unpack-initial failed");
+ return 0;
+ }
+
+ /* Examine the initial part of the delta to figure out
+ * the result size.
+ */
+ data = delta_head;
+
+ /* ignore base size */
+ get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+
+ /* Read the result size */
+ return get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+}
+
+int unpack_object_header(struct packed_git *p,
+ struct pack_window **w_curs,
+ off_t *curpos,
+ unsigned long *sizep)
+{
+ unsigned char *base;
+ unsigned long left;
+ unsigned long used;
+ enum object_type type;
+
+ /* use_pack() assures us we have [base, base + 20) available
+ * as a range that we can look at. (Its actually the hash
+ * size that is assured.) With our object header encoding
+ * the maximum deflated object size is 2^137, which is just
+ * insane, so we know won't exceed what we have been given.
+ */
+ base = use_pack(p, w_curs, *curpos, &left);
+ used = unpack_object_header_buffer(base, left, &type, sizep);
+ if (!used) {
+ type = OBJ_BAD;
+ } else
+ *curpos += used;
+
+ return type;
+}
+
+void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1)
+{
+ unsigned i;
+ for (i = 0; i < p->num_bad_objects; i++)
+ if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
+ return;
+ p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
+ st_mult(GIT_MAX_RAWSZ,
+ st_add(p->num_bad_objects, 1)));
+ hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
+ p->num_bad_objects++;
+}
+
+const struct packed_git *has_packed_and_bad(const unsigned char *sha1)
+{
+ struct packed_git *p;
+ unsigned i;
+
+ for (p = packed_git; p; p = p->next)
+ for (i = 0; i < p->num_bad_objects; i++)
+ if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+ return p;
+ return NULL;
+}
+
+static off_t get_delta_base(struct packed_git *p,
+ struct pack_window **w_curs,
+ off_t *curpos,
+ enum object_type type,
+ off_t delta_obj_offset)
+{
+ unsigned char *base_info = use_pack(p, w_curs, *curpos, NULL);
+ off_t base_offset;
+
+ /* use_pack() assured us we have [base_info, base_info + 20)
+ * as a range that we can look at without walking off the
+ * end of the mapped window. Its actually the hash size
+ * that is assured. An OFS_DELTA longer than the hash size
+ * is stupid, as then a REF_DELTA would be smaller to store.
+ */
+ if (type == OBJ_OFS_DELTA) {
+ unsigned used = 0;
+ unsigned char c = base_info[used++];
+ base_offset = c & 127;
+ while (c & 128) {
+ base_offset += 1;
+ if (!base_offset || MSB(base_offset, 7))
+ return 0; /* overflow */
+ c = base_info[used++];
+ base_offset = (base_offset << 7) + (c & 127);
+ }
+ base_offset = delta_obj_offset - base_offset;
+ if (base_offset <= 0 || base_offset >= delta_obj_offset)
+ return 0; /* out of bound */
+ *curpos += used;
+ } else if (type == OBJ_REF_DELTA) {
+ /* The base entry _must_ be in the same pack */
+ base_offset = find_pack_entry_one(base_info, p);
+ *curpos += 20;
+ } else
+ die("I am totally screwed");
+ return base_offset;
+}
+
+/*
+ * Like get_delta_base above, but we return the sha1 instead of the pack
+ * offset. This means it is cheaper for REF deltas (we do not have to do
+ * the final object lookup), but more expensive for OFS deltas (we
+ * have to load the revidx to convert the offset back into a sha1).
+ */
+static const unsigned char *get_delta_base_sha1(struct packed_git *p,
+ struct pack_window **w_curs,
+ off_t curpos,
+ enum object_type type,
+ off_t delta_obj_offset)
+{
+ if (type == OBJ_REF_DELTA) {
+ unsigned char *base = use_pack(p, w_curs, curpos, NULL);
+ return base;
+ } else if (type == OBJ_OFS_DELTA) {
+ struct revindex_entry *revidx;
+ off_t base_offset = get_delta_base(p, w_curs, &curpos,
+ type, delta_obj_offset);
+
+ if (!base_offset)
+ return NULL;
+
+ revidx = find_pack_revindex(p, base_offset);
+ if (!revidx)
+ return NULL;
+
+ return nth_packed_object_sha1(p, revidx->nr);
+ } else
+ return NULL;
+}
+
+static int retry_bad_packed_offset(struct packed_git *p, off_t obj_offset)
+{
+ int type;
+ struct revindex_entry *revidx;
+ const unsigned char *sha1;
+ revidx = find_pack_revindex(p, obj_offset);
+ if (!revidx)
+ return OBJ_BAD;
+ sha1 = nth_packed_object_sha1(p, revidx->nr);
+ mark_bad_packed_object(p, sha1);
+ type = sha1_object_info(sha1, NULL);
+ if (type <= OBJ_NONE)
+ return OBJ_BAD;
+ return type;
+}
+
+#define POI_STACK_PREALLOC 64
+
+static enum object_type packed_to_object_type(struct packed_git *p,
+ off_t obj_offset,
+ enum object_type type,
+ struct pack_window **w_curs,
+ off_t curpos)
+{
+ off_t small_poi_stack[POI_STACK_PREALLOC];
+ off_t *poi_stack = small_poi_stack;
+ int poi_stack_nr = 0, poi_stack_alloc = POI_STACK_PREALLOC;
+
+ while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+ off_t base_offset;
+ unsigned long size;
+ /* Push the object we're going to leave behind */
+ if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
+ poi_stack_alloc = alloc_nr(poi_stack_nr);
+ ALLOC_ARRAY(poi_stack, poi_stack_alloc);
+ memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
+ } else {
+ ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
+ }
+ poi_stack[poi_stack_nr++] = obj_offset;
+ /* If parsing the base offset fails, just unwind */
+ base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
+ if (!base_offset)
+ goto unwind;
+ curpos = obj_offset = base_offset;
+ type = unpack_object_header(p, w_curs, &curpos, &size);
+ if (type <= OBJ_NONE) {
+ /* If getting the base itself fails, we first
+ * retry the base, otherwise unwind */
+ type = retry_bad_packed_offset(p, base_offset);
+ if (type > OBJ_NONE)
+ goto out;
+ goto unwind;
+ }
+ }
+
+ switch (type) {
+ case OBJ_BAD:
+ case OBJ_COMMIT:
+ case OBJ_TREE:
+ case OBJ_BLOB:
+ case OBJ_TAG:
+ break;
+ default:
+ error("unknown object type %i at offset %"PRIuMAX" in %s",
+ type, (uintmax_t)obj_offset, p->pack_name);
+ type = OBJ_BAD;
+ }
+
+out:
+ if (poi_stack != small_poi_stack)
+ free(poi_stack);
+ return type;
+
+unwind:
+ while (poi_stack_nr) {
+ obj_offset = poi_stack[--poi_stack_nr];
+ type = retry_bad_packed_offset(p, obj_offset);
+ if (type > OBJ_NONE)
+ goto out;
+ }
+ type = OBJ_BAD;
+ goto out;
+}
+
+static struct hashmap delta_base_cache;
+static size_t delta_base_cached;
+
+static LIST_HEAD(delta_base_cache_lru);
+
+struct delta_base_cache_key {
+ struct packed_git *p;
+ off_t base_offset;
+};
+
+struct delta_base_cache_entry {
+ struct hashmap hash;
+ struct delta_base_cache_key key;
+ struct list_head lru;
+ void *data;
+ unsigned long size;
+ enum object_type type;
+};
+
+static unsigned int pack_entry_hash(struct packed_git *p, off_t base_offset)
+{
+ unsigned int hash;
+
+ hash = (unsigned int)(intptr_t)p + (unsigned int)base_offset;
+ hash += (hash >> 8) + (hash >> 16);
+ return hash;
+}
+
+static struct delta_base_cache_entry *
+get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)
+{
+ struct hashmap_entry entry;
+ struct delta_base_cache_key key;
+
+ if (!delta_base_cache.cmpfn)
+ return NULL;
+
+ hashmap_entry_init(&entry, pack_entry_hash(p, base_offset));
+ key.p = p;
+ key.base_offset = base_offset;
+ return hashmap_get(&delta_base_cache, &entry, &key);
+}
+
+static int delta_base_cache_key_eq(const struct delta_base_cache_key *a,
+ const struct delta_base_cache_key *b)
+{
+ return a->p == b->p && a->base_offset == b->base_offset;
+}
+
+static int delta_base_cache_hash_cmp(const void *unused_cmp_data,
+ const void *va, const void *vb,
+ const void *vkey)
+{
+ const struct delta_base_cache_entry *a = va, *b = vb;
+ const struct delta_base_cache_key *key = vkey;
+ if (key)
+ return !delta_base_cache_key_eq(&a->key, key);
+ else
+ return !delta_base_cache_key_eq(&a->key, &b->key);
+}
+
+static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
+{
+ return !!get_delta_base_cache_entry(p, base_offset);
+}
+
+/*
+ * Remove the entry from the cache, but do _not_ free the associated
+ * entry data. The caller takes ownership of the "data" buffer, and
+ * should copy out any fields it wants before detaching.
+ */
+static void detach_delta_base_cache_entry(struct delta_base_cache_entry *ent)
+{
+ hashmap_remove(&delta_base_cache, ent, &ent->key);
+ list_del(&ent->lru);
+ delta_base_cached -= ent->size;
+ free(ent);
+}
+
+static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
+ unsigned long *base_size, enum object_type *type)
+{
+ struct delta_base_cache_entry *ent;
+
+ ent = get_delta_base_cache_entry(p, base_offset);
+ if (!ent)
+ return unpack_entry(p, base_offset, type, base_size);
+
+ if (type)
+ *type = ent->type;
+ if (base_size)
+ *base_size = ent->size;
+ return xmemdupz(ent->data, ent->size);
+}
+
+static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
+{
+ free(ent->data);
+ detach_delta_base_cache_entry(ent);
+}
+
+void clear_delta_base_cache(void)
+{
+ struct list_head *lru, *tmp;
+ list_for_each_safe(lru, tmp, &delta_base_cache_lru) {
+ struct delta_base_cache_entry *entry =
+ list_entry(lru, struct delta_base_cache_entry, lru);
+ release_delta_base_cache(entry);
+ }
+}
+
+static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
+ void *base, unsigned long base_size, enum object_type type)
+{
+ struct delta_base_cache_entry *ent = xmalloc(sizeof(*ent));
+ struct list_head *lru, *tmp;
+
+ delta_base_cached += base_size;
+
+ list_for_each_safe(lru, tmp, &delta_base_cache_lru) {
+ struct delta_base_cache_entry *f =
+ list_entry(lru, struct delta_base_cache_entry, lru);
+ if (delta_base_cached <= delta_base_cache_limit)
+ break;
+ release_delta_base_cache(f);
+ }
+
+ ent->key.p = p;
+ ent->key.base_offset = base_offset;
+ ent->type = type;
+ ent->data = base;
+ ent->size = base_size;
+ list_add_tail(&ent->lru, &delta_base_cache_lru);
+
+ if (!delta_base_cache.cmpfn)
+ hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0);
+ hashmap_entry_init(ent, pack_entry_hash(p, base_offset));
+ hashmap_add(&delta_base_cache, ent);
+}
+
+int packed_object_info(struct packed_git *p, off_t obj_offset,
+ struct object_info *oi)
+{
+ struct pack_window *w_curs = NULL;
+ unsigned long size;
+ off_t curpos = obj_offset;
+ enum object_type type;
+
+ /*
+ * We always get the representation type, but only convert it to
+ * a "real" type later if the caller is interested.
+ */
+ if (oi->contentp) {
+ *oi->contentp = cache_or_unpack_entry(p, obj_offset, oi->sizep,
+ &type);
+ if (!*oi->contentp)
+ type = OBJ_BAD;
+ } else {
+ type = unpack_object_header(p, &w_curs, &curpos, &size);
+ }
+
+ if (!oi->contentp && oi->sizep) {
+ if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+ off_t tmp_pos = curpos;
+ off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
+ type, obj_offset);
+ if (!base_offset) {
+ type = OBJ_BAD;
+ goto out;
+ }
+ *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
+ if (*oi->sizep == 0) {
+ type = OBJ_BAD;
+ goto out;
+ }
+ } else {
+ *oi->sizep = size;
+ }
+ }
+
+ if (oi->disk_sizep) {
+ struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+ *oi->disk_sizep = revidx[1].offset - obj_offset;
+ }
+
+ if (oi->typep || oi->typename) {
+ enum object_type ptot;
+ ptot = packed_to_object_type(p, obj_offset, type, &w_curs,
+ curpos);
+ if (oi->typep)
+ *oi->typep = ptot;
+ if (oi->typename) {
+ const char *tn = typename(ptot);
+ if (tn)
+ strbuf_addstr(oi->typename, tn);
+ }
+ if (ptot < 0) {
+ type = OBJ_BAD;
+ goto out;
+ }
+ }
+
+ if (oi->delta_base_sha1) {
+ if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+ const unsigned char *base;
+
+ base = get_delta_base_sha1(p, &w_curs, curpos,
+ type, obj_offset);
+ if (!base) {
+ type = OBJ_BAD;
+ goto out;
+ }
+
+ hashcpy(oi->delta_base_sha1, base);
+ } else
+ hashclr(oi->delta_base_sha1);
+ }
+
+ oi->whence = in_delta_base_cache(p, obj_offset) ? OI_DBCACHED :
+ OI_PACKED;
+
+out:
+ unuse_pack(&w_curs);
+ return type;
+}
+
+static void *unpack_compressed_entry(struct packed_git *p,
+ struct pack_window **w_curs,
+ off_t curpos,
+ unsigned long size)
+{
+ int st;
+ git_zstream stream;
+ unsigned char *buffer, *in;
+
+ buffer = xmallocz_gently(size);
+ if (!buffer)
+ return NULL;
+ memset(&stream, 0, sizeof(stream));
+ stream.next_out = buffer;
+ stream.avail_out = size + 1;
+
+ git_inflate_init(&stream);
+ do {
+ in = use_pack(p, w_curs, curpos, &stream.avail_in);
+ stream.next_in = in;
+ st = git_inflate(&stream, Z_FINISH);
+ if (!stream.avail_out)
+ break; /* the payload is larger than it should be */
+ curpos += stream.next_in - in;
+ } while (st == Z_OK || st == Z_BUF_ERROR);
+ git_inflate_end(&stream);
+ if ((st != Z_STREAM_END) || stream.total_out != size) {
+ free(buffer);
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
+{
+ static struct trace_key pack_access = TRACE_KEY_INIT(PACK_ACCESS);
+ trace_printf_key(&pack_access, "%s %"PRIuMAX"\n",
+ p->pack_name, (uintmax_t)obj_offset);
+}
+
+int do_check_packed_object_crc;
+
+#define UNPACK_ENTRY_STACK_PREALLOC 64
+struct unpack_entry_stack_ent {
+ off_t obj_offset;
+ off_t curpos;
+ unsigned long size;
+};
+
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+ unsigned long *size)
+{
+ struct object_info oi = OBJECT_INFO_INIT;
+ void *content;
+ oi.typep = type;
+ oi.sizep = size;
+ oi.contentp = &content;
+
+ if (sha1_object_info_extended(sha1, &oi, 0) < 0)
+ return NULL;
+ return content;
+}
+
+void *unpack_entry(struct packed_git *p, off_t obj_offset,
+ enum object_type *final_type, unsigned long *final_size)
+{
+ struct pack_window *w_curs = NULL;
+ off_t curpos = obj_offset;
+ void *data = NULL;
+ unsigned long size;
+ enum object_type type;
+ struct unpack_entry_stack_ent small_delta_stack[UNPACK_ENTRY_STACK_PREALLOC];
+ struct unpack_entry_stack_ent *delta_stack = small_delta_stack;
+ int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
+ int base_from_cache = 0;
+
+ write_pack_access_log(p, obj_offset);
+
+ /* PHASE 1: drill down to the innermost base object */
+ for (;;) {
+ off_t base_offset;
+ int i;
+ struct delta_base_cache_entry *ent;
+
+ ent = get_delta_base_cache_entry(p, curpos);
+ if (ent) {
+ type = ent->type;
+ data = ent->data;
+ size = ent->size;
+ detach_delta_base_cache_entry(ent);
+ base_from_cache = 1;
+ break;
+ }
+
+ if (do_check_packed_object_crc && p->index_version > 1) {
+ struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+ off_t len = revidx[1].offset - obj_offset;
+ if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
+ const unsigned char *sha1 =
+ nth_packed_object_sha1(p, revidx->nr);
+ error("bad packed object CRC for %s",
+ sha1_to_hex(sha1));
+ mark_bad_packed_object(p, sha1);
+ data = NULL;
+ goto out;
+ }
+ }
+
+ type = unpack_object_header(p, &w_curs, &curpos, &size);
+ if (type != OBJ_OFS_DELTA && type != OBJ_REF_DELTA)
+ break;
+
+ base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+ if (!base_offset) {
+ error("failed to validate delta base reference "
+ "at offset %"PRIuMAX" from %s",
+ (uintmax_t)curpos, p->pack_name);
+ /* bail to phase 2, in hopes of recovery */
+ data = NULL;
+ break;
+ }
+
+ /* push object, proceed to base */
+ if (delta_stack_nr >= delta_stack_alloc
+ && delta_stack == small_delta_stack) {
+ delta_stack_alloc = alloc_nr(delta_stack_nr);
+ ALLOC_ARRAY(delta_stack, delta_stack_alloc);
+ memcpy(delta_stack, small_delta_stack,
+ sizeof(*delta_stack)*delta_stack_nr);
+ } else {
+ ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
+ }
+ i = delta_stack_nr++;
+ delta_stack[i].obj_offset = obj_offset;
+ delta_stack[i].curpos = curpos;
+ delta_stack[i].size = size;
+
+ curpos = obj_offset = base_offset;
+ }
+
+ /* PHASE 2: handle the base */
+ switch (type) {
+ case OBJ_OFS_DELTA:
+ case OBJ_REF_DELTA:
+ if (data)
+ die("BUG: unpack_entry: left loop at a valid delta");
+ break;
+ case OBJ_COMMIT:
+ case OBJ_TREE:
+ case OBJ_BLOB:
+ case OBJ_TAG:
+ if (!base_from_cache)
+ data = unpack_compressed_entry(p, &w_curs, curpos, size);
+ break;
+ default:
+ data = NULL;
+ error("unknown object type %i at offset %"PRIuMAX" in %s",
+ type, (uintmax_t)obj_offset, p->pack_name);
+ }
+
+ /* PHASE 3: apply deltas in order */
+
+ /* invariants:
+ * 'data' holds the base data, or NULL if there was corruption
+ */
+ while (delta_stack_nr) {
+ void *delta_data;
+ void *base = data;
+ void *external_base = NULL;
+ unsigned long delta_size, base_size = size;
+ int i;
+
+ data = NULL;
+
+ if (base)
+ add_delta_base_cache(p, obj_offset, base, base_size, type);
+
+ if (!base) {
+ /*
+ * We're probably in deep shit, but let's try to fetch
+ * the required base anyway from another pack or loose.
+ * This is costly but should happen only in the presence
+ * of a corrupted pack, and is better than failing outright.
+ */
+ struct revindex_entry *revidx;
+ const unsigned char *base_sha1;
+ revidx = find_pack_revindex(p, obj_offset);
+ if (revidx) {
+ base_sha1 = nth_packed_object_sha1(p, revidx->nr);
+ error("failed to read delta base object %s"
+ " at offset %"PRIuMAX" from %s",
+ sha1_to_hex(base_sha1), (uintmax_t)obj_offset,
+ p->pack_name);
+ mark_bad_packed_object(p, base_sha1);
+ base = read_object(base_sha1, &type, &base_size);
+ external_base = base;
+ }
+ }
+
+ i = --delta_stack_nr;
+ obj_offset = delta_stack[i].obj_offset;
+ curpos = delta_stack[i].curpos;
+ delta_size = delta_stack[i].size;
+
+ if (!base)
+ continue;
+
+ delta_data = unpack_compressed_entry(p, &w_curs, curpos, delta_size);
+
+ if (!delta_data) {
+ error("failed to unpack compressed delta "
+ "at offset %"PRIuMAX" from %s",
+ (uintmax_t)curpos, p->pack_name);
+ data = NULL;
+ free(external_base);
+ continue;
+ }
+
+ data = patch_delta(base, base_size,
+ delta_data, delta_size,
+ &size);
+
+ /*
+ * We could not apply the delta; warn the user, but keep going.
+ * Our failure will be noticed either in the next iteration of
+ * the loop, or if this is the final delta, in the caller when
+ * we return NULL. Those code paths will take care of making
+ * a more explicit warning and retrying with another copy of
+ * the object.
+ */
+ if (!data)
+ error("failed to apply delta");
+
+ free(delta_data);
+ free(external_base);
+ }
+
+ if (final_type)
+ *final_type = type;
+ if (final_size)
+ *final_size = size;
+
+out:
+ unuse_pack(&w_curs);
+
+ if (delta_stack != small_delta_stack)
+ free(delta_stack);
+
+ return data;
+}
+
+const unsigned char *nth_packed_object_sha1(struct packed_git *p,
+ uint32_t n)
+{
+ const unsigned char *index = p->index_data;
+ if (!index) {
+ if (open_pack_index(p))
+ return NULL;
+ index = p->index_data;
+ }
+ if (n >= p->num_objects)
+ return NULL;
+ index += 4 * 256;
+ if (p->index_version == 1) {
+ return index + 24 * n + 4;
+ } else {
+ index += 8;
+ return index + 20 * n;
+ }
+}
+
+const struct object_id *nth_packed_object_oid(struct object_id *oid,
+ struct packed_git *p,
+ uint32_t n)
+{
+ const unsigned char *hash = nth_packed_object_sha1(p, n);
+ if (!hash)
+ return NULL;
+ hashcpy(oid->hash, hash);
+ return oid;
+}
+
+void check_pack_index_ptr(const struct packed_git *p, const void *vptr)
+{
+ const unsigned char *ptr = vptr;
+ const unsigned char *start = p->index_data;
+ const unsigned char *end = start + p->index_size;
+ if (ptr < start)
+ die(_("offset before start of pack index for %s (corrupt index?)"),
+ p->pack_name);
+ /* No need to check for underflow; .idx files must be at least 8 bytes */
+ if (ptr >= end - 8)
+ die(_("offset beyond end of pack index for %s (truncated index?)"),
+ p->pack_name);
+}
+
+off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
+{
+ const unsigned char *index = p->index_data;
+ index += 4 * 256;
+ if (p->index_version == 1) {
+ return ntohl(*((uint32_t *)(index + 24 * n)));
+ } else {
+ uint32_t off;
+ index += 8 + p->num_objects * (20 + 4);
+ off = ntohl(*((uint32_t *)(index + 4 * n)));
+ if (!(off & 0x80000000))
+ return off;
+ index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
+ check_pack_index_ptr(p, index);
+ return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
+ ntohl(*((uint32_t *)(index + 4)));
+ }
+}
+
+off_t find_pack_entry_one(const unsigned char *sha1,
+ struct packed_git *p)
+{
+ const uint32_t *level1_ofs = p->index_data;
+ const unsigned char *index = p->index_data;
+ unsigned hi, lo, stride;
+ static int debug_lookup = -1;
+
+ if (debug_lookup < 0)
+ debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
+
+ if (!index) {
+ if (open_pack_index(p))
+ return 0;
+ level1_ofs = p->index_data;
+ index = p->index_data;
+ }
+ if (p->index_version > 1) {
+ level1_ofs += 2;
+ index += 8;
+ }
+ index += 4 * 256;
+ hi = ntohl(level1_ofs[*sha1]);
+ lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
+ if (p->index_version > 1) {
+ stride = 20;
+ } else {
+ stride = 24;
+ index += 4;
+ }
+
+ if (debug_lookup)
+ printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
+ sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
+
+ while (lo < hi) {
+ unsigned mi = (lo + hi) / 2;
+ int cmp = hashcmp(index + mi * stride, sha1);
+
+ if (debug_lookup)
+ printf("lo %u hi %u rg %u mi %u\n",
+ lo, hi, hi - lo, mi);
+ if (!cmp)
+ return nth_packed_object_offset(p, mi);
+ if (cmp > 0)
+ hi = mi;
+ else
+ lo = mi+1;
+ }
+ return 0;
+}
+
+int is_pack_valid(struct packed_git *p)
+{
+ /* An already open pack is known to be valid. */
+ if (p->pack_fd != -1)
+ return 1;
+
+ /* If the pack has one window completely covering the
+ * file size, the pack is known to be valid even if
+ * the descriptor is not currently open.
+ */
+ if (p->windows) {
+ struct pack_window *w = p->windows;
+
+ if (!w->offset && w->len == p->pack_size)
+ return 1;
+ }
+
+ /* Force the pack to open to prove its valid. */
+ return !open_packed_git(p);
+}
+
+struct packed_git *find_sha1_pack(const unsigned char *sha1,
+ struct packed_git *packs)
+{
+ struct packed_git *p;
+
+ for (p = packs; p; p = p->next) {
+ if (find_pack_entry_one(sha1, p))
+ return p;
+ }
+ return NULL;
+
+}
+
+static int fill_pack_entry(const unsigned char *sha1,
+ struct pack_entry *e,
+ struct packed_git *p)
+{
+ off_t offset;
+
+ if (p->num_bad_objects) {
+ unsigned i;
+ for (i = 0; i < p->num_bad_objects; i++)
+ if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+ return 0;
+ }
+
+ offset = find_pack_entry_one(sha1, p);
+ if (!offset)
+ return 0;
+
+ /*
+ * We are about to tell the caller where they can locate the
+ * requested object. We better make sure the packfile is
+ * still here and can be accessed before supplying that
+ * answer, as it may have been deleted since the index was
+ * loaded!
+ */
+ if (!is_pack_valid(p))
+ return 0;
+ e->offset = offset;
+ e->p = p;
+ hashcpy(e->sha1, sha1);
+ return 1;
+}
+
+/*
+ * Iff a pack file contains the object named by sha1, return true and
+ * store its location to e.
+ */
+int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
+{
+ struct mru_entry *p;
+
+ prepare_packed_git();
+ if (!packed_git)
+ return 0;
+
+ for (p = packed_git_mru->head; p; p = p->next) {
+ if (fill_pack_entry(sha1, e, p->item)) {
+ mru_mark(packed_git_mru, p);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int has_sha1_pack(const unsigned char *sha1)
+{
+ struct pack_entry e;
+ return find_pack_entry(sha1, &e);
+}
+
+int has_pack_index(const unsigned char *sha1)
+{
+ struct stat st;
+ if (stat(sha1_pack_index_name(sha1), &st))
+ return 0;
+ return 1;
+}
+
+static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
+{
+ uint32_t i;
+ int r = 0;
+
+ for (i = 0; i < p->num_objects; i++) {
+ struct object_id oid;
+
+ if (!nth_packed_object_oid(&oid, p, i))
+ return error("unable to get sha1 of object %u in %s",
+ i, p->pack_name);
+
+ r = cb(&oid, p, i, data);
+ if (r)
+ break;
+ }
+ return r;
+}
+
+int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
+{
+ struct packed_git *p;
+ int r = 0;
+ int pack_errors = 0;
+
+ prepare_packed_git();
+ for (p = packed_git; p; p = p->next) {
+ if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
+ continue;
+ if (open_pack_index(p)) {
+ pack_errors = 1;
+ continue;
+ }
+ r = for_each_object_in_pack(p, cb, data);
+ if (r)
+ break;
+ }
+ return r ? r : pack_errors;
+}
diff --git a/packfile.h b/packfile.h
new file mode 100644
index 0000000000..0cdeb54dcd
--- /dev/null
+++ b/packfile.h
@@ -0,0 +1,138 @@
+#ifndef PACKFILE_H
+#define PACKFILE_H
+
+/*
+ * 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
+ * any existing contents. A pointer to buf->buf is returned as a convenience.
+ *
+ * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
+ */
+extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
+
+/*
+ * Return the name of the (local) packfile with the specified sha1 in
+ * its name. The return value is a pointer to memory that is
+ * overwritten each time this function is called.
+ */
+extern char *sha1_pack_name(const unsigned char *sha1);
+
+/*
+ * Return the name of the (local) pack index file with the specified
+ * sha1 in its name. The return value is a pointer to memory that is
+ * overwritten each time this function is called.
+ */
+extern char *sha1_pack_index_name(const unsigned char *sha1);
+
+extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
+
+/* A hook to report invalid files in pack directory */
+#define PACKDIR_FILE_PACK 1
+#define PACKDIR_FILE_IDX 2
+#define PACKDIR_FILE_GARBAGE 4
+extern void (*report_garbage)(unsigned seen_bits, const char *path);
+
+extern void prepare_packed_git(void);
+extern void reprepare_packed_git(void);
+extern void install_packed_git(struct packed_git *pack);
+
+/*
+ * Give a rough count of objects in the repository. This sacrifices accuracy
+ * for speed.
+ */
+unsigned long approximate_object_count(void);
+
+extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
+ struct packed_git *packs);
+
+extern void pack_report(void);
+
+/*
+ * mmap the index file for the specified packfile (if it is not
+ * already mmapped). Return 0 on success.
+ */
+extern int open_pack_index(struct packed_git *);
+
+/*
+ * munmap the index file for the specified packfile (if it is
+ * currently mmapped).
+ */
+extern void close_pack_index(struct packed_git *);
+
+extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
+extern void close_pack_windows(struct packed_git *);
+extern void close_all_packs(void);
+extern void unuse_pack(struct pack_window **);
+extern void clear_delta_base_cache(void);
+extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
+
+/*
+ * Make sure that a pointer access into an mmap'd index file is within bounds,
+ * and can provide at least 8 bytes of data.
+ *
+ * Note that this is only necessary for variable-length segments of the file
+ * (like the 64-bit extended offset table), as we compare the size to the
+ * fixed-length parts when we open the file.
+ */
+extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
+
+/*
+ * Return the SHA-1 of the nth object within the specified packfile.
+ * Open the index if it is not already open. The return value points
+ * at the SHA-1 within the mmapped index. Return NULL if there is an
+ * error.
+ */
+extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
+/*
+ * Like nth_packed_object_sha1, but write the data into the object specified by
+ * the the first argument. Returns the first argument on success, and NULL on
+ * error.
+ */
+extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
+
+/*
+ * Return the offset of the nth object within the specified packfile.
+ * The index must already be opened.
+ */
+extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
+
+/*
+ * If the object named sha1 is present in the specified packfile,
+ * return its offset within the packfile; otherwise, return 0.
+ */
+extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
+
+extern int is_pack_valid(struct packed_git *);
+extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
+extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
+extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
+extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
+
+extern void release_pack_memory(size_t);
+
+/* global flag to enable extra checks when accessing packed objects */
+extern int do_check_packed_object_crc;
+
+extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
+
+extern void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1);
+extern const struct packed_git *has_packed_and_bad(const unsigned char *sha1);
+
+extern int find_pack_entry(const unsigned char *sha1, struct pack_entry *e);
+
+extern int has_sha1_pack(const unsigned char *sha1);
+
+extern int has_pack_index(const unsigned char *sha1);
+
+/*
+ * Iterate over packed objects in both the local
+ * repository and any alternates repositories (unless the
+ * FOR_EACH_OBJECT_LOCAL_ONLY flag, defined in cache.h, is set).
+ */
+typedef int each_packed_object_fn(const struct object_id *oid,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *data);
+extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags);
+
+#endif
diff --git a/pager.c b/pager.c
index 4dd9e1b265..92b23e6cd1 100644
--- a/pager.c
+++ b/pager.c
@@ -194,7 +194,7 @@ static int pager_command_config(const char *var, const char *value, void *vdata)
const char *cmd;
if (skip_prefix(var, "pager.", &cmd) && !strcmp(cmd, data->cmd)) {
- int b = git_config_maybe_bool(var, value);
+ int b = git_parse_maybe_bool(value);
if (b >= 0)
data->want = b;
else {
diff --git a/patch-ids.c b/patch-ids.c
index b4166b0f38..7a583b3011 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -35,11 +35,16 @@ int commit_patch_id(struct commit *commit, struct diff_options *options,
* the side of safety. The actual value being negative does not have
* any significance; only that it is non-zero matters.
*/
-static int patch_id_cmp(struct diff_options *opt,
- struct patch_id *a,
- struct patch_id *b,
+static int patch_id_cmp(const void *cmpfn_data,
+ const void *entry,
+ const void *entry_or_key,
const void *unused_keydata)
{
+ /* NEEDSWORK: const correctness? */
+ struct diff_options *opt = (void *)cmpfn_data;
+ struct patch_id *a = (void *)entry;
+ struct patch_id *b = (void *)entry_or_key;
+
if (is_null_oid(&a->patch_id) &&
commit_patch_id(a->commit, opt, &a->patch_id, 0))
return error("Could not get patch ID for %s",
@@ -58,8 +63,7 @@ int init_patch_ids(struct patch_ids *ids)
ids->diffopts.detect_rename = 0;
DIFF_OPT_SET(&ids->diffopts, RECURSIVE);
diff_setup_done(&ids->diffopts);
- hashmap_init(&ids->patches, (hashmap_cmp_fn)patch_id_cmp,
- &ids->diffopts, 256);
+ hashmap_init(&ids->patches, patch_id_cmp, &ids->diffopts, 256);
return 0;
}
diff --git a/path.c b/path.c
index e50d2befcf..b533ec938d 100644
--- a/path.c
+++ b/path.c
@@ -9,6 +9,7 @@
#include "worktree.h"
#include "submodule-config.h"
#include "path.h"
+#include "packfile.h"
static int get_st_mode_bits(const char *path, int *mode)
{
diff --git a/perl/Git.pm b/perl/Git.pm
index f4b56e6d4d..ffa09ace92 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -532,7 +532,7 @@ If TIME is not supplied, the current local time is used.
=cut
sub get_tz_offset {
- # some systmes don't handle or mishandle %z, so be creative.
+ # some systems don't handle or mishandle %z, so be creative.
my $t = shift || time;
my $gm = timegm(localtime($t));
my $sign = qw( + + - )[ $gm <=> $t ];
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index 98518f4ddb..bc4eed3d75 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -1416,7 +1416,7 @@ sub parse_svn_date {
delete $ENV{TZ};
}
- my $our_TZ = get_tz_offset();
+ my $our_TZ = get_tz_offset($epoch_in_UTC);
# This converts $epoch_in_UTC into our local timezone.
my ($sec, $min, $hour, $mday, $mon, $year,
diff --git a/pkt-line.c b/pkt-line.c
index 9d845ecc3c..7db9119573 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -171,25 +171,6 @@ int packet_write_fmt_gently(int fd, const char *fmt, ...)
return status;
}
-int packet_writel(int fd, const char *line, ...)
-{
- va_list args;
- int err;
- va_start(args, line);
- for (;;) {
- if (!line)
- break;
- if (strlen(line) > LARGE_PACKET_DATA_MAX)
- return -1;
- err = packet_write_fmt_gently(fd, "%s\n", line);
- if (err)
- return err;
- line = va_arg(args, const char*);
- }
- va_end(args);
- return packet_flush_gently(fd);
-}
-
static int packet_write_gently(const int fd_out, const char *buf, size_t size)
{
static char packet_write_buffer[LARGE_PACKET_MAX];
diff --git a/pkt-line.h b/pkt-line.h
index 450183b649..66ef610fc4 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -25,8 +25,6 @@ void packet_buf_flush(struct strbuf *buf);
void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
int packet_flush_gently(int fd);
int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
-LAST_ARG_MUST_BE_NULL
-int packet_writel(int fd, const char *line, ...);
int write_packetized_from_fd(int fd_in, int fd_out);
int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);
diff --git a/pretty.c b/pretty.c
index e4b561c582..94eab5c89e 100644
--- a/pretty.c
+++ b/pretty.c
@@ -871,16 +871,6 @@ const char *format_subject(struct strbuf *sb, const char *msg,
return msg;
}
-static void format_trailers(struct strbuf *sb, const char *msg)
-{
- struct trailer_info info;
-
- trailer_info_get(&info, msg);
- strbuf_add(sb, info.trailer_start,
- info.trailer_end - info.trailer_start);
- trailer_info_release(&info);
-}
-
static void parse_commit_message(struct format_commit_context *c)
{
const char *msg = c->message + c->message_off;
@@ -947,6 +937,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
struct format_commit_context *c)
{
const char *rest = placeholder;
+ const char *basic_color = NULL;
if (placeholder[1] == '(') {
const char *begin = placeholder + 2;
@@ -955,23 +946,41 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
if (!end)
return 0;
+
if (skip_prefix(begin, "auto,", &begin)) {
if (!want_color(c->pretty_ctx->color))
return end - placeholder + 1;
+ } else if (skip_prefix(begin, "always,", &begin)) {
+ /* nothing to do; we do not respect want_color at all */
+ } else {
+ /* the default is the same as "auto" */
+ if (!want_color(c->pretty_ctx->color))
+ return end - placeholder + 1;
}
+
if (color_parse_mem(begin, end - begin, color) < 0)
die(_("unable to parse --pretty format"));
strbuf_addstr(sb, color);
return end - placeholder + 1;
}
+
+ /*
+ * We handle things like "%C(red)" above; for historical reasons, there
+ * are a few colors that can be specified without parentheses (and
+ * they cannot support things like "auto" or "always" at all).
+ */
if (skip_prefix(placeholder + 1, "red", &rest))
- strbuf_addstr(sb, GIT_COLOR_RED);
+ basic_color = GIT_COLOR_RED;
else if (skip_prefix(placeholder + 1, "green", &rest))
- strbuf_addstr(sb, GIT_COLOR_GREEN);
+ basic_color = GIT_COLOR_GREEN;
else if (skip_prefix(placeholder + 1, "blue", &rest))
- strbuf_addstr(sb, GIT_COLOR_BLUE);
+ basic_color = GIT_COLOR_BLUE;
else if (skip_prefix(placeholder + 1, "reset", &rest))
- strbuf_addstr(sb, GIT_COLOR_RESET);
+ basic_color = GIT_COLOR_RESET;
+
+ if (basic_color && want_color(c->pretty_ctx->color))
+ strbuf_addstr(sb, basic_color);
+
return rest - placeholder;
}
@@ -1055,6 +1064,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
const struct commit *commit = c->commit;
const char *msg = c->message;
struct commit_list *p;
+ const char *arg;
int ch;
/* these are independent of the commit */
@@ -1273,9 +1283,18 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
return 1;
}
- if (starts_with(placeholder, "(trailers)")) {
- format_trailers(sb, msg + c->subject_off);
- return strlen("(trailers)");
+ if (skip_prefix(placeholder, "(trailers", &arg)) {
+ struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+ while (*arg == ':') {
+ if (skip_prefix(arg, ":only", &arg))
+ opts.only_trailers = 1;
+ else if (skip_prefix(arg, ":unfold", &arg))
+ opts.unfold = 1;
+ }
+ if (*arg == ')') {
+ format_trailers_from_commit(sb, msg + c->subject_off, &opts);
+ return arg - placeholder + 1;
+ }
}
return 0; /* unknown placeholder */
diff --git a/progress.c b/progress.c
index 73e36d4a42..289678d43d 100644
--- a/progress.c
+++ b/progress.c
@@ -34,7 +34,7 @@ struct progress {
unsigned total;
unsigned last_percent;
unsigned delay;
- unsigned delayed_percent_treshold;
+ unsigned delayed_percent_threshold;
struct throughput *throughput;
uint64_t start_ns;
};
@@ -88,7 +88,7 @@ static int display(struct progress *progress, unsigned n, const char *done)
return 0;
if (progress->total) {
unsigned percent = n * 100 / progress->total;
- if (percent > progress->delayed_percent_treshold) {
+ if (percent > progress->delayed_percent_threshold) {
/* inhibit this progress report entirely */
clear_progress_signal();
progress->delay = -1;
@@ -205,8 +205,8 @@ int display_progress(struct progress *progress, unsigned n)
return progress ? display(progress, n, NULL) : 0;
}
-struct progress *start_progress_delay(const char *title, unsigned total,
- unsigned percent_treshold, unsigned delay)
+static struct progress *start_progress_delay(const char *title, unsigned total,
+ unsigned percent_threshold, unsigned delay)
{
struct progress *progress = malloc(sizeof(*progress));
if (!progress) {
@@ -219,7 +219,7 @@ struct progress *start_progress_delay(const char *title, unsigned total,
progress->total = total;
progress->last_value = -1;
progress->last_percent = -1;
- progress->delayed_percent_treshold = percent_treshold;
+ progress->delayed_percent_threshold = percent_threshold;
progress->delay = delay;
progress->throughput = NULL;
progress->start_ns = getnanotime();
@@ -227,6 +227,11 @@ struct progress *start_progress_delay(const char *title, unsigned total,
return progress;
}
+struct progress *start_delayed_progress(const char *title, unsigned total)
+{
+ return start_progress_delay(title, total, 0, 2);
+}
+
struct progress *start_progress(const char *title, unsigned total)
{
return start_progress_delay(title, total, 0, 0);
diff --git a/progress.h b/progress.h
index 611e4c4d42..6392b63371 100644
--- a/progress.h
+++ b/progress.h
@@ -6,8 +6,7 @@ struct progress;
void display_throughput(struct progress *progress, off_t total);
int display_progress(struct progress *progress, unsigned n);
struct progress *start_progress(const char *title, unsigned total);
-struct progress *start_progress_delay(const char *title, unsigned total,
- unsigned percent_treshold, unsigned delay);
+struct progress *start_delayed_progress(const char *title, unsigned total);
void stop_progress(struct progress **progress);
void stop_progress_msg(struct progress **progress, const char *msg);
diff --git a/reachable.c b/reachable.c
index c62efbfd43..d1ac5d97ef 100644
--- a/reachable.c
+++ b/reachable.c
@@ -9,6 +9,7 @@
#include "cache-tree.h"
#include "progress.h"
#include "list-objects.h"
+#include "packfile.h"
struct connectivity_progress {
struct progress *progress;
diff --git a/read-cache.c b/read-cache.c
index 2121b6e7bb..40da87ea71 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -160,9 +160,9 @@ static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
int fd = git_open_cloexec(ce->name, O_RDONLY);
if (fd >= 0) {
- unsigned char sha1[20];
- if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
- match = hashcmp(sha1, ce->oid.hash);
+ struct object_id oid;
+ if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
+ match = oidcmp(&oid, &ce->oid);
/* index_fd() closed the file descriptor already */
}
return match;
@@ -515,9 +515,8 @@ int remove_index_entry_at(struct index_state *istate, int pos)
istate->cache_nr--;
if (pos >= istate->cache_nr)
return 0;
- memmove(istate->cache + pos,
- istate->cache + pos + 1,
- (istate->cache_nr - pos) * sizeof(struct cache_entry *));
+ MOVE_ARRAY(istate->cache + pos, istate->cache + pos + 1,
+ istate->cache_nr - pos);
return 1;
}
@@ -690,7 +689,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
return 0;
}
if (!intent_only) {
- if (index_path(ce->oid.hash, path, st, HASH_WRITE_OBJECT)) {
+ if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
free(ce);
return error("unable to index file %s", path);
}
@@ -1500,6 +1499,7 @@ struct ondisk_cache_entry_extended {
};
/* These are only used for v3 or lower */
+#define align_padding_size(size, len) ((size + (len) + 8) & ~7) - (size + len)
#define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
#define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
#define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
@@ -2033,7 +2033,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
}
/* Copy miscellaneous fields but not the name */
-static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
+static void copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
struct cache_entry *ce)
{
short flags;
@@ -2057,32 +2057,35 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
struct ondisk_cache_entry_extended *ondisk2;
ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
- return ondisk2->name;
- }
- else {
- return ondisk->name;
}
}
static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
- struct strbuf *previous_name)
+ struct strbuf *previous_name, struct ondisk_cache_entry *ondisk)
{
int size;
- struct ondisk_cache_entry *ondisk;
int saved_namelen = saved_namelen; /* compiler workaround */
- char *name;
int result;
+ static unsigned char padding[8] = { 0x00 };
if (ce->ce_flags & CE_STRIP_NAME) {
saved_namelen = ce_namelen(ce);
ce->ce_namelen = 0;
}
+ if (ce->ce_flags & CE_EXTENDED)
+ size = offsetof(struct ondisk_cache_entry_extended, name);
+ else
+ size = offsetof(struct ondisk_cache_entry, name);
+
if (!previous_name) {
- size = ondisk_ce_size(ce);
- ondisk = xcalloc(1, size);
- name = copy_cache_entry_to_ondisk(ondisk, ce);
- memcpy(name, ce->name, ce_namelen(ce));
+ int len = ce_namelen(ce);
+ copy_cache_entry_to_ondisk(ondisk, ce);
+ result = ce_write(c, fd, ondisk, size);
+ if (!result)
+ result = ce_write(c, fd, ce->name, len);
+ if (!result)
+ result = ce_write(c, fd, padding, align_padding_size(size, len));
} else {
int common, to_remove, prefix_size;
unsigned char to_remove_vi[16];
@@ -2095,16 +2098,12 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
to_remove = previous_name->len - common;
prefix_size = encode_varint(to_remove, to_remove_vi);
- if (ce->ce_flags & CE_EXTENDED)
- size = offsetof(struct ondisk_cache_entry_extended, name);
- else
- size = offsetof(struct ondisk_cache_entry, name);
- size += prefix_size + (ce_namelen(ce) - common + 1);
-
- ondisk = xcalloc(1, size);
- name = copy_cache_entry_to_ondisk(ondisk, ce);
- memcpy(name, to_remove_vi, prefix_size);
- memcpy(name + prefix_size, ce->name + common, ce_namelen(ce) - common);
+ copy_cache_entry_to_ondisk(ondisk, ce);
+ result = ce_write(c, fd, ondisk, size);
+ if (!result)
+ result = ce_write(c, fd, to_remove_vi, prefix_size);
+ if (!result)
+ result = ce_write(c, fd, ce->name + common, ce_namelen(ce) - common + 1);
strbuf_splice(previous_name, common, to_remove,
ce->name + common, ce_namelen(ce) - common);
@@ -2114,8 +2113,6 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
ce->ce_flags &= ~CE_STRIP_NAME;
}
- result = ce_write(c, fd, ondisk, size);
- free(ondisk);
return result;
}
@@ -2193,10 +2190,11 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
int newfd = tempfile->fd;
git_SHA_CTX c;
struct cache_header hdr;
- int i, err, removed, extended, hdr_version;
+ int i, err = 0, removed, extended, hdr_version;
struct cache_entry **cache = istate->cache;
int entries = istate->cache_nr;
struct stat st;
+ struct ondisk_cache_entry_extended ondisk;
struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
int drop_cache_tree = 0;
@@ -2233,6 +2231,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
return -1;
previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
+
for (i = 0; i < entries; i++) {
struct cache_entry *ce = cache[i];
if (ce->ce_flags & CE_REMOVE)
@@ -2248,15 +2247,21 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (allow)
warning(msg, ce->name);
else
- return error(msg, ce->name);
+ err = error(msg, ce->name);
drop_cache_tree = 1;
}
- if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
- return -1;
+ if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
+ err = -1;
+
+ if (err)
+ break;
}
strbuf_release(&previous_name_buf);
+ if (err)
+ return err;
+
/* Write extension data here */
if (!strip_extensions && istate->split_index) {
struct strbuf sb = STRBUF_INIT;
diff --git a/ref-filter.c b/ref-filter.c
index ae6ecbd1cf..bc591f4f3d 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -97,14 +97,19 @@ static struct used_atom {
} u;
} *used_atom;
static int used_atom_cnt, need_tagged, need_symref;
-static int need_color_reset_at_eol;
-static void color_atom_parser(struct used_atom *atom, const char *color_value)
+static void color_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *color_value)
{
if (!color_value)
die(_("expected format: %%(color:<color>)"));
if (color_parse(color_value, atom->u.color) < 0)
die(_("unrecognized color: %%(color:%s)"), color_value);
+ /*
+ * We check this after we've parsed the color, which lets us complain
+ * about syntactically bogus color names even if they won't be used.
+ */
+ if (!want_color(format->use_color))
+ color_parse("", atom->u.color);
}
static void refname_atom_parser_internal(struct refname_atom *atom,
@@ -127,7 +132,7 @@ static void refname_atom_parser_internal(struct refname_atom *atom,
die(_("unrecognized %%(%s) argument: %s"), name, arg);
}
-static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
+static void remote_ref_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
struct string_list params = STRING_LIST_INIT_DUP;
int i;
@@ -161,28 +166,28 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
string_list_clear(&params, 0);
}
-static void body_atom_parser(struct used_atom *atom, const char *arg)
+static void body_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
if (arg)
die(_("%%(body) does not take arguments"));
atom->u.contents.option = C_BODY_DEP;
}
-static void subject_atom_parser(struct used_atom *atom, const char *arg)
+static void subject_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
if (arg)
die(_("%%(subject) does not take arguments"));
atom->u.contents.option = C_SUB;
}
-static void trailers_atom_parser(struct used_atom *atom, const char *arg)
+static void trailers_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
if (arg)
die(_("%%(trailers) does not take arguments"));
atom->u.contents.option = C_TRAILERS;
}
-static void contents_atom_parser(struct used_atom *atom, const char *arg)
+static void contents_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
if (!arg)
atom->u.contents.option = C_BARE;
@@ -202,7 +207,7 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)
die(_("unrecognized %%(contents) argument: %s"), arg);
}
-static void objectname_atom_parser(struct used_atom *atom, const char *arg)
+static void objectname_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
if (!arg)
atom->u.objectname.option = O_FULL;
@@ -219,7 +224,7 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)
die(_("unrecognized %%(objectname) argument: %s"), arg);
}
-static void refname_atom_parser(struct used_atom *atom, const char *arg)
+static void refname_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
refname_atom_parser_internal(&atom->u.refname, arg, atom->name);
}
@@ -235,7 +240,7 @@ static align_type parse_align_position(const char *s)
return -1;
}
-static void align_atom_parser(struct used_atom *atom, const char *arg)
+static void align_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
struct align *align = &atom->u.align;
struct string_list params = STRING_LIST_INIT_DUP;
@@ -274,7 +279,7 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)
string_list_clear(&params, 0);
}
-static void if_atom_parser(struct used_atom *atom, const char *arg)
+static void if_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
if (!arg) {
atom->u.if_then_else.cmp_status = COMPARE_NONE;
@@ -288,7 +293,7 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)
}
}
-static void head_atom_parser(struct used_atom *atom, const char *arg)
+static void head_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
struct object_id unused;
@@ -298,7 +303,7 @@ static void head_atom_parser(struct used_atom *atom, const char *arg)
static struct {
const char *name;
cmp_type cmp_type;
- void (*parser)(struct used_atom *atom, const char *arg);
+ void (*parser)(const struct ref_format *format, struct used_atom *atom, const char *arg);
} valid_atom[] = {
{ "refname" , FIELD_STR, refname_atom_parser },
{ "objecttype" },
@@ -365,7 +370,8 @@ struct atom_value {
/*
* Used to parse format string and sort specifiers
*/
-int parse_ref_filter_atom(const char *atom, const char *ep)
+static int parse_ref_filter_atom(const struct ref_format *format,
+ const char *atom, const char *ep)
{
const char *sp;
const char *arg;
@@ -413,7 +419,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
arg = used_atom[at].name + (arg - atom) + 1;
memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
if (valid_atom[i].parser)
- valid_atom[i].parser(&used_atom[at], arg);
+ valid_atom[i].parser(format, &used_atom[at], arg);
if (*atom == '*')
need_tagged = 1;
if (!strcmp(valid_atom[i].name, "symref"))
@@ -657,24 +663,26 @@ static const char *find_next(const char *cp)
* Make sure the format string is well formed, and parse out
* the used atoms.
*/
-int verify_ref_format(const char *format)
+int verify_ref_format(struct ref_format *format)
{
const char *cp, *sp;
- need_color_reset_at_eol = 0;
- for (cp = format; *cp && (sp = find_next(cp)); ) {
+ format->need_color_reset_at_eol = 0;
+ for (cp = format->format; *cp && (sp = find_next(cp)); ) {
const char *color, *ep = strchr(sp, ')');
int at;
if (!ep)
return error(_("malformed format string %s"), sp);
/* sp points at "%(" and ep points at the closing ")" */
- at = parse_ref_filter_atom(sp + 2, ep);
+ at = parse_ref_filter_atom(format, sp + 2, ep);
cp = ep + 1;
if (skip_prefix(used_atom[at].name, "color:", &color))
- need_color_reset_at_eol = !!strcmp(color, "reset");
+ format->need_color_reset_at_eol = !!strcmp(color, "reset");
}
+ if (format->need_color_reset_at_eol && !want_color(format->use_color))
+ format->need_color_reset_at_eol = 0;
return 0;
}
@@ -2060,35 +2068,34 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting
}
}
-void format_ref_array_item(struct ref_array_item *info, const char *format,
- int quote_style, struct strbuf *final_buf)
+void format_ref_array_item(struct ref_array_item *info,
+ const struct ref_format *format,
+ struct strbuf *final_buf)
{
const char *cp, *sp, *ep;
struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
- state.quote_style = quote_style;
+ state.quote_style = format->quote_style;
push_stack_element(&state.stack);
- for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
+ for (cp = format->format; *cp && (sp = find_next(cp)); cp = ep + 1) {
struct atom_value *atomv;
ep = strchr(sp, ')');
if (cp < sp)
append_literal(cp, sp, &state);
- get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
+ get_ref_atom_value(info,
+ parse_ref_filter_atom(format, sp + 2, ep),
+ &atomv);
atomv->handler(atomv, &state);
}
if (*cp) {
sp = cp + strlen(cp);
append_literal(cp, sp, &state);
}
- if (need_color_reset_at_eol) {
+ if (format->need_color_reset_at_eol) {
struct atom_value resetv;
- char color[COLOR_MAXLEN] = "";
-
- if (color_parse("reset", color) < 0)
- die("BUG: couldn't parse 'reset' as a color");
- resetv.s = color;
+ resetv.s = GIT_COLOR_RESET;
append_atom(&resetv, &state);
}
if (state.stack->prev)
@@ -2097,26 +2104,38 @@ void format_ref_array_item(struct ref_array_item *info, const char *format,
pop_stack_element(&state.stack);
}
-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
+void show_ref_array_item(struct ref_array_item *info,
+ const struct ref_format *format)
{
struct strbuf final_buf = STRBUF_INIT;
- format_ref_array_item(info, format, quote_style, &final_buf);
+ format_ref_array_item(info, format, &final_buf);
fwrite(final_buf.buf, 1, final_buf.len, stdout);
strbuf_release(&final_buf);
putchar('\n');
}
void pretty_print_ref(const char *name, const unsigned char *sha1,
- const char *format)
+ const struct ref_format *format)
{
struct ref_array_item *ref_item;
ref_item = new_ref_array_item(name, sha1, 0);
ref_item->kind = ref_kind_from_refname(name);
- show_ref_array_item(ref_item, format, 0);
+ show_ref_array_item(ref_item, format);
free_array_item(ref_item);
}
+static int parse_sorting_atom(const char *atom)
+{
+ /*
+ * This parses an atom using a dummy ref_format, since we don't
+ * actually care about the formatting details.
+ */
+ struct ref_format dummy = REF_FORMAT_INIT;
+ const char *end = atom + strlen(atom);
+ return parse_ref_filter_atom(&dummy, atom, end);
+}
+
/* If no sorting option is given, use refname to sort as default */
struct ref_sorting *ref_default_sorting(void)
{
@@ -2125,18 +2144,13 @@ struct ref_sorting *ref_default_sorting(void)
struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));
sorting->next = NULL;
- sorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));
+ sorting->atom = parse_sorting_atom(cstr_name);
return sorting;
}
-int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
+void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
{
- struct ref_sorting **sorting_tail = opt->value;
struct ref_sorting *s;
- int len;
-
- if (!arg) /* should --no-sort void the list ? */
- return -1;
s = xcalloc(1, sizeof(*s));
s->next = *sorting_tail;
@@ -2149,8 +2163,14 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
if (skip_prefix(arg, "version:", &arg) ||
skip_prefix(arg, "v:", &arg))
s->version = 1;
- len = strlen(arg);
- s->atom = parse_ref_filter_atom(arg, arg+len);
+ s->atom = parse_sorting_atom(arg);
+}
+
+int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
+{
+ if (!arg) /* should --no-sort void the list ? */
+ return -1;
+ parse_ref_sorting(opt->value, arg);
return 0;
}
diff --git a/ref-filter.h b/ref-filter.h
index 6552024f09..0d98342b34 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -72,6 +72,21 @@ struct ref_filter {
verbose;
};
+struct ref_format {
+ /*
+ * Set these to define the format; make sure you call
+ * verify_ref_format() afterwards to finalize.
+ */
+ const char *format;
+ int quote_style;
+ int use_color;
+
+ /* Internal state to ref-filter */
+ int need_color_reset_at_eol;
+};
+
+#define REF_FORMAT_INIT { NULL, 0, -1 }
+
/* Macros for checking --merged and --no-merged options */
#define _OPT_MERGED_NO_MERGED(option, filter, h) \
{ OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \
@@ -90,17 +105,18 @@ struct ref_filter {
int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
/* Clear all memory allocated to ref_array */
void ref_array_clear(struct ref_array *array);
-/* Parse format string and sort specifiers */
-int parse_ref_filter_atom(const char *atom, const char *ep);
/* Used to verify if the given format is correct and to parse out the used atoms */
-int verify_ref_format(const char *format);
+int verify_ref_format(struct ref_format *format);
/* Sort the given ref_array as per the ref_sorting provided */
void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
/* Based on the given format and quote_style, fill the strbuf */
-void format_ref_array_item(struct ref_array_item *info, const char *format,
- int quote_style, struct strbuf *final_buf);
+void format_ref_array_item(struct ref_array_item *info,
+ const struct ref_format *format,
+ struct strbuf *final_buf);
/* Print the ref using the given format and quote_style */
-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);
+void show_ref_array_item(struct ref_array_item *info, const struct ref_format *format);
+/* Parse a single sort specifier and add it to the list */
+void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *atom);
/* Callback function for parsing the sort option */
int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);
/* Default sort option based on refname */
@@ -117,6 +133,6 @@ void setup_ref_filter_porcelain_msg(void);
* name must be a fully qualified refname.
*/
void pretty_print_ref(const char *name, const unsigned char *sha1,
- const char *format);
+ const struct ref_format *format);
#endif /* REF_FILTER_H */
diff --git a/reflog-walk.c b/reflog-walk.c
index 081f89b70d..74ebe5148f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -94,45 +94,6 @@ static int get_reflog_recno_by_time(struct complete_reflogs *array,
return -1;
}
-struct commit_info_lifo {
- struct commit_info {
- struct commit *commit;
- void *util;
- } *items;
- int nr, alloc;
-};
-
-static struct commit_info *get_commit_info(struct commit *commit,
- struct commit_info_lifo *lifo, int pop)
-{
- int i;
- for (i = 0; i < lifo->nr; i++)
- if (lifo->items[i].commit == commit) {
- struct commit_info *result = &lifo->items[i];
- if (pop) {
- if (i + 1 < lifo->nr)
- memmove(lifo->items + i,
- lifo->items + i + 1,
- (lifo->nr - i) *
- sizeof(struct commit_info));
- lifo->nr--;
- }
- return result;
- }
- return NULL;
-}
-
-static void add_commit_info(struct commit *commit, void *util,
- struct commit_info_lifo *lifo)
-{
- struct commit_info *info;
- ALLOC_GROW(lifo->items, lifo->nr + 1, lifo->alloc);
- info = lifo->items + lifo->nr;
- info->commit = commit;
- info->util = util;
- lifo->nr++;
-}
-
struct commit_reflog {
int recno;
enum selector_type {
@@ -144,7 +105,8 @@ struct commit_reflog {
};
struct reflog_walk_info {
- struct commit_info_lifo reflogs;
+ struct commit_reflog **logs;
+ size_t nr, alloc;
struct string_list complete_reflogs;
struct commit_reflog *last_commit_reflog;
};
@@ -233,52 +195,10 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
commit_reflog->selector = selector;
commit_reflog->reflogs = reflogs;
- add_commit_info(commit, commit_reflog, &info->reflogs);
- return 0;
-}
+ ALLOC_GROW(info->logs, info->nr + 1, info->alloc);
+ info->logs[info->nr++] = commit_reflog;
-void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
-{
- struct commit_info *commit_info =
- get_commit_info(commit, &info->reflogs, 0);
- struct commit_reflog *commit_reflog;
- struct object *logobj;
- struct reflog_info *reflog;
-
- info->last_commit_reflog = NULL;
- if (!commit_info)
- return;
-
- commit_reflog = commit_info->util;
- if (commit_reflog->recno < 0) {
- commit->parents = NULL;
- return;
- }
- info->last_commit_reflog = commit_reflog;
-
- do {
- reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
- commit_reflog->recno--;
- logobj = parse_object(&reflog->ooid);
- } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
-
- if (!logobj && commit_reflog->recno >= 0 && is_null_oid(&reflog->ooid)) {
- /* a root commit, but there are still more entries to show */
- reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
- logobj = parse_object(&reflog->noid);
- if (!logobj)
- logobj = parse_object(&reflog->ooid);
- }
-
- if (!logobj || logobj->type != OBJ_COMMIT) {
- commit_info->commit = NULL;
- commit->parents = NULL;
- return;
- }
- commit_info->commit = (struct commit *)logobj;
-
- commit->parents = xcalloc(1, sizeof(struct commit_list));
- commit->parents->item = commit_info->commit;
+ return 0;
}
void get_reflog_selector(struct strbuf *sb,
@@ -344,6 +264,18 @@ const char *get_reflog_ident(struct reflog_walk_info *reflog_info)
return info->email;
}
+timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info)
+{
+ struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
+ struct reflog_info *info;
+
+ if (!commit_reflog)
+ return 0;
+
+ info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
+ return info->timestamp;
+}
+
void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
const struct date_mode *dmode, int force_date)
{
@@ -365,3 +297,53 @@ void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
strbuf_release(&selector);
}
}
+
+int reflog_walk_empty(struct reflog_walk_info *info)
+{
+ return !info || !info->nr;
+}
+
+static struct commit *next_reflog_commit(struct commit_reflog *log)
+{
+ for (; log->recno >= 0; log->recno--) {
+ struct reflog_info *entry = &log->reflogs->items[log->recno];
+ struct object *obj = parse_object(&entry->noid);
+
+ if (obj && obj->type == OBJ_COMMIT)
+ return (struct commit *)obj;
+ }
+ return NULL;
+}
+
+static timestamp_t log_timestamp(struct commit_reflog *log)
+{
+ return log->reflogs->items[log->recno].timestamp;
+}
+
+struct commit *next_reflog_entry(struct reflog_walk_info *walk)
+{
+ struct commit_reflog *best = NULL;
+ struct commit *best_commit = NULL;
+ size_t i;
+
+ for (i = 0; i < walk->nr; i++) {
+ struct commit_reflog *log = walk->logs[i];
+ struct commit *commit = next_reflog_commit(log);
+
+ if (!commit)
+ continue;
+
+ if (!best || log_timestamp(log) > log_timestamp(best)) {
+ best = log;
+ best_commit = commit;
+ }
+ }
+
+ if (best) {
+ best->recno--;
+ walk->last_commit_reflog = best;
+ return best_commit;
+ }
+
+ return NULL;
+}
diff --git a/reflog-walk.h b/reflog-walk.h
index 27886f793e..7553c448fe 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -8,16 +8,19 @@ struct reflog_walk_info;
extern void init_reflog_walk(struct reflog_walk_info **info);
extern int add_reflog_for_walk(struct reflog_walk_info *info,
struct commit *commit, const char *name);
-extern void fake_reflog_parent(struct reflog_walk_info *info,
- struct commit *commit);
extern void show_reflog_message(struct reflog_walk_info *info, int,
const struct date_mode *, int force_date);
extern void get_reflog_message(struct strbuf *sb,
struct reflog_walk_info *reflog_info);
extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
+extern timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info);
extern void get_reflog_selector(struct strbuf *sb,
struct reflog_walk_info *reflog_info,
const struct date_mode *dmode, int force_date,
int shorten);
+extern int reflog_walk_empty(struct reflog_walk_info *walk);
+
+struct commit *next_reflog_entry(struct reflog_walk_info *reflog_info);
+
#endif
diff --git a/refs.c b/refs.c
index ba22f4acef..b0106b8162 100644
--- a/refs.c
+++ b/refs.c
@@ -174,6 +174,24 @@ int refname_is_safe(const char *refname)
return 1;
}
+/*
+ * Return true if refname, which has the specified oid and flags, can
+ * be resolved to an object in the database. If the referred-to object
+ * does not exist, emit a warning and return false.
+ */
+int ref_resolves_to_object(const char *refname,
+ const struct object_id *oid,
+ unsigned int flags)
+{
+ if (flags & REF_ISBROKEN)
+ return 0;
+ if (!has_sha1_file(oid->hash)) {
+ error("%s does not point to a valid object!", refname);
+ return 0;
+ }
+ return 1;
+}
+
char *refs_resolve_refdup(struct ref_store *refs,
const char *refname, int resolve_flags,
unsigned char *sha1, int *flags)
@@ -561,6 +579,21 @@ enum ref_type ref_type(const char *refname)
return REF_TYPE_NORMAL;
}
+long get_files_ref_lock_timeout_ms(void)
+{
+ static int configured = 0;
+
+ /* The default timeout is 100 ms: */
+ static int timeout_ms = 100;
+
+ if (!configured) {
+ git_config_get_int("core.filesreflocktimeout", &timeout_ms);
+ configured = 1;
+ }
+
+ return timeout_ms;
+}
+
static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
const unsigned char *old_sha1, struct strbuf *err)
{
@@ -573,7 +606,9 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
filename = git_path("%s", pseudoref);
- fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+ fd = hold_lock_file_for_update_timeout(&lock, filename,
+ LOCK_DIE_ON_ERROR,
+ get_files_ref_lock_timeout_ms());
if (fd < 0) {
strbuf_addf(err, "could not open '%s' for writing: %s",
filename, strerror(errno));
@@ -616,8 +651,9 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
int fd;
unsigned char actual_old_sha1[20];
- fd = hold_lock_file_for_update(&lock, filename,
- LOCK_DIE_ON_ERROR);
+ fd = hold_lock_file_for_update_timeout(
+ &lock, filename, LOCK_DIE_ON_ERROR,
+ get_files_ref_lock_timeout_ms());
if (fd < 0)
die_errno(_("Could not open '%s' for writing"), filename);
if (read_ref(pseudoref, actual_old_sha1))
@@ -818,7 +854,7 @@ int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, in
for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
if (!cb.reccnt) {
- if (flags & GET_SHA1_QUIETLY)
+ if (flags & GET_OID_QUIETLY)
exit(128);
else
die("Log for %s is empty.", refname);
@@ -1160,7 +1196,7 @@ int ref_is_hidden(const char *refname, const char *refname_full)
const char *match = hide_refs->items[i].string;
const char *subject;
int neg = 0;
- int len;
+ const char *p;
if (*match == '!') {
neg = 1;
@@ -1175,10 +1211,9 @@ int ref_is_hidden(const char *refname, const char *refname_full)
}
/* refname can be NULL when namespaces are used. */
- if (!subject || !starts_with(subject, match))
- continue;
- len = strlen(match);
- if (!subject[len] || subject[len] == '/')
+ if (subject &&
+ skip_prefix(subject, match, &p) &&
+ (!*p || *p == '/'))
return !neg;
}
return 0;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 0404f2c233..fccbc24ac4 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3,6 +3,7 @@
#include "../refs.h"
#include "refs-internal.h"
#include "ref-cache.h"
+#include "packed-backend.h"
#include "../iterator.h"
#include "../dir-iterator.h"
#include "../lockfile.h"
@@ -16,39 +17,6 @@ struct ref_lock {
};
/*
- * Return true if refname, which has the specified oid and flags, can
- * be resolved to an object in the database. If the referred-to object
- * does not exist, emit a warning and return false.
- */
-static int ref_resolves_to_object(const char *refname,
- const struct object_id *oid,
- unsigned int flags)
-{
- if (flags & REF_ISBROKEN)
- return 0;
- if (!has_sha1_file(oid->hash)) {
- error("%s does not point to a valid object!", refname);
- return 0;
- }
- return 1;
-}
-
-struct packed_ref_cache {
- struct ref_cache *cache;
-
- /*
- * Count of references to the data structure in this instance,
- * including the pointer from files_ref_store::packed if any.
- * The data will not be freed as long as the reference count
- * is nonzero.
- */
- unsigned int referrers;
-
- /* The metadata from when this packed-refs cache was read */
- struct stat_validity validity;
-};
-
-/*
* Future: need to be in "struct repository"
* when doing a full libification.
*/
@@ -58,54 +26,12 @@ struct files_ref_store {
char *gitdir;
char *gitcommondir;
- char *packed_refs_path;
struct ref_cache *loose;
- struct packed_ref_cache *packed;
- /*
- * Lock used for the "packed-refs" file. Note that this (and
- * thus the enclosing `files_ref_store`) must not be freed.
- */
- struct lock_file packed_refs_lock;
+ struct ref_store *packed_ref_store;
};
-/*
- * Increment the reference count of *packed_refs.
- */
-static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
-{
- packed_refs->referrers++;
-}
-
-/*
- * Decrease the reference count of *packed_refs. If it goes to zero,
- * free *packed_refs and return true; otherwise return false.
- */
-static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
-{
- if (!--packed_refs->referrers) {
- free_ref_cache(packed_refs->cache);
- stat_validity_clear(&packed_refs->validity);
- free(packed_refs);
- return 1;
- } else {
- return 0;
- }
-}
-
-static void clear_packed_ref_cache(struct files_ref_store *refs)
-{
- if (refs->packed) {
- struct packed_ref_cache *packed_refs = refs->packed;
-
- if (is_lock_file_locked(&refs->packed_refs_lock))
- die("BUG: packed-ref cache cleared while locked");
- refs->packed = NULL;
- release_packed_ref_cache(packed_refs);
- }
-}
-
static void clear_loose_ref_cache(struct files_ref_store *refs)
{
if (refs->loose) {
@@ -132,7 +58,8 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
get_common_dir_noenv(&sb, gitdir);
refs->gitcommondir = strbuf_detach(&sb, NULL);
strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
- refs->packed_refs_path = strbuf_detach(&sb, NULL);
+ refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
+ strbuf_release(&sb);
return ref_store;
}
@@ -175,156 +102,6 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
return refs;
}
-/* The length of a peeled reference line in packed-refs, including EOL: */
-#define PEELED_LINE_LENGTH 42
-
-/*
- * The packed-refs header line that we write out. Perhaps other
- * traits will be added later. The trailing space is required.
- */
-static const char PACKED_REFS_HEADER[] =
- "# pack-refs with: peeled fully-peeled \n";
-
-/*
- * Parse one line from a packed-refs file. Write the SHA1 to sha1.
- * Return a pointer to the refname within the line (null-terminated),
- * or NULL if there was a problem.
- */
-static const char *parse_ref_line(struct strbuf *line, struct object_id *oid)
-{
- const char *ref;
-
- if (parse_oid_hex(line->buf, oid, &ref) < 0)
- return NULL;
- if (!isspace(*ref++))
- return NULL;
-
- if (isspace(*ref))
- return NULL;
-
- if (line->buf[line->len - 1] != '\n')
- return NULL;
- line->buf[--line->len] = 0;
-
- return ref;
-}
-
-/*
- * Read from `packed_refs_file` into a newly-allocated
- * `packed_ref_cache` and return it. The return value will already
- * have its reference count incremented.
- *
- * A comment line of the form "# pack-refs with: " may contain zero or
- * more traits. We interpret the traits as follows:
- *
- * No traits:
- *
- * Probably no references are peeled. But if the file contains a
- * peeled value for a reference, we will use it.
- *
- * peeled:
- *
- * References under "refs/tags/", if they *can* be peeled, *are*
- * peeled in this file. References outside of "refs/tags/" are
- * probably not peeled even if they could have been, but if we find
- * a peeled value for such a reference we will use it.
- *
- * fully-peeled:
- *
- * All references in the file that can be peeled are peeled.
- * Inversely (and this is more important), any references in the
- * file for which no peeled value is recorded is not peelable. This
- * trait should typically be written alongside "peeled" for
- * compatibility with older clients, but we do not require it
- * (i.e., "peeled" is a no-op if "fully-peeled" is set).
- */
-static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
-{
- FILE *f;
- struct packed_ref_cache *packed_refs = xcalloc(1, sizeof(*packed_refs));
- struct ref_entry *last = NULL;
- struct strbuf line = STRBUF_INIT;
- enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
- struct ref_dir *dir;
-
- acquire_packed_ref_cache(packed_refs);
- packed_refs->cache = create_ref_cache(NULL, NULL);
- packed_refs->cache->root->flag &= ~REF_INCOMPLETE;
-
- f = fopen(packed_refs_file, "r");
- if (!f) {
- if (errno == ENOENT) {
- /*
- * This is OK; it just means that no
- * "packed-refs" file has been written yet,
- * which is equivalent to it being empty.
- */
- return packed_refs;
- } else {
- die_errno("couldn't read %s", packed_refs_file);
- }
- }
-
- stat_validity_update(&packed_refs->validity, fileno(f));
-
- dir = get_ref_dir(packed_refs->cache->root);
- while (strbuf_getwholeline(&line, f, '\n') != EOF) {
- struct object_id oid;
- const char *refname;
- const char *traits;
-
- if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
- if (strstr(traits, " fully-peeled "))
- peeled = PEELED_FULLY;
- else if (strstr(traits, " peeled "))
- peeled = PEELED_TAGS;
- /* perhaps other traits later as well */
- continue;
- }
-
- refname = parse_ref_line(&line, &oid);
- if (refname) {
- int flag = REF_ISPACKED;
-
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- if (!refname_is_safe(refname))
- die("packed refname is dangerous: %s", refname);
- oidclr(&oid);
- flag |= REF_BAD_NAME | REF_ISBROKEN;
- }
- last = create_ref_entry(refname, &oid, flag);
- if (peeled == PEELED_FULLY ||
- (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
- last->flag |= REF_KNOWS_PEELED;
- add_ref_entry(dir, last);
- continue;
- }
- if (last &&
- line.buf[0] == '^' &&
- line.len == PEELED_LINE_LENGTH &&
- line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
- !get_oid_hex(line.buf + 1, &oid)) {
- oidcpy(&last->u.value.peeled, &oid);
- /*
- * Regardless of what the file header said,
- * we definitely know the value of *this*
- * reference:
- */
- last->flag |= REF_KNOWS_PEELED;
- }
- }
-
- fclose(f);
- strbuf_release(&line);
-
- return packed_refs;
-}
-
-static const char *files_packed_refs_path(struct files_ref_store *refs)
-{
- return refs->packed_refs_path;
-}
-
static void files_reflog_path(struct files_ref_store *refs,
struct strbuf *sb,
const char *refname)
@@ -371,70 +148,6 @@ static void files_ref_path(struct files_ref_store *refs,
}
/*
- * Check that the packed refs cache (if any) still reflects the
- * contents of the file. If not, clear the cache.
- */
-static void validate_packed_ref_cache(struct files_ref_store *refs)
-{
- if (refs->packed &&
- !stat_validity_check(&refs->packed->validity,
- files_packed_refs_path(refs)))
- clear_packed_ref_cache(refs);
-}
-
-/*
- * Get the packed_ref_cache for the specified files_ref_store,
- * creating and populating it if it hasn't been read before or if the
- * file has been changed (according to its `validity` field) since it
- * was last read. On the other hand, if we hold the lock, then assume
- * that the file hasn't been changed out from under us, so skip the
- * extra `stat()` call in `stat_validity_check()`.
- */
-static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)
-{
- const char *packed_refs_file = files_packed_refs_path(refs);
-
- if (!is_lock_file_locked(&refs->packed_refs_lock))
- validate_packed_ref_cache(refs);
-
- if (!refs->packed)
- refs->packed = read_packed_refs(packed_refs_file);
-
- return refs->packed;
-}
-
-static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
-{
- return get_ref_dir(packed_ref_cache->cache->root);
-}
-
-static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
-{
- return get_packed_ref_dir(get_packed_ref_cache(refs));
-}
-
-/*
- * Add a reference to the in-memory packed reference cache. This may
- * only be called while the packed-refs file is locked (see
- * lock_packed_refs()). To actually write the packed-refs file, call
- * commit_packed_refs().
- */
-static void add_packed_ref(struct files_ref_store *refs,
- const char *refname, const struct object_id *oid)
-{
- struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs);
-
- if (!is_lock_file_locked(&refs->packed_refs_lock))
- die("BUG: packed refs not locked");
-
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
- die("Reference has invalid format: '%s'", refname);
-
- add_ref_entry(get_packed_ref_dir(packed_ref_cache),
- create_ref_entry(refname, oid, REF_ISPACKED));
-}
-
-/*
* Read the loose references from the namespace dirname into dir
* (without recursing). dirname must end with '/'. dir must be the
* directory entry corresponding to dirname.
@@ -556,39 +269,6 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
return refs->loose;
}
-/*
- * Return the ref_entry for the given refname from the packed
- * references. If it does not exist, return NULL.
- */
-static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
- const char *refname)
-{
- return find_ref_entry(get_packed_refs(refs), refname);
-}
-
-/*
- * A loose ref file doesn't exist; check for a packed ref.
- */
-static int resolve_packed_ref(struct files_ref_store *refs,
- const char *refname,
- unsigned char *sha1, unsigned int *flags)
-{
- struct ref_entry *entry;
-
- /*
- * The loose reference file does not exist; check for a packed
- * reference.
- */
- entry = get_packed_ref(refs, refname);
- if (entry) {
- hashcpy(sha1, entry->u.value.oid.hash);
- *flags |= REF_ISPACKED;
- return 0;
- }
- /* refname is not a packed reference. */
- return -1;
-}
-
static int files_read_raw_ref(struct ref_store *ref_store,
const char *refname, unsigned char *sha1,
struct strbuf *referent, unsigned int *type)
@@ -632,7 +312,8 @@ stat_ref:
if (lstat(path, &st) < 0) {
if (errno != ENOENT)
goto out;
- if (resolve_packed_ref(refs, refname, sha1, type)) {
+ if (refs_read_raw_ref(refs->packed_ref_store, refname,
+ sha1, referent, type)) {
errno = ENOENT;
goto out;
}
@@ -671,7 +352,8 @@ stat_ref:
* ref is supposed to be, there could still be a
* packed ref:
*/
- if (resolve_packed_ref(refs, refname, sha1, type)) {
+ if (refs_read_raw_ref(refs->packed_ref_store, refname,
+ sha1, referent, type)) {
errno = EISDIR;
goto out;
}
@@ -855,7 +537,9 @@ retry:
if (!lock->lk)
lock->lk = xcalloc(1, sizeof(struct lock_file));
- if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 0) {
+ if (hold_lock_file_for_update_timeout(
+ lock->lk, ref_file.buf, LOCK_NO_DEREF,
+ get_files_ref_lock_timeout_ms()) < 0) {
if (errno == ENOENT && --attempts_remaining > 0) {
/*
* Maybe somebody just deleted one of the
@@ -950,11 +634,11 @@ retry:
/*
* If the ref did not exist and we are creating it,
- * make sure there is no existing ref that conflicts
- * with refname:
+ * make sure there is no existing packed ref that
+ * conflicts with refname:
*/
if (refs_verify_refname_available(
- &refs->base, refname,
+ refs->packed_ref_store, refname,
extras, skip, err))
goto error_return;
}
@@ -1001,15 +685,9 @@ static int files_peel_ref(struct ref_store *ref_store,
* be expensive and (b) loose references anyway usually do not
* have REF_KNOWS_PEELED.
*/
- if (flag & REF_ISPACKED) {
- struct ref_entry *r = get_packed_ref(refs, refname);
- if (r) {
- if (peel_entry(r, 0))
- return -1;
- hashcpy(sha1, r->u.value.peeled.hash);
- return 0;
- }
- }
+ if (flag & REF_ISPACKED &&
+ !refs_peel_ref(refs->packed_ref_store, refname, sha1))
+ return 0;
return peel_object(base, sha1);
}
@@ -1017,7 +695,6 @@ static int files_peel_ref(struct ref_store *ref_store,
struct files_ref_iterator {
struct ref_iterator base;
- struct packed_ref_cache *packed_ref_cache;
struct ref_iterator *iter0;
unsigned int flags;
};
@@ -1070,7 +747,6 @@ static int files_ref_iterator_abort(struct ref_iterator *ref_iterator)
if (iter->iter0)
ok = ref_iterator_abort(iter->iter0);
- release_packed_ref_cache(iter->packed_ref_cache);
base_ref_iterator_free(ref_iterator);
return ok;
}
@@ -1112,18 +788,28 @@ static struct ref_iterator *files_ref_iterator_begin(
* (If they've already been read, that's OK; we only need to
* guarantee that they're read before the packed refs, not
* *how much* before.) After that, we call
- * get_packed_ref_cache(), which internally checks whether the
- * packed-ref cache is up to date with what is on disk, and
- * re-reads it if not.
+ * packed_ref_iterator_begin(), which internally checks
+ * whether the packed-ref cache is up to date with what is on
+ * disk, and re-reads it if not.
*/
loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
prefix, 1);
- iter->packed_ref_cache = get_packed_ref_cache(refs);
- acquire_packed_ref_cache(iter->packed_ref_cache);
- packed_iter = cache_ref_iterator_begin(iter->packed_ref_cache->cache,
- prefix, 0);
+ /*
+ * The packed-refs file might contain broken references, for
+ * example an old version of a reference that points at an
+ * object that has since been garbage-collected. This is OK as
+ * long as there is a corresponding loose reference that
+ * overrides it, and we don't want to emit an error message in
+ * this case. So ask the packed_ref_store for all of its
+ * references, and (if needed) do our own check for broken
+ * ones in files_ref_iterator_advance(), after we have merged
+ * the packed and loose references.
+ */
+ packed_iter = refs_ref_iterator_begin(
+ refs->packed_ref_store, prefix, 0,
+ DO_FOR_EACH_INCLUDE_BROKEN);
iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter);
iter->flags = flags;
@@ -1181,7 +867,9 @@ static int create_reflock(const char *path, void *cb)
{
struct lock_file *lk = cb;
- return hold_lock_file_for_update(lk, path, LOCK_NO_DEREF) < 0 ? -1 : 0;
+ return hold_lock_file_for_update_timeout(
+ lk, path, LOCK_NO_DEREF,
+ get_files_ref_lock_timeout_ms()) < 0 ? -1 : 0;
}
/*
@@ -1255,7 +943,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
* our refname.
*/
if (is_null_oid(&lock->old_oid) &&
- refs_verify_refname_available(&refs->base, refname,
+ refs_verify_refname_available(refs->packed_ref_store, refname,
extras, skip, err)) {
last_errno = ENOTDIR;
goto error_return;
@@ -1287,124 +975,6 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
return lock;
}
-/*
- * Write an entry to the packed-refs file for the specified refname.
- * If peeled is non-NULL, write it as the entry's peeled value.
- */
-static void write_packed_entry(FILE *fh, const char *refname,
- const unsigned char *sha1,
- const unsigned char *peeled)
-{
- fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname);
- if (peeled)
- fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled));
-}
-
-/*
- * Lock the packed-refs file for writing. Flags is passed to
- * hold_lock_file_for_update(). Return 0 on success. On errors, set
- * errno appropriately and return a nonzero value.
- */
-static int lock_packed_refs(struct files_ref_store *refs, int flags)
-{
- static int timeout_configured = 0;
- static int timeout_value = 1000;
- struct packed_ref_cache *packed_ref_cache;
-
- files_assert_main_repository(refs, "lock_packed_refs");
-
- if (!timeout_configured) {
- git_config_get_int("core.packedrefstimeout", &timeout_value);
- timeout_configured = 1;
- }
-
- if (hold_lock_file_for_update_timeout(
- &refs->packed_refs_lock, files_packed_refs_path(refs),
- flags, timeout_value) < 0)
- return -1;
-
- /*
- * Now that we hold the `packed-refs` lock, make sure that our
- * cache matches the current version of the file. Normally
- * `get_packed_ref_cache()` does that for us, but that
- * function assumes that when the file is locked, any existing
- * cache is still valid. We've just locked the file, but it
- * might have changed the moment *before* we locked it.
- */
- validate_packed_ref_cache(refs);
-
- packed_ref_cache = get_packed_ref_cache(refs);
- /* Increment the reference count to prevent it from being freed: */
- acquire_packed_ref_cache(packed_ref_cache);
- return 0;
-}
-
-/*
- * Write the current version of the packed refs cache from memory to
- * disk. The packed-refs file must already be locked for writing (see
- * lock_packed_refs()). Return zero on success. On errors, set errno
- * and return a nonzero value
- */
-static int commit_packed_refs(struct files_ref_store *refs)
-{
- struct packed_ref_cache *packed_ref_cache =
- get_packed_ref_cache(refs);
- int ok, error = 0;
- int save_errno = 0;
- FILE *out;
- struct ref_iterator *iter;
-
- files_assert_main_repository(refs, "commit_packed_refs");
-
- if (!is_lock_file_locked(&refs->packed_refs_lock))
- die("BUG: packed-refs not locked");
-
- out = fdopen_lock_file(&refs->packed_refs_lock, "w");
- if (!out)
- die_errno("unable to fdopen packed-refs descriptor");
-
- fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
-
- iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0);
- while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- struct object_id peeled;
- int peel_error = ref_iterator_peel(iter, &peeled);
-
- write_packed_entry(out, iter->refname, iter->oid->hash,
- peel_error ? NULL : peeled.hash);
- }
-
- if (ok != ITER_DONE)
- die("error while iterating over references");
-
- if (commit_lock_file(&refs->packed_refs_lock)) {
- save_errno = errno;
- error = -1;
- }
- release_packed_ref_cache(packed_ref_cache);
- errno = save_errno;
- return error;
-}
-
-/*
- * Rollback the lockfile for the packed-refs file, and discard the
- * in-memory packed reference cache. (The packed-refs file will be
- * read anew if it is needed again after this function is called.)
- */
-static void rollback_packed_refs(struct files_ref_store *refs)
-{
- struct packed_ref_cache *packed_ref_cache =
- get_packed_ref_cache(refs);
-
- files_assert_main_repository(refs, "rollback_packed_refs");
-
- if (!is_lock_file_locked(&refs->packed_refs_lock))
- die("BUG: packed-refs not locked");
- rollback_lock_file(&refs->packed_refs_lock);
- release_packed_ref_cache(packed_ref_cache);
- clear_packed_ref_cache(refs);
-}
-
struct ref_to_prune {
struct ref_to_prune *next;
unsigned char sha1[20];
@@ -1527,12 +1097,11 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
"pack_refs");
struct ref_iterator *iter;
- struct ref_dir *packed_refs;
int ok;
struct ref_to_prune *refs_to_prune = NULL;
+ struct strbuf err = STRBUF_INIT;
- lock_packed_refs(refs, LOCK_DIE_ON_ERROR);
- packed_refs = get_packed_refs(refs);
+ packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
@@ -1541,8 +1110,6 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
- struct ref_entry *packed_entry;
-
if (!should_pack_ref(iter->refname, iter->oid, iter->flags,
flags))
continue;
@@ -1553,17 +1120,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
* we don't copy the peeled status, because we want it
* to be re-peeled.
*/
- packed_entry = find_ref_entry(packed_refs, iter->refname);
- if (packed_entry) {
- /* Overwrite existing packed entry with info from loose entry */
- packed_entry->flag = REF_ISPACKED;
- oidcpy(&packed_entry->u.value.oid, iter->oid);
- } else {
- packed_entry = create_ref_entry(iter->refname, iter->oid,
- REF_ISPACKED);
- add_ref_entry(packed_refs, packed_entry);
- }
- oidclr(&packed_entry->u.value.peeled);
+ add_packed_ref(refs->packed_ref_store, iter->refname, iter->oid);
/* Schedule the loose reference for pruning if requested. */
if ((flags & PACK_REFS_PRUNE)) {
@@ -1577,69 +1134,15 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
if (ok != ITER_DONE)
die("error while iterating over references");
- if (commit_packed_refs(refs))
- die_errno("unable to overwrite old ref-pack file");
+ if (commit_packed_refs(refs->packed_ref_store, &err))
+ die("unable to overwrite old ref-pack file: %s", err.buf);
+ packed_refs_unlock(refs->packed_ref_store);
prune_refs(refs, refs_to_prune);
+ strbuf_release(&err);
return 0;
}
-/*
- * Rewrite the packed-refs file, omitting any refs listed in
- * 'refnames'. On error, leave packed-refs unchanged, write an error
- * message to 'err', and return a nonzero value.
- *
- * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
- */
-static int repack_without_refs(struct files_ref_store *refs,
- struct string_list *refnames, struct strbuf *err)
-{
- struct ref_dir *packed;
- struct string_list_item *refname;
- int ret, needs_repacking = 0, removed = 0;
-
- files_assert_main_repository(refs, "repack_without_refs");
- assert(err);
-
- /* Look for a packed ref */
- for_each_string_list_item(refname, refnames) {
- if (get_packed_ref(refs, refname->string)) {
- needs_repacking = 1;
- break;
- }
- }
-
- /* Avoid locking if we have nothing to do */
- if (!needs_repacking)
- return 0; /* no refname exists in packed refs */
-
- if (lock_packed_refs(refs, 0)) {
- unable_to_lock_message(files_packed_refs_path(refs), errno, err);
- return -1;
- }
- packed = get_packed_refs(refs);
-
- /* Remove refnames from the cache */
- for_each_string_list_item(refname, refnames)
- if (remove_entry_from_dir(packed, refname->string) != -1)
- removed = 1;
- if (!removed) {
- /*
- * All packed entries disappeared while we were
- * acquiring the lock.
- */
- rollback_packed_refs(refs);
- return 0;
- }
-
- /* Write what remains */
- ret = commit_packed_refs(refs);
- if (ret)
- strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
- strerror(errno));
- return ret;
-}
-
static int files_delete_refs(struct ref_store *ref_store, const char *msg,
struct string_list *refnames, unsigned int flags)
{
@@ -1651,24 +1154,16 @@ static int files_delete_refs(struct ref_store *ref_store, const char *msg,
if (!refnames->nr)
return 0;
- result = repack_without_refs(refs, refnames, &err);
- if (result) {
- /*
- * If we failed to rewrite the packed-refs file, then
- * it is unsafe to try to remove loose refs, because
- * doing so might expose an obsolete packed value for
- * a reference that might even point at an object that
- * has been garbage collected.
- */
- if (refnames->nr == 1)
- error(_("could not delete reference %s: %s"),
- refnames->items[0].string, err.buf);
- else
- error(_("could not delete references: %s"), err.buf);
+ if (packed_refs_lock(refs->packed_ref_store, 0, &err))
+ goto error;
- goto out;
+ if (repack_without_refs(refs->packed_ref_store, refnames, &err)) {
+ packed_refs_unlock(refs->packed_ref_store);
+ goto error;
}
+ packed_refs_unlock(refs->packed_ref_store);
+
for (i = 0; i < refnames->nr; i++) {
const char *refname = refnames->items[i].string;
@@ -1676,9 +1171,24 @@ static int files_delete_refs(struct ref_store *ref_store, const char *msg,
result |= error(_("could not remove reference %s"), refname);
}
-out:
strbuf_release(&err);
return result;
+
+error:
+ /*
+ * If we failed to rewrite the packed-refs file, then it is
+ * unsafe to try to remove loose refs, because doing so might
+ * expose an obsolete packed value for a reference that might
+ * even point at an object that has been garbage collected.
+ */
+ if (refnames->nr == 1)
+ error(_("could not delete reference %s: %s"),
+ refnames->items[0].string, err.buf);
+ else
+ error(_("could not delete references: %s"), err.buf);
+
+ strbuf_release(&err);
+ return -1;
}
/*
@@ -3070,11 +2580,19 @@ static int files_transaction_finish(struct ref_store *ref_store,
}
}
- if (repack_without_refs(refs, &refs_to_delete, err)) {
+ if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
+ if (repack_without_refs(refs->packed_ref_store, &refs_to_delete, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ packed_refs_unlock(refs->packed_ref_store);
+ goto cleanup;
+ }
+
+ packed_refs_unlock(refs->packed_ref_store);
+
/* Delete the reflogs of any references that were deleted: */
for_each_string_list_item(ref_to_delete, &refs_to_delete) {
strbuf_reset(&sb);
@@ -3181,9 +2699,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
}
}
- if (lock_packed_refs(refs, 0)) {
- strbuf_addf(err, "unable to lock packed-refs file: %s",
- strerror(errno));
+ if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
@@ -3193,18 +2709,17 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
if ((update->flags & REF_HAVE_NEW) &&
!is_null_oid(&update->new_oid))
- add_packed_ref(refs, update->refname,
+ add_packed_ref(refs->packed_ref_store, update->refname,
&update->new_oid);
}
- if (commit_packed_refs(refs)) {
- strbuf_addf(err, "unable to commit packed-refs file: %s",
- strerror(errno));
+ if (commit_packed_refs(refs->packed_ref_store, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
cleanup:
+ packed_refs_unlock(refs->packed_ref_store);
transaction->state = REF_TRANSACTION_CLOSED;
string_list_clear(&affected_refnames, 0);
return ret;
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
new file mode 100644
index 0000000000..412c85034f
--- /dev/null
+++ b/refs/packed-backend.c
@@ -0,0 +1,885 @@
+#include "../cache.h"
+#include "../config.h"
+#include "../refs.h"
+#include "refs-internal.h"
+#include "ref-cache.h"
+#include "packed-backend.h"
+#include "../iterator.h"
+#include "../lockfile.h"
+
+struct packed_ref_cache {
+ struct ref_cache *cache;
+
+ /*
+ * Count of references to the data structure in this instance,
+ * including the pointer from files_ref_store::packed if any.
+ * The data will not be freed as long as the reference count
+ * is nonzero.
+ */
+ unsigned int referrers;
+
+ /* The metadata from when this packed-refs cache was read */
+ struct stat_validity validity;
+};
+
+/*
+ * Increment the reference count of *packed_refs.
+ */
+static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+ packed_refs->referrers++;
+}
+
+/*
+ * Decrease the reference count of *packed_refs. If it goes to zero,
+ * free *packed_refs and return true; otherwise return false.
+ */
+static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+ if (!--packed_refs->referrers) {
+ free_ref_cache(packed_refs->cache);
+ stat_validity_clear(&packed_refs->validity);
+ free(packed_refs);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * A container for `packed-refs`-related data. It is not (yet) a
+ * `ref_store`.
+ */
+struct packed_ref_store {
+ struct ref_store base;
+
+ unsigned int store_flags;
+
+ /* The path of the "packed-refs" file: */
+ char *path;
+
+ /*
+ * A cache of the values read from the `packed-refs` file, if
+ * it might still be current; otherwise, NULL.
+ */
+ struct packed_ref_cache *cache;
+
+ /*
+ * Lock used for the "packed-refs" file. Note that this (and
+ * thus the enclosing `packed_ref_store`) must not be freed.
+ */
+ struct lock_file lock;
+
+ /*
+ * Temporary file used when rewriting new contents to the
+ * "packed-refs" file. Note that this (and thus the enclosing
+ * `packed_ref_store`) must not be freed.
+ */
+ struct tempfile tempfile;
+};
+
+struct ref_store *packed_ref_store_create(const char *path,
+ unsigned int store_flags)
+{
+ struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
+ struct ref_store *ref_store = (struct ref_store *)refs;
+
+ base_ref_store_init(ref_store, &refs_be_packed);
+ refs->store_flags = store_flags;
+
+ refs->path = xstrdup(path);
+ return ref_store;
+}
+
+/*
+ * Die if refs is not the main ref store. caller is used in any
+ * necessary error messages.
+ */
+static void packed_assert_main_repository(struct packed_ref_store *refs,
+ const char *caller)
+{
+ if (refs->store_flags & REF_STORE_MAIN)
+ return;
+
+ die("BUG: operation %s only allowed for main ref store", caller);
+}
+
+/*
+ * Downcast `ref_store` to `packed_ref_store`. Die if `ref_store` is
+ * not a `packed_ref_store`. Also die if `packed_ref_store` doesn't
+ * support at least the flags specified in `required_flags`. `caller`
+ * is used in any necessary error messages.
+ */
+static struct packed_ref_store *packed_downcast(struct ref_store *ref_store,
+ unsigned int required_flags,
+ const char *caller)
+{
+ struct packed_ref_store *refs;
+
+ if (ref_store->be != &refs_be_packed)
+ die("BUG: ref_store is type \"%s\" not \"packed\" in %s",
+ ref_store->be->name, caller);
+
+ refs = (struct packed_ref_store *)ref_store;
+
+ if ((refs->store_flags & required_flags) != required_flags)
+ die("BUG: unallowed operation (%s), requires %x, has %x\n",
+ caller, required_flags, refs->store_flags);
+
+ return refs;
+}
+
+static void clear_packed_ref_cache(struct packed_ref_store *refs)
+{
+ if (refs->cache) {
+ struct packed_ref_cache *cache = refs->cache;
+
+ refs->cache = NULL;
+ release_packed_ref_cache(cache);
+ }
+}
+
+/* The length of a peeled reference line in packed-refs, including EOL: */
+#define PEELED_LINE_LENGTH 42
+
+/*
+ * Parse one line from a packed-refs file. Write the SHA1 to sha1.
+ * Return a pointer to the refname within the line (null-terminated),
+ * or NULL if there was a problem.
+ */
+static const char *parse_ref_line(struct strbuf *line, struct object_id *oid)
+{
+ const char *ref;
+
+ if (parse_oid_hex(line->buf, oid, &ref) < 0)
+ return NULL;
+ if (!isspace(*ref++))
+ return NULL;
+
+ if (isspace(*ref))
+ return NULL;
+
+ if (line->buf[line->len - 1] != '\n')
+ return NULL;
+ line->buf[--line->len] = 0;
+
+ return ref;
+}
+
+/*
+ * Read from `packed_refs_file` into a newly-allocated
+ * `packed_ref_cache` and return it. The return value will already
+ * have its reference count incremented.
+ *
+ * A comment line of the form "# pack-refs with: " may contain zero or
+ * more traits. We interpret the traits as follows:
+ *
+ * No traits:
+ *
+ * Probably no references are peeled. But if the file contains a
+ * peeled value for a reference, we will use it.
+ *
+ * peeled:
+ *
+ * References under "refs/tags/", if they *can* be peeled, *are*
+ * peeled in this file. References outside of "refs/tags/" are
+ * probably not peeled even if they could have been, but if we find
+ * a peeled value for such a reference we will use it.
+ *
+ * fully-peeled:
+ *
+ * All references in the file that can be peeled are peeled.
+ * Inversely (and this is more important), any references in the
+ * file for which no peeled value is recorded is not peelable. This
+ * trait should typically be written alongside "peeled" for
+ * compatibility with older clients, but we do not require it
+ * (i.e., "peeled" is a no-op if "fully-peeled" is set).
+ */
+static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
+{
+ FILE *f;
+ struct packed_ref_cache *packed_refs = xcalloc(1, sizeof(*packed_refs));
+ struct ref_entry *last = NULL;
+ struct strbuf line = STRBUF_INIT;
+ enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
+ struct ref_dir *dir;
+
+ acquire_packed_ref_cache(packed_refs);
+ packed_refs->cache = create_ref_cache(NULL, NULL);
+ packed_refs->cache->root->flag &= ~REF_INCOMPLETE;
+
+ f = fopen(packed_refs_file, "r");
+ if (!f) {
+ if (errno == ENOENT) {
+ /*
+ * This is OK; it just means that no
+ * "packed-refs" file has been written yet,
+ * which is equivalent to it being empty.
+ */
+ return packed_refs;
+ } else {
+ die_errno("couldn't read %s", packed_refs_file);
+ }
+ }
+
+ stat_validity_update(&packed_refs->validity, fileno(f));
+
+ dir = get_ref_dir(packed_refs->cache->root);
+ while (strbuf_getwholeline(&line, f, '\n') != EOF) {
+ struct object_id oid;
+ const char *refname;
+ const char *traits;
+
+ if (!line.len || line.buf[line.len - 1] != '\n')
+ die("unterminated line in %s: %s", packed_refs_file, line.buf);
+
+ if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
+ if (strstr(traits, " fully-peeled "))
+ peeled = PEELED_FULLY;
+ else if (strstr(traits, " peeled "))
+ peeled = PEELED_TAGS;
+ /* perhaps other traits later as well */
+ continue;
+ }
+
+ refname = parse_ref_line(&line, &oid);
+ if (refname) {
+ int flag = REF_ISPACKED;
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (!refname_is_safe(refname))
+ die("packed refname is dangerous: %s", refname);
+ oidclr(&oid);
+ flag |= REF_BAD_NAME | REF_ISBROKEN;
+ }
+ last = create_ref_entry(refname, &oid, flag);
+ if (peeled == PEELED_FULLY ||
+ (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
+ last->flag |= REF_KNOWS_PEELED;
+ add_ref_entry(dir, last);
+ } else if (last &&
+ line.buf[0] == '^' &&
+ line.len == PEELED_LINE_LENGTH &&
+ line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
+ !get_oid_hex(line.buf + 1, &oid)) {
+ oidcpy(&last->u.value.peeled, &oid);
+ /*
+ * Regardless of what the file header said,
+ * we definitely know the value of *this*
+ * reference:
+ */
+ last->flag |= REF_KNOWS_PEELED;
+ } else {
+ strbuf_setlen(&line, line.len - 1);
+ die("unexpected line in %s: %s", packed_refs_file, line.buf);
+ }
+ }
+
+ fclose(f);
+ strbuf_release(&line);
+
+ return packed_refs;
+}
+
+/*
+ * Check that the packed refs cache (if any) still reflects the
+ * contents of the file. If not, clear the cache.
+ */
+static void validate_packed_ref_cache(struct packed_ref_store *refs)
+{
+ if (refs->cache &&
+ !stat_validity_check(&refs->cache->validity, refs->path))
+ clear_packed_ref_cache(refs);
+}
+
+/*
+ * Get the packed_ref_cache for the specified packed_ref_store,
+ * creating and populating it if it hasn't been read before or if the
+ * file has been changed (according to its `validity` field) since it
+ * was last read. On the other hand, if we hold the lock, then assume
+ * that the file hasn't been changed out from under us, so skip the
+ * extra `stat()` call in `stat_validity_check()`.
+ */
+static struct packed_ref_cache *get_packed_ref_cache(struct packed_ref_store *refs)
+{
+ if (!is_lock_file_locked(&refs->lock))
+ validate_packed_ref_cache(refs);
+
+ if (!refs->cache)
+ refs->cache = read_packed_refs(refs->path);
+
+ return refs->cache;
+}
+
+static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
+{
+ return get_ref_dir(packed_ref_cache->cache->root);
+}
+
+static struct ref_dir *get_packed_refs(struct packed_ref_store *refs)
+{
+ return get_packed_ref_dir(get_packed_ref_cache(refs));
+}
+
+/*
+ * Add or overwrite a reference in the in-memory packed reference
+ * cache. This may only be called while the packed-refs file is locked
+ * (see packed_refs_lock()). To actually write the packed-refs file,
+ * call commit_packed_refs().
+ */
+void add_packed_ref(struct ref_store *ref_store,
+ const char *refname, const struct object_id *oid)
+{
+ struct packed_ref_store *refs =
+ packed_downcast(ref_store, REF_STORE_WRITE,
+ "add_packed_ref");
+ struct ref_dir *packed_refs;
+ struct ref_entry *packed_entry;
+
+ if (!is_lock_file_locked(&refs->lock))
+ die("BUG: packed refs not locked");
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
+ die("Reference has invalid format: '%s'", refname);
+
+ packed_refs = get_packed_refs(refs);
+ packed_entry = find_ref_entry(packed_refs, refname);
+ if (packed_entry) {
+ /* Overwrite the existing entry: */
+ oidcpy(&packed_entry->u.value.oid, oid);
+ packed_entry->flag = REF_ISPACKED;
+ oidclr(&packed_entry->u.value.peeled);
+ } else {
+ packed_entry = create_ref_entry(refname, oid, REF_ISPACKED);
+ add_ref_entry(packed_refs, packed_entry);
+ }
+}
+
+/*
+ * Return the ref_entry for the given refname from the packed
+ * references. If it does not exist, return NULL.
+ */
+static struct ref_entry *get_packed_ref(struct packed_ref_store *refs,
+ const char *refname)
+{
+ return find_ref_entry(get_packed_refs(refs), refname);
+}
+
+static int packed_read_raw_ref(struct ref_store *ref_store,
+ const char *refname, unsigned char *sha1,
+ struct strbuf *referent, unsigned int *type)
+{
+ struct packed_ref_store *refs =
+ packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
+
+ struct ref_entry *entry;
+
+ *type = 0;
+
+ entry = get_packed_ref(refs, refname);
+ if (!entry) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ hashcpy(sha1, entry->u.value.oid.hash);
+ *type = REF_ISPACKED;
+ return 0;
+}
+
+static int packed_peel_ref(struct ref_store *ref_store,
+ const char *refname, unsigned char *sha1)
+{
+ struct packed_ref_store *refs =
+ packed_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,
+ "peel_ref");
+ struct ref_entry *r = get_packed_ref(refs, refname);
+
+ if (!r || peel_entry(r, 0))
+ return -1;
+
+ hashcpy(sha1, r->u.value.peeled.hash);
+ return 0;
+}
+
+struct packed_ref_iterator {
+ struct ref_iterator base;
+
+ struct packed_ref_cache *cache;
+ struct ref_iterator *iter0;
+ unsigned int flags;
+};
+
+static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
+{
+ struct packed_ref_iterator *iter =
+ (struct packed_ref_iterator *)ref_iterator;
+ int ok;
+
+ while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
+ if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+ ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
+ continue;
+
+ if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+ !ref_resolves_to_object(iter->iter0->refname,
+ iter->iter0->oid,
+ iter->iter0->flags))
+ continue;
+
+ iter->base.refname = iter->iter0->refname;
+ iter->base.oid = iter->iter0->oid;
+ iter->base.flags = iter->iter0->flags;
+ return ITER_OK;
+ }
+
+ iter->iter0 = NULL;
+ if (ref_iterator_abort(ref_iterator) != ITER_DONE)
+ ok = ITER_ERROR;
+
+ return ok;
+}
+
+static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
+ struct object_id *peeled)
+{
+ struct packed_ref_iterator *iter =
+ (struct packed_ref_iterator *)ref_iterator;
+
+ return ref_iterator_peel(iter->iter0, peeled);
+}
+
+static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator)
+{
+ struct packed_ref_iterator *iter =
+ (struct packed_ref_iterator *)ref_iterator;
+ int ok = ITER_DONE;
+
+ if (iter->iter0)
+ ok = ref_iterator_abort(iter->iter0);
+
+ release_packed_ref_cache(iter->cache);
+ base_ref_iterator_free(ref_iterator);
+ return ok;
+}
+
+static struct ref_iterator_vtable packed_ref_iterator_vtable = {
+ packed_ref_iterator_advance,
+ packed_ref_iterator_peel,
+ packed_ref_iterator_abort
+};
+
+static struct ref_iterator *packed_ref_iterator_begin(
+ struct ref_store *ref_store,
+ const char *prefix, unsigned int flags)
+{
+ struct packed_ref_store *refs;
+ struct packed_ref_iterator *iter;
+ struct ref_iterator *ref_iterator;
+ unsigned int required_flags = REF_STORE_READ;
+
+ if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+ required_flags |= REF_STORE_ODB;
+ refs = packed_downcast(ref_store, required_flags, "ref_iterator_begin");
+
+ iter = xcalloc(1, sizeof(*iter));
+ ref_iterator = &iter->base;
+ base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
+
+ /*
+ * Note that get_packed_ref_cache() internally checks whether
+ * the packed-ref cache is up to date with what is on disk,
+ * and re-reads it if not.
+ */
+
+ iter->cache = get_packed_ref_cache(refs);
+ acquire_packed_ref_cache(iter->cache);
+ iter->iter0 = cache_ref_iterator_begin(iter->cache->cache, prefix, 0);
+
+ iter->flags = flags;
+
+ return ref_iterator;
+}
+
+/*
+ * Write an entry to the packed-refs file for the specified refname.
+ * If peeled is non-NULL, write it as the entry's peeled value. On
+ * error, return a nonzero value and leave errno set at the value left
+ * by the failing call to `fprintf()`.
+ */
+static int write_packed_entry(FILE *fh, const char *refname,
+ const unsigned char *sha1,
+ const unsigned char *peeled)
+{
+ if (fprintf(fh, "%s %s\n", sha1_to_hex(sha1), refname) < 0 ||
+ (peeled && fprintf(fh, "^%s\n", sha1_to_hex(peeled)) < 0))
+ return -1;
+
+ return 0;
+}
+
+int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
+{
+ struct packed_ref_store *refs =
+ packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
+ "packed_refs_lock");
+ static int timeout_configured = 0;
+ static int timeout_value = 1000;
+ struct packed_ref_cache *packed_ref_cache;
+
+ if (!timeout_configured) {
+ git_config_get_int("core.packedrefstimeout", &timeout_value);
+ timeout_configured = 1;
+ }
+
+ /*
+ * Note that we close the lockfile immediately because we
+ * don't write new content to it, but rather to a separate
+ * tempfile.
+ */
+ if (hold_lock_file_for_update_timeout(
+ &refs->lock,
+ refs->path,
+ flags, timeout_value) < 0) {
+ unable_to_lock_message(refs->path, errno, err);
+ return -1;
+ }
+
+ if (close_lock_file(&refs->lock)) {
+ strbuf_addf(err, "unable to close %s: %s", refs->path, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Now that we hold the `packed-refs` lock, make sure that our
+ * cache matches the current version of the file. Normally
+ * `get_packed_ref_cache()` does that for us, but that
+ * function assumes that when the file is locked, any existing
+ * cache is still valid. We've just locked the file, but it
+ * might have changed the moment *before* we locked it.
+ */
+ validate_packed_ref_cache(refs);
+
+ packed_ref_cache = get_packed_ref_cache(refs);
+ /* Increment the reference count to prevent it from being freed: */
+ acquire_packed_ref_cache(packed_ref_cache);
+ return 0;
+}
+
+void packed_refs_unlock(struct ref_store *ref_store)
+{
+ struct packed_ref_store *refs = packed_downcast(
+ ref_store,
+ REF_STORE_READ | REF_STORE_WRITE,
+ "packed_refs_unlock");
+
+ if (!is_lock_file_locked(&refs->lock))
+ die("BUG: packed_refs_unlock() called when not locked");
+ rollback_lock_file(&refs->lock);
+ release_packed_ref_cache(refs->cache);
+}
+
+int packed_refs_is_locked(struct ref_store *ref_store)
+{
+ struct packed_ref_store *refs = packed_downcast(
+ ref_store,
+ REF_STORE_READ | REF_STORE_WRITE,
+ "packed_refs_is_locked");
+
+ return is_lock_file_locked(&refs->lock);
+}
+
+/*
+ * The packed-refs header line that we write out. Perhaps other
+ * traits will be added later. The trailing space is required.
+ */
+static const char PACKED_REFS_HEADER[] =
+ "# pack-refs with: peeled fully-peeled \n";
+
+/*
+ * Write the current version of the packed refs cache from memory to
+ * disk. The packed-refs file must already be locked for writing (see
+ * packed_refs_lock()). Return zero on success. On errors, rollback
+ * the lockfile, write an error message to `err`, and return a nonzero
+ * value.
+ */
+int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
+{
+ struct packed_ref_store *refs =
+ packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
+ "commit_packed_refs");
+ struct packed_ref_cache *packed_ref_cache =
+ get_packed_ref_cache(refs);
+ int ok;
+ int ret = -1;
+ struct strbuf sb = STRBUF_INIT;
+ FILE *out;
+ struct ref_iterator *iter;
+ char *packed_refs_path;
+
+ if (!is_lock_file_locked(&refs->lock))
+ die("BUG: commit_packed_refs() called when unlocked");
+
+ /*
+ * If packed-refs is a symlink, we want to overwrite the
+ * symlinked-to file, not the symlink itself. Also, put the
+ * staging file next to it:
+ */
+ packed_refs_path = get_locked_file_path(&refs->lock);
+ strbuf_addf(&sb, "%s.new", packed_refs_path);
+ if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
+ strbuf_addf(err, "unable to create file %s: %s",
+ sb.buf, strerror(errno));
+ strbuf_release(&sb);
+ goto out;
+ }
+ strbuf_release(&sb);
+
+ out = fdopen_tempfile(&refs->tempfile, "w");
+ if (!out) {
+ strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s",
+ strerror(errno));
+ goto error;
+ }
+
+ if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0) {
+ strbuf_addf(err, "error writing to %s: %s",
+ get_tempfile_path(&refs->tempfile), strerror(errno));
+ goto error;
+ }
+
+ iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0);
+ while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+ struct object_id peeled;
+ int peel_error = ref_iterator_peel(iter, &peeled);
+
+ if (write_packed_entry(out, iter->refname, iter->oid->hash,
+ peel_error ? NULL : peeled.hash)) {
+ strbuf_addf(err, "error writing to %s: %s",
+ get_tempfile_path(&refs->tempfile),
+ strerror(errno));
+ ref_iterator_abort(iter);
+ goto error;
+ }
+ }
+
+ if (ok != ITER_DONE) {
+ strbuf_addf(err, "unable to rewrite packed-refs file: "
+ "error iterating over old contents");
+ goto error;
+ }
+
+ if (rename_tempfile(&refs->tempfile, packed_refs_path)) {
+ strbuf_addf(err, "error replacing %s: %s",
+ refs->path, strerror(errno));
+ goto out;
+ }
+
+ ret = 0;
+ goto out;
+
+error:
+ delete_tempfile(&refs->tempfile);
+
+out:
+ free(packed_refs_path);
+ return ret;
+}
+
+/*
+ * Rewrite the packed-refs file, omitting any refs listed in
+ * 'refnames'. On error, leave packed-refs unchanged, write an error
+ * message to 'err', and return a nonzero value. The packed refs lock
+ * must be held when calling this function; it will still be held when
+ * the function returns.
+ *
+ * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
+ */
+int repack_without_refs(struct ref_store *ref_store,
+ struct string_list *refnames, struct strbuf *err)
+{
+ struct packed_ref_store *refs =
+ packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
+ "repack_without_refs");
+ struct ref_dir *packed;
+ struct string_list_item *refname;
+ int needs_repacking = 0, removed = 0;
+
+ packed_assert_main_repository(refs, "repack_without_refs");
+ assert(err);
+
+ if (!is_lock_file_locked(&refs->lock))
+ die("BUG: repack_without_refs called without holding lock");
+
+ /* Look for a packed ref */
+ for_each_string_list_item(refname, refnames) {
+ if (get_packed_ref(refs, refname->string)) {
+ needs_repacking = 1;
+ break;
+ }
+ }
+
+ /* Avoid locking if we have nothing to do */
+ if (!needs_repacking)
+ return 0; /* no refname exists in packed refs */
+
+ packed = get_packed_refs(refs);
+
+ /* Remove refnames from the cache */
+ for_each_string_list_item(refname, refnames)
+ if (remove_entry_from_dir(packed, refname->string) != -1)
+ removed = 1;
+ if (!removed) {
+ /*
+ * All packed entries disappeared while we were
+ * acquiring the lock.
+ */
+ clear_packed_ref_cache(refs);
+ return 0;
+ }
+
+ /* Write what remains */
+ return commit_packed_refs(&refs->base, err);
+}
+
+static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+static int packed_transaction_prepare(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ die("BUG: not implemented yet");
+}
+
+static int packed_transaction_abort(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ die("BUG: not implemented yet");
+}
+
+static int packed_transaction_finish(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ die("BUG: not implemented yet");
+}
+
+static int packed_initial_transaction_commit(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ return ref_transaction_commit(transaction, err);
+}
+
+static int packed_delete_refs(struct ref_store *ref_store, const char *msg,
+ struct string_list *refnames, unsigned int flags)
+{
+ die("BUG: not implemented yet");
+}
+
+static int packed_pack_refs(struct ref_store *ref_store, unsigned int flags)
+{
+ /*
+ * Packed refs are already packed. It might be that loose refs
+ * are packed *into* a packed refs store, but that is done by
+ * updating the packed references via a transaction.
+ */
+ return 0;
+}
+
+static int packed_create_symref(struct ref_store *ref_store,
+ const char *refname, const char *target,
+ const char *logmsg)
+{
+ die("BUG: packed reference store does not support symrefs");
+}
+
+static int packed_rename_ref(struct ref_store *ref_store,
+ const char *oldrefname, const char *newrefname,
+ const char *logmsg)
+{
+ die("BUG: packed reference store does not support renaming references");
+}
+
+static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store)
+{
+ return empty_ref_iterator_begin();
+}
+
+static int packed_for_each_reflog_ent(struct ref_store *ref_store,
+ const char *refname,
+ each_reflog_ent_fn fn, void *cb_data)
+{
+ return 0;
+}
+
+static int packed_for_each_reflog_ent_reverse(struct ref_store *ref_store,
+ const char *refname,
+ each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ return 0;
+}
+
+static int packed_reflog_exists(struct ref_store *ref_store,
+ const char *refname)
+{
+ return 0;
+}
+
+static int packed_create_reflog(struct ref_store *ref_store,
+ const char *refname, int force_create,
+ struct strbuf *err)
+{
+ die("BUG: packed reference store does not support reflogs");
+}
+
+static int packed_delete_reflog(struct ref_store *ref_store,
+ const char *refname)
+{
+ return 0;
+}
+
+static int packed_reflog_expire(struct ref_store *ref_store,
+ const char *refname, const unsigned char *sha1,
+ unsigned int flags,
+ reflog_expiry_prepare_fn prepare_fn,
+ reflog_expiry_should_prune_fn should_prune_fn,
+ reflog_expiry_cleanup_fn cleanup_fn,
+ void *policy_cb_data)
+{
+ return 0;
+}
+
+struct ref_storage_be refs_be_packed = {
+ NULL,
+ "packed",
+ packed_ref_store_create,
+ packed_init_db,
+ packed_transaction_prepare,
+ packed_transaction_finish,
+ packed_transaction_abort,
+ packed_initial_transaction_commit,
+
+ packed_pack_refs,
+ packed_peel_ref,
+ packed_create_symref,
+ packed_delete_refs,
+ packed_rename_ref,
+
+ packed_ref_iterator_begin,
+ packed_read_raw_ref,
+
+ packed_reflog_iterator_begin,
+ packed_for_each_reflog_ent,
+ packed_for_each_reflog_ent_reverse,
+ packed_reflog_exists,
+ packed_create_reflog,
+ packed_delete_reflog,
+ packed_reflog_expire
+};
diff --git a/refs/packed-backend.h b/refs/packed-backend.h
new file mode 100644
index 0000000000..03b7c1de95
--- /dev/null
+++ b/refs/packed-backend.h
@@ -0,0 +1,25 @@
+#ifndef REFS_PACKED_BACKEND_H
+#define REFS_PACKED_BACKEND_H
+
+struct ref_store *packed_ref_store_create(const char *path,
+ unsigned int store_flags);
+
+/*
+ * Lock the packed-refs file for writing. Flags is passed to
+ * hold_lock_file_for_update(). Return 0 on success. On errors, write
+ * an error message to `err` and return a nonzero value.
+ */
+int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err);
+
+void packed_refs_unlock(struct ref_store *ref_store);
+int packed_refs_is_locked(struct ref_store *ref_store);
+
+void add_packed_ref(struct ref_store *ref_store,
+ const char *refname, const struct object_id *oid);
+
+int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err);
+
+int repack_without_refs(struct ref_store *ref_store,
+ struct string_list *refnames, struct strbuf *err);
+
+#endif /* REFS_PACKED_BACKEND_H */
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 192f9f85c9..b02dc5a7e3 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -62,6 +62,12 @@
#define REF_DELETED_LOOSE 0x200
/*
+ * Return the length of time to retry acquiring a loose reference lock
+ * before giving up, in milliseconds:
+ */
+long get_files_ref_lock_timeout_ms(void);
+
+/*
* Return true iff refname is minimally safe. "Safe" here means that
* deleting a loose reference by this name will not do any damage, for
* example by causing a file that is not a reference to be deleted.
@@ -77,6 +83,15 @@
*/
int refname_is_safe(const char *refname);
+/*
+ * Helper function: return true if refname, which has the specified
+ * oid and flags, can be resolved to an object in the database. If the
+ * referred-to object does not exist, emit a warning and return false.
+ */
+int ref_resolves_to_object(const char *refname,
+ const struct object_id *oid,
+ unsigned int flags);
+
enum peel_status {
/* object was peeled successfully: */
PEEL_PEELED = 0,
@@ -655,6 +670,7 @@ struct ref_storage_be {
};
extern struct ref_storage_be refs_be_files;
+extern struct ref_storage_be refs_be_packed;
/*
* A representation of the reference store for the main repository or
diff --git a/remote.c b/remote.c
index 60d0043921..43c317e4e9 100644
--- a/remote.c
+++ b/remote.c
@@ -134,10 +134,14 @@ struct remotes_hash_key {
};
static int remotes_hash_cmp(const void *unused_cmp_data,
- const struct remote *a,
- const struct remote *b,
- const struct remotes_hash_key *key)
+ const void *entry,
+ const void *entry_or_key,
+ const void *keydata)
{
+ const struct remote *a = entry;
+ const struct remote *b = entry_or_key;
+ const struct remotes_hash_key *key = keydata;
+
if (key)
return strncmp(a->name, key->str, key->len) || a->name[key->len];
else
@@ -147,7 +151,7 @@ static int remotes_hash_cmp(const void *unused_cmp_data,
static inline void init_remotes_hash(void)
{
if (!remotes_hash.cmpfn)
- hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, NULL, 0);
+ hashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);
}
static struct remote *make_remote(const char *name, int len)
@@ -1081,7 +1085,7 @@ static int try_explicit_object_name(const char *name,
return 0;
}
- if (get_sha1(name, oid.hash))
+ if (get_oid(name, &oid))
return -1;
if (match) {
@@ -2297,8 +2301,8 @@ static int parse_push_cas_option(struct push_cas_option *cas, const char *arg, i
if (!*colon)
entry->use_tracking = 1;
else if (!colon[1])
- hashclr(entry->expect);
- else if (get_sha1(colon + 1, entry->expect))
+ oidclr(&entry->expect);
+ else if (get_oid(colon + 1, &entry->expect))
return error("cannot parse expected object name '%s'", colon + 1);
return 0;
}
@@ -2345,7 +2349,7 @@ static void apply_cas(struct push_cas_option *cas,
continue;
ref->expect_old_sha1 = 1;
if (!entry->use_tracking)
- hashcpy(ref->old_oid_expect.hash, cas->entry[i].expect);
+ oidcpy(&ref->old_oid_expect, &entry->expect);
else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
oidclr(&ref->old_oid_expect);
return;
diff --git a/remote.h b/remote.h
index 6c28cd3e4b..2ecf4c8c74 100644
--- a/remote.h
+++ b/remote.h
@@ -282,7 +282,7 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet
struct push_cas_option {
unsigned use_tracking_for_rest:1;
struct push_cas {
- unsigned char expect[20];
+ struct object_id expect;
unsigned use_tracking:1;
char *refname;
} *entry;
diff --git a/repository.c b/repository.c
index edca907404..f107af7d76 100644
--- a/repository.c
+++ b/repository.c
@@ -4,7 +4,9 @@
#include "submodule-config.h"
/* The main repository */
-static struct repository the_repo;
+static struct repository the_repo = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &the_index, 0, 0
+};
struct repository *the_repository = &the_repo;
static char *git_path_from_env(const char *envvar, const char *git_dir,
@@ -158,7 +160,7 @@ int repo_submodule_init(struct repository *submodule,
struct strbuf worktree = STRBUF_INIT;
int ret = 0;
- sub = submodule_from_cache(superproject, null_sha1, path);
+ sub = submodule_from_cache(superproject, &null_oid, path);
if (!sub) {
ret = -1;
goto out;
@@ -235,8 +237,6 @@ int repo_read_index(struct repository *repo)
{
if (!repo->index)
repo->index = xcalloc(1, sizeof(*repo->index));
- else
- discard_index(repo->index);
return read_index_from(repo->index, repo->index_file);
}
diff --git a/repository.h b/repository.h
index 417787f3ef..7f5e24a0a2 100644
--- a/repository.h
+++ b/repository.h
@@ -92,6 +92,14 @@ extern int repo_submodule_init(struct repository *submodule,
const char *path);
extern void repo_clear(struct repository *repo);
+/*
+ * Populates the repository's index from its index_file, an index struct will
+ * be allocated if needed.
+ *
+ * Return the number of index entries in the populated index or a value less
+ * than zero if an error occured. If the repository's index has already been
+ * populated then the number of entries will simply be returned.
+ */
extern int repo_read_index(struct repository *repo);
#endif /* REPOSITORY_H */
diff --git a/rerere.c b/rerere.c
index 70634d456c..d77235645e 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1133,14 +1133,14 @@ int rerere_forget(struct pathspec *pathspec)
* Garbage collection support
*/
-static time_t rerere_created_at(struct rerere_id *id)
+static timestamp_t rerere_created_at(struct rerere_id *id)
{
struct stat st;
return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
}
-static time_t rerere_last_used_at(struct rerere_id *id)
+static timestamp_t rerere_last_used_at(struct rerere_id *id)
{
struct stat st;
@@ -1157,11 +1157,11 @@ static void unlink_rr_item(struct rerere_id *id)
id->collection->status[id->variant] = 0;
}
-static void prune_one(struct rerere_id *id, time_t now,
- int cutoff_resolve, int cutoff_noresolve)
+static void prune_one(struct rerere_id *id,
+ timestamp_t cutoff_resolve, timestamp_t cutoff_noresolve)
{
- time_t then;
- int cutoff;
+ timestamp_t then;
+ timestamp_t cutoff;
then = rerere_last_used_at(id);
if (then)
@@ -1172,7 +1172,7 @@ static void prune_one(struct rerere_id *id, time_t now,
return;
cutoff = cutoff_noresolve;
}
- if (then < now - cutoff * 86400)
+ if (then < cutoff)
unlink_rr_item(id);
}
@@ -1182,15 +1182,15 @@ void rerere_gc(struct string_list *rr)
DIR *dir;
struct dirent *e;
int i;
- time_t now = time(NULL);
- int cutoff_noresolve = 15;
- int cutoff_resolve = 60;
+ timestamp_t now = time(NULL);
+ timestamp_t cutoff_noresolve = now - 15 * 86400;
+ timestamp_t cutoff_resolve = now - 60 * 86400;
if (setup_rerere(rr, 0) < 0)
return;
- git_config_get_int("gc.rerereresolved", &cutoff_resolve);
- git_config_get_int("gc.rerereunresolved", &cutoff_noresolve);
+ git_config_get_expiry_in_days("gc.rerereresolved", &cutoff_resolve, now);
+ git_config_get_expiry_in_days("gc.rerereunresolved", &cutoff_noresolve, now);
git_config(git_default_config, NULL);
dir = opendir(git_path("rr-cache"));
if (!dir)
@@ -1211,7 +1211,7 @@ void rerere_gc(struct string_list *rr)
for (id.variant = 0, id.collection = rr_dir;
id.variant < id.collection->status_nr;
id.variant++) {
- prune_one(&id, now, cutoff_resolve, cutoff_noresolve);
+ prune_one(&id, cutoff_resolve, cutoff_noresolve);
if (id.collection->status[id.variant])
now_empty = 0;
}
diff --git a/revision.c b/revision.c
index 6603af9444..94a5e98525 100644
--- a/revision.c
+++ b/revision.c
@@ -19,6 +19,7 @@
#include "dir.h"
#include "cache-tree.h"
#include "bisect.h"
+#include "packfile.h"
volatile show_early_output_fn_t show_early_output;
@@ -148,16 +149,14 @@ static void add_pending_object_with_path(struct rev_info *revs,
if (revs->reflog_info && obj->type == OBJ_COMMIT) {
struct strbuf buf = STRBUF_INIT;
int len = interpret_branch_name(name, 0, &buf, 0);
- int st;
if (0 < len && name[len] && buf.len)
strbuf_addstr(&buf, name + len);
- st = add_reflog_for_walk(revs->reflog_info,
- (struct commit *)obj,
- buf.buf[0] ? buf.buf: name);
+ add_reflog_for_walk(revs->reflog_info,
+ (struct commit *)obj,
+ buf.buf[0] ? buf.buf: name);
strbuf_release(&buf);
- if (st)
- return;
+ return; /* do not add the commit itself */
}
add_object_array_with_path(obj, name, &revs->pending, mode, path);
}
@@ -1168,6 +1167,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
{
cb->all_revs = revs;
cb->all_flags = flags;
+ revs->rev_input_given = 1;
}
void clear_ref_exclusion(struct string_list **ref_excludes_p)
@@ -1303,7 +1303,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
flags ^= UNINTERESTING | BOTTOM;
arg++;
}
- if (get_sha1_committish(arg, oid.hash))
+ if (get_oid_committish(arg, &oid))
return 0;
while (1) {
it = get_reference(revs, arg, &oid, 0);
@@ -1452,7 +1452,7 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
unsigned int a_flags, b_flags;
int symmetric = 0;
unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
- unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH;
+ unsigned int oc_flags = GET_OID_COMMITTISH | GET_OID_RECORD_PATH;
a_name = arg;
if (!*a_name)
@@ -1466,8 +1466,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
if (!*b_name)
b_name = "HEAD";
- if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) ||
- get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc))
+ if (get_oid_with_context(a_name, oc_flags, &a_oid, a_oc) ||
+ get_oid_with_context(b_name, oc_flags, &b_oid, b_oc))
return -1;
if (!cant_be_filename) {
@@ -1548,7 +1548,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
int local_flags;
const char *arg = arg_;
int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
- unsigned get_sha1_flags = GET_SHA1_RECORD_PATH;
+ unsigned get_sha1_flags = GET_OID_RECORD_PATH;
flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
@@ -1599,9 +1599,9 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
}
if (revarg_opt & REVARG_COMMITTISH)
- get_sha1_flags |= GET_SHA1_COMMITTISH;
+ get_sha1_flags |= GET_OID_COMMITTISH;
- if (get_sha1_with_context(arg, get_sha1_flags, oid.hash, &oc))
+ if (get_oid_with_context(arg, get_sha1_flags, &oid, &oc))
return revs->ignore_missing ? 0 : -1;
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
@@ -2315,11 +2315,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
opt->tweak(revs, opt);
if (revs->show_merge)
prepare_show_merge(revs);
- if (revs->def && !revs->pending.nr && !got_rev_arg) {
+ if (revs->def && !revs->pending.nr && !revs->rev_input_given && !got_rev_arg) {
struct object_id oid;
struct object *object;
struct object_context oc;
- if (get_sha1_with_context(revs->def, 0, oid.hash, &oc))
+ if (get_oid_with_context(revs->def, 0, &oid, &oc))
diagnose_missing_default(revs->def);
object = get_reference(revs, revs->def, &oid, 0);
add_pending_object_with_mode(revs, object, revs->def, oc.mode);
@@ -2364,6 +2364,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (revs->reverse && revs->reflog_info)
die("cannot combine --reverse with --walk-reflogs");
+ if (revs->reflog_info && revs->limited)
+ die("cannot combine --walk-reflogs with history-limiting options");
if (revs->rewrite_parents && revs->children.name)
die("cannot combine --parents and --children");
@@ -2963,6 +2965,18 @@ static inline int want_ancestry(const struct rev_info *revs)
return (revs->rewrite_parents || revs->children.name);
}
+/*
+ * Return a timestamp to be used for --since/--until comparisons for this
+ * commit, based on the revision options.
+ */
+static timestamp_t comparison_date(const struct rev_info *revs,
+ struct commit *commit)
+{
+ return revs->reflog_info ?
+ get_reflog_timestamp(revs->reflog_info) :
+ commit->date;
+}
+
enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
{
if (commit->object.flags & SHOWN)
@@ -2973,8 +2987,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
return commit_show;
if (commit->object.flags & UNINTERESTING)
return commit_ignore;
- if (revs->min_age != -1 && (commit->date > revs->min_age))
- return commit_ignore;
+ if (revs->min_age != -1 &&
+ comparison_date(revs, commit) > revs->min_age)
+ return commit_ignore;
if (revs->min_parents || (revs->max_parents >= 0)) {
int n = commit_list_count(commit->parents);
if ((n < revs->min_parents) ||
@@ -3107,17 +3122,19 @@ static void track_linear(struct rev_info *revs, struct commit *commit)
static struct commit *get_revision_1(struct rev_info *revs)
{
- if (!revs->commits)
- return NULL;
+ while (1) {
+ struct commit *commit;
- do {
- struct commit *commit = pop_commit(&revs->commits);
+ if (revs->reflog_info)
+ commit = next_reflog_entry(revs->reflog_info);
+ else
+ commit = pop_commit(&revs->commits);
- if (revs->reflog_info) {
- save_parents(revs, commit);
- fake_reflog_parent(revs->reflog_info, commit);
+ if (!commit)
+ return NULL;
+
+ if (revs->reflog_info)
commit->object.flags &= ~(ADDED | SEEN | SHOWN);
- }
/*
* If we haven't done the list limiting, we need to look at
@@ -3126,9 +3143,12 @@ static struct commit *get_revision_1(struct rev_info *revs)
*/
if (!revs->limited) {
if (revs->max_age != -1 &&
- (commit->date < revs->max_age))
+ comparison_date(revs, commit) < revs->max_age)
continue;
- if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
+
+ if (revs->reflog_info)
+ try_to_simplify_commit(revs, commit);
+ else if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
if (!revs->ignore_missing_links)
die("Failed to traverse parents of commit %s",
oid_to_hex(&commit->object.oid));
@@ -3146,8 +3166,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
track_linear(revs, commit);
return commit;
}
- } while (revs->commits);
- return NULL;
+ }
}
/*
diff --git a/revision.h b/revision.h
index f96e7f7f49..bc18487d6f 100644
--- a/revision.h
+++ b/revision.h
@@ -71,6 +71,13 @@ struct rev_info {
const char *def;
struct pathspec prune_data;
+ /*
+ * Whether the arguments parsed by setup_revisions() included any
+ * "input" revisions that might still have yielded an empty pending
+ * list (e.g., patterns like "--all" or "--glob").
+ */
+ int rev_input_given;
+
/* topo-sort */
enum rev_sort_order sort_order;
diff --git a/run-command.c b/run-command.c
index 9e36151bf9..b5e6eb37c0 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1533,7 +1533,7 @@ static int pp_start_one(struct parallel_processes *pp)
if (start_command(&pp->children[i].process)) {
code = pp->start_failure(&pp->children[i].err,
pp->data,
- &pp->children[i].data);
+ pp->children[i].data);
strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
strbuf_reset(&pp->children[i].err);
if (code)
@@ -1601,7 +1601,7 @@ static int pp_collect_finished(struct parallel_processes *pp)
code = pp->task_finished(code,
&pp->children[i].err, pp->data,
- &pp->children[i].data);
+ pp->children[i].data);
if (code)
result = code;
diff --git a/sequencer.c b/sequencer.c
index 3010faf863..fcceabb80f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -127,6 +127,7 @@ static GIT_PATH_FUNC(rebase_path_onto, "rebase-merge/onto")
static GIT_PATH_FUNC(rebase_path_autostash, "rebase-merge/autostash")
static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
+static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
static inline int is_rebase_i(const struct replay_opts *opts)
{
@@ -691,7 +692,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
static int is_original_commit_empty(struct commit *commit)
{
- const unsigned char *ptree_sha1;
+ const struct object_id *ptree_oid;
if (parse_commit(commit))
return error(_("could not parse commit %s\n"),
@@ -701,12 +702,12 @@ static int is_original_commit_empty(struct commit *commit)
if (parse_commit(parent))
return error(_("could not parse parent commit %s\n"),
oid_to_hex(&parent->object.oid));
- ptree_sha1 = parent->tree->object.oid.hash;
+ ptree_oid = &parent->tree->object.oid;
} else {
- ptree_sha1 = EMPTY_TREE_SHA1_BIN; /* commit is root */
+ ptree_oid = &empty_tree_oid; /* commit is root */
}
- return !hashcmp(ptree_sha1, commit->tree->object.oid.hash);
+ return !oidcmp(ptree_oid, &commit->tree->object.oid);
}
/*
@@ -896,18 +897,18 @@ static int update_squash_messages(enum todo_command command,
static void flush_rewritten_pending(void) {
struct strbuf buf = STRBUF_INIT;
- unsigned char newsha1[20];
+ struct object_id newoid;
FILE *out;
- if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 &&
- !get_sha1("HEAD", newsha1) &&
+ if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), (GIT_MAX_HEXSZ + 1) * 2) > 0 &&
+ !get_oid("HEAD", &newoid) &&
(out = fopen_or_warn(rebase_path_rewritten_list(), "a"))) {
char *bol = buf.buf, *eol;
while (*bol) {
eol = strchrnul(bol, '\n');
fprintf(out, "%.*s %s\n", (int)(eol - bol),
- bol, sha1_to_hex(newsha1));
+ bol, oid_to_hex(&newoid));
if (!*eol)
break;
bol = eol + 1;
@@ -1438,7 +1439,11 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
else if (!strcmp(key, "options.strategy-option")) {
ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
opts->xopts[opts->xopts_nr++] = xstrdup(value);
- } else
+ } else if (!strcmp(key, "options.allow-rerere-auto"))
+ opts->allow_rerere_auto =
+ git_config_bool_or_int(key, value, &error_flag) ?
+ RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
+ else
return error(_("invalid key: %s"), key);
if (!error_flag)
@@ -1479,6 +1484,15 @@ static int read_populate_opts(struct replay_opts *opts)
free(opts->gpg_sign);
opts->gpg_sign = xstrdup(buf.buf + 2);
}
+ strbuf_reset(&buf);
+ }
+
+ if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
+ if (!strcmp(buf.buf, "--rerere-autoupdate"))
+ opts->allow_rerere_auto = RERERE_AUTOUPDATE;
+ else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
+ opts->allow_rerere_auto = RERERE_NOAUTOUPDATE;
+ strbuf_reset(&buf);
}
if (file_exists(rebase_path_verbose()))
@@ -1594,36 +1608,37 @@ static int rollback_is_safe(void)
return !oidcmp(&actual_head, &expected_head);
}
-static int reset_for_rollback(const unsigned char *sha1)
+static int reset_for_rollback(const struct object_id *oid)
{
const char *argv[4]; /* reset --merge <arg> + NULL */
argv[0] = "reset";
argv[1] = "--merge";
- argv[2] = sha1_to_hex(sha1);
+ argv[2] = oid_to_hex(oid);
argv[3] = NULL;
return run_command_v_opt(argv, RUN_GIT_CMD);
}
static int rollback_single_pick(void)
{
- unsigned char head_sha1[20];
+ struct object_id head_oid;
if (!file_exists(git_path_cherry_pick_head()) &&
!file_exists(git_path_revert_head()))
return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", 0, head_sha1, NULL))
+ if (read_ref_full("HEAD", 0, head_oid.hash, NULL))
return error(_("cannot resolve HEAD"));
- if (is_null_sha1(head_sha1))
+ if (is_null_oid(&head_oid))
return error(_("cannot abort from a branch yet to be born"));
- return reset_for_rollback(head_sha1);
+ return reset_for_rollback(&head_oid);
}
int sequencer_rollback(struct replay_opts *opts)
{
FILE *f;
- unsigned char sha1[20];
+ struct object_id oid;
struct strbuf buf = STRBUF_INIT;
+ const char *p;
f = fopen(git_path_head_file(), "r");
if (!f && errno == ENOENT) {
@@ -1643,12 +1658,12 @@ int sequencer_rollback(struct replay_opts *opts)
goto fail;
}
fclose(f);
- if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
+ if (parse_oid_hex(buf.buf, &oid, &p) || *p != '\0') {
error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
git_path_head_file());
goto fail;
}
- if (is_null_sha1(sha1)) {
+ if (is_null_oid(&oid)) {
error(_("cannot abort from a branch yet to be born"));
goto fail;
}
@@ -1658,7 +1673,7 @@ int sequencer_rollback(struct replay_opts *opts)
warning(_("You seem to have moved HEAD. "
"Not rewinding, check your HEAD!"));
} else
- if (reset_for_rollback(sha1))
+ if (reset_for_rollback(&oid))
goto fail;
strbuf_release(&buf);
return sequencer_remove_state(opts);
@@ -1742,6 +1757,10 @@ static int save_opts(struct replay_opts *opts)
"options.strategy-option",
opts->xopts[i], "^$", 0);
}
+ if (opts->allow_rerere_auto)
+ res |= git_config_set_in_file_gently(opts_file, "options.allow-rerere-auto",
+ opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
+ "true" : "false");
return res;
}
@@ -1788,13 +1807,13 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
static int intend_to_amend(void)
{
- unsigned char head[20];
+ struct object_id head;
char *p;
- if (get_sha1("HEAD", head))
+ if (get_oid("HEAD", &head))
return error(_("cannot read HEAD"));
- p = sha1_to_hex(head);
+ p = oid_to_hex(&head);
return write_message(p, strlen(p), rebase_path_amend(), 1);
}
@@ -2079,10 +2098,10 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
if (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&
starts_with(head_ref.buf, "refs/")) {
const char *msg;
- unsigned char head[20], orig[20];
+ struct object_id head, orig;
int res;
- if (get_sha1("HEAD", head)) {
+ if (get_oid("HEAD", &head)) {
res = error(_("cannot read HEAD"));
cleanup_head_ref:
strbuf_release(&head_ref);
@@ -2090,7 +2109,7 @@ cleanup_head_ref:
return res;
}
if (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||
- get_sha1_hex(buf.buf, orig)) {
+ get_oid_hex(buf.buf, &orig)) {
res = error(_("could not read orig-head"));
goto cleanup_head_ref;
}
@@ -2101,7 +2120,7 @@ cleanup_head_ref:
}
msg = reflog_message(opts, "finish", "%s onto %s",
head_ref.buf, buf.buf);
- if (update_ref(msg, head_ref.buf, head, orig,
+ if (update_ref(msg, head_ref.buf, head.hash, orig.hash,
REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {
res = error(_("could not update %s"),
head_ref.buf);
@@ -2129,8 +2148,8 @@ cleanup_head_ref:
log_tree_opt.disable_stdin = 1;
if (read_oneliner(&buf, rebase_path_orig_head(), 0) &&
- !get_sha1(buf.buf, orig.hash) &&
- !get_sha1("HEAD", head.hash)) {
+ !get_oid(buf.buf, &orig) &&
+ !get_oid("HEAD", &head)) {
diff_tree_oid(&orig, &head, "",
&log_tree_opt.diffopt);
log_tree_diff_flush(&log_tree_opt);
@@ -2205,16 +2224,16 @@ static int commit_staged_changes(struct replay_opts *opts)
if (file_exists(rebase_path_amend())) {
struct strbuf rev = STRBUF_INIT;
- unsigned char head[20], to_amend[20];
+ struct object_id head, to_amend;
- if (get_sha1("HEAD", head))
+ if (get_oid("HEAD", &head))
return error(_("cannot amend non-existing commit"));
if (!read_oneliner(&rev, rebase_path_amend(), 0))
return error(_("invalid file: '%s'"), rebase_path_amend());
- if (get_sha1_hex(rev.buf, to_amend))
+ if (get_oid_hex(rev.buf, &to_amend))
return error(_("invalid contents: '%s'"),
rebase_path_amend());
- if (hashcmp(head, to_amend))
+ if (oidcmp(&head, &to_amend))
return error(_("\nYou have uncommitted changes in your "
"working tree. Please, commit them\n"
"first and then run 'git rebase "
@@ -2266,7 +2285,7 @@ int sequencer_continue(struct replay_opts *opts)
struct object_id oid;
if (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&
- !get_sha1_committish(buf.buf, oid.hash))
+ !get_oid_committish(buf.buf, &oid))
record_in_rewritten(&oid, peek_command(&todo_list, 0));
strbuf_release(&buf);
}
diff --git a/server-info.c b/server-info.c
index 5ec5b1d827..26a6c20b7d 100644
--- a/server-info.c
+++ b/server-info.c
@@ -3,6 +3,7 @@
#include "object.h"
#include "commit.h"
#include "tag.h"
+#include "packfile.h"
/*
* Create the file "path" by writing to a temporary file and renaming
diff --git a/setup.c b/setup.c
index 860507e1fd..23950173fc 100644
--- a/setup.c
+++ b/setup.c
@@ -1027,7 +1027,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
{
static struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
- const char *prefix, *env_prefix;
+ const char *prefix;
/*
* We may have read an incomplete configuration before
@@ -1085,16 +1085,6 @@ const char *setup_git_directory_gently(int *nongit_ok)
die("BUG: unhandled setup_git_directory_1() result");
}
- /*
- * NEEDSWORK: This was a hack in order to get ls-files and grep to have
- * properly formated output when recursing submodules. Once ls-files
- * and grep have been changed to perform this recursing in-process this
- * needs to be removed.
- */
- env_prefix = getenv(GIT_TOPLEVEL_PREFIX_ENVIRONMENT);
- if (env_prefix)
- prefix = env_prefix;
-
if (prefix)
setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
else
diff --git a/sha1-lookup.c b/sha1-lookup.c
index 5f069214d9..2552b7902c 100644
--- a/sha1-lookup.c
+++ b/sha1-lookup.c
@@ -99,219 +99,3 @@ int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
} while (lo < hi);
return -lo-1;
}
-
-/*
- * Conventional binary search loop looks like this:
- *
- * unsigned lo, hi;
- * do {
- * unsigned mi = (lo + hi) / 2;
- * int cmp = "entry pointed at by mi" minus "target";
- * if (!cmp)
- * return (mi is the wanted one)
- * if (cmp > 0)
- * hi = mi; "mi is larger than target"
- * else
- * lo = mi+1; "mi is smaller than target"
- * } while (lo < hi);
- *
- * The invariants are:
- *
- * - When entering the loop, lo points at a slot that is never
- * above the target (it could be at the target), hi points at a
- * slot that is guaranteed to be above the target (it can never
- * be at the target).
- *
- * - We find a point 'mi' between lo and hi (mi could be the same
- * as lo, but never can be as same as hi), and check if it hits
- * the target. There are three cases:
- *
- * - if it is a hit, we are happy.
- *
- * - if it is strictly higher than the target, we set it to hi,
- * and repeat the search.
- *
- * - if it is strictly lower than the target, we update lo to
- * one slot after it, because we allow lo to be at the target.
- *
- * If the loop exits, there is no matching entry.
- *
- * When choosing 'mi', we do not have to take the "middle" but
- * anywhere in between lo and hi, as long as lo <= mi < hi is
- * satisfied. When we somehow know that the distance between the
- * target and lo is much shorter than the target and hi, we could
- * pick mi that is much closer to lo than the midway.
- *
- * Now, we can take advantage of the fact that SHA-1 is a good hash
- * function, and as long as there are enough entries in the table, we
- * can expect uniform distribution. An entry that begins with for
- * example "deadbeef..." is much likely to appear much later than in
- * the midway of the table. It can reasonably be expected to be near
- * 87% (222/256) from the top of the table.
- *
- * However, we do not want to pick "mi" too precisely. If the entry at
- * the 87% in the above example turns out to be higher than the target
- * we are looking for, we would end up narrowing the search space down
- * only by 13%, instead of 50% we would get if we did a simple binary
- * search. So we would want to hedge our bets by being less aggressive.
- *
- * The table at "table" holds at least "nr" entries of "elem_size"
- * bytes each. Each entry has the SHA-1 key at "key_offset". The
- * table is sorted by the SHA-1 key of the entries. The caller wants
- * to find the entry with "key", and knows that the entry at "lo" is
- * not higher than the entry it is looking for, and that the entry at
- * "hi" is higher than the entry it is looking for.
- */
-int sha1_entry_pos(const void *table,
- size_t elem_size,
- size_t key_offset,
- unsigned lo, unsigned hi, unsigned nr,
- const unsigned char *key)
-{
- const unsigned char *base = table;
- const unsigned char *hi_key, *lo_key;
- unsigned ofs_0;
- static int debug_lookup = -1;
-
- if (debug_lookup < 0)
- debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
-
- if (!nr || lo >= hi)
- return -1;
-
- if (nr == hi)
- hi_key = NULL;
- else
- hi_key = base + elem_size * hi + key_offset;
- lo_key = base + elem_size * lo + key_offset;
-
- ofs_0 = 0;
- do {
- int cmp;
- unsigned ofs, mi, range;
- unsigned lov, hiv, kyv;
- const unsigned char *mi_key;
-
- range = hi - lo;
- if (hi_key) {
- for (ofs = ofs_0; ofs < 20; ofs++)
- if (lo_key[ofs] != hi_key[ofs])
- break;
- ofs_0 = ofs;
- /*
- * byte 0 thru (ofs-1) are the same between
- * lo and hi; ofs is the first byte that is
- * different.
- *
- * If ofs==20, then no bytes are different,
- * meaning we have entries with duplicate
- * keys. We know that we are in a solid run
- * of this entry (because the entries are
- * sorted, and our lo and hi are the same,
- * there can be nothing but this single key
- * in between). So we can stop the search.
- * Either one of these entries is it (and
- * we do not care which), or we do not have
- * it.
- *
- * Furthermore, we know that one of our
- * endpoints must be the edge of the run of
- * duplicates. For example, given this
- * sequence:
- *
- * idx 0 1 2 3 4 5
- * key A C C C C D
- *
- * If we are searching for "B", we might
- * hit the duplicate run at lo=1, hi=3
- * (e.g., by first mi=3, then mi=0). But we
- * can never have lo > 1, because B < C.
- * That is, if our key is less than the
- * run, we know that "lo" is the edge, but
- * we can say nothing of "hi". Similarly,
- * if our key is greater than the run, we
- * know that "hi" is the edge, but we can
- * say nothing of "lo".
- *
- * Therefore if we do not find it, we also
- * know where it would go if it did exist:
- * just on the far side of the edge that we
- * know about.
- */
- if (ofs == 20) {
- mi = lo;
- mi_key = base + elem_size * mi + key_offset;
- cmp = memcmp(mi_key, key, 20);
- if (!cmp)
- return mi;
- if (cmp < 0)
- return -1 - hi;
- else
- return -1 - lo;
- }
-
- hiv = hi_key[ofs_0];
- if (ofs_0 < 19)
- hiv = (hiv << 8) | hi_key[ofs_0+1];
- } else {
- hiv = 256;
- if (ofs_0 < 19)
- hiv <<= 8;
- }
- lov = lo_key[ofs_0];
- kyv = key[ofs_0];
- if (ofs_0 < 19) {
- lov = (lov << 8) | lo_key[ofs_0+1];
- kyv = (kyv << 8) | key[ofs_0+1];
- }
- assert(lov < hiv);
-
- if (kyv < lov)
- return -1 - lo;
- if (hiv < kyv)
- return -1 - hi;
-
- /*
- * Even if we know the target is much closer to 'hi'
- * than 'lo', if we pick too precisely and overshoot
- * (e.g. when we know 'mi' is closer to 'hi' than to
- * 'lo', pick 'mi' that is higher than the target), we
- * end up narrowing the search space by a smaller
- * amount (i.e. the distance between 'mi' and 'hi')
- * than what we would have (i.e. about half of 'lo'
- * and 'hi'). Hedge our bets to pick 'mi' less
- * aggressively, i.e. make 'mi' a bit closer to the
- * middle than we would otherwise pick.
- */
- kyv = (kyv * 6 + lov + hiv) / 8;
- if (lov < hiv - 1) {
- if (kyv == lov)
- kyv++;
- else if (kyv == hiv)
- kyv--;
- }
- mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
-
- if (debug_lookup) {
- printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
- printf("ofs %u lov %x, hiv %x, kyv %x\n",
- ofs_0, lov, hiv, kyv);
- }
- if (!(lo <= mi && mi < hi))
- die("assertion failure lo %u mi %u hi %u %s",
- lo, mi, hi, sha1_to_hex(key));
-
- mi_key = base + elem_size * mi + key_offset;
- cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
- if (!cmp)
- return mi;
- if (cmp > 0) {
- hi = mi;
- hi_key = mi_key;
- } else {
- lo = mi + 1;
- lo_key = mi_key + elem_size;
- }
- } while (lo < hi);
- return -lo-1;
-}
diff --git a/sha1_file.c b/sha1_file.c
index b60ae15f70..5f71bbac3e 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -28,11 +28,9 @@
#include "list.h"
#include "mergesort.h"
#include "quote.h"
+#include "packfile.h"
-#define SZ_FMT PRIuMAX
-static inline uintmax_t sz_fmt(size_t s) { return s; }
-
-const unsigned char null_sha1[20];
+const unsigned char null_sha1[GIT_MAX_RAWSZ];
const struct object_id null_oid;
const struct object_id empty_tree_oid = {
EMPTY_TREE_SHA1_BIN_LITERAL
@@ -278,28 +276,6 @@ static const char *alt_sha1_path(struct alternate_object_database *alt,
return buf->buf;
}
- char *odb_pack_name(struct strbuf *buf,
- const unsigned char *sha1,
- const char *ext)
-{
- strbuf_reset(buf);
- strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
- sha1_to_hex(sha1), ext);
- return buf->buf;
-}
-
-char *sha1_pack_name(const unsigned char *sha1)
-{
- static struct strbuf buf = STRBUF_INIT;
- return odb_pack_name(&buf, sha1, "pack");
-}
-
-char *sha1_pack_index_name(const unsigned char *sha1)
-{
- static struct strbuf buf = STRBUF_INIT;
- return odb_pack_name(&buf, sha1, "idx");
-}
-
struct alternate_object_database *alt_odb_list;
static struct alternate_object_database **alt_odb_tail;
@@ -347,6 +323,7 @@ static int alt_odb_usable(struct strbuf *path, const char *normalized_objdir)
* SHA1, an extra slash for the first level indirection, and the
* terminating NUL.
*/
+static void read_info_alternates(const char * relative_base, int depth);
static int link_alt_odb_entry(const char *entry, const char *relative_base,
int depth, const char *normalized_objdir)
{
@@ -448,7 +425,7 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
strbuf_release(&objdirbuf);
}
-void read_info_alternates(const char * relative_base, int depth)
+static void read_info_alternates(const char * relative_base, int depth)
{
char *map;
size_t mapsz;
@@ -704,213 +681,6 @@ static int has_loose_object(const unsigned char *sha1)
return check_and_freshen(sha1, 0);
}
-static unsigned int pack_used_ctr;
-static unsigned int pack_mmap_calls;
-static unsigned int peak_pack_open_windows;
-static unsigned int pack_open_windows;
-static unsigned int pack_open_fds;
-static unsigned int pack_max_fds;
-static size_t peak_pack_mapped;
-static size_t pack_mapped;
-struct packed_git *packed_git;
-
-static struct mru packed_git_mru_storage;
-struct mru *packed_git_mru = &packed_git_mru_storage;
-
-void pack_report(void)
-{
- fprintf(stderr,
- "pack_report: getpagesize() = %10" SZ_FMT "\n"
- "pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n"
- "pack_report: core.packedGitLimit = %10" SZ_FMT "\n",
- sz_fmt(getpagesize()),
- sz_fmt(packed_git_window_size),
- sz_fmt(packed_git_limit));
- fprintf(stderr,
- "pack_report: pack_used_ctr = %10u\n"
- "pack_report: pack_mmap_calls = %10u\n"
- "pack_report: pack_open_windows = %10u / %10u\n"
- "pack_report: pack_mapped = "
- "%10" SZ_FMT " / %10" SZ_FMT "\n",
- pack_used_ctr,
- pack_mmap_calls,
- pack_open_windows, peak_pack_open_windows,
- sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped));
-}
-
-/*
- * Open and mmap the index file at path, perform a couple of
- * consistency checks, then record its information to p. Return 0 on
- * success.
- */
-static int check_packed_git_idx(const char *path, struct packed_git *p)
-{
- void *idx_map;
- struct pack_idx_header *hdr;
- size_t idx_size;
- uint32_t version, nr, i, *index;
- int fd = git_open(path);
- struct stat st;
-
- if (fd < 0)
- return -1;
- if (fstat(fd, &st)) {
- close(fd);
- return -1;
- }
- idx_size = xsize_t(st.st_size);
- if (idx_size < 4 * 256 + 20 + 20) {
- close(fd);
- return error("index file %s is too small", path);
- }
- idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
- close(fd);
-
- hdr = idx_map;
- if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
- version = ntohl(hdr->idx_version);
- if (version < 2 || version > 2) {
- munmap(idx_map, idx_size);
- return error("index file %s is version %"PRIu32
- " and is not supported by this binary"
- " (try upgrading GIT to a newer version)",
- path, version);
- }
- } else
- version = 1;
-
- nr = 0;
- index = idx_map;
- if (version > 1)
- index += 2; /* skip index header */
- for (i = 0; i < 256; i++) {
- uint32_t n = ntohl(index[i]);
- if (n < nr) {
- munmap(idx_map, idx_size);
- return error("non-monotonic index %s", path);
- }
- nr = n;
- }
-
- if (version == 1) {
- /*
- * Total size:
- * - 256 index entries 4 bytes each
- * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
- */
- if (idx_size != 4*256 + nr * 24 + 20 + 20) {
- munmap(idx_map, idx_size);
- return error("wrong index v1 file size in %s", path);
- }
- } else if (version == 2) {
- /*
- * Minimum size:
- * - 8 bytes of header
- * - 256 index entries 4 bytes each
- * - 20-byte sha1 entry * nr
- * - 4-byte crc entry * nr
- * - 4-byte offset entry * nr
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
- * And after the 4-byte offset table might be a
- * variable sized table containing 8-byte entries
- * for offsets larger than 2^31.
- */
- unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
- unsigned long max_size = min_size;
- if (nr)
- max_size += (nr - 1)*8;
- if (idx_size < min_size || idx_size > max_size) {
- munmap(idx_map, idx_size);
- return error("wrong index v2 file size in %s", path);
- }
- if (idx_size != min_size &&
- /*
- * make sure we can deal with large pack offsets.
- * 31-bit signed offset won't be enough, neither
- * 32-bit unsigned one will be.
- */
- (sizeof(off_t) <= 4)) {
- munmap(idx_map, idx_size);
- return error("pack too large for current definition of off_t in %s", path);
- }
- }
-
- p->index_version = version;
- p->index_data = idx_map;
- p->index_size = idx_size;
- p->num_objects = nr;
- return 0;
-}
-
-int open_pack_index(struct packed_git *p)
-{
- char *idx_name;
- size_t len;
- int ret;
-
- if (p->index_data)
- return 0;
-
- if (!strip_suffix(p->pack_name, ".pack", &len))
- die("BUG: pack_name does not end in .pack");
- idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name);
- ret = check_packed_git_idx(idx_name, p);
- free(idx_name);
- return ret;
-}
-
-static void scan_windows(struct packed_git *p,
- struct packed_git **lru_p,
- struct pack_window **lru_w,
- struct pack_window **lru_l)
-{
- struct pack_window *w, *w_l;
-
- for (w_l = NULL, w = p->windows; w; w = w->next) {
- if (!w->inuse_cnt) {
- if (!*lru_w || w->last_used < (*lru_w)->last_used) {
- *lru_p = p;
- *lru_w = w;
- *lru_l = w_l;
- }
- }
- w_l = w;
- }
-}
-
-static int unuse_one_window(struct packed_git *current)
-{
- struct packed_git *p, *lru_p = NULL;
- struct pack_window *lru_w = NULL, *lru_l = NULL;
-
- if (current)
- scan_windows(current, &lru_p, &lru_w, &lru_l);
- for (p = packed_git; p; p = p->next)
- scan_windows(p, &lru_p, &lru_w, &lru_l);
- if (lru_p) {
- munmap(lru_w->base, lru_w->len);
- pack_mapped -= lru_w->len;
- if (lru_l)
- lru_l->next = lru_w->next;
- else
- lru_p->windows = lru_w->next;
- free(lru_w);
- pack_open_windows--;
- return 1;
- }
- return 0;
-}
-
-void release_pack_memory(size_t need)
-{
- size_t cur = pack_mapped;
- while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
- ; /* nothing */
-}
-
static void mmap_limit_check(size_t length)
{
static size_t limit = 0;
@@ -949,681 +719,6 @@ void *xmmap(void *start, size_t length,
return ret;
}
-void close_pack_windows(struct packed_git *p)
-{
- while (p->windows) {
- struct pack_window *w = p->windows;
-
- if (w->inuse_cnt)
- die("pack '%s' still has open windows to it",
- p->pack_name);
- munmap(w->base, w->len);
- pack_mapped -= w->len;
- pack_open_windows--;
- p->windows = w->next;
- free(w);
- }
-}
-
-static int close_pack_fd(struct packed_git *p)
-{
- if (p->pack_fd < 0)
- return 0;
-
- close(p->pack_fd);
- pack_open_fds--;
- p->pack_fd = -1;
-
- return 1;
-}
-
-static void close_pack(struct packed_git *p)
-{
- close_pack_windows(p);
- close_pack_fd(p);
- close_pack_index(p);
-}
-
-void close_all_packs(void)
-{
- struct packed_git *p;
-
- for (p = packed_git; p; p = p->next)
- if (p->do_not_close)
- die("BUG: want to close pack marked 'do-not-close'");
- else
- close_pack(p);
-}
-
-
-/*
- * The LRU pack is the one with the oldest MRU window, preferring packs
- * with no used windows, or the oldest mtime if it has no windows allocated.
- */
-static void find_lru_pack(struct packed_git *p, struct packed_git **lru_p, struct pack_window **mru_w, int *accept_windows_inuse)
-{
- struct pack_window *w, *this_mru_w;
- int has_windows_inuse = 0;
-
- /*
- * Reject this pack if it has windows and the previously selected
- * one does not. If this pack does not have windows, reject
- * it if the pack file is newer than the previously selected one.
- */
- if (*lru_p && !*mru_w && (p->windows || p->mtime > (*lru_p)->mtime))
- return;
-
- for (w = this_mru_w = p->windows; w; w = w->next) {
- /*
- * Reject this pack if any of its windows are in use,
- * but the previously selected pack did not have any
- * inuse windows. Otherwise, record that this pack
- * has windows in use.
- */
- if (w->inuse_cnt) {
- if (*accept_windows_inuse)
- has_windows_inuse = 1;
- else
- return;
- }
-
- if (w->last_used > this_mru_w->last_used)
- this_mru_w = w;
-
- /*
- * Reject this pack if it has windows that have been
- * used more recently than the previously selected pack.
- * If the previously selected pack had windows inuse and
- * we have not encountered a window in this pack that is
- * inuse, skip this check since we prefer a pack with no
- * inuse windows to one that has inuse windows.
- */
- if (*mru_w && *accept_windows_inuse == has_windows_inuse &&
- this_mru_w->last_used > (*mru_w)->last_used)
- return;
- }
-
- /*
- * Select this pack.
- */
- *mru_w = this_mru_w;
- *lru_p = p;
- *accept_windows_inuse = has_windows_inuse;
-}
-
-static int close_one_pack(void)
-{
- struct packed_git *p, *lru_p = NULL;
- struct pack_window *mru_w = NULL;
- int accept_windows_inuse = 1;
-
- for (p = packed_git; p; p = p->next) {
- if (p->pack_fd == -1)
- continue;
- find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);
- }
-
- if (lru_p)
- return close_pack_fd(lru_p);
-
- return 0;
-}
-
-void unuse_pack(struct pack_window **w_cursor)
-{
- struct pack_window *w = *w_cursor;
- if (w) {
- w->inuse_cnt--;
- *w_cursor = NULL;
- }
-}
-
-void close_pack_index(struct packed_git *p)
-{
- if (p->index_data) {
- munmap((void *)p->index_data, p->index_size);
- p->index_data = NULL;
- }
-}
-
-static unsigned int get_max_fd_limit(void)
-{
-#ifdef RLIMIT_NOFILE
- {
- struct rlimit lim;
-
- if (!getrlimit(RLIMIT_NOFILE, &lim))
- return lim.rlim_cur;
- }
-#endif
-
-#ifdef _SC_OPEN_MAX
- {
- long open_max = sysconf(_SC_OPEN_MAX);
- if (0 < open_max)
- return open_max;
- /*
- * Otherwise, we got -1 for one of the two
- * reasons:
- *
- * (1) sysconf() did not understand _SC_OPEN_MAX
- * and signaled an error with -1; or
- * (2) sysconf() said there is no limit.
- *
- * We _could_ clear errno before calling sysconf() to
- * tell these two cases apart and return a huge number
- * in the latter case to let the caller cap it to a
- * value that is not so selfish, but letting the
- * fallback OPEN_MAX codepath take care of these cases
- * is a lot simpler.
- */
- }
-#endif
-
-#ifdef OPEN_MAX
- return OPEN_MAX;
-#else
- return 1; /* see the caller ;-) */
-#endif
-}
-
-/*
- * Do not call this directly as this leaks p->pack_fd on error return;
- * call open_packed_git() instead.
- */
-static int open_packed_git_1(struct packed_git *p)
-{
- struct stat st;
- struct pack_header hdr;
- unsigned char sha1[20];
- unsigned char *idx_sha1;
- long fd_flag;
-
- if (!p->index_data && open_pack_index(p))
- return error("packfile %s index unavailable", p->pack_name);
-
- if (!pack_max_fds) {
- unsigned int max_fds = get_max_fd_limit();
-
- /* Save 3 for stdin/stdout/stderr, 22 for work */
- if (25 < max_fds)
- pack_max_fds = max_fds - 25;
- else
- pack_max_fds = 1;
- }
-
- while (pack_max_fds <= pack_open_fds && close_one_pack())
- ; /* nothing */
-
- p->pack_fd = git_open(p->pack_name);
- if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
- return -1;
- pack_open_fds++;
-
- /* If we created the struct before we had the pack we lack size. */
- if (!p->pack_size) {
- if (!S_ISREG(st.st_mode))
- return error("packfile %s not a regular file", p->pack_name);
- p->pack_size = st.st_size;
- } else if (p->pack_size != st.st_size)
- return error("packfile %s size changed", p->pack_name);
-
- /* We leave these file descriptors open with sliding mmap;
- * there is no point keeping them open across exec(), though.
- */
- fd_flag = fcntl(p->pack_fd, F_GETFD, 0);
- if (fd_flag < 0)
- return error("cannot determine file descriptor flags");
- fd_flag |= FD_CLOEXEC;
- if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
- return error("cannot set FD_CLOEXEC");
-
- /* Verify we recognize this pack file format. */
- if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
- return error("file %s is far too short to be a packfile", p->pack_name);
- if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
- return error("file %s is not a GIT packfile", p->pack_name);
- if (!pack_version_ok(hdr.hdr_version))
- return error("packfile %s is version %"PRIu32" and not"
- " supported (try upgrading GIT to a newer version)",
- p->pack_name, ntohl(hdr.hdr_version));
-
- /* Verify the pack matches its index. */
- if (p->num_objects != ntohl(hdr.hdr_entries))
- return error("packfile %s claims to have %"PRIu32" objects"
- " while index indicates %"PRIu32" objects",
- p->pack_name, ntohl(hdr.hdr_entries),
- p->num_objects);
- if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
- return error("end of packfile %s is unavailable", p->pack_name);
- if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
- return error("packfile %s signature is unavailable", p->pack_name);
- idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
- if (hashcmp(sha1, idx_sha1))
- return error("packfile %s does not match index", p->pack_name);
- return 0;
-}
-
-static int open_packed_git(struct packed_git *p)
-{
- if (!open_packed_git_1(p))
- return 0;
- close_pack_fd(p);
- return -1;
-}
-
-static int in_window(struct pack_window *win, off_t offset)
-{
- /* We must promise at least 20 bytes (one hash) after the
- * offset is available from this window, otherwise the offset
- * is not actually in this window and a different window (which
- * has that one hash excess) must be used. This is to support
- * the object header and delta base parsing routines below.
- */
- off_t win_off = win->offset;
- return win_off <= offset
- && (offset + 20) <= (win_off + win->len);
-}
-
-unsigned char *use_pack(struct packed_git *p,
- struct pack_window **w_cursor,
- off_t offset,
- unsigned long *left)
-{
- struct pack_window *win = *w_cursor;
-
- /* Since packfiles end in a hash of their content and it's
- * pointless to ask for an offset into the middle of that
- * hash, and the in_window function above wouldn't match
- * don't allow an offset too close to the end of the file.
- */
- if (!p->pack_size && p->pack_fd == -1 && open_packed_git(p))
- die("packfile %s cannot be accessed", p->pack_name);
- if (offset > (p->pack_size - 20))
- die("offset beyond end of packfile (truncated pack?)");
- if (offset < 0)
- die(_("offset before end of packfile (broken .idx?)"));
-
- if (!win || !in_window(win, offset)) {
- if (win)
- win->inuse_cnt--;
- for (win = p->windows; win; win = win->next) {
- if (in_window(win, offset))
- break;
- }
- if (!win) {
- size_t window_align = packed_git_window_size / 2;
- off_t len;
-
- if (p->pack_fd == -1 && open_packed_git(p))
- die("packfile %s cannot be accessed", p->pack_name);
-
- win = xcalloc(1, sizeof(*win));
- win->offset = (offset / window_align) * window_align;
- len = p->pack_size - win->offset;
- if (len > packed_git_window_size)
- len = packed_git_window_size;
- win->len = (size_t)len;
- pack_mapped += win->len;
- while (packed_git_limit < pack_mapped
- && unuse_one_window(p))
- ; /* nothing */
- win->base = xmmap(NULL, win->len,
- PROT_READ, MAP_PRIVATE,
- p->pack_fd, win->offset);
- if (win->base == MAP_FAILED)
- die_errno("packfile %s cannot be mapped",
- p->pack_name);
- if (!win->offset && win->len == p->pack_size
- && !p->do_not_close)
- close_pack_fd(p);
- pack_mmap_calls++;
- pack_open_windows++;
- if (pack_mapped > peak_pack_mapped)
- peak_pack_mapped = pack_mapped;
- if (pack_open_windows > peak_pack_open_windows)
- peak_pack_open_windows = pack_open_windows;
- win->next = p->windows;
- p->windows = win;
- }
- }
- if (win != *w_cursor) {
- win->last_used = pack_used_ctr++;
- win->inuse_cnt++;
- *w_cursor = win;
- }
- offset -= win->offset;
- if (left)
- *left = win->len - xsize_t(offset);
- return win->base + offset;
-}
-
-static struct packed_git *alloc_packed_git(int extra)
-{
- struct packed_git *p = xmalloc(st_add(sizeof(*p), extra));
- memset(p, 0, sizeof(*p));
- p->pack_fd = -1;
- return p;
-}
-
-static void try_to_free_pack_memory(size_t size)
-{
- release_pack_memory(size);
-}
-
-struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
-{
- static int have_set_try_to_free_routine;
- struct stat st;
- size_t alloc;
- struct packed_git *p;
-
- if (!have_set_try_to_free_routine) {
- have_set_try_to_free_routine = 1;
- set_try_to_free_routine(try_to_free_pack_memory);
- }
-
- /*
- * Make sure a corresponding .pack file exists and that
- * the index looks sane.
- */
- if (!strip_suffix_mem(path, &path_len, ".idx"))
- return NULL;
-
- /*
- * ".pack" is long enough to hold any suffix we're adding (and
- * the use xsnprintf double-checks that)
- */
- alloc = st_add3(path_len, strlen(".pack"), 1);
- p = alloc_packed_git(alloc);
- memcpy(p->pack_name, path, path_len);
-
- xsnprintf(p->pack_name + path_len, alloc - path_len, ".keep");
- if (!access(p->pack_name, F_OK))
- p->pack_keep = 1;
-
- xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
- if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
- free(p);
- return NULL;
- }
-
- /* ok, it looks sane as far as we can check without
- * actually mapping the pack file.
- */
- p->pack_size = st.st_size;
- p->pack_local = local;
- p->mtime = st.st_mtime;
- if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
- hashclr(p->sha1);
- return p;
-}
-
-struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
-{
- const char *path = sha1_pack_name(sha1);
- size_t alloc = st_add(strlen(path), 1);
- struct packed_git *p = alloc_packed_git(alloc);
-
- memcpy(p->pack_name, path, alloc); /* includes NUL */
- hashcpy(p->sha1, sha1);
- if (check_packed_git_idx(idx_path, p)) {
- free(p);
- return NULL;
- }
-
- return p;
-}
-
-void install_packed_git(struct packed_git *pack)
-{
- if (pack->pack_fd != -1)
- pack_open_fds++;
-
- pack->next = packed_git;
- packed_git = pack;
-}
-
-void (*report_garbage)(unsigned seen_bits, const char *path);
-
-static void report_helper(const struct string_list *list,
- int seen_bits, int first, int last)
-{
- if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX))
- return;
-
- for (; first < last; first++)
- report_garbage(seen_bits, list->items[first].string);
-}
-
-static void report_pack_garbage(struct string_list *list)
-{
- int i, baselen = -1, first = 0, seen_bits = 0;
-
- if (!report_garbage)
- return;
-
- string_list_sort(list);
-
- for (i = 0; i < list->nr; i++) {
- const char *path = list->items[i].string;
- if (baselen != -1 &&
- strncmp(path, list->items[first].string, baselen)) {
- report_helper(list, seen_bits, first, i);
- baselen = -1;
- seen_bits = 0;
- }
- if (baselen == -1) {
- const char *dot = strrchr(path, '.');
- if (!dot) {
- report_garbage(PACKDIR_FILE_GARBAGE, path);
- continue;
- }
- baselen = dot - path + 1;
- first = i;
- }
- if (!strcmp(path + baselen, "pack"))
- seen_bits |= 1;
- else if (!strcmp(path + baselen, "idx"))
- seen_bits |= 2;
- }
- report_helper(list, seen_bits, first, list->nr);
-}
-
-static void prepare_packed_git_one(char *objdir, int local)
-{
- struct strbuf path = STRBUF_INIT;
- size_t dirnamelen;
- DIR *dir;
- struct dirent *de;
- struct string_list garbage = STRING_LIST_INIT_DUP;
-
- strbuf_addstr(&path, objdir);
- strbuf_addstr(&path, "/pack");
- dir = opendir(path.buf);
- if (!dir) {
- if (errno != ENOENT)
- error_errno("unable to open object pack directory: %s",
- path.buf);
- strbuf_release(&path);
- return;
- }
- strbuf_addch(&path, '/');
- dirnamelen = path.len;
- while ((de = readdir(dir)) != NULL) {
- struct packed_git *p;
- size_t base_len;
-
- if (is_dot_or_dotdot(de->d_name))
- continue;
-
- strbuf_setlen(&path, dirnamelen);
- strbuf_addstr(&path, de->d_name);
-
- base_len = path.len;
- if (strip_suffix_mem(path.buf, &base_len, ".idx")) {
- /* Don't reopen a pack we already have. */
- for (p = packed_git; p; p = p->next) {
- size_t len;
- if (strip_suffix(p->pack_name, ".pack", &len) &&
- len == base_len &&
- !memcmp(p->pack_name, path.buf, len))
- break;
- }
- if (p == NULL &&
- /*
- * See if it really is a valid .idx file with
- * corresponding .pack file that we can map.
- */
- (p = add_packed_git(path.buf, path.len, local)) != NULL)
- install_packed_git(p);
- }
-
- if (!report_garbage)
- continue;
-
- if (ends_with(de->d_name, ".idx") ||
- ends_with(de->d_name, ".pack") ||
- ends_with(de->d_name, ".bitmap") ||
- ends_with(de->d_name, ".keep"))
- string_list_append(&garbage, path.buf);
- else
- report_garbage(PACKDIR_FILE_GARBAGE, path.buf);
- }
- closedir(dir);
- report_pack_garbage(&garbage);
- string_list_clear(&garbage, 0);
- strbuf_release(&path);
-}
-
-static int approximate_object_count_valid;
-
-/*
- * Give a fast, rough count of the number of objects in the repository. This
- * ignores loose objects completely. If you have a lot of them, then either
- * you should repack because your performance will be awful, or they are
- * all unreachable objects about to be pruned, in which case they're not really
- * interesting as a measure of repo size in the first place.
- */
-unsigned long approximate_object_count(void)
-{
- static unsigned long count;
- if (!approximate_object_count_valid) {
- struct packed_git *p;
-
- prepare_packed_git();
- count = 0;
- for (p = packed_git; p; p = p->next) {
- if (open_pack_index(p))
- continue;
- count += p->num_objects;
- }
- }
- return count;
-}
-
-static void *get_next_packed_git(const void *p)
-{
- return ((const struct packed_git *)p)->next;
-}
-
-static void set_next_packed_git(void *p, void *next)
-{
- ((struct packed_git *)p)->next = next;
-}
-
-static int sort_pack(const void *a_, const void *b_)
-{
- const struct packed_git *a = a_;
- const struct packed_git *b = b_;
- int st;
-
- /*
- * Local packs tend to contain objects specific to our
- * variant of the project than remote ones. In addition,
- * remote ones could be on a network mounted filesystem.
- * Favor local ones for these reasons.
- */
- st = a->pack_local - b->pack_local;
- if (st)
- return -st;
-
- /*
- * Younger packs tend to contain more recent objects,
- * and more recent objects tend to get accessed more
- * often.
- */
- if (a->mtime < b->mtime)
- return 1;
- else if (a->mtime == b->mtime)
- return 0;
- return -1;
-}
-
-static void rearrange_packed_git(void)
-{
- packed_git = llist_mergesort(packed_git, get_next_packed_git,
- set_next_packed_git, sort_pack);
-}
-
-static void prepare_packed_git_mru(void)
-{
- struct packed_git *p;
-
- mru_clear(packed_git_mru);
- for (p = packed_git; p; p = p->next)
- mru_append(packed_git_mru, p);
-}
-
-static int prepare_packed_git_run_once = 0;
-void prepare_packed_git(void)
-{
- struct alternate_object_database *alt;
-
- if (prepare_packed_git_run_once)
- return;
- prepare_packed_git_one(get_object_directory(), 1);
- prepare_alt_odb();
- for (alt = alt_odb_list; alt; alt = alt->next)
- prepare_packed_git_one(alt->path, 0);
- rearrange_packed_git();
- prepare_packed_git_mru();
- prepare_packed_git_run_once = 1;
-}
-
-void reprepare_packed_git(void)
-{
- approximate_object_count_valid = 0;
- prepare_packed_git_run_once = 0;
- prepare_packed_git();
-}
-
-static void mark_bad_packed_object(struct packed_git *p,
- const unsigned char *sha1)
-{
- unsigned i;
- for (i = 0; i < p->num_bad_objects; i++)
- if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
- return;
- p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
- st_mult(GIT_MAX_RAWSZ,
- st_add(p->num_bad_objects, 1)));
- hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
- p->num_bad_objects++;
-}
-
-static const struct packed_git *has_packed_and_bad(const unsigned char *sha1)
-{
- struct packed_git *p;
- unsigned i;
-
- for (p = packed_git; p; p = p->next)
- for (i = 0; i < p->num_bad_objects; i++)
- if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
- return p;
- return NULL;
-}
-
/*
* With an in-core object data in "map", rehash it to make sure the
* object name actually matches "sha1" to detect object corruption.
@@ -1794,31 +889,6 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
return map_sha1_file_1(NULL, sha1, size);
}
-unsigned long unpack_object_header_buffer(const unsigned char *buf,
- unsigned long len, enum object_type *type, unsigned long *sizep)
-{
- unsigned shift;
- unsigned long size, c;
- unsigned long used = 0;
-
- c = buf[used++];
- *type = (c >> 4) & 7;
- size = c & 15;
- shift = 4;
- while (c & 0x80) {
- if (len <= used || bitsizeof(long) <= shift) {
- error("bad object header");
- size = used = 0;
- break;
- }
- c = buf[used++];
- size += (c & 0x7f) << shift;
- shift += 7;
- }
- *sizep = size;
- return used;
-}
-
static int unpack_sha1_short_header(git_zstream *stream,
unsigned char *map, unsigned long mapsize,
void *buffer, unsigned long bufsiz)
@@ -2005,904 +1075,6 @@ int parse_sha1_header(const char *hdr, unsigned long *sizep)
return parse_sha1_header_extended(hdr, &oi, 0);
}
-unsigned long get_size_from_delta(struct packed_git *p,
- struct pack_window **w_curs,
- off_t curpos)
-{
- const unsigned char *data;
- unsigned char delta_head[20], *in;
- git_zstream stream;
- int st;
-
- memset(&stream, 0, sizeof(stream));
- stream.next_out = delta_head;
- stream.avail_out = sizeof(delta_head);
-
- git_inflate_init(&stream);
- do {
- in = use_pack(p, w_curs, curpos, &stream.avail_in);
- stream.next_in = in;
- st = git_inflate(&stream, Z_FINISH);
- curpos += stream.next_in - in;
- } while ((st == Z_OK || st == Z_BUF_ERROR) &&
- stream.total_out < sizeof(delta_head));
- git_inflate_end(&stream);
- if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) {
- error("delta data unpack-initial failed");
- return 0;
- }
-
- /* Examine the initial part of the delta to figure out
- * the result size.
- */
- data = delta_head;
-
- /* ignore base size */
- get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
-
- /* Read the result size */
- return get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
-}
-
-static off_t get_delta_base(struct packed_git *p,
- struct pack_window **w_curs,
- off_t *curpos,
- enum object_type type,
- off_t delta_obj_offset)
-{
- unsigned char *base_info = use_pack(p, w_curs, *curpos, NULL);
- off_t base_offset;
-
- /* use_pack() assured us we have [base_info, base_info + 20)
- * as a range that we can look at without walking off the
- * end of the mapped window. Its actually the hash size
- * that is assured. An OFS_DELTA longer than the hash size
- * is stupid, as then a REF_DELTA would be smaller to store.
- */
- if (type == OBJ_OFS_DELTA) {
- unsigned used = 0;
- unsigned char c = base_info[used++];
- base_offset = c & 127;
- while (c & 128) {
- base_offset += 1;
- if (!base_offset || MSB(base_offset, 7))
- return 0; /* overflow */
- c = base_info[used++];
- base_offset = (base_offset << 7) + (c & 127);
- }
- base_offset = delta_obj_offset - base_offset;
- if (base_offset <= 0 || base_offset >= delta_obj_offset)
- return 0; /* out of bound */
- *curpos += used;
- } else if (type == OBJ_REF_DELTA) {
- /* The base entry _must_ be in the same pack */
- base_offset = find_pack_entry_one(base_info, p);
- *curpos += 20;
- } else
- die("I am totally screwed");
- return base_offset;
-}
-
-/*
- * Like get_delta_base above, but we return the sha1 instead of the pack
- * offset. This means it is cheaper for REF deltas (we do not have to do
- * the final object lookup), but more expensive for OFS deltas (we
- * have to load the revidx to convert the offset back into a sha1).
- */
-static const unsigned char *get_delta_base_sha1(struct packed_git *p,
- struct pack_window **w_curs,
- off_t curpos,
- enum object_type type,
- off_t delta_obj_offset)
-{
- if (type == OBJ_REF_DELTA) {
- unsigned char *base = use_pack(p, w_curs, curpos, NULL);
- return base;
- } else if (type == OBJ_OFS_DELTA) {
- struct revindex_entry *revidx;
- off_t base_offset = get_delta_base(p, w_curs, &curpos,
- type, delta_obj_offset);
-
- if (!base_offset)
- return NULL;
-
- revidx = find_pack_revindex(p, base_offset);
- if (!revidx)
- return NULL;
-
- return nth_packed_object_sha1(p, revidx->nr);
- } else
- return NULL;
-}
-
-int unpack_object_header(struct packed_git *p,
- struct pack_window **w_curs,
- off_t *curpos,
- unsigned long *sizep)
-{
- unsigned char *base;
- unsigned long left;
- unsigned long used;
- enum object_type type;
-
- /* use_pack() assures us we have [base, base + 20) available
- * as a range that we can look at. (Its actually the hash
- * size that is assured.) With our object header encoding
- * the maximum deflated object size is 2^137, which is just
- * insane, so we know won't exceed what we have been given.
- */
- base = use_pack(p, w_curs, *curpos, &left);
- used = unpack_object_header_buffer(base, left, &type, sizep);
- if (!used) {
- type = OBJ_BAD;
- } else
- *curpos += used;
-
- return type;
-}
-
-static int retry_bad_packed_offset(struct packed_git *p, off_t obj_offset)
-{
- int type;
- struct revindex_entry *revidx;
- const unsigned char *sha1;
- revidx = find_pack_revindex(p, obj_offset);
- if (!revidx)
- return OBJ_BAD;
- sha1 = nth_packed_object_sha1(p, revidx->nr);
- mark_bad_packed_object(p, sha1);
- type = sha1_object_info(sha1, NULL);
- if (type <= OBJ_NONE)
- return OBJ_BAD;
- return type;
-}
-
-#define POI_STACK_PREALLOC 64
-
-static enum object_type packed_to_object_type(struct packed_git *p,
- off_t obj_offset,
- enum object_type type,
- struct pack_window **w_curs,
- off_t curpos)
-{
- off_t small_poi_stack[POI_STACK_PREALLOC];
- off_t *poi_stack = small_poi_stack;
- int poi_stack_nr = 0, poi_stack_alloc = POI_STACK_PREALLOC;
-
- while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
- off_t base_offset;
- unsigned long size;
- /* Push the object we're going to leave behind */
- if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
- poi_stack_alloc = alloc_nr(poi_stack_nr);
- ALLOC_ARRAY(poi_stack, poi_stack_alloc);
- memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
- } else {
- ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
- }
- poi_stack[poi_stack_nr++] = obj_offset;
- /* If parsing the base offset fails, just unwind */
- base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
- if (!base_offset)
- goto unwind;
- curpos = obj_offset = base_offset;
- type = unpack_object_header(p, w_curs, &curpos, &size);
- if (type <= OBJ_NONE) {
- /* If getting the base itself fails, we first
- * retry the base, otherwise unwind */
- type = retry_bad_packed_offset(p, base_offset);
- if (type > OBJ_NONE)
- goto out;
- goto unwind;
- }
- }
-
- switch (type) {
- case OBJ_BAD:
- case OBJ_COMMIT:
- case OBJ_TREE:
- case OBJ_BLOB:
- case OBJ_TAG:
- break;
- default:
- error("unknown object type %i at offset %"PRIuMAX" in %s",
- type, (uintmax_t)obj_offset, p->pack_name);
- type = OBJ_BAD;
- }
-
-out:
- if (poi_stack != small_poi_stack)
- free(poi_stack);
- return type;
-
-unwind:
- while (poi_stack_nr) {
- obj_offset = poi_stack[--poi_stack_nr];
- type = retry_bad_packed_offset(p, obj_offset);
- if (type > OBJ_NONE)
- goto out;
- }
- type = OBJ_BAD;
- goto out;
-}
-
-static struct hashmap delta_base_cache;
-static size_t delta_base_cached;
-
-static LIST_HEAD(delta_base_cache_lru);
-
-struct delta_base_cache_key {
- struct packed_git *p;
- off_t base_offset;
-};
-
-struct delta_base_cache_entry {
- struct hashmap hash;
- struct delta_base_cache_key key;
- struct list_head lru;
- void *data;
- unsigned long size;
- enum object_type type;
-};
-
-static unsigned int pack_entry_hash(struct packed_git *p, off_t base_offset)
-{
- unsigned int hash;
-
- hash = (unsigned int)(intptr_t)p + (unsigned int)base_offset;
- hash += (hash >> 8) + (hash >> 16);
- return hash;
-}
-
-static struct delta_base_cache_entry *
-get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)
-{
- struct hashmap_entry entry;
- struct delta_base_cache_key key;
-
- if (!delta_base_cache.cmpfn)
- return NULL;
-
- hashmap_entry_init(&entry, pack_entry_hash(p, base_offset));
- key.p = p;
- key.base_offset = base_offset;
- return hashmap_get(&delta_base_cache, &entry, &key);
-}
-
-static int delta_base_cache_key_eq(const struct delta_base_cache_key *a,
- const struct delta_base_cache_key *b)
-{
- return a->p == b->p && a->base_offset == b->base_offset;
-}
-
-static int delta_base_cache_hash_cmp(const void *unused_cmp_data,
- const void *va, const void *vb,
- const void *vkey)
-{
- const struct delta_base_cache_entry *a = va, *b = vb;
- const struct delta_base_cache_key *key = vkey;
- if (key)
- return !delta_base_cache_key_eq(&a->key, key);
- else
- return !delta_base_cache_key_eq(&a->key, &b->key);
-}
-
-static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
-{
- return !!get_delta_base_cache_entry(p, base_offset);
-}
-
-/*
- * Remove the entry from the cache, but do _not_ free the associated
- * entry data. The caller takes ownership of the "data" buffer, and
- * should copy out any fields it wants before detaching.
- */
-static void detach_delta_base_cache_entry(struct delta_base_cache_entry *ent)
-{
- hashmap_remove(&delta_base_cache, ent, &ent->key);
- list_del(&ent->lru);
- delta_base_cached -= ent->size;
- free(ent);
-}
-
-static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
- unsigned long *base_size, enum object_type *type)
-{
- struct delta_base_cache_entry *ent;
-
- ent = get_delta_base_cache_entry(p, base_offset);
- if (!ent)
- return unpack_entry(p, base_offset, type, base_size);
-
- if (type)
- *type = ent->type;
- if (base_size)
- *base_size = ent->size;
- return xmemdupz(ent->data, ent->size);
-}
-
-static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
-{
- free(ent->data);
- detach_delta_base_cache_entry(ent);
-}
-
-void clear_delta_base_cache(void)
-{
- struct list_head *lru, *tmp;
- list_for_each_safe(lru, tmp, &delta_base_cache_lru) {
- struct delta_base_cache_entry *entry =
- list_entry(lru, struct delta_base_cache_entry, lru);
- release_delta_base_cache(entry);
- }
-}
-
-static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
- void *base, unsigned long base_size, enum object_type type)
-{
- struct delta_base_cache_entry *ent = xmalloc(sizeof(*ent));
- struct list_head *lru, *tmp;
-
- delta_base_cached += base_size;
-
- list_for_each_safe(lru, tmp, &delta_base_cache_lru) {
- struct delta_base_cache_entry *f =
- list_entry(lru, struct delta_base_cache_entry, lru);
- if (delta_base_cached <= delta_base_cache_limit)
- break;
- release_delta_base_cache(f);
- }
-
- ent->key.p = p;
- ent->key.base_offset = base_offset;
- ent->type = type;
- ent->data = base;
- ent->size = base_size;
- list_add_tail(&ent->lru, &delta_base_cache_lru);
-
- if (!delta_base_cache.cmpfn)
- hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0);
- hashmap_entry_init(ent, pack_entry_hash(p, base_offset));
- hashmap_add(&delta_base_cache, ent);
-}
-
-int packed_object_info(struct packed_git *p, off_t obj_offset,
- struct object_info *oi)
-{
- struct pack_window *w_curs = NULL;
- unsigned long size;
- off_t curpos = obj_offset;
- enum object_type type;
-
- /*
- * We always get the representation type, but only convert it to
- * a "real" type later if the caller is interested.
- */
- if (oi->contentp) {
- *oi->contentp = cache_or_unpack_entry(p, obj_offset, oi->sizep,
- &type);
- if (!*oi->contentp)
- type = OBJ_BAD;
- } else {
- type = unpack_object_header(p, &w_curs, &curpos, &size);
- }
-
- if (!oi->contentp && oi->sizep) {
- if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
- off_t tmp_pos = curpos;
- off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
- type, obj_offset);
- if (!base_offset) {
- type = OBJ_BAD;
- goto out;
- }
- *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
- if (*oi->sizep == 0) {
- type = OBJ_BAD;
- goto out;
- }
- } else {
- *oi->sizep = size;
- }
- }
-
- if (oi->disk_sizep) {
- struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
- *oi->disk_sizep = revidx[1].offset - obj_offset;
- }
-
- if (oi->typep || oi->typename) {
- enum object_type ptot;
- ptot = packed_to_object_type(p, obj_offset, type, &w_curs,
- curpos);
- if (oi->typep)
- *oi->typep = ptot;
- if (oi->typename) {
- const char *tn = typename(ptot);
- if (tn)
- strbuf_addstr(oi->typename, tn);
- }
- if (ptot < 0) {
- type = OBJ_BAD;
- goto out;
- }
- }
-
- if (oi->delta_base_sha1) {
- if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
- const unsigned char *base;
-
- base = get_delta_base_sha1(p, &w_curs, curpos,
- type, obj_offset);
- if (!base) {
- type = OBJ_BAD;
- goto out;
- }
-
- hashcpy(oi->delta_base_sha1, base);
- } else
- hashclr(oi->delta_base_sha1);
- }
-
-out:
- unuse_pack(&w_curs);
- return type;
-}
-
-static void *unpack_compressed_entry(struct packed_git *p,
- struct pack_window **w_curs,
- off_t curpos,
- unsigned long size)
-{
- int st;
- git_zstream stream;
- unsigned char *buffer, *in;
-
- buffer = xmallocz_gently(size);
- if (!buffer)
- return NULL;
- memset(&stream, 0, sizeof(stream));
- stream.next_out = buffer;
- stream.avail_out = size + 1;
-
- git_inflate_init(&stream);
- do {
- in = use_pack(p, w_curs, curpos, &stream.avail_in);
- stream.next_in = in;
- st = git_inflate(&stream, Z_FINISH);
- if (!stream.avail_out)
- break; /* the payload is larger than it should be */
- curpos += stream.next_in - in;
- } while (st == Z_OK || st == Z_BUF_ERROR);
- git_inflate_end(&stream);
- if ((st != Z_STREAM_END) || stream.total_out != size) {
- free(buffer);
- return NULL;
- }
-
- return buffer;
-}
-
-static void *read_object(const unsigned char *sha1, enum object_type *type,
- unsigned long *size);
-
-static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
-{
- static struct trace_key pack_access = TRACE_KEY_INIT(PACK_ACCESS);
- trace_printf_key(&pack_access, "%s %"PRIuMAX"\n",
- p->pack_name, (uintmax_t)obj_offset);
-}
-
-int do_check_packed_object_crc;
-
-#define UNPACK_ENTRY_STACK_PREALLOC 64
-struct unpack_entry_stack_ent {
- off_t obj_offset;
- off_t curpos;
- unsigned long size;
-};
-
-void *unpack_entry(struct packed_git *p, off_t obj_offset,
- enum object_type *final_type, unsigned long *final_size)
-{
- struct pack_window *w_curs = NULL;
- off_t curpos = obj_offset;
- void *data = NULL;
- unsigned long size;
- enum object_type type;
- struct unpack_entry_stack_ent small_delta_stack[UNPACK_ENTRY_STACK_PREALLOC];
- struct unpack_entry_stack_ent *delta_stack = small_delta_stack;
- int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
- int base_from_cache = 0;
-
- write_pack_access_log(p, obj_offset);
-
- /* PHASE 1: drill down to the innermost base object */
- for (;;) {
- off_t base_offset;
- int i;
- struct delta_base_cache_entry *ent;
-
- ent = get_delta_base_cache_entry(p, curpos);
- if (ent) {
- type = ent->type;
- data = ent->data;
- size = ent->size;
- detach_delta_base_cache_entry(ent);
- base_from_cache = 1;
- break;
- }
-
- if (do_check_packed_object_crc && p->index_version > 1) {
- struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
- off_t len = revidx[1].offset - obj_offset;
- if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
- const unsigned char *sha1 =
- nth_packed_object_sha1(p, revidx->nr);
- error("bad packed object CRC for %s",
- sha1_to_hex(sha1));
- mark_bad_packed_object(p, sha1);
- unuse_pack(&w_curs);
- return NULL;
- }
- }
-
- type = unpack_object_header(p, &w_curs, &curpos, &size);
- if (type != OBJ_OFS_DELTA && type != OBJ_REF_DELTA)
- break;
-
- base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
- if (!base_offset) {
- error("failed to validate delta base reference "
- "at offset %"PRIuMAX" from %s",
- (uintmax_t)curpos, p->pack_name);
- /* bail to phase 2, in hopes of recovery */
- data = NULL;
- break;
- }
-
- /* push object, proceed to base */
- if (delta_stack_nr >= delta_stack_alloc
- && delta_stack == small_delta_stack) {
- delta_stack_alloc = alloc_nr(delta_stack_nr);
- ALLOC_ARRAY(delta_stack, delta_stack_alloc);
- memcpy(delta_stack, small_delta_stack,
- sizeof(*delta_stack)*delta_stack_nr);
- } else {
- ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
- }
- i = delta_stack_nr++;
- delta_stack[i].obj_offset = obj_offset;
- delta_stack[i].curpos = curpos;
- delta_stack[i].size = size;
-
- curpos = obj_offset = base_offset;
- }
-
- /* PHASE 2: handle the base */
- switch (type) {
- case OBJ_OFS_DELTA:
- case OBJ_REF_DELTA:
- if (data)
- die("BUG: unpack_entry: left loop at a valid delta");
- break;
- case OBJ_COMMIT:
- case OBJ_TREE:
- case OBJ_BLOB:
- case OBJ_TAG:
- if (!base_from_cache)
- data = unpack_compressed_entry(p, &w_curs, curpos, size);
- break;
- default:
- data = NULL;
- error("unknown object type %i at offset %"PRIuMAX" in %s",
- type, (uintmax_t)obj_offset, p->pack_name);
- }
-
- /* PHASE 3: apply deltas in order */
-
- /* invariants:
- * 'data' holds the base data, or NULL if there was corruption
- */
- while (delta_stack_nr) {
- void *delta_data;
- void *base = data;
- void *external_base = NULL;
- unsigned long delta_size, base_size = size;
- int i;
-
- data = NULL;
-
- if (base)
- add_delta_base_cache(p, obj_offset, base, base_size, type);
-
- if (!base) {
- /*
- * We're probably in deep shit, but let's try to fetch
- * the required base anyway from another pack or loose.
- * This is costly but should happen only in the presence
- * of a corrupted pack, and is better than failing outright.
- */
- struct revindex_entry *revidx;
- const unsigned char *base_sha1;
- revidx = find_pack_revindex(p, obj_offset);
- if (revidx) {
- base_sha1 = nth_packed_object_sha1(p, revidx->nr);
- error("failed to read delta base object %s"
- " at offset %"PRIuMAX" from %s",
- sha1_to_hex(base_sha1), (uintmax_t)obj_offset,
- p->pack_name);
- mark_bad_packed_object(p, base_sha1);
- base = read_object(base_sha1, &type, &base_size);
- external_base = base;
- }
- }
-
- i = --delta_stack_nr;
- obj_offset = delta_stack[i].obj_offset;
- curpos = delta_stack[i].curpos;
- delta_size = delta_stack[i].size;
-
- if (!base)
- continue;
-
- delta_data = unpack_compressed_entry(p, &w_curs, curpos, delta_size);
-
- if (!delta_data) {
- error("failed to unpack compressed delta "
- "at offset %"PRIuMAX" from %s",
- (uintmax_t)curpos, p->pack_name);
- data = NULL;
- free(external_base);
- continue;
- }
-
- data = patch_delta(base, base_size,
- delta_data, delta_size,
- &size);
-
- /*
- * We could not apply the delta; warn the user, but keep going.
- * Our failure will be noticed either in the next iteration of
- * the loop, or if this is the final delta, in the caller when
- * we return NULL. Those code paths will take care of making
- * a more explicit warning and retrying with another copy of
- * the object.
- */
- if (!data)
- error("failed to apply delta");
-
- free(delta_data);
- free(external_base);
- }
-
- if (final_type)
- *final_type = type;
- if (final_size)
- *final_size = size;
-
- unuse_pack(&w_curs);
-
- if (delta_stack != small_delta_stack)
- free(delta_stack);
-
- return data;
-}
-
-const unsigned char *nth_packed_object_sha1(struct packed_git *p,
- uint32_t n)
-{
- const unsigned char *index = p->index_data;
- if (!index) {
- if (open_pack_index(p))
- return NULL;
- index = p->index_data;
- }
- if (n >= p->num_objects)
- return NULL;
- index += 4 * 256;
- if (p->index_version == 1) {
- return index + 24 * n + 4;
- } else {
- index += 8;
- return index + 20 * n;
- }
-}
-
-const struct object_id *nth_packed_object_oid(struct object_id *oid,
- struct packed_git *p,
- uint32_t n)
-{
- const unsigned char *hash = nth_packed_object_sha1(p, n);
- if (!hash)
- return NULL;
- hashcpy(oid->hash, hash);
- return oid;
-}
-
-void check_pack_index_ptr(const struct packed_git *p, const void *vptr)
-{
- const unsigned char *ptr = vptr;
- const unsigned char *start = p->index_data;
- const unsigned char *end = start + p->index_size;
- if (ptr < start)
- die(_("offset before start of pack index for %s (corrupt index?)"),
- p->pack_name);
- /* No need to check for underflow; .idx files must be at least 8 bytes */
- if (ptr >= end - 8)
- die(_("offset beyond end of pack index for %s (truncated index?)"),
- p->pack_name);
-}
-
-off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
-{
- const unsigned char *index = p->index_data;
- index += 4 * 256;
- if (p->index_version == 1) {
- return ntohl(*((uint32_t *)(index + 24 * n)));
- } else {
- uint32_t off;
- index += 8 + p->num_objects * (20 + 4);
- off = ntohl(*((uint32_t *)(index + 4 * n)));
- if (!(off & 0x80000000))
- return off;
- index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
- check_pack_index_ptr(p, index);
- return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
- ntohl(*((uint32_t *)(index + 4)));
- }
-}
-
-off_t find_pack_entry_one(const unsigned char *sha1,
- struct packed_git *p)
-{
- const uint32_t *level1_ofs = p->index_data;
- const unsigned char *index = p->index_data;
- unsigned hi, lo, stride;
- static int use_lookup = -1;
- static int debug_lookup = -1;
-
- if (debug_lookup < 0)
- debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
-
- if (!index) {
- if (open_pack_index(p))
- return 0;
- level1_ofs = p->index_data;
- index = p->index_data;
- }
- if (p->index_version > 1) {
- level1_ofs += 2;
- index += 8;
- }
- index += 4 * 256;
- hi = ntohl(level1_ofs[*sha1]);
- lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
- if (p->index_version > 1) {
- stride = 20;
- } else {
- stride = 24;
- index += 4;
- }
-
- if (debug_lookup)
- printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
- sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
-
- if (use_lookup < 0)
- use_lookup = !!getenv("GIT_USE_LOOKUP");
- if (use_lookup) {
- int pos = sha1_entry_pos(index, stride, 0,
- lo, hi, p->num_objects, sha1);
- if (pos < 0)
- return 0;
- return nth_packed_object_offset(p, pos);
- }
-
- do {
- unsigned mi = (lo + hi) / 2;
- int cmp = hashcmp(index + mi * stride, sha1);
-
- if (debug_lookup)
- printf("lo %u hi %u rg %u mi %u\n",
- lo, hi, hi - lo, mi);
- if (!cmp)
- return nth_packed_object_offset(p, mi);
- if (cmp > 0)
- hi = mi;
- else
- lo = mi+1;
- } while (lo < hi);
- return 0;
-}
-
-int is_pack_valid(struct packed_git *p)
-{
- /* An already open pack is known to be valid. */
- if (p->pack_fd != -1)
- return 1;
-
- /* If the pack has one window completely covering the
- * file size, the pack is known to be valid even if
- * the descriptor is not currently open.
- */
- if (p->windows) {
- struct pack_window *w = p->windows;
-
- if (!w->offset && w->len == p->pack_size)
- return 1;
- }
-
- /* Force the pack to open to prove its valid. */
- return !open_packed_git(p);
-}
-
-static int fill_pack_entry(const unsigned char *sha1,
- struct pack_entry *e,
- struct packed_git *p)
-{
- off_t offset;
-
- if (p->num_bad_objects) {
- unsigned i;
- for (i = 0; i < p->num_bad_objects; i++)
- if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
- return 0;
- }
-
- offset = find_pack_entry_one(sha1, p);
- if (!offset)
- return 0;
-
- /*
- * We are about to tell the caller where they can locate the
- * requested object. We better make sure the packfile is
- * still here and can be accessed before supplying that
- * answer, as it may have been deleted since the index was
- * loaded!
- */
- if (!is_pack_valid(p))
- return 0;
- e->offset = offset;
- e->p = p;
- hashcpy(e->sha1, sha1);
- return 1;
-}
-
-/*
- * Iff a pack file contains the object named by sha1, return true and
- * store its location to e.
- */
-static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
-{
- struct mru_entry *p;
-
- prepare_packed_git();
- if (!packed_git)
- return 0;
-
- for (p = packed_git_mru->head; p; p = p->next) {
- if (fill_pack_entry(sha1, e, p->item)) {
- mru_mark(packed_git_mru, p);
- return 1;
- }
- }
- return 0;
-}
-
-struct packed_git *find_sha1_pack(const unsigned char *sha1,
- struct packed_git *packs)
-{
- struct packed_git *p;
-
- for (p = packs; p; p = p->next) {
- if (find_pack_entry_one(sha1, p))
- return p;
- }
- return NULL;
-
-}
-
static int sha1_loose_object_info(const unsigned char *sha1,
struct object_info *oi,
int flags)
@@ -2973,6 +1145,7 @@ static int sha1_loose_object_info(const unsigned char *sha1,
if (oi->sizep == &size_scratch)
oi->sizep = NULL;
strbuf_release(&hdrbuf);
+ oi->whence = OI_LOOSE;
return (status < 0) ? status : 0;
}
@@ -3010,10 +1183,8 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
if (!find_pack_entry(real, &e)) {
/* Most likely it's a loose object. */
- if (!sha1_loose_object_info(real, oi, flags)) {
- oi->whence = OI_LOOSE;
+ if (!sha1_loose_object_info(real, oi, flags))
return 0;
- }
/* Not a loose object; someone else may have just packed it. */
if (flags & OBJECT_INFO_QUICK) {
@@ -3036,10 +1207,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
if (rtype < 0) {
mark_bad_packed_object(e.p, real);
return sha1_object_info_extended(real, oi, 0);
- } else if (in_delta_base_cache(e.p, e.offset)) {
- oi->whence = OI_DBCACHED;
- } else {
- oi->whence = OI_PACKED;
+ } else if (oi->whence == OI_PACKED) {
oi->u.packed.offset = e.offset;
oi->u.packed.pack = e.p;
oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
@@ -3063,28 +1231,18 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
return type;
}
-static void *read_packed_sha1(const unsigned char *sha1,
- enum object_type *type, unsigned long *size)
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+ unsigned long *size)
{
- struct pack_entry e;
- void *data;
+ struct object_info oi = OBJECT_INFO_INIT;
+ void *content;
+ oi.typep = type;
+ oi.sizep = size;
+ oi.contentp = &content;
- if (!find_pack_entry(sha1, &e))
+ if (sha1_object_info_extended(sha1, &oi, 0) < 0)
return NULL;
- data = cache_or_unpack_entry(e.p, e.offset, size, type);
- if (!data) {
- /*
- * We're probably in deep shit, but let's try to fetch
- * the required object anyway from another pack or loose.
- * This should happen only in the presence of a corrupted
- * pack, and is better than failing outright.
- */
- error("failed to read object %s at offset %"PRIuMAX" from %s",
- sha1_to_hex(sha1), (uintmax_t)e.offset, e.p->pack_name);
- mark_bad_packed_object(e.p, sha1);
- data = read_object(sha1, type, size);
- }
- return data;
+ return content;
}
int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
@@ -3105,20 +1263,6 @@ int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
return 0;
}
-static void *read_object(const unsigned char *sha1, enum object_type *type,
- unsigned long *size)
-{
- struct object_info oi = OBJECT_INFO_INIT;
- void *content;
- oi.typep = type;
- oi.sizep = size;
- oi.contentp = &content;
-
- if (sha1_object_info_extended(sha1, &oi, 0) < 0)
- return NULL;
- return content;
-}
-
/*
* This function dies on corrupt objects; the callers who want to
* deal with them should arrange to call read_object() and give error
@@ -3437,7 +1581,7 @@ int write_sha1_file(const void *buf, unsigned long len, const char *type, unsign
}
int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type,
- unsigned char *sha1, unsigned flags)
+ struct object_id *oid, unsigned flags)
{
char *header;
int hdrlen, status = 0;
@@ -3445,13 +1589,13 @@ int hash_sha1_file_literally(const void *buf, unsigned long len, const char *typ
/* type string, SP, %lu of the length plus NUL must fit this */
hdrlen = strlen(type) + 32;
header = xmalloc(hdrlen);
- write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen);
+ write_sha1_file_prepare(buf, len, type, oid->hash, header, &hdrlen);
if (!(flags & HASH_WRITE_OBJECT))
goto cleanup;
- if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
+ if (freshen_packed_object(oid->hash) || freshen_loose_object(oid->hash))
goto cleanup;
- status = write_loose_object(sha1, header, hdrlen, buf, len, 0);
+ status = write_loose_object(oid->hash, header, hdrlen, buf, len, 0);
cleanup:
free(header);
@@ -3469,7 +1613,7 @@ int force_object_loose(const unsigned char *sha1, time_t mtime)
if (has_loose_object(sha1))
return 0;
- buf = read_packed_sha1(sha1, &type, &len);
+ buf = read_object(sha1, &type, &len);
if (!buf)
return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1;
@@ -3479,20 +1623,6 @@ int force_object_loose(const unsigned char *sha1, time_t mtime)
return ret;
}
-int has_pack_index(const unsigned char *sha1)
-{
- struct stat st;
- if (stat(sha1_pack_index_name(sha1), &st))
- return 0;
- return 1;
-}
-
-int has_sha1_pack(const unsigned char *sha1)
-{
- struct pack_entry e;
- return find_pack_entry(sha1, &e);
-}
-
int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
{
if (!startup_info->have_repository)
@@ -3655,14 +1785,14 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
* binary blobs, they generally do not want to get any conversion, and
* callers should avoid this code path when filters are requested.
*/
-static int index_stream(unsigned char *sha1, int fd, size_t size,
+static int index_stream(struct object_id *oid, int fd, size_t size,
enum object_type type, const char *path,
unsigned flags)
{
- return index_bulk_checkin(sha1, fd, size, type, path, flags);
+ return index_bulk_checkin(oid->hash, fd, size, type, path, flags);
}
-int index_fd(unsigned char *sha1, int fd, struct stat *st,
+int index_fd(struct object_id *oid, int fd, struct stat *st,
enum object_type type, const char *path, unsigned flags)
{
int ret;
@@ -3672,21 +1802,21 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st,
* die() for large files.
*/
if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
- ret = index_stream_convert_blob(sha1, fd, path, flags);
+ ret = index_stream_convert_blob(oid->hash, fd, path, flags);
else if (!S_ISREG(st->st_mode))
- ret = index_pipe(sha1, fd, type, path, flags);
+ ret = index_pipe(oid->hash, fd, type, path, flags);
else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
(path && would_convert_to_git(&the_index, path)))
- ret = index_core(sha1, fd, xsize_t(st->st_size), type, path,
+ ret = index_core(oid->hash, fd, xsize_t(st->st_size), type, path,
flags);
else
- ret = index_stream(sha1, fd, xsize_t(st->st_size), type, path,
+ ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
flags);
close(fd);
return ret;
}
-int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags)
+int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags)
{
int fd;
struct strbuf sb = STRBUF_INIT;
@@ -3696,7 +1826,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned
fd = open(path, O_RDONLY);
if (fd < 0)
return error_errno("open(\"%s\")", path);
- if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)
+ if (index_fd(oid, fd, st, OBJ_BLOB, path, flags) < 0)
return error("%s: failed to insert into database",
path);
break;
@@ -3704,14 +1834,14 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned
if (strbuf_readlink(&sb, path, st->st_size))
return error_errno("readlink(\"%s\")", path);
if (!(flags & HASH_WRITE_OBJECT))
- hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
- else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
+ hash_sha1_file(sb.buf, sb.len, blob_type, oid->hash);
+ else if (write_sha1_file(sb.buf, sb.len, blob_type, oid->hash))
return error("%s: failed to insert into database",
path);
strbuf_release(&sb);
break;
case S_IFDIR:
- return resolve_gitlink_ref(path, "HEAD", sha1);
+ return resolve_gitlink_ref(path, "HEAD", oid->hash);
default:
return error("%s: unsupported file type", path);
}
@@ -3885,46 +2015,6 @@ int for_each_loose_object(each_loose_object_fn cb, void *data, unsigned flags)
return foreach_alt_odb(loose_from_alt_odb, &alt);
}
-static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
-{
- uint32_t i;
- int r = 0;
-
- for (i = 0; i < p->num_objects; i++) {
- struct object_id oid;
-
- if (!nth_packed_object_oid(&oid, p, i))
- return error("unable to get sha1 of object %u in %s",
- i, p->pack_name);
-
- r = cb(&oid, p, i, data);
- if (r)
- break;
- }
- return r;
-}
-
-int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
-{
- struct packed_git *p;
- int r = 0;
- int pack_errors = 0;
-
- prepare_packed_git();
- for (p = packed_git; p; p = p->next) {
- if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
- continue;
- if (open_pack_index(p)) {
- pack_errors = 1;
- continue;
- }
- r = for_each_object_in_pack(p, cb, data);
- if (r)
- break;
- }
- return r ? r : pack_errors;
-}
-
static int check_stream_sha1(git_zstream *stream,
const char *hdr,
unsigned long size,
diff --git a/sha1_name.c b/sha1_name.c
index 74fcb6d788..134ac9742f 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -9,8 +9,9 @@
#include "remote.h"
#include "dir.h"
#include "sha1-array.h"
+#include "packfile.h"
-static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
+static int get_oid_oneline(const char *, struct object_id *, struct commit_list *);
typedef int (*disambiguate_hint_fn)(const struct object_id *, void *);
@@ -200,7 +201,7 @@ static void find_short_packed_object(struct disambiguate_state *ds)
#define SHORT_NAME_AMBIGUOUS (-2)
static int finish_object_disambiguation(struct disambiguate_state *ds,
- unsigned char *sha1)
+ struct object_id *oid)
{
if (ds->ambiguous)
return SHORT_NAME_AMBIGUOUS;
@@ -229,7 +230,7 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
if (!ds->candidate_ok)
return SHORT_NAME_AMBIGUOUS;
- hashcpy(sha1, ds->candidate.hash);
+ oidcpy(oid, &ds->candidate);
return 0;
}
@@ -385,35 +386,35 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)
return 0;
}
-static int get_short_sha1(const char *name, int len, unsigned char *sha1,
+static int get_short_oid(const char *name, int len, struct object_id *oid,
unsigned flags)
{
int status;
struct disambiguate_state ds;
- int quietly = !!(flags & GET_SHA1_QUIETLY);
+ int quietly = !!(flags & GET_OID_QUIETLY);
if (init_object_disambiguation(name, len, &ds) < 0)
return -1;
- if (HAS_MULTI_BITS(flags & GET_SHA1_DISAMBIGUATORS))
- die("BUG: multiple get_short_sha1 disambiguator flags");
+ if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
+ die("BUG: multiple get_short_oid disambiguator flags");
- if (flags & GET_SHA1_COMMIT)
+ if (flags & GET_OID_COMMIT)
ds.fn = disambiguate_commit_only;
- else if (flags & GET_SHA1_COMMITTISH)
+ else if (flags & GET_OID_COMMITTISH)
ds.fn = disambiguate_committish_only;
- else if (flags & GET_SHA1_TREE)
+ else if (flags & GET_OID_TREE)
ds.fn = disambiguate_tree_only;
- else if (flags & GET_SHA1_TREEISH)
+ else if (flags & GET_OID_TREEISH)
ds.fn = disambiguate_treeish_only;
- else if (flags & GET_SHA1_BLOB)
+ else if (flags & GET_OID_BLOB)
ds.fn = disambiguate_blob_only;
else
ds.fn = default_disambiguate_hint;
find_short_object_filename(&ds);
find_short_packed_object(&ds);
- status = finish_object_disambiguation(&ds, sha1);
+ status = finish_object_disambiguation(&ds, oid);
if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
error(_("short SHA1 %s is ambiguous"), ds.hex_pfx);
@@ -500,12 +501,12 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
}
sha1_to_hex_r(hex, sha1);
- if (len == 40 || !len)
- return 40;
+ if (len == GIT_SHA1_HEXSZ || !len)
+ return GIT_SHA1_HEXSZ;
exists = has_sha1_file(sha1);
- while (len < 40) {
- unsigned char sha1_ret[20];
- status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
+ while (len < GIT_SHA1_HEXSZ) {
+ struct object_id oid_ret;
+ status = get_short_oid(hex, len, &oid_ret, GET_OID_QUIETLY);
if (exists
? !status
: status == SHORT_NAME_NOT_FOUND) {
@@ -578,10 +579,10 @@ static inline int push_mark(const char *string, int len)
return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
}
-static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
+static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
-static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
+static int get_oid_basic(const char *str, int len, struct object_id *oid,
unsigned int flags)
{
static const char *warn_msg = "refname '%.*s' is ambiguous.";
@@ -595,14 +596,14 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
"examine these refs and maybe delete them. Turn this message off by\n"
"running \"git config advice.objectNameWarning false\"");
- unsigned char tmp_sha1[20];
+ struct object_id tmp_oid;
char *real_ref = NULL;
int refs_found = 0;
int at, reflog_len, nth_prior = 0;
- if (len == 40 && !get_sha1_hex(str, sha1)) {
+ if (len == GIT_SHA1_HEXSZ && !get_oid_hex(str, oid)) {
if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
- refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
+ refs_found = dwim_ref(str, len, tmp_oid.hash, &real_ref);
if (refs_found > 0) {
warning(warn_msg, len, str);
if (advice_object_name_warning)
@@ -644,7 +645,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
int detached;
if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
- detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1));
+ detached = (buf.len == GIT_SHA1_HEXSZ && !get_oid_hex(buf.buf, oid));
strbuf_release(&buf);
if (detached)
return 0;
@@ -653,18 +654,18 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
if (!len && reflog_len)
/* allow "@{...}" to mean the current branch reflog */
- refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
+ refs_found = dwim_ref("HEAD", 4, oid->hash, &real_ref);
else if (reflog_len)
- refs_found = dwim_log(str, len, sha1, &real_ref);
+ refs_found = dwim_log(str, len, oid->hash, &real_ref);
else
- refs_found = dwim_ref(str, len, sha1, &real_ref);
+ refs_found = dwim_ref(str, len, oid->hash, &real_ref);
if (!refs_found)
return -1;
- if (warn_ambiguous_refs && !(flags & GET_SHA1_QUIETLY) &&
+ if (warn_ambiguous_refs && !(flags & GET_OID_QUIETLY) &&
(refs_found > 1 ||
- !get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
+ !get_short_oid(str, len, &tmp_oid, GET_OID_QUIETLY)))
warning(warn_msg, len, str);
if (reflog_len) {
@@ -696,7 +697,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
return -1;
}
}
- if (read_ref_at(real_ref, flags, at_time, nth, sha1, NULL,
+ if (read_ref_at(real_ref, flags, at_time, nth, oid->hash, NULL,
&co_time, &co_tz, &co_cnt)) {
if (!len) {
if (starts_with(real_ref, "refs/heads/")) {
@@ -709,13 +710,13 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
}
}
if (at_time) {
- if (!(flags & GET_SHA1_QUIETLY)) {
+ if (!(flags & GET_OID_QUIETLY)) {
warning("Log for '%.*s' only goes "
"back to %s.", len, str,
show_date(co_time, co_tz, DATE_MODE(RFC2822)));
}
} else {
- if (flags & GET_SHA1_QUIETLY) {
+ if (flags & GET_OID_QUIETLY) {
exit(128);
}
die("Log for '%.*s' only has %d entries.",
@@ -729,10 +730,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
}
static int get_parent(const char *name, int len,
- unsigned char *result, int idx)
+ struct object_id *result, int idx)
{
struct object_id oid;
- int ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH);
+ int ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH);
struct commit *commit;
struct commit_list *p;
@@ -742,13 +743,13 @@ static int get_parent(const char *name, int len,
if (parse_commit(commit))
return -1;
if (!idx) {
- hashcpy(result, commit->object.oid.hash);
+ oidcpy(result, &commit->object.oid);
return 0;
}
p = commit->parents;
while (p) {
if (!--idx) {
- hashcpy(result, p->item->object.oid.hash);
+ oidcpy(result, &p->item->object.oid);
return 0;
}
p = p->next;
@@ -757,13 +758,13 @@ static int get_parent(const char *name, int len,
}
static int get_nth_ancestor(const char *name, int len,
- unsigned char *result, int generation)
+ struct object_id *result, int generation)
{
struct object_id oid;
struct commit *commit;
int ret;
- ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH);
+ ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH);
if (ret)
return ret;
commit = lookup_commit_reference(&oid);
@@ -775,7 +776,7 @@ static int get_nth_ancestor(const char *name, int len,
return -1;
commit = commit->parents->item;
}
- hashcpy(result, commit->object.oid.hash);
+ oidcpy(result, &commit->object.oid);
return 0;
}
@@ -804,7 +805,7 @@ struct object *peel_to_type(const char *name, int namelen,
}
}
-static int peel_onion(const char *name, int len, unsigned char *sha1,
+static int peel_onion(const char *name, int len, struct object_id *oid,
unsigned lookup_flags)
{
struct object_id outer;
@@ -849,13 +850,13 @@ static int peel_onion(const char *name, int len, unsigned char *sha1,
else
return -1;
- lookup_flags &= ~GET_SHA1_DISAMBIGUATORS;
+ lookup_flags &= ~GET_OID_DISAMBIGUATORS;
if (expected_type == OBJ_COMMIT)
- lookup_flags |= GET_SHA1_COMMITTISH;
+ lookup_flags |= GET_OID_COMMITTISH;
else if (expected_type == OBJ_TREE)
- lookup_flags |= GET_SHA1_TREEISH;
+ lookup_flags |= GET_OID_TREEISH;
- if (get_sha1_1(name, sp - name - 2, outer.hash, lookup_flags))
+ if (get_oid_1(name, sp - name - 2, &outer, lookup_flags))
return -1;
o = parse_object(&outer);
@@ -865,7 +866,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1,
o = deref_tag(o, name, sp - name - 2);
if (!o || (!o->parsed && !parse_object(&o->oid)))
return -1;
- hashcpy(sha1, o->oid.hash);
+ oidcpy(oid, &o->oid);
return 0;
}
@@ -878,7 +879,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1,
if (!o)
return -1;
- hashcpy(sha1, o->oid.hash);
+ oidcpy(oid, &o->oid);
if (sp[0] == '/') {
/* "$commit^{/foo}" */
char *prefix;
@@ -894,17 +895,17 @@ static int peel_onion(const char *name, int len, unsigned char *sha1,
prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
commit_list_insert((struct commit *)o, &list);
- ret = get_sha1_oneline(prefix, sha1, list);
+ ret = get_oid_oneline(prefix, oid, list);
free(prefix);
return ret;
}
return 0;
}
-static int get_describe_name(const char *name, int len, unsigned char *sha1)
+static int get_describe_name(const char *name, int len, struct object_id *oid)
{
const char *cp;
- unsigned flags = GET_SHA1_QUIETLY | GET_SHA1_COMMIT;
+ unsigned flags = GET_OID_QUIETLY | GET_OID_COMMIT;
for (cp = name + len - 1; name + 2 <= cp; cp--) {
char ch = *cp;
@@ -915,14 +916,14 @@ static int get_describe_name(const char *name, int len, unsigned char *sha1)
if (ch == 'g' && cp[-1] == '-') {
cp++;
len -= cp - name;
- return get_short_sha1(cp, len, sha1, flags);
+ return get_short_oid(cp, len, oid, flags);
}
}
}
return -1;
}
-static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags)
+static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags)
{
int ret, has_suffix;
const char *cp;
@@ -949,25 +950,25 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l
if (!num && len1 == len - 1)
num = 1;
if (has_suffix == '^')
- return get_parent(name, len1, sha1, num);
+ return get_parent(name, len1, oid, num);
/* else if (has_suffix == '~') -- goes without saying */
- return get_nth_ancestor(name, len1, sha1, num);
+ return get_nth_ancestor(name, len1, oid, num);
}
- ret = peel_onion(name, len, sha1, lookup_flags);
+ ret = peel_onion(name, len, oid, lookup_flags);
if (!ret)
return 0;
- ret = get_sha1_basic(name, len, sha1, lookup_flags);
+ ret = get_oid_basic(name, len, oid, lookup_flags);
if (!ret)
return 0;
/* It could be describe output that is "SOMETHING-gXXXX" */
- ret = get_describe_name(name, len, sha1);
+ ret = get_describe_name(name, len, oid);
if (!ret)
return 0;
- return get_short_sha1(name, len, sha1, lookup_flags);
+ return get_short_oid(name, len, oid, lookup_flags);
}
/*
@@ -1004,7 +1005,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
return 0;
}
-static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
+static int get_oid_oneline(const char *prefix, struct object_id *oid,
struct commit_list *list)
{
struct commit_list *backup = NULL, *l;
@@ -1044,7 +1045,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
unuse_commit_buffer(commit, buf);
if (matches) {
- hashcpy(sha1, commit->object.oid.hash);
+ oidcpy(oid, &commit->object.oid);
found = 1;
break;
}
@@ -1140,7 +1141,7 @@ int get_oid_mb(const char *name, struct object_id *oid)
struct strbuf sb;
strbuf_init(&sb, dots - name);
strbuf_add(&sb, name, dots - name);
- st = get_sha1_committish(sb.buf, oid_tmp.hash);
+ st = get_oid_committish(sb.buf, &oid_tmp);
strbuf_release(&sb);
}
if (st)
@@ -1149,7 +1150,7 @@ int get_oid_mb(const char *name, struct object_id *oid)
if (!one)
return -1;
- if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", oid_tmp.hash))
+ if (get_oid_committish(dots[3] ? (dots + 3) : "HEAD", &oid_tmp))
return -1;
two = lookup_commit_reference_gently(&oid_tmp, 0);
if (!two)
@@ -1338,21 +1339,13 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
}
/*
- * This is like "get_sha1_basic()", except it allows "sha1 expressions",
+ * This is like "get_oid_basic()", except it allows "object ID expressions",
* notably "xyz^" for "parent of xyz"
*/
-int get_sha1(const char *name, unsigned char *sha1)
-{
- struct object_context unused;
- return get_sha1_with_context(name, 0, sha1, &unused);
-}
-
-/*
- * This is like "get_sha1()", but for struct object_id.
- */
int get_oid(const char *name, struct object_id *oid)
{
- return get_sha1(name, oid->hash);
+ struct object_context unused;
+ return get_oid_with_context(name, 0, oid, &unused);
}
@@ -1366,49 +1359,49 @@ int get_oid(const char *name, struct object_id *oid)
* commit-ish. It is merely to give a hint to the disambiguation
* machinery.
*/
-int get_sha1_committish(const char *name, unsigned char *sha1)
+int get_oid_committish(const char *name, struct object_id *oid)
{
struct object_context unused;
- return get_sha1_with_context(name, GET_SHA1_COMMITTISH,
- sha1, &unused);
+ return get_oid_with_context(name, GET_OID_COMMITTISH,
+ oid, &unused);
}
-int get_sha1_treeish(const char *name, unsigned char *sha1)
+int get_oid_treeish(const char *name, struct object_id *oid)
{
struct object_context unused;
- return get_sha1_with_context(name, GET_SHA1_TREEISH,
- sha1, &unused);
+ return get_oid_with_context(name, GET_OID_TREEISH,
+ oid, &unused);
}
-int get_sha1_commit(const char *name, unsigned char *sha1)
+int get_oid_commit(const char *name, struct object_id *oid)
{
struct object_context unused;
- return get_sha1_with_context(name, GET_SHA1_COMMIT,
- sha1, &unused);
+ return get_oid_with_context(name, GET_OID_COMMIT,
+ oid, &unused);
}
-int get_sha1_tree(const char *name, unsigned char *sha1)
+int get_oid_tree(const char *name, struct object_id *oid)
{
struct object_context unused;
- return get_sha1_with_context(name, GET_SHA1_TREE,
- sha1, &unused);
+ return get_oid_with_context(name, GET_OID_TREE,
+ oid, &unused);
}
-int get_sha1_blob(const char *name, unsigned char *sha1)
+int get_oid_blob(const char *name, struct object_id *oid)
{
struct object_context unused;
- return get_sha1_with_context(name, GET_SHA1_BLOB,
- sha1, &unused);
+ return get_oid_with_context(name, GET_OID_BLOB,
+ oid, &unused);
}
/* Must be called only when object_name:filename doesn't exist. */
-static void diagnose_invalid_sha1_path(const char *prefix,
- const char *filename,
- const unsigned char *tree_sha1,
- const char *object_name,
- int object_name_len)
+static void diagnose_invalid_oid_path(const char *prefix,
+ const char *filename,
+ const struct object_id *tree_oid,
+ const char *object_name,
+ int object_name_len)
{
- unsigned char sha1[20];
+ struct object_id oid;
unsigned mode;
if (!prefix)
@@ -1420,8 +1413,8 @@ static void diagnose_invalid_sha1_path(const char *prefix,
if (is_missing_file_error(errno)) {
char *fullname = xstrfmt("%s%s", prefix, filename);
- if (!get_tree_entry(tree_sha1, fullname,
- sha1, &mode)) {
+ if (!get_tree_entry(tree_oid->hash, fullname,
+ oid.hash, &mode)) {
die("Path '%s' exists, but not '%s'.\n"
"Did you mean '%.*s:%s' aka '%.*s:./%s'?",
fullname,
@@ -1504,24 +1497,24 @@ static char *resolve_relative_path(const char *rel)
rel);
}
-static int get_sha1_with_context_1(const char *name,
- unsigned flags,
- const char *prefix,
- unsigned char *sha1,
- struct object_context *oc)
+static int get_oid_with_context_1(const char *name,
+ unsigned flags,
+ const char *prefix,
+ struct object_id *oid,
+ struct object_context *oc)
{
int ret, bracket_depth;
int namelen = strlen(name);
const char *cp;
- int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
+ int only_to_die = flags & GET_OID_ONLY_TO_DIE;
if (only_to_die)
- flags |= GET_SHA1_QUIETLY;
+ flags |= GET_OID_QUIETLY;
memset(oc, 0, sizeof(*oc));
oc->mode = S_IFINVALID;
strbuf_init(&oc->symlink_path, 0);
- ret = get_sha1_1(name, namelen, sha1, flags);
+ ret = get_oid_1(name, namelen, oid, flags);
if (!ret)
return ret;
/*
@@ -1541,7 +1534,7 @@ static int get_sha1_with_context_1(const char *name,
for_each_ref(handle_one_ref, &list);
commit_list_sort_by_date(&list);
- return get_sha1_oneline(name + 2, sha1, list);
+ return get_oid_oneline(name + 2, oid, list);
}
if (namelen < 3 ||
name[2] != ':' ||
@@ -1559,7 +1552,7 @@ static int get_sha1_with_context_1(const char *name,
namelen = strlen(cp);
}
- if (flags & GET_SHA1_RECORD_PATH)
+ if (flags & GET_OID_RECORD_PATH)
oc->path = xstrdup(cp);
if (!active_cache)
@@ -1573,7 +1566,7 @@ static int get_sha1_with_context_1(const char *name,
memcmp(ce->name, cp, namelen))
break;
if (ce_stage(ce) == stage) {
- hashcpy(sha1, ce->oid.hash);
+ oidcpy(oid, &ce->oid);
oc->mode = ce->ce_mode;
free(new_path);
return 0;
@@ -1594,36 +1587,36 @@ static int get_sha1_with_context_1(const char *name,
break;
}
if (*cp == ':') {
- unsigned char tree_sha1[20];
+ struct object_id tree_oid;
int len = cp - name;
unsigned sub_flags = flags;
- sub_flags &= ~GET_SHA1_DISAMBIGUATORS;
- sub_flags |= GET_SHA1_TREEISH;
+ sub_flags &= ~GET_OID_DISAMBIGUATORS;
+ sub_flags |= GET_OID_TREEISH;
- if (!get_sha1_1(name, len, tree_sha1, sub_flags)) {
+ if (!get_oid_1(name, len, &tree_oid, sub_flags)) {
const char *filename = cp+1;
char *new_filename = NULL;
new_filename = resolve_relative_path(filename);
if (new_filename)
filename = new_filename;
- if (flags & GET_SHA1_FOLLOW_SYMLINKS) {
- ret = get_tree_entry_follow_symlinks(tree_sha1,
- filename, sha1, &oc->symlink_path,
+ if (flags & GET_OID_FOLLOW_SYMLINKS) {
+ ret = get_tree_entry_follow_symlinks(tree_oid.hash,
+ filename, oid->hash, &oc->symlink_path,
&oc->mode);
} else {
- ret = get_tree_entry(tree_sha1, filename,
- sha1, &oc->mode);
+ ret = get_tree_entry(tree_oid.hash, filename,
+ oid->hash, &oc->mode);
if (ret && only_to_die) {
- diagnose_invalid_sha1_path(prefix,
+ diagnose_invalid_oid_path(prefix,
filename,
- tree_sha1,
+ &tree_oid,
name, len);
}
}
- hashcpy(oc->tree, tree_sha1);
- if (flags & GET_SHA1_RECORD_PATH)
+ hashcpy(oc->tree, tree_oid.hash);
+ if (flags & GET_OID_RECORD_PATH)
oc->path = xstrdup(filename);
free(new_filename);
@@ -1646,13 +1639,13 @@ static int get_sha1_with_context_1(const char *name,
void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
{
struct object_context oc;
- unsigned char sha1[20];
- get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
+ struct object_id oid;
+ get_oid_with_context_1(name, GET_OID_ONLY_TO_DIE, prefix, &oid, &oc);
}
-int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc)
+int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc)
{
- if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
+ if (flags & GET_OID_FOLLOW_SYMLINKS && flags & GET_OID_ONLY_TO_DIE)
die("BUG: incompatible flags for get_sha1_with_context");
- return get_sha1_with_context_1(str, flags, NULL, sha1, oc);
+ return get_oid_with_context_1(str, flags, NULL, oid, oc);
}
diff --git a/shallow.c b/shallow.c
index 54359d5490..f5591e56da 100644
--- a/shallow.c
+++ b/shallow.c
@@ -107,7 +107,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
cur_depth++;
if ((depth != INFINITE_DEPTH && cur_depth >= depth) ||
(is_repository_shallow() && !commit->parents &&
- (graft = lookup_commit_graft(commit->object.oid.hash)) != NULL &&
+ (graft = lookup_commit_graft(&commit->object.oid)) != NULL &&
graft->nr_parent < 0)) {
commit_list_insert(commit, &result);
commit->object.flags |= shallow_flag;
@@ -398,7 +398,7 @@ 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].hash);
+ graft = lookup_commit_graft(&sa->oid[i]);
if (graft && graft->nr_parent < 0)
continue;
info->ours[info->nr_ours++] = i;
diff --git a/strbuf.c b/strbuf.c
index 89d22e3b09..323c49ceb3 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -476,6 +476,7 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
/* Translate slopbuf to NULL, as we cannot call realloc on it */
if (!sb->alloc)
sb->buf = NULL;
+ errno = 0;
r = getdelim(&sb->buf, &sb->alloc, term, fp);
if (r > 0) {
diff --git a/strbuf.h b/strbuf.h
index 2075384e0b..e705b94db5 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -68,7 +68,7 @@ struct strbuf {
};
extern char strbuf_slopbuf[];
-#define STRBUF_INIT { 0, 0, strbuf_slopbuf }
+#define STRBUF_INIT { .alloc = 0, .len = 0, .buf = strbuf_slopbuf }
/**
* Life Cycle Functions
diff --git a/streaming.c b/streaming.c
index 9afa66b8be..6f1c60f12b 100644
--- a/streaming.c
+++ b/streaming.c
@@ -3,6 +3,7 @@
*/
#include "cache.h"
#include "streaming.h"
+#include "packfile.h"
enum input_source {
stream_error = -1,
diff --git a/string-list.c b/string-list.c
index c650500c6e..806b4c8723 100644
--- a/string-list.c
+++ b/string-list.c
@@ -43,9 +43,8 @@ static int add_entry(int insert_at, struct string_list *list, const char *string
ALLOC_GROW(list->items, list->nr+1, list->alloc);
if (index < list->nr)
- memmove(list->items + index + 1, list->items + index,
- (list->nr - index)
- * sizeof(struct string_list_item));
+ MOVE_ARRAY(list->items + index + 1, list->items + index,
+ list->nr - index);
list->items[index].string = list->strdup_strings ?
xstrdup(string) : (char *)string;
list->items[index].util = NULL;
@@ -77,8 +76,7 @@ void string_list_remove(struct string_list *list, const char *string,
free(list->items[i].util);
list->nr--;
- memmove(list->items + i, list->items + i + 1,
- (list->nr - i) * sizeof(struct string_list_item));
+ MOVE_ARRAY(list->items + i, list->items + i + 1, list->nr - i);
}
}
diff --git a/sub-process.c b/sub-process.c
index a3cfab1a9d..6ccfaaba99 100644
--- a/sub-process.c
+++ b/sub-process.c
@@ -6,10 +6,13 @@
#include "pkt-line.h"
int cmd2process_cmp(const void *unused_cmp_data,
- const struct subprocess_entry *e1,
- const struct subprocess_entry *e2,
+ const void *entry,
+ const void *entry_or_key,
const void *unused_keydata)
{
+ const struct subprocess_entry *e1 = entry;
+ const struct subprocess_entry *e2 = entry_or_key;
+
return strcmp(e1->cmd, e2->cmd);
}
@@ -105,3 +108,107 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
hashmap_add(hashmap, entry);
return 0;
}
+
+static int handshake_version(struct child_process *process,
+ const char *welcome_prefix, int *versions,
+ int *chosen_version)
+{
+ int version_scratch;
+ int i;
+ char *line;
+ const char *p;
+
+ if (!chosen_version)
+ chosen_version = &version_scratch;
+
+ if (packet_write_fmt_gently(process->in, "%s-client\n",
+ welcome_prefix))
+ return error("Could not write client identification");
+ for (i = 0; versions[i]; i++) {
+ if (packet_write_fmt_gently(process->in, "version=%d\n",
+ versions[i]))
+ return error("Could not write requested version");
+ }
+ if (packet_flush_gently(process->in))
+ return error("Could not write flush packet");
+
+ if (!(line = packet_read_line(process->out, NULL)) ||
+ !skip_prefix(line, welcome_prefix, &p) ||
+ strcmp(p, "-server"))
+ return error("Unexpected line '%s', expected %s-server",
+ line ? line : "<flush packet>", welcome_prefix);
+ if (!(line = packet_read_line(process->out, NULL)) ||
+ !skip_prefix(line, "version=", &p) ||
+ strtol_i(p, 10, chosen_version))
+ return error("Unexpected line '%s', expected version",
+ line ? line : "<flush packet>");
+ if ((line = packet_read_line(process->out, NULL)))
+ return error("Unexpected line '%s', expected flush", line);
+
+ /* Check to make sure that the version received is supported */
+ for (i = 0; versions[i]; i++) {
+ if (versions[i] == *chosen_version)
+ break;
+ }
+ if (!versions[i])
+ return error("Version %d not supported", *chosen_version);
+
+ return 0;
+}
+
+static int handshake_capabilities(struct child_process *process,
+ struct subprocess_capability *capabilities,
+ unsigned int *supported_capabilities)
+{
+ int i;
+ char *line;
+
+ for (i = 0; capabilities[i].name; i++) {
+ if (packet_write_fmt_gently(process->in, "capability=%s\n",
+ capabilities[i].name))
+ return error("Could not write requested capability");
+ }
+ if (packet_flush_gently(process->in))
+ return error("Could not write flush packet");
+
+ while ((line = packet_read_line(process->out, NULL))) {
+ const char *p;
+ if (!skip_prefix(line, "capability=", &p))
+ continue;
+
+ for (i = 0;
+ capabilities[i].name && strcmp(p, capabilities[i].name);
+ i++)
+ ;
+ if (capabilities[i].name) {
+ if (supported_capabilities)
+ *supported_capabilities |= capabilities[i].flag;
+ } else {
+ warning("subprocess '%s' requested unsupported capability '%s'",
+ process->argv[0], p);
+ }
+ }
+
+ return 0;
+}
+
+int subprocess_handshake(struct subprocess_entry *entry,
+ const char *welcome_prefix,
+ int *versions,
+ int *chosen_version,
+ struct subprocess_capability *capabilities,
+ unsigned int *supported_capabilities)
+{
+ int retval;
+ struct child_process *process = &entry->process;
+
+ sigchain_push(SIGPIPE, SIG_IGN);
+
+ retval = handshake_version(process, welcome_prefix, versions,
+ chosen_version) ||
+ handshake_capabilities(process, capabilities,
+ supported_capabilities);
+
+ sigchain_pop(SIGPIPE);
+ return retval;
+}
diff --git a/sub-process.h b/sub-process.h
index 96a2cca360..49701998c9 100644
--- a/sub-process.h
+++ b/sub-process.h
@@ -6,35 +6,66 @@
#include "run-command.h"
/*
- * Generic implementation of background process infrastructure.
- * See: Documentation/technical/api-sub-process.txt
+ * The sub-process API makes it possible to run background sub-processes
+ * for the entire lifetime of a Git invocation. If Git needs to communicate
+ * with an external process multiple times, then this can reduces the process
+ * invocation overhead. Git and the sub-process communicate through stdin and
+ * stdout.
+ *
+ * The sub-processes are kept in a hashmap by command name and looked up
+ * via the subprocess_find_entry function. If an existing instance can not
+ * be found then a new process should be created and started. When the
+ * parent git command terminates, all sub-processes are also terminated.
+ *
+ * This API is based on the run-command API.
*/
/* data structures */
+/* Members should not be accessed directly. */
struct subprocess_entry {
struct hashmap_entry ent; /* must be the first member! */
const char *cmd;
struct child_process process;
};
+struct subprocess_capability {
+ const char *name;
+
+ /*
+ * subprocess_handshake will "|=" this value to supported_capabilities
+ * if the server reports that it supports this capability.
+ */
+ unsigned int flag;
+};
+
/* subprocess functions */
+/* Function to test two subprocess hashmap entries for equality. */
extern int cmd2process_cmp(const void *unused_cmp_data,
- const struct subprocess_entry *e1,
- const struct subprocess_entry *e2,
+ const void *e1,
+ const void *e2,
const void *unused_keydata);
+/*
+ * User-supplied function to initialize the sub-process. This is
+ * typically used to negotiate the interface version and capabilities.
+ */
typedef int(*subprocess_start_fn)(struct subprocess_entry *entry);
+
+/* Start a subprocess and add it to the subprocess hashmap. */
int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd,
subprocess_start_fn startfn);
+/* Kill a subprocess and remove it from the subprocess hashmap. */
void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry);
+/* Find a subprocess in the subprocess hashmap. */
struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd);
/* subprocess helper functions */
+/* Get the underlying `struct child_process` from a subprocess. */
static inline struct child_process *subprocess_get_child_process(
struct subprocess_entry *entry)
{
@@ -42,6 +73,22 @@ static inline struct child_process *subprocess_get_child_process(
}
/*
+ * Perform the version and capability negotiation as described in the "Long
+ * Running Filter Process" section of the gitattributes documentation using the
+ * given requested versions and capabilities. The "versions" and "capabilities"
+ * parameters are arrays terminated by a 0 or blank struct.
+ *
+ * This function is typically called when a subprocess is started (as part of
+ * the "startfn" passed to subprocess_start).
+ */
+int subprocess_handshake(struct subprocess_entry *entry,
+ const char *welcome_prefix,
+ int *versions,
+ int *chosen_version,
+ struct subprocess_capability *capabilities,
+ unsigned int *supported_capabilities);
+
+/*
* Helper function that will read packets looking for "status=<foo>"
* key/value pairs and return the value from the last "status" packet
*/
diff --git a/submodule-config.c b/submodule-config.c
index 5fe2d07877..2aa8a1747f 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -18,6 +18,7 @@ struct submodule_cache {
struct hashmap for_path;
struct hashmap for_name;
unsigned initialized:1;
+ unsigned gitmodules_read:1;
};
/*
@@ -35,19 +36,25 @@ enum lookup_type {
};
static int config_path_cmp(const void *unused_cmp_data,
- const struct submodule_entry *a,
- const struct submodule_entry *b,
+ const void *entry,
+ const void *entry_or_key,
const void *unused_keydata)
{
+ const struct submodule_entry *a = entry;
+ const struct submodule_entry *b = entry_or_key;
+
return strcmp(a->config->path, b->config->path) ||
hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
}
static int config_name_cmp(const void *unused_cmp_data,
- const struct submodule_entry *a,
- const struct submodule_entry *b,
+ const void *entry,
+ const void *entry_or_key,
const void *unused_keydata)
{
+ const struct submodule_entry *a = entry;
+ const struct submodule_entry *b = entry_or_key;
+
return strcmp(a->config->name, b->config->name) ||
hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
}
@@ -59,8 +66,8 @@ static struct submodule_cache *submodule_cache_alloc(void)
static void submodule_cache_init(struct submodule_cache *cache)
{
- hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, NULL, 0);
- hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, NULL, 0);
+ hashmap_init(&cache->for_path, config_path_cmp, NULL, 0);
+ hashmap_init(&cache->for_name, config_name_cmp, NULL, 0);
cache->initialized = 1;
}
@@ -93,6 +100,7 @@ static void submodule_cache_clear(struct submodule_cache *cache)
hashmap_free(&cache->for_path, 1);
hashmap_free(&cache->for_name, 1);
cache->initialized = 0;
+ cache->gitmodules_read = 0;
}
void submodule_cache_free(struct submodule_cache *cache)
@@ -232,7 +240,7 @@ static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,
static int parse_fetch_recurse(const char *opt, const char *arg,
int die_on_error)
{
- switch (git_config_maybe_bool(opt, arg)) {
+ switch (git_parse_maybe_bool(arg)) {
case 1:
return RECURSE_SUBMODULES_ON;
case 0:
@@ -248,6 +256,14 @@ static int parse_fetch_recurse(const char *opt, const char *arg,
}
}
+int parse_submodule_fetchjobs(const char *var, const char *value)
+{
+ int fetchjobs = git_config_int(var, value);
+ if (fetchjobs < 0)
+ die(_("negative values not allowed for submodule.fetchjobs"));
+ return fetchjobs;
+}
+
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
{
return parse_fetch_recurse(opt, arg, 1);
@@ -277,7 +293,7 @@ int option_fetch_parse_recurse_submodules(const struct option *opt,
static int parse_update_recurse(const char *opt, const char *arg,
int die_on_error)
{
- switch (git_config_maybe_bool(opt, arg)) {
+ switch (git_parse_maybe_bool(arg)) {
case 1:
return RECURSE_SUBMODULES_ON;
case 0:
@@ -297,7 +313,7 @@ int parse_update_recurse_submodules_arg(const char *opt, const char *arg)
static int parse_push_recurse(const char *opt, const char *arg,
int die_on_error)
{
- switch (git_config_maybe_bool(opt, arg)) {
+ switch (git_parse_maybe_bool(arg)) {
case 1:
/* There's no simple "on" value when pushing */
if (die_on_error)
@@ -441,19 +457,19 @@ static int parse_config(const char *var, const char *value, void *data)
return ret;
}
-int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
- unsigned char *gitmodules_sha1,
- struct strbuf *rev)
+static int gitmodule_oid_from_commit(const struct object_id *treeish_name,
+ struct object_id *gitmodules_oid,
+ struct strbuf *rev)
{
int ret = 0;
- if (is_null_sha1(treeish_name)) {
- hashclr(gitmodules_sha1);
+ if (is_null_oid(treeish_name)) {
+ oidclr(gitmodules_oid);
return 1;
}
- strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(treeish_name));
- if (get_sha1(rev->buf, gitmodules_sha1) >= 0)
+ strbuf_addf(rev, "%s:.gitmodules", oid_to_hex(treeish_name));
+ if (get_oid(rev->buf, gitmodules_oid) >= 0)
ret = 1;
return ret;
@@ -464,13 +480,13 @@ int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
* revisions.
*/
static const struct submodule *config_from(struct submodule_cache *cache,
- const unsigned char *treeish_name, const char *key,
+ const struct object_id *treeish_name, const char *key,
enum lookup_type lookup_type)
{
struct strbuf rev = STRBUF_INIT;
unsigned long config_size;
char *config = NULL;
- unsigned char sha1[20];
+ struct object_id oid;
enum object_type type;
const struct submodule *submodule = NULL;
struct parse_config_parameter parameter;
@@ -490,28 +506,28 @@ static const struct submodule *config_from(struct submodule_cache *cache,
return entry->config;
}
- if (!gitmodule_sha1_from_commit(treeish_name, sha1, &rev))
+ if (!gitmodule_oid_from_commit(treeish_name, &oid, &rev))
goto out;
switch (lookup_type) {
case lookup_name:
- submodule = cache_lookup_name(cache, sha1, key);
+ submodule = cache_lookup_name(cache, oid.hash, key);
break;
case lookup_path:
- submodule = cache_lookup_path(cache, sha1, key);
+ submodule = cache_lookup_path(cache, oid.hash, key);
break;
}
if (submodule)
goto out;
- config = read_sha1_file(sha1, &type, &config_size);
+ config = read_sha1_file(oid.hash, &type, &config_size);
if (!config || type != OBJ_BLOB)
goto out;
/* fill the submodule config into the cache */
parameter.cache = cache;
- parameter.treeish_name = treeish_name;
- parameter.gitmodules_sha1 = sha1;
+ parameter.treeish_name = treeish_name->hash;
+ parameter.gitmodules_sha1 = oid.hash;
parameter.overwrite = 0;
git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
config, config_size, &parameter);
@@ -520,9 +536,9 @@ static const struct submodule *config_from(struct submodule_cache *cache,
switch (lookup_type) {
case lookup_name:
- return cache_lookup_name(cache, sha1, key);
+ return cache_lookup_name(cache, oid.hash, key);
case lookup_path:
- return cache_lookup_path(cache, sha1, key);
+ return cache_lookup_path(cache, oid.hash, key);
default:
return NULL;
}
@@ -544,13 +560,11 @@ static void submodule_cache_check_init(struct repository *repo)
submodule_cache_init(repo->submodule_cache);
}
-int submodule_config_option(struct repository *repo,
- const char *var, const char *value)
+static int gitmodules_cb(const char *var, const char *value, void *data)
{
+ struct repository *repo = data;
struct parse_config_parameter parameter;
- submodule_cache_check_init(repo);
-
parameter.cache = repo->submodule_cache;
parameter.treeish_name = NULL;
parameter.gitmodules_sha1 = null_sha1;
@@ -559,30 +573,71 @@ int submodule_config_option(struct repository *repo,
return parse_config(var, value, &parameter);
}
-int parse_submodule_config_option(const char *var, const char *value)
+void repo_read_gitmodules(struct repository *repo)
{
- return submodule_config_option(the_repository, var, value);
+ submodule_cache_check_init(repo);
+
+ if (repo->worktree) {
+ char *gitmodules;
+
+ if (repo_read_index(repo) < 0)
+ return;
+
+ gitmodules = repo_worktree_path(repo, GITMODULES_FILE);
+
+ if (!is_gitmodules_unmerged(repo->index))
+ git_config_from_file(gitmodules_cb, gitmodules, repo);
+
+ free(gitmodules);
+ }
+
+ repo->submodule_cache->gitmodules_read = 1;
}
-const struct submodule *submodule_from_name(const unsigned char *treeish_name,
- const char *name)
+void gitmodules_config_oid(const struct object_id *commit_oid)
{
+ struct strbuf rev = STRBUF_INIT;
+ struct object_id oid;
+
submodule_cache_check_init(the_repository);
+
+ if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
+ git_config_from_blob_oid(gitmodules_cb, rev.buf,
+ &oid, the_repository);
+ }
+ strbuf_release(&rev);
+
+ the_repository->submodule_cache->gitmodules_read = 1;
+}
+
+static void gitmodules_read_check(struct repository *repo)
+{
+ submodule_cache_check_init(repo);
+
+ /* read the repo's .gitmodules file if it hasn't been already */
+ if (!repo->submodule_cache->gitmodules_read)
+ repo_read_gitmodules(repo);
+}
+
+const struct submodule *submodule_from_name(const struct object_id *treeish_name,
+ const char *name)
+{
+ gitmodules_read_check(the_repository);
return config_from(the_repository->submodule_cache, treeish_name, name, lookup_name);
}
-const struct submodule *submodule_from_path(const unsigned char *treeish_name,
+const struct submodule *submodule_from_path(const struct object_id *treeish_name,
const char *path)
{
- submodule_cache_check_init(the_repository);
+ gitmodules_read_check(the_repository);
return config_from(the_repository->submodule_cache, treeish_name, path, lookup_path);
}
const struct submodule *submodule_from_cache(struct repository *repo,
- const unsigned char *treeish_name,
+ const struct object_id *treeish_name,
const char *key)
{
- submodule_cache_check_init(repo);
+ gitmodules_read_check(repo);
return config_from(repo->submodule_cache, treeish_name,
key, lookup_path);
}
diff --git a/submodule-config.h b/submodule-config.h
index 233bfcb7ff..e3845831f6 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -27,25 +27,22 @@ struct repository;
extern void submodule_cache_free(struct submodule_cache *cache);
+extern int parse_submodule_fetchjobs(const char *var, const char *value);
extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
struct option;
extern int option_fetch_parse_recurse_submodules(const struct option *opt,
const char *arg, int unset);
extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
-extern int parse_submodule_config_option(const char *var, const char *value);
-extern int submodule_config_option(struct repository *repo,
- const char *var, const char *value);
+extern void repo_read_gitmodules(struct repository *repo);
+extern void gitmodules_config_oid(const struct object_id *commit_oid);
extern const struct submodule *submodule_from_name(
- const unsigned char *commit_or_tree, const char *name);
+ const struct object_id *commit_or_tree, const char *name);
extern const struct submodule *submodule_from_path(
- const unsigned char *commit_or_tree, const char *path);
+ const struct object_id *commit_or_tree, const char *path);
extern const struct submodule *submodule_from_cache(struct repository *repo,
- const unsigned char *treeish_name,
+ const struct object_id *treeish_name,
const char *key);
-extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
- unsigned char *gitmodules_sha1,
- struct strbuf *rev);
extern void submodule_free(void);
#endif /* SUBMODULE_CONFIG_H */
diff --git a/submodule.c b/submodule.c
index 6531c5d609..3cea8221e0 100644
--- a/submodule.c
+++ b/submodule.c
@@ -20,37 +20,53 @@
#include "worktree.h"
#include "parse-options.h"
-static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
-static int parallel_jobs = 1;
static struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP;
static int initialized_fetch_ref_tips;
static struct oid_array ref_tips_before_fetch;
static struct oid_array ref_tips_after_fetch;
/*
- * The following flag is set if the .gitmodules file is unmerged. We then
- * disable recursion for all submodules where .git/config doesn't have a
- * matching config entry because we can't guess what might be configured in
- * .gitmodules unless the user resolves the conflict. When a command line
- * option is given (which always overrides configuration) this flag will be
- * ignored.
+ * Check if the .gitmodules file is unmerged. Parsing of the .gitmodules file
+ * will be disabled because we can't guess what might be configured in
+ * .gitmodules unless the user resolves the conflict.
*/
-static int gitmodules_is_unmerged;
+int is_gitmodules_unmerged(const struct index_state *istate)
+{
+ int pos = index_name_pos(istate, GITMODULES_FILE, strlen(GITMODULES_FILE));
+ if (pos < 0) { /* .gitmodules not found or isn't merged */
+ pos = -1 - pos;
+ if (istate->cache_nr > pos) { /* there is a .gitmodules */
+ const struct cache_entry *ce = istate->cache[pos];
+ if (ce_namelen(ce) == strlen(GITMODULES_FILE) &&
+ !strcmp(ce->name, GITMODULES_FILE))
+ return 1;
+ }
+ }
+
+ return 0;
+}
/*
- * This flag is set if the .gitmodules file had unstaged modifications on
- * startup. This must be checked before allowing modifications to the
- * .gitmodules file with the intention to stage them later, because when
- * continuing we would stage the modifications the user didn't stage herself
- * too. That might change in a future version when we learn to stage the
- * changes we do ourselves without staging any previous modifications.
+ * Check if the .gitmodules file has unstaged modifications. This must be
+ * checked before allowing modifications to the .gitmodules file with the
+ * intention to stage them later, because when continuing we would stage the
+ * modifications the user didn't stage herself too. That might change in a
+ * future version when we learn to stage the changes we do ourselves without
+ * staging any previous modifications.
*/
-static int gitmodules_is_modified;
-
-int is_staging_gitmodules_ok(void)
+int is_staging_gitmodules_ok(const struct index_state *istate)
{
- return !gitmodules_is_modified;
+ int pos = index_name_pos(istate, GITMODULES_FILE, strlen(GITMODULES_FILE));
+
+ if ((pos >= 0) && (pos < istate->cache_nr)) {
+ struct stat st;
+ if (lstat(GITMODULES_FILE, &st) == 0 &&
+ ce_match_stat(istate->cache[pos], &st, 0) & DATA_CHANGED)
+ return 0;
+ }
+
+ return 1;
}
/*
@@ -63,13 +79,13 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
struct strbuf entry = STRBUF_INIT;
const struct submodule *submodule;
- if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
+ if (!file_exists(GITMODULES_FILE)) /* Do nothing without .gitmodules */
return -1;
- if (gitmodules_is_unmerged)
+ if (is_gitmodules_unmerged(&the_index))
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
- submodule = submodule_from_path(null_sha1, oldpath);
+ submodule = submodule_from_path(&null_oid, oldpath);
if (!submodule || !submodule->name) {
warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
return -1;
@@ -77,7 +93,7 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
strbuf_addstr(&entry, "submodule.");
strbuf_addstr(&entry, submodule->name);
strbuf_addstr(&entry, ".path");
- if (git_config_set_in_file_gently(".gitmodules", entry.buf, newpath) < 0) {
+ if (git_config_set_in_file_gently(GITMODULES_FILE, entry.buf, newpath) < 0) {
/* Maybe the user already did that, don't error out here */
warning(_("Could not update .gitmodules entry %s"), entry.buf);
strbuf_release(&entry);
@@ -97,20 +113,20 @@ int remove_path_from_gitmodules(const char *path)
struct strbuf sect = STRBUF_INIT;
const struct submodule *submodule;
- if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
+ if (!file_exists(GITMODULES_FILE)) /* Do nothing without .gitmodules */
return -1;
- if (gitmodules_is_unmerged)
+ if (is_gitmodules_unmerged(&the_index))
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
- submodule = submodule_from_path(null_sha1, path);
+ submodule = submodule_from_path(&null_oid, path);
if (!submodule || !submodule->name) {
warning(_("Could not find section in .gitmodules where path=%s"), path);
return -1;
}
strbuf_addstr(&sect, "submodule.");
strbuf_addstr(&sect, submodule->name);
- if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) {
+ if (git_config_rename_section_in_file(GITMODULES_FILE, sect.buf, NULL) < 0) {
/* Maybe the user already did that, don't error out here */
warning(_("Could not remove .gitmodules entry for %s"), path);
strbuf_release(&sect);
@@ -122,7 +138,7 @@ int remove_path_from_gitmodules(const char *path)
void stage_updated_gitmodules(void)
{
- if (add_file_to_cache(".gitmodules", 0))
+ if (add_file_to_cache(GITMODULES_FILE, 0))
die(_("staging updated .gitmodules failed"));
}
@@ -147,42 +163,20 @@ done:
void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
const char *path)
{
- const struct submodule *submodule = submodule_from_path(null_sha1, path);
+ const struct submodule *submodule = submodule_from_path(&null_oid, path);
if (submodule) {
- if (submodule->ignore)
- handle_ignore_submodules_arg(diffopt, submodule->ignore);
- else if (gitmodules_is_unmerged)
- DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
- }
-}
+ const char *ignore;
+ char *key;
-/* For loading from the .gitmodules file. */
-static int git_modules_config(const char *var, const char *value, void *cb)
-{
- if (!strcmp(var, "submodule.fetchjobs")) {
- parallel_jobs = git_config_int(var, value);
- if (parallel_jobs < 0)
- die(_("negative values not allowed for submodule.fetchJobs"));
- return 0;
- } else if (starts_with(var, "submodule."))
- return parse_submodule_config_option(var, value);
- else if (!strcmp(var, "fetch.recursesubmodules")) {
- config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
- return 0;
- }
- return 0;
-}
+ key = xstrfmt("submodule.%s.ignore", submodule->name);
+ if (repo_config_get_string_const(the_repository, key, &ignore))
+ ignore = submodule->ignore;
+ free(key);
-/* Loads all submodule settings from the config. */
-int submodule_config(const char *var, const char *value, void *cb)
-{
- if (!strcmp(var, "submodule.recurse")) {
- int v = git_config_bool(var, value) ?
- RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
- config_update_recurse_submodules = v;
- return 0;
- } else {
- return git_modules_config(var, value, cb);
+ if (ignore)
+ handle_ignore_submodules_arg(diffopt, ignore);
+ else if (is_gitmodules_unmerged(&the_index))
+ DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
}
}
@@ -214,74 +208,6 @@ int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
return 0;
}
-void load_submodule_cache(void)
-{
- if (config_update_recurse_submodules == RECURSE_SUBMODULES_OFF)
- return;
-
- gitmodules_config();
- git_config(submodule_config, NULL);
-}
-
-void gitmodules_config(void)
-{
- const char *work_tree = get_git_work_tree();
- if (work_tree) {
- struct strbuf gitmodules_path = STRBUF_INIT;
- int pos;
- strbuf_addstr(&gitmodules_path, work_tree);
- strbuf_addstr(&gitmodules_path, "/.gitmodules");
- if (read_cache() < 0)
- die("index file corrupt");
- pos = cache_name_pos(".gitmodules", 11);
- if (pos < 0) { /* .gitmodules not found or isn't merged */
- pos = -1 - pos;
- if (active_nr > pos) { /* there is a .gitmodules */
- const struct cache_entry *ce = active_cache[pos];
- if (ce_namelen(ce) == 11 &&
- !memcmp(ce->name, ".gitmodules", 11))
- gitmodules_is_unmerged = 1;
- }
- } else if (pos < active_nr) {
- struct stat st;
- if (lstat(".gitmodules", &st) == 0 &&
- ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED)
- gitmodules_is_modified = 1;
- }
-
- if (!gitmodules_is_unmerged)
- git_config_from_file(git_modules_config,
- gitmodules_path.buf, NULL);
- strbuf_release(&gitmodules_path);
- }
-}
-
-static int gitmodules_cb(const char *var, const char *value, void *data)
-{
- struct repository *repo = data;
- return submodule_config_option(repo, var, value);
-}
-
-void repo_read_gitmodules(struct repository *repo)
-{
- char *gitmodules_path = repo_worktree_path(repo, ".gitmodules");
-
- git_config_from_file(gitmodules_cb, gitmodules_path, repo);
- free(gitmodules_path);
-}
-
-void gitmodules_config_sha1(const unsigned char *commit_sha1)
-{
- struct strbuf rev = STRBUF_INIT;
- unsigned char sha1[20];
-
- if (gitmodule_sha1_from_commit(commit_sha1, sha1, &rev)) {
- git_config_from_blob_sha1(git_modules_config, rev.buf,
- sha1, NULL);
- }
- strbuf_release(&rev);
-}
-
/*
* Determine if a submodule has been initialized at a given 'path'
*/
@@ -293,7 +219,7 @@ int is_submodule_active(struct repository *repo, const char *path)
const struct string_list *sl;
const struct submodule *module;
- module = submodule_from_cache(repo, null_sha1, path);
+ module = submodule_from_cache(repo, &null_oid, path);
/* early return if there isn't a path->module mapping */
if (!module)
@@ -410,24 +336,38 @@ void die_path_inside_submodule(const struct index_state *istate,
}
}
-int parse_submodule_update_strategy(const char *value,
- struct submodule_update_strategy *dst)
+enum submodule_update_type parse_submodule_update_type(const char *value)
{
- free((void*)dst->command);
- dst->command = NULL;
if (!strcmp(value, "none"))
- dst->type = SM_UPDATE_NONE;
+ return SM_UPDATE_NONE;
else if (!strcmp(value, "checkout"))
- dst->type = SM_UPDATE_CHECKOUT;
+ return SM_UPDATE_CHECKOUT;
else if (!strcmp(value, "rebase"))
- dst->type = SM_UPDATE_REBASE;
+ return SM_UPDATE_REBASE;
else if (!strcmp(value, "merge"))
- dst->type = SM_UPDATE_MERGE;
- else if (skip_prefix(value, "!", &value)) {
- dst->type = SM_UPDATE_COMMAND;
- dst->command = xstrdup(value);
- } else
+ return SM_UPDATE_MERGE;
+ else if (*value == '!')
+ return SM_UPDATE_COMMAND;
+ else
+ return SM_UPDATE_UNSPECIFIED;
+}
+
+int parse_submodule_update_strategy(const char *value,
+ struct submodule_update_strategy *dst)
+{
+ enum submodule_update_type type;
+
+ free((void*)dst->command);
+ dst->command = NULL;
+
+ type = parse_submodule_update_type(value);
+ if (type == SM_UPDATE_UNSPECIFIED)
return -1;
+
+ dst->type = type;
+ if (type == SM_UPDATE_COMMAND)
+ dst->command = xstrdup(value + 1);
+
return 0;
}
@@ -490,9 +430,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
return prepare_revision_walk(rev);
}
-static void print_submodule_summary(struct rev_info *rev, FILE *f,
- const char *line_prefix,
- const char *del, const char *add, const char *reset)
+static void print_submodule_summary(struct rev_info *rev, struct diff_options *o)
{
static const char format[] = " %m %s";
struct strbuf sb = STRBUF_INIT;
@@ -503,18 +441,12 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
ctx.date_mode = rev->date_mode;
ctx.output_encoding = get_log_output_encoding();
strbuf_setlen(&sb, 0);
- strbuf_addstr(&sb, line_prefix);
- if (commit->object.flags & SYMMETRIC_LEFT) {
- if (del)
- strbuf_addstr(&sb, del);
- }
- else if (add)
- strbuf_addstr(&sb, add);
format_commit_message(commit, format, &sb, &ctx);
- if (reset)
- strbuf_addstr(&sb, reset);
strbuf_addch(&sb, '\n');
- fprintf(f, "%s", sb.buf);
+ if (commit->object.flags & SYMMETRIC_LEFT)
+ diff_emit_submodule_del(o, sb.buf);
+ else
+ diff_emit_submodule_add(o, sb.buf);
}
strbuf_release(&sb);
}
@@ -541,11 +473,9 @@ void prepare_submodule_repo_env(struct argv_array *out)
* attempt to lookup both the left and right commits and put them into the
* left and right pointers.
*/
-static void show_submodule_header(FILE *f, const char *path,
- const char *line_prefix,
+static void show_submodule_header(struct diff_options *o, const char *path,
struct object_id *one, struct object_id *two,
- unsigned dirty_submodule, const char *meta,
- const char *reset,
+ unsigned dirty_submodule,
struct commit **left, struct commit **right,
struct commit_list **merge_bases)
{
@@ -554,11 +484,10 @@ static void show_submodule_header(FILE *f, const char *path,
int fast_forward = 0, fast_backward = 0;
if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
- fprintf(f, "%sSubmodule %s contains untracked content\n",
- line_prefix, path);
+ diff_emit_submodule_untracked(o, path);
+
if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
- fprintf(f, "%sSubmodule %s contains modified content\n",
- line_prefix, path);
+ diff_emit_submodule_modified(o, path);
if (is_null_oid(one))
message = "(new submodule)";
@@ -600,31 +529,29 @@ static void show_submodule_header(FILE *f, const char *path,
}
output_header:
- strbuf_addf(&sb, "%s%sSubmodule %s ", line_prefix, meta, path);
+ strbuf_addf(&sb, "Submodule %s ", path);
strbuf_add_unique_abbrev(&sb, one->hash, DEFAULT_ABBREV);
strbuf_addstr(&sb, (fast_backward || fast_forward) ? ".." : "...");
strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV);
if (message)
- strbuf_addf(&sb, " %s%s\n", message, reset);
+ strbuf_addf(&sb, " %s\n", message);
else
- strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset);
- fwrite(sb.buf, sb.len, 1, f);
+ strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
+ diff_emit_submodule_header(o, sb.buf);
strbuf_release(&sb);
}
-void show_submodule_summary(FILE *f, const char *path,
- const char *line_prefix,
+void show_submodule_summary(struct diff_options *o, const char *path,
struct object_id *one, struct object_id *two,
- unsigned dirty_submodule, const char *meta,
- const char *del, const char *add, const char *reset)
+ unsigned dirty_submodule)
{
struct rev_info rev;
struct commit *left = NULL, *right = NULL;
struct commit_list *merge_bases = NULL;
- show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
- meta, reset, &left, &right, &merge_bases);
+ show_submodule_header(o, path, one, two, dirty_submodule,
+ &left, &right, &merge_bases);
/*
* If we don't have both a left and a right pointer, there is no
@@ -636,11 +563,11 @@ void show_submodule_summary(FILE *f, const char *path,
/* Treat revision walker failure the same as missing commits */
if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
- fprintf(f, "%s(revision walker failed)\n", line_prefix);
+ diff_emit_submodule_error(o, "(revision walker failed)\n");
goto out;
}
- print_submodule_summary(&rev, f, line_prefix, del, add, reset);
+ print_submodule_summary(&rev, o);
out:
if (merge_bases)
@@ -649,21 +576,18 @@ out:
clear_commit_marks(right, ~0);
}
-void show_submodule_inline_diff(FILE *f, const char *path,
- const char *line_prefix,
+void show_submodule_inline_diff(struct diff_options *o, const char *path,
struct object_id *one, struct object_id *two,
- unsigned dirty_submodule, const char *meta,
- const char *del, const char *add, const char *reset,
- const struct diff_options *o)
+ unsigned dirty_submodule)
{
const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid;
struct commit *left = NULL, *right = NULL;
struct commit_list *merge_bases = NULL;
- struct strbuf submodule_dir = STRBUF_INIT;
struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf sb = STRBUF_INIT;
- show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
- meta, reset, &left, &right, &merge_bases);
+ show_submodule_header(o, path, one, two, dirty_submodule,
+ &left, &right, &merge_bases);
/* We need a valid left and right commit to display a difference */
if (!(left || is_null_oid(one)) ||
@@ -675,16 +599,16 @@ void show_submodule_inline_diff(FILE *f, const char *path,
if (right)
new = two;
- fflush(f);
cp.git_cmd = 1;
cp.dir = path;
- cp.out = dup(fileno(f));
+ cp.out = -1;
cp.no_stdin = 1;
/* TODO: other options may need to be passed here. */
argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL);
+ argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
+ "always" : "never");
- argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
o->b_prefix, path);
@@ -707,11 +631,17 @@ void show_submodule_inline_diff(FILE *f, const char *path,
argv_array_push(&cp.args, oid_to_hex(new));
prepare_submodule_repo_env(&cp.env_array);
- if (run_command(&cp))
- fprintf(f, "(diff failed)\n");
+ if (start_command(&cp))
+ diff_emit_submodule_error(o, "(diff failed)\n");
+
+ while (strbuf_getwholeline_fd(&sb, cp.out, '\n') != EOF)
+ diff_emit_submodule_pipethrough(o, sb.buf, sb.len);
+
+ if (finish_command(&cp))
+ diff_emit_submodule_error(o, "(diff failed)\n");
done:
- strbuf_release(&submodule_dir);
+ strbuf_release(&sb);
if (merge_bases)
free_commit_list(merge_bases);
if (left)
@@ -720,11 +650,6 @@ done:
clear_commit_marks(right, ~0);
}
-void set_config_fetch_recurse_submodules(int value)
-{
- config_fetch_recurse_submodules = value;
-}
-
int should_update_submodules(void)
{
return config_update_recurse_submodules == RECURSE_SUBMODULES_ON;
@@ -738,7 +663,7 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce)
if (!should_update_submodules())
return NULL;
- return submodule_from_path(null_sha1, ce->name);
+ return submodule_from_path(&null_oid, ce->name);
}
static struct oid_array *submodule_commits(struct string_list *submodules,
@@ -1015,7 +940,8 @@ static int push_submodule(const char *path,
* Perform a check in the submodule to see if the remote and refspec work.
* Die if the submodule can't be pushed.
*/
-static void submodule_push_check(const char *path, const struct remote *remote,
+static void submodule_push_check(const char *path, const char *head,
+ const struct remote *remote,
const char **refspec, int refspec_nr)
{
struct child_process cp = CHILD_PROCESS_INIT;
@@ -1023,6 +949,7 @@ static void submodule_push_check(const char *path, const struct remote *remote,
argv_array_push(&cp.args, "submodule--helper");
argv_array_push(&cp.args, "push-check");
+ argv_array_push(&cp.args, head);
argv_array_push(&cp.args, remote->name);
for (i = 0; i < refspec_nr; i++)
@@ -1061,10 +988,20 @@ int push_unpushed_submodules(struct oid_array *commits,
* won't be propagated due to the remote being unconfigured (e.g. a URL
* instead of a remote name).
*/
- if (remote->origin != REMOTE_UNCONFIGURED)
+ if (remote->origin != REMOTE_UNCONFIGURED) {
+ char *head;
+ struct object_id head_oid;
+
+ head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+ if (!head)
+ die(_("Failed to resolve HEAD as a valid ref."));
+
for (i = 0; i < needs_pushing.nr; i++)
submodule_push_check(needs_pushing.items[i].string,
- remote, refspec, refspec_nr);
+ head, remote,
+ refspec, refspec_nr);
+ free(head);
+ }
/* Actually push the submodules */
for (i = 0; i < needs_pushing.nr; i++) {
@@ -1145,7 +1082,6 @@ int submodule_touches_in_range(struct object_id *excl_oid,
struct argv_array args = ARGV_ARRAY_INIT;
int ret;
- gitmodules_config();
/* No need to check if there are no submodules configured */
if (!submodule_from_path(NULL, NULL))
return 0;
@@ -1170,10 +1106,11 @@ struct submodule_parallel_fetch {
const char *work_tree;
const char *prefix;
int command_line_option;
+ int default_option;
int quiet;
int result;
};
-#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0}
+#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0}
static int get_next_submodule(struct child_process *cp,
struct strbuf *err, void *data, void **task_cb)
@@ -1192,29 +1129,36 @@ static int get_next_submodule(struct child_process *cp,
if (!S_ISGITLINK(ce->ce_mode))
continue;
- submodule = submodule_from_path(null_sha1, ce->name);
- if (!submodule)
- submodule = submodule_from_name(null_sha1, ce->name);
+ submodule = submodule_from_path(&null_oid, ce->name);
default_argv = "yes";
if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
- if (submodule &&
- submodule->fetch_recurse !=
- RECURSE_SUBMODULES_NONE) {
- if (submodule->fetch_recurse ==
- RECURSE_SUBMODULES_OFF)
+ int fetch_recurse = RECURSE_SUBMODULES_NONE;
+
+ if (submodule) {
+ char *key;
+ const char *value;
+
+ fetch_recurse = submodule->fetch_recurse;
+ key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
+ if (!repo_config_get_string_const(the_repository, key, &value)) {
+ fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
+ }
+ free(key);
+ }
+
+ if (fetch_recurse != RECURSE_SUBMODULES_NONE) {
+ if (fetch_recurse == RECURSE_SUBMODULES_OFF)
continue;
- if (submodule->fetch_recurse ==
- RECURSE_SUBMODULES_ON_DEMAND) {
+ if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) {
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
continue;
default_argv = "on-demand";
}
} else {
- if ((config_fetch_recurse_submodules == RECURSE_SUBMODULES_OFF) ||
- gitmodules_is_unmerged)
+ if (spf->default_option == RECURSE_SUBMODULES_OFF)
continue;
- if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) {
+ if (spf->default_option == RECURSE_SUBMODULES_ON_DEMAND) {
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
continue;
default_argv = "on-demand";
@@ -1281,6 +1225,7 @@ static int fetch_finish(int retvalue, struct strbuf *err,
int fetch_populated_submodules(const struct argv_array *options,
const char *prefix, int command_line_option,
+ int default_option,
int quiet, int max_parallel_jobs)
{
int i;
@@ -1288,6 +1233,7 @@ int fetch_populated_submodules(const struct argv_array *options,
spf.work_tree = get_git_work_tree();
spf.command_line_option = command_line_option;
+ spf.default_option = default_option;
spf.quiet = quiet;
spf.prefix = prefix;
@@ -1303,9 +1249,6 @@ int fetch_populated_submodules(const struct argv_array *options,
argv_array_push(&spf.args, "--recurse-submodules-default");
/* default value, "--submodule-prefix" and its value are added later */
- if (max_parallel_jobs < 0)
- max_parallel_jobs = parallel_jobs;
-
calculate_changed_submodule_paths();
run_processes_parallel(max_parallel_jobs,
get_next_submodule,
@@ -1570,7 +1513,7 @@ int submodule_move_head(const char *path,
if (old && !is_submodule_populated_gently(path, error_code_ptr))
return 0;
- sub = submodule_from_path(null_sha1, path);
+ sub = submodule_from_path(&null_oid, path);
if (!sub)
die("BUG: could not get submodule information for '%s'", path);
@@ -1825,11 +1768,6 @@ int merge_submodule(struct object_id *result, const char *path,
return 0;
}
-int parallel_submodules(void)
-{
- return parallel_jobs;
-}
-
/*
* Embeds a single submodules git directory into the superprojects git dir,
* non recursively.
@@ -1852,7 +1790,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
real_old_git_dir = real_pathdup(old_git_dir, 1);
- sub = submodule_from_path(null_sha1, path);
+ sub = submodule_from_path(&null_oid, path);
if (!sub)
die(_("could not lookup name for submodule '%s'"), path);
@@ -1908,7 +1846,7 @@ void absorb_git_dir_into_superproject(const char *prefix,
* superproject did not rewrite the git file links yet,
* fix it now.
*/
- sub = submodule_from_path(null_sha1, path);
+ sub = submodule_from_path(&null_oid, path);
if (!sub)
die(_("could not lookup name for submodule '%s'"), path);
connect_work_tree_and_git_dir(path,
@@ -2050,8 +1988,7 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
strbuf_addstr(buf, git_dir);
}
if (!is_git_directory(buf->buf)) {
- gitmodules_config();
- sub = submodule_from_path(null_sha1, submodule);
+ sub = submodule_from_path(&null_oid, submodule);
if (!sub) {
ret = -1;
goto cleanup;
diff --git a/submodule.h b/submodule.h
index e85b144863..6b52133c88 100644
--- a/submodule.h
+++ b/submodule.h
@@ -33,22 +33,18 @@ struct submodule_update_strategy {
};
#define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}
-extern int is_staging_gitmodules_ok(void);
+extern int is_gitmodules_unmerged(const struct index_state *istate);
+extern int is_staging_gitmodules_ok(const struct index_state *istate);
extern int update_path_in_gitmodules(const char *oldpath, const char *newpath);
extern int remove_path_from_gitmodules(const char *path);
extern void stage_updated_gitmodules(void);
extern void set_diffopt_flags_from_submodule_config(struct diff_options *,
const char *path);
-extern int submodule_config(const char *var, const char *value, void *cb);
extern int git_default_submodule_config(const char *var, const char *value, void *cb);
struct option;
int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
const char *arg, int unset);
-void load_submodule_cache(void);
-extern void gitmodules_config(void);
-extern void repo_read_gitmodules(struct repository *repo);
-extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
extern int is_submodule_active(struct repository *repo, const char *path);
/*
* Determine if a submodule has been populated at a given 'path' by checking if
@@ -61,22 +57,17 @@ extern void die_in_unpopulated_submodule(const struct index_state *istate,
const char *prefix);
extern void die_path_inside_submodule(const struct index_state *istate,
const struct pathspec *ps);
+extern enum submodule_update_type parse_submodule_update_type(const char *value);
extern int parse_submodule_update_strategy(const char *value,
struct submodule_update_strategy *dst);
extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
extern void handle_ignore_submodules_arg(struct diff_options *, const char *);
-extern void show_submodule_summary(FILE *f, const char *path,
- const char *line_prefix,
+extern void show_submodule_summary(struct diff_options *o, const char *path,
struct object_id *one, struct object_id *two,
- unsigned dirty_submodule, const char *meta,
- const char *del, const char *add, const char *reset);
-extern void show_submodule_inline_diff(FILE *f, const char *path,
- const char *line_prefix,
+ unsigned dirty_submodule);
+extern void show_submodule_inline_diff(struct diff_options *o, const char *path,
struct object_id *one, struct object_id *two,
- unsigned dirty_submodule, const char *meta,
- const char *del, const char *add, const char *reset,
- const struct diff_options *opt);
-extern void set_config_fetch_recurse_submodules(int value);
+ unsigned dirty_submodule);
/* Check if we want to update any submodule.*/
extern int should_update_submodules(void);
/*
@@ -87,6 +78,7 @@ extern const struct submodule *submodule_from_ce(const struct cache_entry *ce);
extern void check_for_new_submodule_commits(struct object_id *oid);
extern int fetch_populated_submodules(const struct argv_array *options,
const char *prefix, int command_line_option,
+ int default_option,
int quiet, int max_parallel_jobs);
extern unsigned is_submodule_modified(const char *path, int ignore_untracked);
extern int submodule_uses_gitfile(const char *path);
@@ -112,7 +104,6 @@ extern int push_unpushed_submodules(struct oid_array *commits,
const struct string_list *push_options,
int dry_run);
extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
-extern int parallel_submodules(void);
/*
* Given a submodule path (as in the index), return the repository
* path of that submodule in 'buf'. Return -1 on error or when the
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 095d7395f3..6004c81f0b 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -13,20 +13,20 @@ static const char *get_value(const struct test_entry *e)
return e->key + strlen(e->key) + 1;
}
-static int test_entry_cmp(const void *unused_cmp_data,
- const struct test_entry *e1,
- const struct test_entry *e2,
- const char* key)
+static int test_entry_cmp(const void *cmp_data,
+ const void *entry,
+ const void *entry_or_key,
+ const void *keydata)
{
- return strcmp(e1->key, key ? key : e2->key);
-}
-
-static int test_entry_cmp_icase(const void *unused_cmp_data,
- const struct test_entry *e1,
- const struct test_entry *e2,
- const char* key)
-{
- return strcasecmp(e1->key, key ? key : e2->key);
+ const int ignore_case = cmp_data ? *((int *)cmp_data) : 0;
+ const struct test_entry *e1 = entry;
+ const struct test_entry *e2 = entry_or_key;
+ const char *key = keydata;
+
+ if (ignore_case)
+ return strcasecmp(e1->key, key ? key : e2->key);
+ else
+ return strcmp(e1->key, key ? key : e2->key);
}
static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
@@ -96,8 +96,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
if (method & TEST_ADD) {
/* test adding to the map */
for (j = 0; j < rounds; j++) {
- hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp,
- NULL, 0);
+ hashmap_init(&map, test_entry_cmp, NULL, 0);
/* add entries */
for (i = 0; i < TEST_SIZE; i++) {
@@ -109,7 +108,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
}
} else {
/* test map lookups */
- hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, NULL, 0);
+ hashmap_init(&map, test_entry_cmp, NULL, 0);
/* fill the map (sparsely if specified) */
j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
@@ -151,8 +150,7 @@ int cmd_main(int argc, const char **argv)
/* init hash map */
icase = argc > 1 && !strcmp("ignorecase", argv[1]);
- hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase
- : test_entry_cmp), NULL, 0);
+ hashmap_init(&map, test_entry_cmp, &icase, 0);
/* process commands from stdin */
while (fgets(line, sizeof(line), stdin)) {
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 1ebe0f750c..2b3c5092a1 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -38,6 +38,20 @@ struct test_data {
const char *alternative; /* output: ... or this. */
};
+/*
+ * Compatibility wrappers for OpenBSD, whose basename(3) and dirname(3)
+ * have const parameters.
+ */
+static char *posix_basename(char *path)
+{
+ return basename(path);
+}
+
+static char *posix_dirname(char *path)
+{
+ return dirname(path);
+}
+
static int test_function(struct test_data *data, char *(*func)(char *input),
const char *funcname)
{
@@ -251,10 +265,10 @@ int cmd_main(int argc, const char **argv)
}
if (argc == 2 && !strcmp(argv[1], "basename"))
- return test_function(basename_data, basename, argv[1]);
+ return test_function(basename_data, posix_basename, argv[1]);
if (argc == 2 && !strcmp(argv[1], "dirname"))
- return test_function(dirname_data, dirname, argv[1]);
+ return test_function(dirname_data, posix_dirname, argv[1]);
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index c6c57bba0d..f23db3b19a 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -10,11 +10,6 @@ static void die_usage(int argc, const char **argv, const char *msg)
exit(1);
}
-static int git_test_config(const char *var, const char *value, void *cb)
-{
- return parse_submodule_config_option(var, value);
-}
-
int cmd_main(int argc, const char **argv)
{
const char **arg = argv;
@@ -37,11 +32,9 @@ int cmd_main(int argc, const char **argv)
die_usage(argc, argv, "Wrong number of arguments.");
setup_git_directory();
- gitmodules_config();
- git_config(git_test_config, NULL);
while (*arg) {
- unsigned char commit_sha1[20];
+ struct object_id commit_oid;
const struct submodule *submodule;
const char *commit;
const char *path_or_name;
@@ -50,14 +43,14 @@ int cmd_main(int argc, const char **argv)
path_or_name = arg[1];
if (commit[0] == '\0')
- hashclr(commit_sha1);
- else if (get_sha1(commit, commit_sha1) < 0)
+ oidclr(&commit_oid);
+ else if (get_oid(commit, &commit_oid) < 0)
die_usage(argc, argv, "Commit not found.");
if (lookup_name) {
- submodule = submodule_from_name(commit_sha1, path_or_name);
+ submodule = submodule_from_name(&commit_oid, path_or_name);
} else
- submodule = submodule_from_path(commit_sha1, path_or_name);
+ submodule = submodule_from_path(&commit_oid, path_or_name);
if (!submodule)
die_usage(argc, argv, "Submodule not found.");
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
new file mode 100644
index 0000000000..b7ee039669
--- /dev/null
+++ b/t/helper/test-write-cache.c
@@ -0,0 +1,23 @@
+#include "cache.h"
+#include "lockfile.h"
+
+static struct lock_file index_lock;
+
+int cmd_main(int argc, const char **argv)
+{
+ int i, cnt = 1, lockfd;
+ if (argc == 2)
+ cnt = strtol(argv[1], NULL, 0);
+ setup_git_directory();
+ read_cache();
+ for (i = 0; i < cnt; i++) {
+ lockfd = hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+ if (0 <= lockfd) {
+ write_locked_index(&the_index, &index_lock, COMMIT_LOCK);
+ } else {
+ rollback_lock_file(&index_lock);
+ }
+ }
+
+ return 0;
+}
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index ec2aa8f687..43679a4c64 100755
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -31,6 +31,7 @@ then
chmod 0700 ./gpghome &&
GNUPGHOME="$(pwd)/gpghome" &&
export GNUPGHOME &&
+ (gpgconf --kill gpg-agent 2>&1 >/dev/null || : ) &&
gpg --homedir "${GNUPGHOME}" 2>/dev/null --import \
"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
gpg --homedir "${GNUPGHOME}" 2>/dev/null --import-ownertrust \
diff --git a/t/perf/p0007-write-cache.sh b/t/perf/p0007-write-cache.sh
new file mode 100755
index 0000000000..261fe92fd9
--- /dev/null
+++ b/t/perf/p0007-write-cache.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description="Tests performance of writing the index"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success "setup repo" '
+ if git rev-parse --verify refs/heads/p0006-ballast^{commit}
+ then
+ echo Assuming synthetic repo from many-files.sh
+ git config --local core.sparsecheckout 1
+ cat >.git/info/sparse-checkout <<-EOF
+ /*
+ !ballast/*
+ EOF
+ else
+ echo Assuming non-synthetic repo...
+ fi &&
+ nr_files=$(git ls-files | wc -l)
+'
+
+count=3
+test_perf "write_locked_index $count times ($nr_files files)" "
+ test-write-cache $count
+"
+
+test_done
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index c4814d248f..86c1a51654 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -315,18 +315,44 @@ test_expect_success 'init with separate gitdir' '
test_path_is_dir realgitdir/refs
'
-test_expect_success 'init in long base path' '
+test_lazy_prereq GETCWD_IGNORES_PERMS '
+ base=GETCWD_TEST_BASE_DIR &&
+ mkdir -p $base/dir &&
+ chmod 100 $base ||
+ error "bug in test script: cannot prepare $base"
+
+ (cd $base/dir && /bin/pwd -P)
+ status=$?
+
+ chmod 700 $base &&
+ rm -rf $base ||
+ error "bug in test script: cannot clean $base"
+ return $status
+'
+
+check_long_base_path () {
# exceed initial buffer size of strbuf_getcwd()
component=123456789abcdef &&
test_when_finished "chmod 0700 $component; rm -rf $component" &&
p31=$component/$component &&
p127=$p31/$p31/$p31/$p31 &&
mkdir -p $p127 &&
- chmod 0111 $component &&
+ if test $# = 1
+ then
+ chmod $1 $component
+ fi &&
(
cd $p127 &&
git init newdir
)
+}
+
+test_expect_success 'init in long base path' '
+ check_long_base_path
+'
+
+test_expect_success GETCWD_IGNORES_PERMS 'init in long restricted base path' '
+ check_long_base_path 0111
'
test_expect_success 're-init on .git file' '
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 161f560446..46f8e583c3 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -28,7 +28,7 @@ file_size () {
}
filter_git () {
- rm -f rot13-filter.log &&
+ rm -f *.log &&
git "$@"
}
@@ -42,10 +42,10 @@ test_cmp_count () {
for FILE in "$expect" "$actual"
do
sort "$FILE" | uniq -c |
- sed -e "s/^ *[0-9][0-9]*[ ]*IN: /x IN: /" >"$FILE.tmp" &&
- mv "$FILE.tmp" "$FILE" || return
+ sed -e "s/^ *[0-9][0-9]*[ ]*IN: /x IN: /" >"$FILE.tmp"
done &&
- test_cmp "$expect" "$actual"
+ test_cmp "$expect.tmp" "$actual.tmp" &&
+ rm "$expect.tmp" "$actual.tmp"
}
# Compare two files but exclude all `clean` invocations because Git can
@@ -56,10 +56,10 @@ test_cmp_exclude_clean () {
actual=$2
for FILE in "$expect" "$actual"
do
- grep -v "IN: clean" "$FILE" >"$FILE.tmp" &&
- mv "$FILE.tmp" "$FILE"
+ grep -v "IN: clean" "$FILE" >"$FILE.tmp"
done &&
- test_cmp "$expect" "$actual"
+ test_cmp "$expect.tmp" "$actual.tmp" &&
+ rm "$expect.tmp" "$actual.tmp"
}
# Check that the contents of two files are equal and that their rot13 version
@@ -342,7 +342,7 @@ test_expect_success 'diff does not reuse worktree files that need cleaning' '
'
test_expect_success PERL 'required process filter should filter data' '
- test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -375,7 +375,7 @@ test_expect_success PERL 'required process filter should filter data' '
IN: clean testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
STOP
EOF
- test_cmp_count expected.log rot13-filter.log &&
+ test_cmp_count expected.log debug.log &&
git commit -m "test commit 2" &&
rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" &&
@@ -388,7 +388,7 @@ test_expect_success PERL 'required process filter should filter data' '
IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
STOP
EOF
- test_cmp_exclude_clean expected.log rot13-filter.log &&
+ test_cmp_exclude_clean expected.log debug.log &&
filter_git checkout --quiet --no-progress empty-branch &&
cat >expected.log <<-EOF &&
@@ -397,7 +397,7 @@ test_expect_success PERL 'required process filter should filter data' '
IN: clean test.r $S [OK] -- OUT: $S . [OK]
STOP
EOF
- test_cmp_exclude_clean expected.log rot13-filter.log &&
+ test_cmp_exclude_clean expected.log debug.log &&
filter_git checkout --quiet --no-progress master &&
cat >expected.log <<-EOF &&
@@ -409,7 +409,7 @@ test_expect_success PERL 'required process filter should filter data' '
IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
STOP
EOF
- test_cmp_exclude_clean expected.log rot13-filter.log &&
+ test_cmp_exclude_clean expected.log debug.log &&
test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
@@ -419,7 +419,7 @@ test_expect_success PERL 'required process filter should filter data' '
test_expect_success PERL 'required process filter takes precedence' '
test_config_global filter.protocol.clean false &&
- test_config_global filter.protocol.process "rot13-filter.pl clean" &&
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -439,12 +439,12 @@ test_expect_success PERL 'required process filter takes precedence' '
IN: clean test.r $S [OK] -- OUT: $S . [OK]
STOP
EOF
- test_cmp_count expected.log rot13-filter.log
+ test_cmp_count expected.log debug.log
)
'
test_expect_success PERL 'required process filter should be used only for "clean" operation only' '
- test_config_global filter.protocol.process "rot13-filter.pl clean" &&
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
rm -rf repo &&
mkdir repo &&
(
@@ -462,7 +462,7 @@ test_expect_success PERL 'required process filter should be used only for "clean
IN: clean test.r $S [OK] -- OUT: $S . [OK]
STOP
EOF
- test_cmp_count expected.log rot13-filter.log &&
+ test_cmp_count expected.log debug.log &&
rm test.r &&
@@ -474,12 +474,12 @@ test_expect_success PERL 'required process filter should be used only for "clean
init handshake complete
STOP
EOF
- test_cmp_exclude_clean expected.log rot13-filter.log
+ test_cmp_exclude_clean expected.log debug.log
)
'
test_expect_success PERL 'required process filter should process multiple packets' '
- test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
@@ -514,7 +514,7 @@ test_expect_success PERL 'required process filter should process multiple packet
IN: clean 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
STOP
EOF
- test_cmp_count expected.log rot13-filter.log &&
+ test_cmp_count expected.log debug.log &&
rm -f *.file &&
@@ -529,7 +529,7 @@ test_expect_success PERL 'required process filter should process multiple packet
IN: smudge 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
STOP
EOF
- test_cmp_exclude_clean expected.log rot13-filter.log &&
+ test_cmp_exclude_clean expected.log debug.log &&
for FILE in *.file
do
@@ -539,7 +539,7 @@ test_expect_success PERL 'required process filter should process multiple packet
'
test_expect_success PERL 'required process filter with clean error should fail' '
- test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -558,7 +558,7 @@ test_expect_success PERL 'required process filter with clean error should fail'
'
test_expect_success PERL 'process filter should restart after unexpected write failure' '
- test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -579,7 +579,7 @@ test_expect_success PERL 'process filter should restart after unexpected write f
git add . &&
rm -f *.r &&
- rm -f rot13-filter.log &&
+ rm -f debug.log &&
git checkout --quiet --no-progress . 2>git-stderr.log &&
grep "smudge write error at" git-stderr.log &&
@@ -588,14 +588,14 @@ test_expect_success PERL 'process filter should restart after unexpected write f
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge smudge-write-fail.r $SF [OK] -- OUT: $SF [WRITE FAIL]
+ IN: smudge smudge-write-fail.r $SF [OK] -- [WRITE FAIL]
START
init handshake complete
IN: smudge test.r $S [OK] -- OUT: $S . [OK]
IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
STOP
EOF
- test_cmp_exclude_clean expected.log rot13-filter.log &&
+ test_cmp_exclude_clean expected.log debug.log &&
test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
@@ -609,7 +609,7 @@ test_expect_success PERL 'process filter should restart after unexpected write f
'
test_expect_success PERL 'process filter should not be restarted if it signals an error' '
- test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -634,12 +634,12 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge error.r $SE [OK] -- OUT: 0 [ERROR]
+ IN: smudge error.r $SE [OK] -- [ERROR]
IN: smudge test.r $S [OK] -- OUT: $S . [OK]
IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
STOP
EOF
- test_cmp_exclude_clean expected.log rot13-filter.log &&
+ test_cmp_exclude_clean expected.log debug.log &&
test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
@@ -648,7 +648,7 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
'
test_expect_success PERL 'process filter abort stops processing of all further files' '
- test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -673,10 +673,10 @@ test_expect_success PERL 'process filter abort stops processing of all further f
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge abort.r $SA [OK] -- OUT: 0 [ABORT]
+ IN: smudge abort.r $SA [OK] -- [ABORT]
STOP
EOF
- test_cmp_exclude_clean expected.log rot13-filter.log &&
+ test_cmp_exclude_clean expected.log debug.log &&
test_cmp "$TEST_ROOT/test.o" test.r &&
test_cmp "$TEST_ROOT/test2.o" test2.r &&
@@ -697,8 +697,124 @@ test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
cp "$TEST_ROOT/test.o" test.r &&
test_must_fail git add . 2>git-stderr.log &&
- grep "does not support filter protocol version" git-stderr.log
+ grep "expected git-filter-server" git-stderr.log
)
'
+test_expect_success PERL 'delayed checkout in process filter' '
+ test_config_global filter.a.process "rot13-filter.pl a.log clean smudge delay" &&
+ test_config_global filter.a.required true &&
+ test_config_global filter.b.process "rot13-filter.pl b.log clean smudge delay" &&
+ test_config_global filter.b.required true &&
+
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+ echo "*.a filter=a" >.gitattributes &&
+ echo "*.b filter=b" >>.gitattributes &&
+ cp "$TEST_ROOT/test.o" test.a &&
+ cp "$TEST_ROOT/test.o" test-delay10.a &&
+ cp "$TEST_ROOT/test.o" test-delay11.a &&
+ cp "$TEST_ROOT/test.o" test-delay20.a &&
+ cp "$TEST_ROOT/test.o" test-delay10.b &&
+ git add . &&
+ git commit -m "test commit"
+ ) &&
+
+ S=$(file_size "$TEST_ROOT/test.o") &&
+ cat >a.exp <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test.a $S [OK] -- OUT: $S . [OK]
+ IN: smudge test-delay10.a $S [OK] -- [DELAYED]
+ IN: smudge test-delay11.a $S [OK] -- [DELAYED]
+ IN: smudge test-delay20.a $S [OK] -- [DELAYED]
+ IN: list_available_blobs test-delay10.a test-delay11.a [OK]
+ IN: smudge test-delay10.a 0 [OK] -- OUT: $S . [OK]
+ IN: smudge test-delay11.a 0 [OK] -- OUT: $S . [OK]
+ IN: list_available_blobs test-delay20.a [OK]
+ IN: smudge test-delay20.a 0 [OK] -- OUT: $S . [OK]
+ IN: list_available_blobs [OK]
+ STOP
+ EOF
+ cat >b.exp <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test-delay10.b $S [OK] -- [DELAYED]
+ IN: list_available_blobs test-delay10.b [OK]
+ IN: smudge test-delay10.b 0 [OK] -- OUT: $S . [OK]
+ IN: list_available_blobs [OK]
+ STOP
+ EOF
+
+ rm -rf repo-cloned &&
+ filter_git clone repo repo-cloned &&
+ test_cmp_count a.exp repo-cloned/a.log &&
+ test_cmp_count b.exp repo-cloned/b.log &&
+
+ (
+ cd repo-cloned &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay11.a &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay20.a &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.b &&
+
+ rm *.a *.b &&
+ filter_git checkout . &&
+ test_cmp_count ../a.exp a.log &&
+ test_cmp_count ../b.exp b.log &&
+
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay11.a &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay20.a &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.b
+ )
+'
+
+test_expect_success PERL 'missing file in delayed checkout' '
+ test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
+ test_config_global filter.bug.required true &&
+
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+ echo "*.a filter=bug" >.gitattributes &&
+ cp "$TEST_ROOT/test.o" missing-delay.a
+ git add . &&
+ git commit -m "test commit"
+ ) &&
+
+ rm -rf repo-cloned &&
+ test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
+ cat git-stderr.log &&
+ grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
+'
+
+test_expect_success PERL 'invalid file in delayed checkout' '
+ test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
+ test_config_global filter.bug.required true &&
+
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+ echo "*.a filter=bug" >.gitattributes &&
+ cp "$TEST_ROOT/test.o" invalid-delay.a &&
+ cp "$TEST_ROOT/test.o" unfiltered
+ git add . &&
+ git commit -m "test commit"
+ ) &&
+
+ rm -rf repo-cloned &&
+ test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
+ grep "error: external filter .* signaled that .unfiltered. is now available although it has not been delayed earlier" git-stderr.log
+'
+
test_done
diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
index 617f581e56..ad685d92f8 100644
--- a/t/t0021/rot13-filter.pl
+++ b/t/t0021/rot13-filter.pl
@@ -2,8 +2,9 @@
# Example implementation for the Git filter protocol version 2
# See Documentation/gitattributes.txt, section "Filter Protocol"
#
-# The script takes the list of supported protocol capabilities as
-# arguments ("clean", "smudge", etc).
+# The first argument defines a debug log file that the script write to.
+# All remaining arguments define a list of supported protocol
+# capabilities ("clean", "smudge", etc).
#
# This implementation supports special test cases:
# (1) If data with the pathname "clean-write-fail.r" is processed with
@@ -17,6 +18,16 @@
# operation then the filter signals that it cannot or does not want
# to process the file and any file after that is processed with the
# same command.
+# (5) If data with a pathname that is a key in the DELAY hash is
+# requested (e.g. "test-delay10.a") then the filter responds with
+# a "delay" status and sets the "requested" field in the DELAY hash.
+# The filter will signal the availability of this object after
+# "count" (field in DELAY hash) "list_available_blobs" commands.
+# (6) If data with the pathname "missing-delay.a" is processed that the
+# filter will drop the path from the "list_available_blobs" response.
+# (7) If data with the pathname "invalid-delay.a" is processed that the
+# filter will add the path "unfiltered" which was not delayed before
+# to the "list_available_blobs" response.
#
use strict;
@@ -24,9 +35,19 @@ use warnings;
use IO::File;
my $MAX_PACKET_CONTENT_SIZE = 65516;
+my $log_file = shift @ARGV;
my @capabilities = @ARGV;
-open my $debug, ">>", "rot13-filter.log" or die "cannot open log file: $!";
+open my $debug, ">>", $log_file or die "cannot open log file: $!";
+
+my %DELAY = (
+ 'test-delay10.a' => { "requested" => 0, "count" => 1 },
+ 'test-delay11.a' => { "requested" => 0, "count" => 1 },
+ 'test-delay20.a' => { "requested" => 0, "count" => 2 },
+ 'test-delay10.b' => { "requested" => 0, "count" => 1 },
+ 'missing-delay.a' => { "requested" => 0, "count" => 1 },
+ 'invalid-delay.a' => { "requested" => 0, "count" => 1 },
+);
sub rot13 {
my $str = shift;
@@ -64,7 +85,7 @@ sub packet_bin_read {
sub packet_txt_read {
my ( $res, $buf ) = packet_bin_read();
- unless ( $buf =~ s/\n$// ) {
+ unless ( $buf eq '' or $buf =~ s/\n$// ) {
die "A non-binary line MUST be terminated by an LF.";
}
return ( $res, $buf );
@@ -99,6 +120,7 @@ packet_flush();
( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability";
( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability";
+( packet_txt_read() eq ( 0, "capability=delay" ) ) || die "bad capability";
( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end";
foreach (@capabilities) {
@@ -109,88 +131,142 @@ print $debug "init handshake complete\n";
$debug->flush();
while (1) {
- my ($command) = packet_txt_read() =~ /^command=(.+)$/;
+ my ( $command ) = packet_txt_read() =~ /^command=(.+)$/;
print $debug "IN: $command";
$debug->flush();
- my ($pathname) = packet_txt_read() =~ /^pathname=(.+)$/;
- print $debug " $pathname";
- $debug->flush();
-
- if ( $pathname eq "" ) {
- die "bad pathname '$pathname'";
- }
+ if ( $command eq "list_available_blobs" ) {
+ # Flush
+ packet_bin_read();
- # Flush
- packet_bin_read();
-
- my $input = "";
- {
- binmode(STDIN);
- my $buffer;
- my $done = 0;
- while ( !$done ) {
- ( $done, $buffer ) = packet_bin_read();
- $input .= $buffer;
+ foreach my $pathname ( sort keys %DELAY ) {
+ if ( $DELAY{$pathname}{"requested"} >= 1 ) {
+ $DELAY{$pathname}{"count"} = $DELAY{$pathname}{"count"} - 1;
+ if ( $pathname eq "invalid-delay.a" ) {
+ # Send Git a pathname that was not delayed earlier
+ packet_txt_write("pathname=unfiltered");
+ }
+ if ( $pathname eq "missing-delay.a" ) {
+ # Do not signal Git that this file is available
+ } elsif ( $DELAY{$pathname}{"count"} == 0 ) {
+ print $debug " $pathname";
+ packet_txt_write("pathname=$pathname");
+ }
+ }
}
- print $debug " " . length($input) . " [OK] -- ";
- $debug->flush();
- }
-
- my $output;
- if ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
- $output = "";
- }
- elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
- $output = rot13($input);
- }
- elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
- $output = rot13($input);
- }
- else {
- die "bad command '$command'";
- }
- print $debug "OUT: " . length($output) . " ";
- $debug->flush();
-
- if ( $pathname eq "error.r" ) {
- print $debug "[ERROR]\n";
- $debug->flush();
- packet_txt_write("status=error");
packet_flush();
- }
- elsif ( $pathname eq "abort.r" ) {
- print $debug "[ABORT]\n";
+
+ print $debug " [OK]\n";
$debug->flush();
- packet_txt_write("status=abort");
+ packet_txt_write("status=success");
packet_flush();
}
else {
- packet_txt_write("status=success");
- packet_flush();
+ my ( $pathname ) = packet_txt_read() =~ /^pathname=(.+)$/;
+ print $debug " $pathname";
+ $debug->flush();
- if ( $pathname eq "${command}-write-fail.r" ) {
- print $debug "[WRITE FAIL]\n";
+ if ( $pathname eq "" ) {
+ die "bad pathname '$pathname'";
+ }
+
+ # Read until flush
+ my ( $done, $buffer ) = packet_txt_read();
+ while ( $buffer ne '' ) {
+ if ( $buffer eq "can-delay=1" ) {
+ if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
+ $DELAY{$pathname}{"requested"} = 1;
+ }
+ } else {
+ die "Unknown message '$buffer'";
+ }
+
+ ( $done, $buffer ) = packet_txt_read();
+ }
+
+ my $input = "";
+ {
+ binmode(STDIN);
+ my $buffer;
+ my $done = 0;
+ while ( !$done ) {
+ ( $done, $buffer ) = packet_bin_read();
+ $input .= $buffer;
+ }
+ print $debug " " . length($input) . " [OK] -- ";
$debug->flush();
- die "${command} write error";
}
- while ( length($output) > 0 ) {
- my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
- packet_bin_write($packet);
- # dots represent the number of packets
- print $debug ".";
- if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
- $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
+ my $output;
+ if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
+ $output = $DELAY{$pathname}{"output"}
+ }
+ elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
+ $output = "";
+ }
+ elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
+ $output = rot13($input);
+ }
+ elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
+ $output = rot13($input);
+ }
+ else {
+ die "bad command '$command'";
+ }
+
+ if ( $pathname eq "error.r" ) {
+ print $debug "[ERROR]\n";
+ $debug->flush();
+ packet_txt_write("status=error");
+ packet_flush();
+ }
+ elsif ( $pathname eq "abort.r" ) {
+ print $debug "[ABORT]\n";
+ $debug->flush();
+ packet_txt_write("status=abort");
+ packet_flush();
+ }
+ elsif ( $command eq "smudge" and
+ exists $DELAY{$pathname} and
+ $DELAY{$pathname}{"requested"} == 1
+ ) {
+ print $debug "[DELAYED]\n";
+ $debug->flush();
+ packet_txt_write("status=delayed");
+ packet_flush();
+ $DELAY{$pathname}{"requested"} = 2;
+ $DELAY{$pathname}{"output"} = $output;
+ }
+ else {
+ packet_txt_write("status=success");
+ packet_flush();
+
+ if ( $pathname eq "${command}-write-fail.r" ) {
+ print $debug "[WRITE FAIL]\n";
+ $debug->flush();
+ die "${command} write error";
}
- else {
- $output = "";
+
+ print $debug "OUT: " . length($output) . " ";
+ $debug->flush();
+
+ while ( length($output) > 0 ) {
+ my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
+ packet_bin_write($packet);
+ # dots represent the number of packets
+ print $debug ".";
+ if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
+ $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
+ }
+ else {
+ $output = "";
+ }
}
+ packet_flush();
+ print $debug " [OK]\n";
+ $debug->flush();
+ packet_flush();
}
- packet_flush();
- print $debug " [OK]\n";
- $debug->flush();
- packet_flush();
}
}
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index e3bf821694..7ca2e65d10 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -51,7 +51,9 @@ test_expect_success \
treeM=$(git write-tree) &&
echo treeM $treeM &&
git ls-tree $treeM &&
- sum bozbar frotz nitfol >M.sum &&
+ cp bozbar bozbar.M &&
+ cp frotz frotz.M &&
+ cp nitfol nitfol.M &&
git diff-tree $treeH $treeM'
test_expect_success \
@@ -61,8 +63,9 @@ test_expect_success \
read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >1-3.out &&
cmp M.out 1-3.out &&
- sum bozbar frotz nitfol >actual3.sum &&
- cmp M.sum actual3.sum &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
check_cache_at bozbar clean &&
check_cache_at frotz clean &&
check_cache_at nitfol clean'
@@ -79,8 +82,9 @@ test_expect_success \
test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
compare_change 4diff.out expected &&
check_cache_at yomin clean &&
- sum bozbar frotz nitfol >actual4.sum &&
- cmp M.sum actual4.sum &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
echo yomin >yomin1 &&
diff yomin yomin1 &&
rm -f yomin1'
@@ -98,8 +102,9 @@ test_expect_success \
test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
compare_change 5diff.out expected &&
check_cache_at yomin dirty &&
- sum bozbar frotz nitfol >actual5.sum &&
- cmp M.sum actual5.sum &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
: dirty index should have prevented -u from checking it out. &&
echo yomin yomin >yomin1 &&
diff yomin yomin1 &&
@@ -115,8 +120,9 @@ test_expect_success \
git ls-files --stage >6.out &&
test_cmp M.out 6.out &&
check_cache_at frotz clean &&
- sum bozbar frotz nitfol >actual3.sum &&
- cmp M.sum actual3.sum &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
echo frotz >frotz1 &&
diff frotz frotz1 &&
rm -f frotz1'
@@ -132,8 +138,8 @@ test_expect_success \
git ls-files --stage >7.out &&
test_cmp M.out 7.out &&
check_cache_at frotz dirty &&
- sum bozbar frotz nitfol >actual7.sum &&
- if cmp M.sum actual7.sum; then false; else :; fi &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp nitfol.M nitfol &&
: dirty index should have prevented -u from checking it out. &&
echo frotz frotz >frotz1 &&
diff frotz frotz1 &&
@@ -165,8 +171,10 @@ test_expect_success \
read_tree_u_must_succeed -m -u $treeH $treeM &&
git ls-files --stage >10.out &&
cmp M.out 10.out &&
- sum bozbar frotz nitfol >actual10.sum &&
- cmp M.sum actual10.sum'
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol
+'
test_expect_success \
'11 - dirty path removed.' \
@@ -209,11 +217,8 @@ test_expect_success \
git ls-files --stage >14.out &&
test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
compare_change 14diff.out expected &&
- sum bozbar frotz >actual14.sum &&
- grep -v nitfol M.sum > expected14.sum &&
- cmp expected14.sum actual14.sum &&
- sum bozbar frotz nitfol >actual14a.sum &&
- if cmp M.sum actual14a.sum; then false; else :; fi &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
check_cache_at nitfol clean &&
echo nitfol nitfol >nitfol1 &&
diff nitfol nitfol1 &&
@@ -231,11 +236,8 @@ test_expect_success \
test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
compare_change 15diff.out expected &&
check_cache_at nitfol dirty &&
- sum bozbar frotz >actual15.sum &&
- grep -v nitfol M.sum > expected15.sum &&
- cmp expected15.sum actual15.sum &&
- sum bozbar frotz nitfol >actual15a.sum &&
- if cmp M.sum actual15a.sum; then false; else :; fi &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
echo nitfol nitfol nitfol >nitfol1 &&
diff nitfol nitfol1 &&
rm -f nitfol1'
@@ -267,8 +269,10 @@ test_expect_success \
git ls-files --stage >18.out &&
test_cmp M.out 18.out &&
check_cache_at bozbar clean &&
- sum bozbar frotz nitfol >actual18.sum &&
- cmp M.sum actual18.sum'
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol
+'
test_expect_success \
'19 - local change already having a good result, further modified.' \
@@ -281,11 +285,8 @@ test_expect_success \
git ls-files --stage >19.out &&
test_cmp M.out 19.out &&
check_cache_at bozbar dirty &&
- sum frotz nitfol >actual19.sum &&
- grep -v bozbar M.sum > expected19.sum &&
- cmp expected19.sum actual19.sum &&
- sum bozbar frotz nitfol >actual19a.sum &&
- if cmp M.sum actual19a.sum; then false; else :; fi &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
echo gnusto gnusto >bozbar1 &&
diff bozbar bozbar1 &&
rm -f bozbar1'
@@ -300,8 +301,10 @@ test_expect_success \
git ls-files --stage >20.out &&
test_cmp M.out 20.out &&
check_cache_at bozbar clean &&
- sum bozbar frotz nitfol >actual20.sum &&
- cmp M.sum actual20.sum'
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol
+'
test_expect_success \
'21 - no local change, dirty cache.' \
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
deleted file mode 100755
index 397ccb6909..0000000000
--- a/t/t1200-tutorial.sh
+++ /dev/null
@@ -1,268 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Johannes Schindelin
-#
-
-test_description='A simple turial in the form of a test case'
-
-. ./test-lib.sh
-
-test_expect_success 'blob' '
- echo "Hello World" > hello &&
- echo "Silly example" > example &&
-
- git update-index --add hello example &&
-
- test blob = "$(git cat-file -t 557db03)"
-'
-
-test_expect_success 'blob 557db03' '
- test "Hello World" = "$(git cat-file blob 557db03)"
-'
-
-echo "It's a new day for git" >>hello
-cat > diff.expect << EOF
-diff --git a/hello b/hello
-index 557db03..263414f 100644
---- a/hello
-+++ b/hello
-@@ -1 +1,2 @@
- Hello World
-+It's a new day for git
-EOF
-
-test_expect_success 'git diff-files -p' '
- git diff-files -p > diff.output &&
- test_cmp diff.expect diff.output
-'
-
-test_expect_success 'git diff' '
- git diff > diff.output &&
- test_cmp diff.expect diff.output
-'
-
-test_expect_success 'tree' '
- tree=$(git write-tree 2>/dev/null) &&
- test 8988da15d077d4829fc51d8544c097def6644dbb = $tree
-'
-
-test_expect_success 'git diff-index -p HEAD' '
- test_tick &&
- tree=$(git write-tree) &&
- commit=$(echo "Initial commit" | git commit-tree $tree) &&
- git update-ref HEAD $commit &&
- git diff-index -p HEAD > diff.output &&
- test_cmp diff.expect diff.output
-'
-
-test_expect_success 'git diff HEAD' '
- git diff HEAD > diff.output &&
- test_cmp diff.expect diff.output
-'
-
-cat > whatchanged.expect << EOF
-commit VARIABLE
-Author: VARIABLE
-Date: VARIABLE
-
- Initial commit
-
-diff --git a/example b/example
-new file mode 100644
-index 0000000..f24c74a
---- /dev/null
-+++ b/example
-@@ -0,0 +1 @@
-+Silly example
-diff --git a/hello b/hello
-new file mode 100644
-index 0000000..557db03
---- /dev/null
-+++ b/hello
-@@ -0,0 +1 @@
-+Hello World
-EOF
-
-test_expect_success 'git whatchanged -p --root' '
- git whatchanged -p --root |
- sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
- -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
- > whatchanged.output &&
- test_cmp whatchanged.expect whatchanged.output
-'
-
-test_expect_success 'git tag my-first-tag' '
- git tag my-first-tag &&
- test_cmp .git/refs/heads/master .git/refs/tags/my-first-tag
-'
-
-test_expect_success 'git checkout -b mybranch' '
- git checkout -b mybranch &&
- test_cmp .git/refs/heads/master .git/refs/heads/mybranch
-'
-
-cat > branch.expect <<EOF
- master
-* mybranch
-EOF
-
-test_expect_success 'git branch' '
- git branch > branch.output &&
- test_cmp branch.expect branch.output
-'
-
-test_expect_success 'git resolve now fails' '
- git checkout mybranch &&
- echo "Work, work, work" >>hello &&
- test_tick &&
- git commit -m "Some work." -i hello &&
-
- git checkout master &&
-
- echo "Play, play, play" >>hello &&
- echo "Lots of fun" >>example &&
- test_tick &&
- git commit -m "Some fun." -i hello example &&
-
- test_must_fail git merge -m "Merge work in mybranch" mybranch
-'
-
-cat > hello << EOF
-Hello World
-It's a new day for git
-Play, play, play
-Work, work, work
-EOF
-
-cat > show-branch.expect << EOF
-* [master] Merge work in mybranch
- ! [mybranch] Some work.
---
-- [master] Merge work in mybranch
-*+ [mybranch] Some work.
-* [master^] Some fun.
-EOF
-
-test_expect_success 'git show-branch' '
- test_tick &&
- git commit -m "Merge work in mybranch" -i hello &&
- git show-branch --topo-order --more=1 master mybranch \
- > show-branch.output &&
- test_cmp show-branch.expect show-branch.output
-'
-
-cat > resolve.expect << EOF
-Updating VARIABLE..VARIABLE
-FASTFORWARD (no commit created; -m option ignored)
- example | 1 +
- hello | 1 +
- 2 files changed, 2 insertions(+)
-EOF
-
-test_expect_success 'git resolve' '
- git checkout mybranch &&
- git merge -m "Merge upstream changes." master |
- sed -e "1s/[0-9a-f]\{7\}/VARIABLE/g" \
- -e "s/^Fast[- ]forward /FASTFORWARD /" >resolve.output
-'
-
-test_expect_success 'git resolve output' '
- test_i18ncmp resolve.expect resolve.output
-'
-
-cat > show-branch2.expect << EOF
-! [master] Merge work in mybranch
- * [mybranch] Merge work in mybranch
---
--- [master] Merge work in mybranch
-EOF
-
-test_expect_success 'git show-branch (part 2)' '
- git show-branch --topo-order master mybranch > show-branch2.output &&
- test_cmp show-branch2.expect show-branch2.output
-'
-
-cat > show-branch3.expect << EOF
-! [master] Merge work in mybranch
- * [mybranch] Merge work in mybranch
---
--- [master] Merge work in mybranch
-+* [master^2] Some work.
-+* [master^] Some fun.
-EOF
-
-test_expect_success 'git show-branch (part 3)' '
- git show-branch --topo-order --more=2 master mybranch \
- > show-branch3.output &&
- test_cmp show-branch3.expect show-branch3.output
-'
-
-test_expect_success 'rewind to "Some fun." and "Some work."' '
- git checkout mybranch &&
- git reset --hard master^2 &&
- git checkout master &&
- git reset --hard master^
-'
-
-cat > show-branch4.expect << EOF
-* [master] Some fun.
- ! [mybranch] Some work.
---
-* [master] Some fun.
- + [mybranch] Some work.
-*+ [master^] Initial commit
-EOF
-
-test_expect_success 'git show-branch (part 4)' '
- git show-branch --topo-order > show-branch4.output &&
- test_cmp show-branch4.expect show-branch4.output
-'
-
-test_expect_success 'manual merge' '
- mb=$(git merge-base HEAD mybranch) &&
- git name-rev --name-only --tags $mb > name-rev.output &&
- test "my-first-tag" = $(cat name-rev.output) &&
-
- git read-tree -m -u $mb HEAD mybranch
-'
-
-cat > ls-files.expect << EOF
-100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example
-100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1 hello
-100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2 hello
-100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
-EOF
-
-test_expect_success 'git ls-files --stage' '
- git ls-files --stage > ls-files.output &&
- test_cmp ls-files.expect ls-files.output
-'
-
-cat > ls-files-unmerged.expect << EOF
-100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1 hello
-100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2 hello
-100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
-EOF
-
-test_expect_success 'git ls-files --unmerged' '
- git ls-files --unmerged > ls-files-unmerged.output &&
- test_cmp ls-files-unmerged.expect ls-files-unmerged.output
-'
-
-test_expect_success 'git-merge-index' '
- test_must_fail git merge-index git-merge-one-file hello
-'
-
-test_expect_success 'git ls-files --stage (part 2)' '
- git ls-files --stage > ls-files.output2 &&
- test_cmp ls-files.expect ls-files.output2
-'
-
-test_expect_success 'git repack' 'git repack'
-test_expect_success 'git prune-packed' 'git prune-packed'
-test_expect_success '-> only packed objects' '
- git prune && # Remove conflict marked blobs
- test $(find .git/objects/[0-9a-f][0-9a-f] -type f -print 2>/dev/null | wc -l) = 0
-'
-
-test_done
diff --git a/t/t1408-packed-refs.sh b/t/t1408-packed-refs.sh
new file mode 100755
index 0000000000..1e44a17eea
--- /dev/null
+++ b/t/t1408-packed-refs.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='packed-refs entries are covered by loose refs'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_tick &&
+ git commit --allow-empty -m one &&
+ one=$(git rev-parse HEAD) &&
+ git for-each-ref >actual &&
+ echo "$one commit refs/heads/master" >expect &&
+ test_cmp expect actual &&
+
+ git pack-refs --all &&
+ git for-each-ref >actual &&
+ echo "$one commit refs/heads/master" >expect &&
+ test_cmp expect actual &&
+
+ git checkout --orphan another &&
+ test_tick &&
+ git commit --allow-empty -m two &&
+ two=$(git rev-parse HEAD) &&
+ git checkout -B master &&
+ git branch -D another &&
+
+ git for-each-ref >actual &&
+ echo "$two commit refs/heads/master" >expect &&
+ test_cmp expect actual &&
+
+ git reflog expire --expire=now --all &&
+ git prune &&
+ git tag -m v1.0 v1.0 master
+'
+
+test_expect_success 'no error from stale entry in packed-refs' '
+ git describe master >actual 2>&1 &&
+ echo "v1.0" >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index b9cb76654b..6ac7734d79 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -171,14 +171,4 @@ test_expect_success 'reflog exists works' '
! git reflog exists refs/heads/nonexistent
'
-# The behavior with two reflogs is buggy and the output is in flux; for now
-# we're just checking that the program works at all without segfaulting.
-test_expect_success 'showing multiple reflogs works' '
- git log -g HEAD HEAD >actual
-'
-
-test_expect_success 'showing multiple reflogs with an old date' '
- git log -g HEAD@{1979-01-01} HEAD >actual
-'
-
test_done
diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh
new file mode 100755
index 0000000000..feb1efd8ff
--- /dev/null
+++ b/t/t1414-reflog-walk.sh
@@ -0,0 +1,135 @@
+#!/bin/sh
+
+test_description='various tests of reflog walk (log -g) behavior'
+. ./test-lib.sh
+
+test_expect_success 'set up some reflog entries' '
+ test_commit one &&
+ test_commit two &&
+ git checkout -b side HEAD^ &&
+ test_commit three &&
+ git merge --no-commit master &&
+ echo evil-merge-content >>one.t &&
+ test_tick &&
+ git commit --no-edit -a
+'
+
+do_walk () {
+ git log -g --format="%gd %gs" "$@"
+}
+
+sq="'"
+test_expect_success 'set up expected reflog' '
+ cat >expect.all <<-EOF
+ HEAD@{0} commit (merge): Merge branch ${sq}master${sq} into side
+ HEAD@{1} commit: three
+ HEAD@{2} checkout: moving from master to side
+ HEAD@{3} commit: two
+ HEAD@{4} commit (initial): one
+ EOF
+'
+
+test_expect_success 'reflog walk shows expected logs' '
+ do_walk >actual &&
+ test_cmp expect.all actual
+'
+
+test_expect_success 'reflog can limit with --no-merges' '
+ grep -v merge expect.all >expect &&
+ do_walk --no-merges >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reflog can limit with pathspecs' '
+ grep two expect.all >expect &&
+ do_walk -- two.t >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pathspec limiting handles merges' '
+ # we pick up:
+ # - the initial commit of one
+ # - the checkout back to commit one
+ # - the evil merge which touched one
+ sed -n "1p;3p;5p" expect.all >expect &&
+ do_walk -- one.t >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--parents shows true parents' '
+ # convert newlines to spaces
+ echo $(git rev-parse HEAD HEAD^1 HEAD^2) >expect &&
+ git rev-list -g --parents -1 HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'walking multiple reflogs shows all' '
+ # We expect to see all entries for all reflogs, but interleaved by
+ # date, with order on the command line breaking ties. We
+ # can use "sort" on the separate lists to generate this,
+ # but note two tricks:
+ #
+ # 1. We use "{" as the delimiter, which lets us skip to the reflog
+ # date specifier as our second field, and then our "-n" numeric
+ # sort ignores the bits after the timestamp.
+ #
+ # 2. POSIX leaves undefined whether this is a stable sort or not. So
+ # we use "-k 1" to ensure that we see HEAD before master before
+ # side when breaking ties.
+ {
+ do_walk --date=unix HEAD &&
+ do_walk --date=unix side &&
+ do_walk --date=unix master
+ } >expect.raw &&
+ sort -t "{" -k 2nr -k 1 <expect.raw >expect &&
+ do_walk --date=unix HEAD master side >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'date-limiting does not interfere with other logs' '
+ do_walk HEAD@{1979-01-01} HEAD >actual &&
+ test_cmp expect.all actual
+'
+
+test_expect_success 'min/max age uses entry date to limit' '
+ # Flip between commits one and two so each ref update actually
+ # does something (and does not get optimized out). We know
+ # that the timestamps of those commits will be before our "min".
+
+ git update-ref -m before refs/heads/minmax one &&
+
+ test_tick &&
+ min=$test_tick &&
+ git update-ref -m min refs/heads/minmax two &&
+
+ test_tick &&
+ max=$test_tick &&
+ git update-ref -m max refs/heads/minmax one &&
+
+ test_tick &&
+ git update-ref -m after refs/heads/minmax two &&
+
+ cat >expect <<-\EOF &&
+ max
+ min
+ EOF
+ git log -g --since=$min --until=$max --format=%gs minmax >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'walk prefers reflog to ref tip' '
+ head=$(git rev-parse HEAD) &&
+ one=$(git rev-parse one) &&
+ ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" &&
+ echo "$head $one $ident broken reflog entry" >>.git/logs/HEAD &&
+
+ echo $one >expect &&
+ git log -g --format=%H -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list -g complains when there are no reflogs' '
+ test_must_fail git rev-list -g
+'
+
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index bb89e1a5db..4087150db1 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -608,6 +608,22 @@ test_expect_success 'fsck errors in packed objects' '
! grep corrupt out
'
+test_expect_success 'fsck fails on corrupt packfile' '
+ hsh=$(git commit-tree -m mycommit HEAD^{tree}) &&
+ pack=$(echo $hsh | git pack-objects .git/objects/pack/pack) &&
+
+ # Corrupt the first byte of the first object. (It contains 3 type bits,
+ # at least one of which is not zero, so setting the first byte to 0 is
+ # sufficient.)
+ chmod a+w .git/objects/pack/pack-$pack.pack &&
+ printf '\0' | dd of=.git/objects/pack/pack-$pack.pack bs=1 conv=notrunc seek=12 &&
+
+ test_when_finished "rm -f .git/objects/pack/pack-$pack.*" &&
+ remove_object $hsh &&
+ test_must_fail git fsck 2>out &&
+ test_i18ngrep "checksum mismatch" out
+'
+
test_expect_success 'fsck finds problems in duplicate loose objects' '
rm -rf broken-duplicate &&
git init broken-duplicate &&
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index dd37ac47c5..51738fb969 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -166,10 +166,9 @@ test_expect_success 'resulting reflog can be shown by log -g' '
oid=$(git rev-parse HEAD) &&
cat >expect <<-EOF &&
HEAD@{0} $oid $msg
- HEAD@{1} $oid $msg
HEAD@{2} $oid checkout: moving from foo to baz
EOF
- git log -g --format="%gd %H %gs" -3 HEAD >actual &&
+ git log -g --format="%gd %H %gs" -2 HEAD >actual &&
test_cmp expect actual
'
@@ -560,6 +559,7 @@ test_expect_success 'use --set-upstream-to modify HEAD' '
test_expect_success 'use --set-upstream-to modify a particular branch' '
git branch my13 &&
git branch --set-upstream-to master my13 &&
+ test_when_finished "git branch --unset-upstream my13" &&
test "$(git config branch.my13.remote)" = "." &&
test "$(git config branch.my13.merge)" = "refs/heads/master"
'
@@ -605,38 +605,8 @@ test_expect_success 'test --unset-upstream on a particular branch' '
test_must_fail git config branch.my14.merge
'
-test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' '
- git update-ref refs/remotes/origin/master HEAD &&
- git branch --set-upstream origin/master 2>actual &&
- test_when_finished git update-ref -d refs/remotes/origin/master &&
- test_when_finished git branch -d origin/master &&
- cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-
-If you wanted to make '"'master'"' track '"'origin/master'"', do this:
-
- git branch -d origin/master
- git branch --set-upstream-to origin/master
-EOF
- test_i18ncmp expected actual
-'
-
-test_expect_success '--set-upstream with two args only shows the deprecation message' '
- git branch --set-upstream master my13 2>actual &&
- test_when_finished git branch --unset-upstream master &&
- cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-EOF
- test_i18ncmp expected actual
-'
-
-test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' '
- git branch --set-upstream my13 2>actual &&
- test_when_finished git branch --unset-upstream my13 &&
- cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-EOF
- test_i18ncmp expected actual
+test_expect_success '--set-upstream fails' '
+ test_must_fail git branch --set-upstream origin/master
'
test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '
@@ -961,19 +931,6 @@ test_expect_success 'attempt to delete a branch merged to its base' '
test_must_fail git branch -d my10
'
-test_expect_success 'use set-upstream on the current branch' '
- git checkout master &&
- git --bare init myupstream.git &&
- git push myupstream.git master:refs/heads/frotz &&
- git remote add origin myupstream.git &&
- git fetch &&
- git branch --set-upstream master origin/frotz &&
-
- test "z$(git config branch.master.remote)" = "zorigin" &&
- test "z$(git config branch.master.merge)" = "zrefs/heads/frotz"
-
-'
-
test_expect_success 'use --edit-description' '
write_script editor <<-\EOF &&
echo "New contents" >"$1"
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index a428ae6703..d2aec0f38b 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -2,6 +2,7 @@
test_description='git branch display tests'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
test_expect_success 'make commits' '
echo content >file &&
@@ -239,4 +240,34 @@ test_expect_success 'git branch --format option' '
test_i18ncmp expect actual
'
+test_expect_success "set up color tests" '
+ echo "<RED>master<RESET>" >expect.color &&
+ echo "master" >expect.bare &&
+ color_args="--format=%(color:red)%(refname:short) --list master"
+'
+
+test_expect_success '%(color) omitted without tty' '
+ TERM=vt100 git branch $color_args >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect.bare actual
+'
+
+test_expect_success TTY '%(color) present with tty' '
+ test_terminal env TERM=vt100 git branch $color_args >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect.color actual
+'
+
+test_expect_success 'color.branch=always overrides auto-color' '
+ git -c color.branch=always branch $color_args >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect.color actual
+'
+
+test_expect_success '--color overrides auto-color' '
+ git branch --color $color_args >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect.color actual
+'
+
test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 9b182a0c32..afa27ffe2d 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -194,6 +194,33 @@ test_expect_success 'notice d/f conflict with existing ref' '
test_must_fail git branch foo/bar/baz/lots/of/extra/components
'
+test_expect_success 'reject packed-refs with unterminated line' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
+ echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs containing junk' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%s\n" "bogus content" >>.git/packed-refs &&
+ echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs with a short SHA-1' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
+ printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
test_expect_success 'timeout if packed-refs.lock exists' '
LOCK=.git/packed-refs.lock &&
>"$LOCK" &&
@@ -211,4 +238,19 @@ test_expect_success 'retry acquiring packed-refs.lock' '
git -c core.packedrefstimeout=3000 pack-refs --all --prune
'
+test_expect_success SYMLINKS 'pack symlinked packed-refs' '
+ # First make sure that symlinking works when reading:
+ git update-ref refs/heads/loosy refs/heads/master &&
+ git for-each-ref >all-refs-before &&
+ mv .git/packed-refs .git/my-deviant-packed-refs &&
+ ln -s my-deviant-packed-refs .git/packed-refs &&
+ git for-each-ref >all-refs-linked &&
+ test_cmp all-refs-before all-refs-linked &&
+ git pack-refs --all --prune &&
+ git for-each-ref >all-refs-packed &&
+ test_cmp all-refs-before all-refs-packed &&
+ test -h .git/packed-refs &&
+ test "$(readlink .git/packed-refs)" = "my-deviant-packed-refs"
+'
+
test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 4428b9086e..fcfdd197bd 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -40,25 +40,6 @@ test_expect_success 'non-interactive rebase --continue works with touched file'
git rebase --continue
'
-test_expect_success 'non-interactive rebase --continue with rerere enabled' '
- test_config rerere.enabled true &&
- test_when_finished "test_might_fail git rebase --abort" &&
- git reset --hard commit-new-file-F2-on-topic-branch &&
- git checkout master &&
- rm -fr .git/rebase-* &&
-
- test_must_fail git rebase --onto master master topic &&
- echo "Resolved" >F2 &&
- git add F2 &&
- cp F2 F2.expected &&
- git rebase --continue &&
-
- git reset --hard commit-new-file-F2-on-topic-branch &&
- git checkout master &&
- test_must_fail git rebase --onto master master topic &&
- test_cmp F2.expected F2
-'
-
test_expect_success 'rebase --continue can not be used with other options' '
test_must_fail git rebase -v --continue &&
test_must_fail git rebase --continue -v
@@ -93,25 +74,75 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
test -f funny.was.run
'
-test_expect_success 'rebase --continue remembers --rerere-autoupdate' '
+test_expect_success 'setup rerere database' '
rm -fr .git/rebase-* &&
git reset --hard commit-new-file-F3-on-topic-branch &&
git checkout master &&
test_commit "commit-new-file-F3" F3 3 &&
- git config rerere.enabled true &&
+ test_config rerere.enabled true &&
test_must_fail git rebase -m master topic &&
echo "Resolved" >F2 &&
+ cp F2 expected-F2 &&
git add F2 &&
test_must_fail git rebase --continue &&
echo "Resolved" >F3 &&
+ cp F3 expected-F3 &&
git add F3 &&
git rebase --continue &&
- git reset --hard topic@{1} &&
- test_must_fail git rebase -m --rerere-autoupdate master &&
- test "$(cat F2)" = "Resolved" &&
- test_must_fail git rebase --continue &&
- test "$(cat F3)" = "Resolved" &&
- git rebase --continue
+ git reset --hard topic@{1}
'
+prepare () {
+ rm -fr .git/rebase-* &&
+ git reset --hard commit-new-file-F3-on-topic-branch &&
+ git checkout master &&
+ test_config rerere.enabled true
+}
+
+test_rerere_autoupdate () {
+ action=$1 &&
+ test_expect_success "rebase $action --continue remembers --rerere-autoupdate" '
+ prepare &&
+ test_must_fail git rebase $action --rerere-autoupdate master topic &&
+ test_cmp expected-F2 F2 &&
+ git diff-files --quiet &&
+ test_must_fail git rebase --continue &&
+ test_cmp expected-F3 F3 &&
+ git diff-files --quiet &&
+ git rebase --continue
+ '
+
+ test_expect_success "rebase $action --continue honors rerere.autoUpdate" '
+ prepare &&
+ test_config rerere.autoupdate true &&
+ test_must_fail git rebase $action master topic &&
+ test_cmp expected-F2 F2 &&
+ git diff-files --quiet &&
+ test_must_fail git rebase --continue &&
+ test_cmp expected-F3 F3 &&
+ git diff-files --quiet &&
+ git rebase --continue
+ '
+
+ test_expect_success "rebase $action --continue remembers --no-rerere-autoupdate" '
+ prepare &&
+ test_config rerere.autoupdate true &&
+ test_must_fail git rebase $action --no-rerere-autoupdate master topic &&
+ test_cmp expected-F2 F2 &&
+ test_must_fail git diff-files --quiet &&
+ git add F2 &&
+ test_must_fail git rebase --continue &&
+ test_cmp expected-F3 F3 &&
+ test_must_fail git diff-files --quiet &&
+ git add F3 &&
+ git rebase --continue
+ '
+}
+
+test_rerere_autoupdate
+test_rerere_autoupdate -m
+GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
+test_rerere_autoupdate -i
+test_rerere_autoupdate --preserve-merges
+
test_done
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
index e6a64816ef..a267b2d144 100755
--- a/t/t3504-cherry-pick-rerere.sh
+++ b/t/t3504-cherry-pick-rerere.sh
@@ -5,14 +5,13 @@ test_description='cherry-pick should rerere for conflicts'
. ./test-lib.sh
test_expect_success setup '
- echo foo >foo &&
- git add foo && test_tick && git commit -q -m 1 &&
- echo foo-master >foo &&
- git add foo && test_tick && git commit -q -m 2 &&
-
- git checkout -b dev HEAD^ &&
- echo foo-dev >foo &&
- git add foo && test_tick && git commit -q -m 3 &&
+ test_commit foo &&
+ test_commit foo-master foo &&
+ test_commit bar-master bar &&
+
+ git checkout -b dev foo &&
+ test_commit foo-dev foo &&
+ test_commit bar-dev bar &&
git config rerere.enabled true
'
@@ -21,23 +20,80 @@ test_expect_success 'conflicting merge' '
'
test_expect_success 'fixup' '
- echo foo-dev >foo &&
- git add foo && test_tick && git commit -q -m 4 &&
- git reset --hard HEAD^ &&
- echo foo-dev >expect
+ echo foo-resolved >foo &&
+ echo bar-resolved >bar &&
+ git commit -am resolved &&
+ cp foo foo-expect &&
+ cp bar bar-expect &&
+ git reset --hard HEAD^
'
-test_expect_success 'cherry-pick conflict' '
- test_must_fail git cherry-pick master &&
- test_cmp expect foo
+test_expect_success 'cherry-pick conflict with --rerere-autoupdate' '
+ test_must_fail git cherry-pick --rerere-autoupdate foo..bar-master &&
+ test_cmp foo-expect foo &&
+ git diff-files --quiet &&
+ test_must_fail git cherry-pick --continue &&
+ test_cmp bar-expect bar &&
+ git diff-files --quiet &&
+ git cherry-pick --continue &&
+ git reset --hard bar-dev
+'
+
+test_expect_success 'cherry-pick conflict repsects rerere.autoUpdate' '
+ test_config rerere.autoUpdate true &&
+ test_must_fail git cherry-pick foo..bar-master &&
+ test_cmp foo-expect foo &&
+ git diff-files --quiet &&
+ test_must_fail git cherry-pick --continue &&
+ test_cmp bar-expect bar &&
+ git diff-files --quiet &&
+ git cherry-pick --continue &&
+ git reset --hard bar-dev
+'
+
+test_expect_success 'cherry-pick conflict with --no-rerere-autoupdate' '
+ test_config rerere.autoUpdate true &&
+ test_must_fail git cherry-pick --no-rerere-autoupdate foo..bar-master &&
+ test_cmp foo-expect foo &&
+ test_must_fail git diff-files --quiet &&
+ git add foo &&
+ test_must_fail git cherry-pick --continue &&
+ test_cmp bar-expect bar &&
+ test_must_fail git diff-files --quiet &&
+ git add bar &&
+ git cherry-pick --continue &&
+ git reset --hard bar-dev
+'
+
+test_expect_success 'cherry-pick --continue rejects --rerere-autoupdate' '
+ test_must_fail git cherry-pick --rerere-autoupdate foo..bar-master &&
+ test_cmp foo-expect foo &&
+ git diff-files --quiet &&
+ test_must_fail git cherry-pick --continue --rerere-autoupdate >actual 2>&1 &&
+ echo "fatal: cherry-pick: --rerere-autoupdate cannot be used with --continue" >expect &&
+ test_i18ncmp expect actual &&
+ test_must_fail git cherry-pick --continue --no-rerere-autoupdate >actual 2>&1 &&
+ echo "fatal: cherry-pick: --no-rerere-autoupdate cannot be used with --continue" >expect &&
+ test_i18ncmp expect actual &&
+ git cherry-pick --abort
'
-test_expect_success 'reconfigure' '
- git config rerere.enabled false &&
- git reset --hard
+test_expect_success 'cherry-pick --rerere-autoupdate more than once' '
+ test_must_fail git cherry-pick --rerere-autoupdate --rerere-autoupdate foo..bar-master &&
+ test_cmp foo-expect foo &&
+ git diff-files --quiet &&
+ git cherry-pick --abort &&
+ test_must_fail git cherry-pick --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate foo..bar-master &&
+ test_cmp foo-expect foo &&
+ git diff-files --quiet &&
+ git cherry-pick --abort &&
+ test_must_fail git cherry-pick --rerere-autoupdate --no-rerere-autoupdate foo..bar-master &&
+ test_must_fail git diff-files --quiet &&
+ git cherry-pick --abort
'
test_expect_success 'cherry-pick conflict without rerere' '
+ test_config rerere.enabled false &&
test_must_fail git cherry-pick master &&
test_must_fail test_cmp expect foo
'
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index f3a4b4a913..0aae21d698 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -356,6 +356,7 @@ test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
test_expect_success 'git add --chmod=[+-]x changes index with already added file' '
rm -f foo3 xfoo3 &&
+ git reset --hard &&
echo foo >foo3 &&
git add foo3 &&
git add --chmod=+x foo3 &&
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 4046817d70..3b1ac1971a 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -444,6 +444,14 @@ test_expect_failure 'stash file to directory' '
test foo = "$(cat file/file)"
'
+test_expect_success 'stash create - no changes' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ git stash create >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
@@ -648,6 +656,20 @@ test_expect_success 'stash branch should not drop the stash if the branch exists
git rev-parse stash@{0} --
'
+test_expect_success 'stash branch should not drop the stash if the apply fails' '
+ git stash clear &&
+ git reset HEAD~1 --hard &&
+ echo foo >file &&
+ git add file &&
+ git commit -m initial &&
+ echo bar >file &&
+ git stash &&
+ echo baz >file &&
+ test_when_finished "git checkout master" &&
+ test_must_fail git stash branch new_branch stash@{0} &&
+ git rev-parse stash@{0} --
+'
+
test_expect_success 'stash apply shows status same as git status (relative to current directory)' '
git stash clear &&
echo 1 >subdir/subfile1 &&
@@ -800,6 +822,18 @@ test_expect_success 'create with multiple arguments for the message' '
test_cmp expect actual
'
+test_expect_success 'create in a detached state' '
+ test_when_finished "git checkout master" &&
+ git checkout HEAD~1 &&
+ >foo &&
+ git add foo &&
+ STASH_ID=$(git stash create) &&
+ HEAD_ID=$(git rev-parse --short HEAD) &&
+ echo "WIP on (no branch): ${HEAD_ID} initial" >expect &&
+ git show --pretty=%s -s ${STASH_ID} >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'stash -- <pathspec> stashes and restores the file' '
>foo &&
>bar &&
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index 193adc7b68..bfde4057ad 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -211,4 +211,21 @@ test_expect_success 'stash push with $IFS character' '
test_path_is_file bar
'
+cat > .gitignore <<EOF
+ignored
+ignored.d/*
+EOF
+
+test_expect_success 'stash previously ignored file' '
+ git reset HEAD &&
+ git add .gitignore &&
+ git commit -m "Add .gitignore" &&
+ >ignored.d/foo &&
+ echo "!ignored.d/foo" >> .gitignore &&
+ git stash save --include-untracked &&
+ test_path_is_missing ignored.d/foo &&
+ git stash pop &&
+ test_path_is_file ignored.d/foo
+'
+
test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 289806d0c7..12d182dc1b 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -972,4 +972,563 @@ test_expect_success 'option overrides diff.wsErrorHighlight' '
'
+test_expect_success 'detect moved code, complete file' '
+ git reset --hard &&
+ cat <<-\EOF >test.c &&
+ #include<stdio.h>
+ main()
+ {
+ printf("Hello World");
+ }
+ EOF
+ git add test.c &&
+ git commit -m "add main function" &&
+ git mv test.c main.c &&
+ test_config color.diff.oldMoved "normal red" &&
+ test_config color.diff.newMoved "normal green" &&
+ git diff HEAD --color-moved=zebra --no-renames | test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/main.c b/main.c<RESET>
+ <BOLD>new file mode 100644<RESET>
+ <BOLD>index 0000000..a986c57<RESET>
+ <BOLD>--- /dev/null<RESET>
+ <BOLD>+++ b/main.c<RESET>
+ <CYAN>@@ -0,0 +1,5 @@<RESET>
+ <BGREEN>+<RESET><BGREEN>#include<stdio.h><RESET>
+ <BGREEN>+<RESET><BGREEN>main()<RESET>
+ <BGREEN>+<RESET><BGREEN>{<RESET>
+ <BGREEN>+<RESET><BGREEN>printf("Hello World");<RESET>
+ <BGREEN>+<RESET><BGREEN>}<RESET>
+ <BOLD>diff --git a/test.c b/test.c<RESET>
+ <BOLD>deleted file mode 100644<RESET>
+ <BOLD>index a986c57..0000000<RESET>
+ <BOLD>--- a/test.c<RESET>
+ <BOLD>+++ /dev/null<RESET>
+ <CYAN>@@ -1,5 +0,0 @@<RESET>
+ <BRED>-#include<stdio.h><RESET>
+ <BRED>-main()<RESET>
+ <BRED>-{<RESET>
+ <BRED>-printf("Hello World");<RESET>
+ <BRED>-}<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'detect malicious moved code, inside file' '
+ test_config color.diff.oldMoved "normal red" &&
+ test_config color.diff.newMoved "normal green" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ git reset --hard &&
+ cat <<-\EOF >main.c &&
+ #include<stdio.h>
+ int stuff()
+ {
+ printf("Hello ");
+ printf("World\n");
+ }
+
+ int secure_foo(struct user *u)
+ {
+ if (!u->is_allowed_foo)
+ return;
+ foo(u);
+ }
+
+ int main()
+ {
+ foo();
+ }
+ EOF
+ cat <<-\EOF >test.c &&
+ #include<stdio.h>
+ int bar()
+ {
+ printf("Hello World, but different\n");
+ }
+
+ int another_function()
+ {
+ bar();
+ }
+ EOF
+ git add main.c test.c &&
+ git commit -m "add main and test file" &&
+ cat <<-\EOF >main.c &&
+ #include<stdio.h>
+ int stuff()
+ {
+ printf("Hello ");
+ printf("World\n");
+ }
+
+ int main()
+ {
+ foo();
+ }
+ EOF
+ cat <<-\EOF >test.c &&
+ #include<stdio.h>
+ int bar()
+ {
+ printf("Hello World, but different\n");
+ }
+
+ int secure_foo(struct user *u)
+ {
+ foo(u);
+ if (!u->is_allowed_foo)
+ return;
+ }
+
+ int another_function()
+ {
+ bar();
+ }
+ EOF
+ git diff HEAD --no-renames --color-moved=zebra| test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/main.c b/main.c<RESET>
+ <BOLD>index 27a619c..7cf9336 100644<RESET>
+ <BOLD>--- a/main.c<RESET>
+ <BOLD>+++ b/main.c<RESET>
+ <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
+ printf("World\n");<RESET>
+ }<RESET>
+ <RESET>
+ <BRED>-int secure_foo(struct user *u)<RESET>
+ <BRED>-{<RESET>
+ <BLUE>-if (!u->is_allowed_foo)<RESET>
+ <BLUE>-return;<RESET>
+ <RED>-foo(u);<RESET>
+ <RED>-}<RESET>
+ <RED>-<RESET>
+ int main()<RESET>
+ {<RESET>
+ foo();<RESET>
+ <BOLD>diff --git a/test.c b/test.c<RESET>
+ <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+ <BOLD>--- a/test.c<RESET>
+ <BOLD>+++ b/test.c<RESET>
+ <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
+ printf("Hello World, but different\n");<RESET>
+ }<RESET>
+ <RESET>
+ <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET>
+ <BGREEN>+<RESET><BGREEN>{<RESET>
+ <GREEN>+<RESET><GREEN>foo(u);<RESET>
+ <BGREEN>+<RESET><BGREEN>if (!u->is_allowed_foo)<RESET>
+ <BGREEN>+<RESET><BGREEN>return;<RESET>
+ <GREEN>+<RESET><GREEN>}<RESET>
+ <GREEN>+<RESET>
+ int another_function()<RESET>
+ {<RESET>
+ bar();<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'plain moved code, inside file' '
+ test_config color.diff.oldMoved "normal red" &&
+ test_config color.diff.newMoved "normal green" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ # needs previous test as setup
+ git diff HEAD --no-renames --color-moved=plain| test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/main.c b/main.c<RESET>
+ <BOLD>index 27a619c..7cf9336 100644<RESET>
+ <BOLD>--- a/main.c<RESET>
+ <BOLD>+++ b/main.c<RESET>
+ <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
+ printf("World\n");<RESET>
+ }<RESET>
+ <RESET>
+ <BRED>-int secure_foo(struct user *u)<RESET>
+ <BRED>-{<RESET>
+ <BRED>-if (!u->is_allowed_foo)<RESET>
+ <BRED>-return;<RESET>
+ <BRED>-foo(u);<RESET>
+ <BRED>-}<RESET>
+ <BRED>-<RESET>
+ int main()<RESET>
+ {<RESET>
+ foo();<RESET>
+ <BOLD>diff --git a/test.c b/test.c<RESET>
+ <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+ <BOLD>--- a/test.c<RESET>
+ <BOLD>+++ b/test.c<RESET>
+ <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
+ printf("Hello World, but different\n");<RESET>
+ }<RESET>
+ <RESET>
+ <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET>
+ <BGREEN>+<RESET><BGREEN>{<RESET>
+ <BGREEN>+<RESET><BGREEN>foo(u);<RESET>
+ <BGREEN>+<RESET><BGREEN>if (!u->is_allowed_foo)<RESET>
+ <BGREEN>+<RESET><BGREEN>return;<RESET>
+ <BGREEN>+<RESET><BGREEN>}<RESET>
+ <BGREEN>+<RESET>
+ int another_function()<RESET>
+ {<RESET>
+ bar();<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
+ git reset --hard &&
+ cat <<-\EOF >lines.txt &&
+ long line 1
+ long line 2
+ long line 3
+ line 4
+ line 5
+ line 6
+ line 7
+ line 8
+ line 9
+ line 10
+ line 11
+ line 12
+ line 13
+ long line 14
+ long line 15
+ long line 16
+ EOF
+ git add lines.txt &&
+ git commit -m "add poetry" &&
+ cat <<-\EOF >lines.txt &&
+ line 4
+ line 5
+ line 6
+ line 7
+ line 8
+ line 9
+ long line 1
+ long line 2
+ long line 3
+ long line 14
+ long line 15
+ long line 16
+ line 10
+ line 11
+ line 12
+ line 13
+ EOF
+ test_config color.diff.oldMoved "magenta" &&
+ test_config color.diff.newMoved "cyan" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ test_config color.diff.oldMovedDimmed "normal magenta" &&
+ test_config color.diff.newMovedDimmed "normal cyan" &&
+ test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+ test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+ git diff HEAD --no-renames --color-moved=dimmed_zebra |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,16 +1,16 @@<RESET>
+ <BMAGENTA>-long line 1<RESET>
+ <BMAGENTA>-long line 2<RESET>
+ <BMAGENTA>-long line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ line 6<RESET>
+ line 7<RESET>
+ line 8<RESET>
+ line 9<RESET>
+ <BCYAN>+<RESET><BCYAN>long line 1<RESET>
+ <BCYAN>+<RESET><BCYAN>long line 2<RESET>
+ <CYAN>+<RESET><CYAN>long line 3<RESET>
+ <YELLOW>+<RESET><YELLOW>long line 14<RESET>
+ <BYELLOW>+<RESET><BYELLOW>long line 15<RESET>
+ <BYELLOW>+<RESET><BYELLOW>long line 16<RESET>
+ line 10<RESET>
+ line 11<RESET>
+ line 12<RESET>
+ line 13<RESET>
+ <BMAGENTA>-long line 14<RESET>
+ <BMAGENTA>-long line 15<RESET>
+ <BMAGENTA>-long line 16<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'cmd option assumes configured colored-moved' '
+ test_config color.diff.oldMoved "magenta" &&
+ test_config color.diff.newMoved "cyan" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ test_config color.diff.oldMovedDimmed "normal magenta" &&
+ test_config color.diff.newMovedDimmed "normal cyan" &&
+ test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+ test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+ test_config diff.colorMoved zebra &&
+ git diff HEAD --no-renames --color-moved |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,16 +1,16 @@<RESET>
+ <MAGENTA>-long line 1<RESET>
+ <MAGENTA>-long line 2<RESET>
+ <MAGENTA>-long line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ line 6<RESET>
+ line 7<RESET>
+ line 8<RESET>
+ line 9<RESET>
+ <CYAN>+<RESET><CYAN>long line 1<RESET>
+ <CYAN>+<RESET><CYAN>long line 2<RESET>
+ <CYAN>+<RESET><CYAN>long line 3<RESET>
+ <YELLOW>+<RESET><YELLOW>long line 14<RESET>
+ <YELLOW>+<RESET><YELLOW>long line 15<RESET>
+ <YELLOW>+<RESET><YELLOW>long line 16<RESET>
+ line 10<RESET>
+ line 11<RESET>
+ line 12<RESET>
+ line 13<RESET>
+ <MAGENTA>-long line 14<RESET>
+ <MAGENTA>-long line 15<RESET>
+ <MAGENTA>-long line 16<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'no effect from --color-moved with --word-diff' '
+ cat <<-\EOF >text.txt &&
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry.
+ EOF
+ git add text.txt &&
+ git commit -a -m "clean state" &&
+ cat <<-\EOF >text.txt &&
+ simply Lorem Ipsum dummy is text of the typesetting and printing industry.
+ EOF
+ git diff --color-moved --word-diff >actual &&
+ git diff --word-diff >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'move detection ignoring whitespace ' '
+ git reset --hard &&
+ cat <<\EOF >lines.txt &&
+line 1
+line 2
+line 3
+line 4
+long line 5
+long line 6
+long line 7
+EOF
+ git add lines.txt &&
+ git commit -m "add poetry" &&
+ cat <<\EOF >lines.txt &&
+ long line 5
+ long line 6
+ long line 7
+line 1
+line 2
+line 3
+line 4
+EOF
+ test_config color.diff.oldMoved "magenta" &&
+ test_config color.diff.newMoved "cyan" &&
+ git diff HEAD --no-renames --color-moved |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,7 +1,7 @@<RESET>
+ <GREEN>+<RESET> <GREEN>long line 5<RESET>
+ <GREEN>+<RESET> <GREEN>long line 6<RESET>
+ <GREEN>+<RESET> <GREEN>long line 7<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ <RED>-long line 5<RESET>
+ <RED>-long line 6<RESET>
+ <RED>-long line 7<RESET>
+ EOF
+ test_cmp expected actual &&
+
+ git diff HEAD --no-renames -w --color-moved |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,7 +1,7 @@<RESET>
+ <CYAN>+<RESET> <CYAN>long line 5<RESET>
+ <CYAN>+<RESET> <CYAN>long line 6<RESET>
+ <CYAN>+<RESET> <CYAN>long line 7<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ <MAGENTA>-long line 5<RESET>
+ <MAGENTA>-long line 6<RESET>
+ <MAGENTA>-long line 7<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success '--color-moved block at end of diff output respects MIN_ALNUM_COUNT' '
+ git reset --hard &&
+ >bar &&
+ cat <<-\EOF >foo &&
+ irrelevant_line
+ line1
+ EOF
+ git add foo bar &&
+ git commit -m x &&
+
+ cat <<-\EOF >bar &&
+ line1
+ EOF
+ cat <<-\EOF >foo &&
+ irrelevant_line
+ EOF
+
+ git diff HEAD --color-moved=zebra --no-renames |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/bar b/bar<RESET>
+ <BOLD>--- a/bar<RESET>
+ <BOLD>+++ b/bar<RESET>
+ <CYAN>@@ -0,0 +1 @@<RESET>
+ <GREEN>+<RESET><GREEN>line1<RESET>
+ <BOLD>diff --git a/foo b/foo<RESET>
+ <BOLD>--- a/foo<RESET>
+ <BOLD>+++ b/foo<RESET>
+ <CYAN>@@ -1,2 +1 @@<RESET>
+ irrelevant_line<RESET>
+ <RED>-line1<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success '--color-moved respects MIN_ALNUM_COUNT' '
+ git reset --hard &&
+ cat <<-\EOF >foo &&
+ nineteen chars 456789
+ irrelevant_line
+ twenty chars 234567890
+ EOF
+ >bar &&
+ git add foo bar &&
+ git commit -m x &&
+
+ cat <<-\EOF >foo &&
+ irrelevant_line
+ EOF
+ cat <<-\EOF >bar &&
+ twenty chars 234567890
+ nineteen chars 456789
+ EOF
+
+ git diff HEAD --color-moved=zebra --no-renames |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/bar b/bar<RESET>
+ <BOLD>--- a/bar<RESET>
+ <BOLD>+++ b/bar<RESET>
+ <CYAN>@@ -0,0 +1,2 @@<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>twenty chars 234567890<RESET>
+ <GREEN>+<RESET><GREEN>nineteen chars 456789<RESET>
+ <BOLD>diff --git a/foo b/foo<RESET>
+ <BOLD>--- a/foo<RESET>
+ <BOLD>+++ b/foo<RESET>
+ <CYAN>@@ -1,3 +1 @@<RESET>
+ <RED>-nineteen chars 456789<RESET>
+ irrelevant_line<RESET>
+ <BOLD;MAGENTA>-twenty chars 234567890<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success '--color-moved treats adjacent blocks as separate for MIN_ALNUM_COUNT' '
+ git reset --hard &&
+ cat <<-\EOF >foo &&
+ 7charsA
+ irrelevant_line
+ 7charsB
+ 7charsC
+ EOF
+ >bar &&
+ git add foo bar &&
+ git commit -m x &&
+
+ cat <<-\EOF >foo &&
+ irrelevant_line
+ EOF
+ cat <<-\EOF >bar &&
+ 7charsB
+ 7charsC
+ 7charsA
+ EOF
+
+ git diff HEAD --color-moved=zebra --no-renames | grep -v "index" | test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/bar b/bar<RESET>
+ <BOLD>--- a/bar<RESET>
+ <BOLD>+++ b/bar<RESET>
+ <CYAN>@@ -0,0 +1,3 @@<RESET>
+ <GREEN>+<RESET><GREEN>7charsB<RESET>
+ <GREEN>+<RESET><GREEN>7charsC<RESET>
+ <GREEN>+<RESET><GREEN>7charsA<RESET>
+ <BOLD>diff --git a/foo b/foo<RESET>
+ <BOLD>--- a/foo<RESET>
+ <BOLD>+++ b/foo<RESET>
+ <CYAN>@@ -1,4 +1 @@<RESET>
+ <RED>-7charsA<RESET>
+ irrelevant_line<RESET>
+ <RED>-7charsB<RESET>
+ <RED>-7charsC<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'move detection with submodules' '
+ test_create_repo bananas &&
+ echo ripe >bananas/recipe &&
+ git -C bananas add recipe &&
+ test_commit fruit &&
+ test_commit -C bananas recipe &&
+ git submodule add ./bananas &&
+ git add bananas &&
+ git commit -a -m "bananas are like a heavy library?" &&
+ echo foul >bananas/recipe &&
+ echo ripe >fruit.t &&
+
+ git diff --submodule=diff --color-moved >actual &&
+
+ # no move detection as the moved line is across repository boundaries.
+ test_decode_color <actual >decoded_actual &&
+ ! grep BGREEN decoded_actual &&
+ ! grep BRED decoded_actual &&
+
+ # nor did we mess with it another way
+ git diff --submodule=diff | test_decode_color >expect &&
+ test_cmp expect decoded_actual
+'
+
test_done
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 518bf9524e..2ffd11a142 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -113,35 +113,6 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)'
! test -s actual4
'
-test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
- git config diff.ignoreSubmodules all &&
- git diff HEAD >actual &&
- ! test -s actual &&
- git config submodule.subname.ignore none &&
- git config submodule.subname.path sub &&
- git diff HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subprev $subprev-dirty &&
- test_cmp expect.body actual.body &&
- git config submodule.subname.ignore all &&
- git diff HEAD >actual2 &&
- ! test -s actual2 &&
- git config submodule.subname.ignore untracked &&
- git diff HEAD >actual3 &&
- sed -e "1,/^@@/d" actual3 >actual3.body &&
- expect_from_to >expect.body $subprev $subprev-dirty &&
- test_cmp expect.body actual3.body &&
- git config submodule.subname.ignore dirty &&
- git diff HEAD >actual4 &&
- ! test -s actual4 &&
- git diff HEAD --ignore-submodules=none >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subprev $subprev-dirty &&
- test_cmp expect.body actual.body &&
- git config --remove-section submodule.subname &&
- git config --unset diff.ignoreSubmodules
-'
-
test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
git config diff.ignoreSubmodules dirty &&
git diff HEAD >actual &&
@@ -208,24 +179,6 @@ test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)'
! test -s actual4
'
-test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
- git config submodule.subname.ignore all &&
- git config submodule.subname.path sub &&
- git diff HEAD >actual2 &&
- ! test -s actual2 &&
- git config submodule.subname.ignore untracked &&
- git diff HEAD >actual3 &&
- ! test -s actual3 &&
- git config submodule.subname.ignore dirty &&
- git diff HEAD >actual4 &&
- ! test -s actual4 &&
- git diff --ignore-submodules=none HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subprev $subprev-dirty &&
- test_cmp expect.body actual.body &&
- git config --remove-section submodule.subname
-'
-
test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
git config --add -f .gitmodules submodule.subname.ignore all &&
git config --add -f .gitmodules submodule.subname.path sub &&
@@ -261,26 +214,6 @@ test_expect_success 'git diff between submodule commits' '
! test -s actual
'
-test_expect_success 'git diff between submodule commits [.git/config]' '
- git diff HEAD^..HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subtip $subprev &&
- test_cmp expect.body actual.body &&
- git config submodule.subname.ignore dirty &&
- git config submodule.subname.path sub &&
- git diff HEAD^..HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subtip $subprev &&
- test_cmp expect.body actual.body &&
- git config submodule.subname.ignore all &&
- git diff HEAD^..HEAD >actual &&
- ! test -s actual &&
- git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subtip $subprev &&
- git config --remove-section submodule.subname
-'
-
test_expect_success 'git diff between submodule commits [.gitmodules]' '
git diff HEAD^..HEAD >actual &&
sed -e "1,/^@@/d" actual >actual.body &&
diff --git a/t/t4062-diff-pickaxe.sh b/t/t4062-diff-pickaxe.sh
index 7c4903f497..1130c8019b 100755
--- a/t/t4062-diff-pickaxe.sh
+++ b/t/t4062-diff-pickaxe.sh
@@ -14,8 +14,10 @@ test_expect_success setup '
test_tick &&
git commit -m "A 4k file"
'
+
+# OpenBSD only supports up to 255 repetitions, so repeat twice for 64*64=4096.
test_expect_success '-G matches' '
- git diff --name-only -G "^0{4096}$" HEAD^ >out &&
+ git diff --name-only -G "^(0{64}){64}$" HEAD^ >out &&
test 4096-zeroes.txt = "$(cat out)"
'
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index d350065f25..4fc27c51f7 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -467,21 +467,42 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
test_cmp one expect
'
-test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
+test_expect_success 'CR-LF line endings && add line && text=auto' '
git config --unset core.whitespace &&
printf "a\r\n" >one &&
+ cp one save-one &&
+ git add one &&
printf "b\r\n" >>one &&
- printf "c\r\n" >>one &&
+ cp one expect &&
+ git diff -- one >patch &&
+ mv save-one one &&
+ echo "one text=auto" >.gitattributes &&
+ git apply patch &&
+ test_cmp one expect
+'
+
+test_expect_success 'CR-LF line endings && change line && text=auto' '
+ printf "a\r\n" >one &&
cp one save-one &&
- printf " \r\n" >>one &&
git add one &&
+ printf "b\r\n" >one &&
cp one expect &&
- printf "d\r\n" >>one &&
git diff -- one >patch &&
mv save-one one &&
- echo d >>expect &&
+ echo "one text=auto" >.gitattributes &&
+ git apply patch &&
+ test_cmp one expect
+'
- git apply --ignore-space-change --whitespace=fix patch &&
+test_expect_success 'LF in repo, CRLF in worktree && change line && text=auto' '
+ printf "a\n" >one &&
+ git add one &&
+ printf "b\r\n" >one &&
+ git diff -- one >patch &&
+ printf "a\r\n" >one &&
+ echo "one text=auto" >.gitattributes &&
+ git -c core.eol=CRLF apply patch &&
+ printf "b\r\n" >expect &&
test_cmp one expect
'
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 44807e218d..73b67b4280 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -40,6 +40,8 @@ test_expect_success 'setup: messages' '
dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
feugait nulla facilisi.
+
+ Reported-by: A N Other <a.n.other@example.com>
EOF
cat >failmail <<-\EOF &&
@@ -93,7 +95,7 @@ test_expect_success setup '
echo world >>file &&
git add file &&
test_tick &&
- git commit -s -F msg &&
+ git commit -F msg &&
git tag second &&
git format-patch --stdout first >patch1 &&
@@ -124,8 +126,6 @@ test_expect_success setup '
echo "Date: $GIT_AUTHOR_DATE" &&
echo &&
sed -e "1,2d" msg &&
- echo &&
- echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
echo "---" &&
git diff-tree --no-commit-id --stat -p second
} >patch1-stgit.eml &&
@@ -144,8 +144,6 @@ test_expect_success setup '
echo "# Parent $_z40" &&
cat msg &&
echo &&
- echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
- echo &&
git diff-tree --no-commit-id -p second
} >patch1-hg.eml &&
@@ -470,13 +468,15 @@ test_expect_success 'am --signoff adds Signed-off-by: line' '
git reset --hard &&
git checkout -b master2 first &&
git am --signoff <patch2 &&
- printf "%s\n" "$signoff" >expected &&
- echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >>expected &&
- git cat-file commit HEAD^ | grep "Signed-off-by:" >actual &&
- test_cmp expected actual &&
- echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
- git cat-file commit HEAD | grep "Signed-off-by:" >actual &&
- test_cmp expected actual
+ {
+ printf "third\n\nSigned-off-by: %s <%s>\n\n" \
+ "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" &&
+ cat msg &&
+ printf "Signed-off-by: %s <%s>\n\n" \
+ "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL"
+ } >expected-log &&
+ git log --pretty=%B -2 HEAD >actual &&
+ test_cmp expected-log actual
'
test_expect_success 'am stays in branch' '
@@ -486,17 +486,60 @@ test_expect_success 'am stays in branch' '
'
test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
- git format-patch --stdout HEAD^ >patch3 &&
- sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," patch3 >patch4 &&
- rm -fr .git/rebase-apply &&
- git reset --hard &&
- git checkout HEAD^ &&
- git am --signoff patch4 &&
- git cat-file commit HEAD >actual &&
- test $(grep -c "^Signed-off-by:" actual) -eq 1
+ git format-patch --stdout first >patch3 &&
+ git reset --hard first &&
+ git am --signoff <patch3 &&
+ git log --pretty=%B -2 HEAD >actual &&
+ test_cmp expected-log actual
+'
+
+test_expect_success 'am --signoff adds Signed-off-by: if another author is preset' '
+ NAME="A N Other" &&
+ EMAIL="a.n.other@example.com" &&
+ {
+ printf "third\n\nSigned-off-by: %s <%s>\nSigned-off-by: %s <%s>\n\n" \
+ "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \
+ "$NAME" "$EMAIL" &&
+ cat msg &&
+ printf "Signed-off-by: %s <%s>\nSigned-off-by: %s <%s>\n\n" \
+ "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \
+ "$NAME" "$EMAIL"
+ } >expected-log &&
+ git reset --hard first &&
+ GIT_COMMITTER_NAME="$NAME" GIT_COMMITTER_EMAIL="$EMAIL" \
+ git am --signoff <patch3 &&
+ git log --pretty=%B -2 HEAD >actual &&
+ test_cmp expected-log actual
+'
+
+test_expect_success 'am --signoff duplicates Signed-off-by: if it is not the last one' '
+ NAME="A N Other" &&
+ EMAIL="a.n.other@example.com" &&
+ {
+ printf "third\n\nSigned-off-by: %s <%s>\n\
+Signed-off-by: %s <%s>\nSigned-off-by: %s <%s>\n\n" \
+ "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \
+ "$NAME" "$EMAIL" \
+ "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" &&
+ cat msg &&
+ printf "Signed-off-by: %s <%s>\nSigned-off-by: %s <%s>\n\
+Signed-off-by: %s <%s>\n\n" \
+ "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \
+ "$NAME" "$EMAIL" \
+ "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL"
+ } >expected-log &&
+ git format-patch --stdout first >patch3 &&
+ git reset --hard first &&
+ git am --signoff <patch3 &&
+ git log --pretty=%B -2 HEAD >actual &&
+ test_cmp expected-log actual
'
test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
+ git format-patch --stdout HEAD^ >tmp &&
+ sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," tmp >patch4 &&
+ git reset --hard HEAD^ &&
+ git am <patch4 &&
git rev-parse HEAD >expected &&
git rev-parse master2 >actual &&
test_cmp expected actual
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 1a080e7823..d97d2bebc9 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -239,6 +239,43 @@ test_expect_success 'old records rest in peace' '
! test -f $rr2/preimage
'
+rerere_gc_custom_expiry_test () {
+ five_days="$1" right_now="$2"
+ test_expect_success "rerere gc with custom expiry ($five_days, $right_now)" '
+ rm -fr .git/rr-cache &&
+ rr=.git/rr-cache/$_z40 &&
+ mkdir -p "$rr" &&
+ >"$rr/preimage" &&
+ >"$rr/postimage" &&
+
+ two_days_ago=$((-2*86400)) &&
+ test-chmtime =$two_days_ago "$rr/preimage" &&
+ test-chmtime =$two_days_ago "$rr/postimage" &&
+
+ find .git/rr-cache -type f | sort >original &&
+
+ git -c "gc.rerereresolved=$five_days" \
+ -c "gc.rerereunresolved=$five_days" rerere gc &&
+ find .git/rr-cache -type f | sort >actual &&
+ test_cmp original actual &&
+
+ git -c "gc.rerereresolved=$five_days" \
+ -c "gc.rerereunresolved=$right_now" rerere gc &&
+ find .git/rr-cache -type f | sort >actual &&
+ test_cmp original actual &&
+
+ git -c "gc.rerereresolved=$right_now" \
+ -c "gc.rerereunresolved=$right_now" rerere gc &&
+ find .git/rr-cache -type f | sort >actual &&
+ >expect &&
+ test_cmp expect actual
+ '
+}
+
+rerere_gc_custom_expiry_test 5 0
+
+rerere_gc_custom_expiry_test 5.days.ago now
+
test_expect_success 'setup: file2 added differently in two branches' '
git reset --hard &&
@@ -419,24 +456,6 @@ count_pre_post () {
test_line_count = "$2" actual
}
-test_expect_success 'rerere gc' '
- find .git/rr-cache -type f >original &&
- xargs test-chmtime -172800 <original &&
-
- git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
- find .git/rr-cache -type f >actual &&
- test_cmp original actual &&
-
- git -c gc.rerereresolved=5 -c gc.rerereunresolved=0 rerere gc &&
- find .git/rr-cache -type f >actual &&
- test_cmp original actual &&
-
- git -c gc.rerereresolved=0 -c gc.rerereunresolved=0 rerere gc &&
- find .git/rr-cache -type f >actual &&
- >expect &&
- test_cmp expect actual
-'
-
merge_conflict_resolve () {
git reset --hard &&
test_must_fail git merge six.1 &&
@@ -446,6 +465,8 @@ merge_conflict_resolve () {
}
test_expect_success 'multiple identical conflicts' '
+ rm -fr .git/rr-cache &&
+ mkdir .git/rr-cache &&
git reset --hard &&
test_seq 1 6 >early &&
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 3f3531f0a4..36d120c969 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -1523,6 +1523,12 @@ test_expect_success 'log diagnoses bogus HEAD' '
test_i18ngrep broken stderr
'
+test_expect_success 'log does not default to HEAD when rev input is given' '
+ >expect &&
+ git log --branches=does-not-exist >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'set up --source tests' '
git checkout --orphan source-a &&
test_commit one &&
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 18aa1b5889..ec5f530102 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -539,25 +539,62 @@ cat >trailers <<EOF
Signed-off-by: A U Thor <author@example.com>
Acked-by: A U Thor <author@example.com>
[ v2 updated patch description ]
-Signed-off-by: A U Thor <author@example.com>
+Signed-off-by: A U Thor
+ <author@example.com>
EOF
-test_expect_success 'pretty format %(trailers) shows trailers' '
+unfold () {
+ perl -0pe 's/\n\s+/ /'
+}
+
+test_expect_success 'set up trailer tests' '
echo "Some contents" >trailerfile &&
git add trailerfile &&
- git commit -F - <<-EOF &&
+ git commit -F - <<-EOF
trailers: this commit message has trailers
This commit is a test commit with trailers at the end. We parse this
- message and display the trailers using %bT
+ message and display the trailers using %(trailers).
$(cat trailers)
EOF
+'
+
+test_expect_success 'pretty format %(trailers) shows trailers' '
git log --no-walk --pretty="%(trailers)" >actual &&
- cat >expect <<-EOF &&
- $(cat trailers)
+ {
+ cat trailers &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
- EOF
+test_expect_success '%(trailers:only) shows only "key: value" trailers' '
+ git log --no-walk --pretty="%(trailers:only)" >actual &&
+ {
+ grep -v patch.description <trailers &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(trailers:unfold) unfolds trailers' '
+ git log --no-walk --pretty="%(trailers:unfold)" >actual &&
+ {
+ unfold <trailers &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success ':only and :unfold work together' '
+ git log --no-walk --pretty="%(trailers:only:unfold)" >actual &&
+ git log --no-walk --pretty="%(trailers:unfold:only)" >reverse &&
+ test_cmp actual reverse &&
+ {
+ grep -v patch.description <trailers | unfold &&
+ echo
+ } >expect &&
test_cmp expect actual
'
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b972296f06..60f040cab8 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -7,11 +7,6 @@ test_description='Test for "git log --decorate" colors'
. ./test-lib.sh
-get_color ()
-{
- git config --get-color no.such.slot "$1"
-}
-
test_expect_success setup '
git config diff.color.commit yellow &&
git config color.decorate.branch green &&
@@ -20,14 +15,14 @@ test_expect_success setup '
git config color.decorate.stash magenta &&
git config color.decorate.HEAD cyan &&
- c_reset=$(get_color reset) &&
+ c_reset="<RESET>" &&
- c_commit=$(get_color yellow) &&
- c_branch=$(get_color green) &&
- c_remoteBranch=$(get_color red) &&
- c_tag=$(get_color "reverse bold yellow") &&
- c_stash=$(get_color magenta) &&
- c_HEAD=$(get_color cyan) &&
+ c_commit="<YELLOW>" &&
+ c_branch="<GREEN>" &&
+ c_remoteBranch="<RED>" &&
+ c_tag="<BOLD;REVERSE;YELLOW>" &&
+ c_stash="<MAGENTA>" &&
+ c_HEAD="<CYAN>" &&
test_commit A &&
git clone . other &&
@@ -59,7 +54,8 @@ EOF
# to this test since it does not contain any decoration, hence --first-parent
test_expect_success 'Commit Decorations Colored Correctly' '
git log --first-parent --abbrev=10 --all --decorate --oneline --color=always |
- sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" >out &&
+ sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" |
+ test_decode_color >out &&
test_cmp expected out
'
diff --git a/t/t5308-pack-detect-duplicates.sh b/t/t5308-pack-detect-duplicates.sh
index 9c5a8766ab..156ae9e9d3 100755
--- a/t/t5308-pack-detect-duplicates.sh
+++ b/t/t5308-pack-detect-duplicates.sh
@@ -56,20 +56,11 @@ test_expect_success 'create batch-check test vectors' '
EOF
'
-test_expect_success 'lookup in duplicated pack (binary search)' '
+test_expect_success 'lookup in duplicated pack' '
git cat-file --batch-check <input >actual &&
test_cmp expect actual
'
-test_expect_success 'lookup in duplicated pack (GIT_USE_LOOKUP)' '
- (
- GIT_USE_LOOKUP=1 &&
- export GIT_USE_LOOKUP &&
- git cat-file --batch-check <input >actual
- ) &&
- test_cmp expect actual
-'
-
test_expect_success 'index-pack can reject packs with duplicates' '
clear_packs &&
create_pack dups.pack 2 &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index f15f7a3329..59c4b778d3 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -305,7 +305,7 @@ test_expect_success '--rebase with conflicts shows advice' '
test_tick &&
git commit -m "Create conflict" seq.txt &&
test_must_fail git pull --rebase . seq 2>err >out &&
- test_i18ngrep "When you have resolved this problem" out
+ test_i18ngrep "Resolve all conflicts manually" out
'
test_expect_success 'failed --rebase shows advice' '
@@ -319,7 +319,7 @@ test_expect_success 'failed --rebase shows advice' '
git checkout -f -b fails-to-rebase HEAD^ &&
test_commit v2-without-cr file "2" file2-lf &&
test_must_fail git pull --rebase . diverging 2>err >out &&
- test_i18ngrep "When you have resolved this problem" out
+ test_i18ngrep "Resolve all conflicts manually" out
'
test_expect_success '--rebase fails with multiple branches' '
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 162baf101f..42251f7f3a 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -193,7 +193,7 @@ test_expect_success "recurseSubmodules=true propagates into submodules" '
add_upstream_commit &&
(
cd downstream &&
- git config fetch.recurseSubmodules true
+ git config fetch.recurseSubmodules true &&
git fetch >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
@@ -218,7 +218,7 @@ test_expect_success "--no-recurse-submodules overrides config setting" '
add_upstream_commit &&
(
cd downstream &&
- git config fetch.recurseSubmodules true
+ git config fetch.recurseSubmodules true &&
git fetch --no-recurse-submodules >../actual.out 2>../actual.err
) &&
! test -s actual.out &&
@@ -232,7 +232,7 @@ test_expect_success "Recursion doesn't happen when no new commits are fetched in
cd submodule &&
git config --unset fetch.recurseSubmodules
) &&
- git config --unset fetch.recurseSubmodules
+ git config --unset fetch.recurseSubmodules &&
git fetch >../actual.out 2>../actual.err
) &&
! test -s actual.out &&
@@ -312,7 +312,7 @@ test_expect_success "Recursion picks up all submodules when necessary" '
) &&
head1=$(git rev-parse --short HEAD^) &&
git add subdir/deepsubmodule &&
- git commit -m "new deepsubmodule"
+ git commit -m "new deepsubmodule" &&
head2=$(git rev-parse --short HEAD) &&
echo "Fetching submodule submodule" > ../expect.err.sub &&
echo "From $pwd/submodule" >> ../expect.err.sub &&
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index beff65b8ac..0f84a53146 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -533,7 +533,8 @@ test_expect_success 'push propagating refspec to a submodule' '
# Fails when refspec includes an object id
test_must_fail git -C work push --recurse-submodules=on-demand origin \
"$(git -C work rev-parse branch2):refs/heads/branch2" &&
- # Fails when refspec includes 'HEAD' as it is unsupported at this time
+ # Fails when refspec includes HEAD and parent and submodule do not
+ # have the same named branch checked out
test_must_fail git -C work push --recurse-submodules=on-demand origin \
HEAD:refs/heads/branch2 &&
@@ -548,4 +549,26 @@ test_expect_success 'push propagating refspec to a submodule' '
test_cmp expected_pub actual_pub
'
+test_expect_success 'push propagating HEAD refspec to a submodule' '
+ git -C work/gar/bage checkout branch2 &&
+ > work/gar/bage/junk12 &&
+ git -C work/gar/bage add junk12 &&
+ git -C work/gar/bage commit -m "Twelfth junk" &&
+
+ git -C work checkout branch2 &&
+ git -C work add gar/bage &&
+ git -C work commit -m "updating gar/bage in branch2" &&
+
+ # Passes since the superproject and submodules HEAD are both on branch2
+ git -C work push --recurse-submodules=on-demand origin \
+ HEAD:refs/heads/branch2 &&
+
+ git -C submodule.git rev-parse branch2 >actual_submodule &&
+ git -C pub.git rev-parse branch2 >actual_pub &&
+ git -C work/gar/bage rev-parse branch2 >expected_submodule &&
+ git -C work rev-parse branch2 >expected_pub &&
+ test_cmp expected_submodule actual_submodule &&
+ test_cmp expected_pub actual_pub
+'
+
test_done
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index 464ffdd147..1cea758f78 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -71,6 +71,13 @@ test_expect_success 'push --signed fails with a receiver without push certificat
test_i18ngrep "the receiving end does not support" err
'
+test_expect_success 'push --signed=1 is accepted' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ test_must_fail git push --signed=1 dst noop ff +noff 2>err &&
+ test_i18ngrep "the receiving end does not support" err
+'
+
test_expect_success GPG 'no certificate for a signed push with no update' '
prepare_dst &&
mkdir -p dst/.git/hooks &&
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index a1dcdb81d7..b326d550f3 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -59,10 +59,14 @@ test_format () {
}
# Feed to --format to provide predictable colored sequences.
+BASIC_COLOR='%Credfoo%Creset'
+COLOR='%C(red)foo%C(reset)'
AUTO_COLOR='%C(auto,red)foo%C(auto,reset)'
+ALWAYS_COLOR='%C(always,red)foo%C(always,reset)'
has_color () {
- printf '\033[31mfoo\033[m\n' >expect &&
- test_cmp expect "$1"
+ test_decode_color <"$1" >decoded &&
+ echo "<RED>foo<RESET>" >expect &&
+ test_cmp expect decoded
}
has_no_color () {
@@ -170,62 +174,84 @@ $added
EOF
-test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<EOF
-commit $head2
-foobarbazxyzzy
-commit $head1
-foobarbazxyzzy
-EOF
-
-test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<EOF
-commit $head2
-foo
-commit $head1
-foo
-EOF
-
-test_expect_success '%C(auto,...) does not enable color by default' '
- git log --format=$AUTO_COLOR -1 >actual &&
- has_no_color actual
+test_expect_success 'basic colors' '
+ cat >expect <<-EOF &&
+ commit $head2
+ <RED>foo<GREEN>bar<BLUE>baz<RESET>xyzzy
+ EOF
+ format="%Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy" &&
+ git rev-list --color --format="$format" -1 master >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect actual
'
-test_expect_success '%C(auto,...) enables colors for color.diff' '
- git -c color.diff=always log --format=$AUTO_COLOR -1 >actual &&
- has_color actual
+test_expect_success 'advanced colors' '
+ cat >expect <<-EOF &&
+ commit $head2
+ <BOLD;RED;BYELLOW>foo<RESET>
+ EOF
+ format="%C(red yellow bold)foo%C(reset)" &&
+ git rev-list --color --format="$format" -1 master >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect actual
'
-test_expect_success '%C(auto,...) enables colors for color.ui' '
- git -c color.ui=always log --format=$AUTO_COLOR -1 >actual &&
- has_color actual
-'
+for spec in \
+ "%Cred:$BASIC_COLOR" \
+ "%C(...):$COLOR" \
+ "%C(auto,...):$AUTO_COLOR"
+do
+ desc=${spec%%:*}
+ color=${spec#*:}
+ test_expect_success "$desc does not enable color by default" '
+ git log --format=$color -1 >actual &&
+ has_no_color actual
+ '
-test_expect_success '%C(auto,...) respects --color' '
- git log --format=$AUTO_COLOR -1 --color >actual &&
- has_color actual
-'
+ test_expect_success "$desc enables colors for color.diff" '
+ git -c color.diff=always log --format=$color -1 >actual &&
+ has_color actual
+ '
-test_expect_success '%C(auto,...) respects --no-color' '
- git -c color.ui=always log --format=$AUTO_COLOR -1 --no-color >actual &&
- has_no_color actual
-'
+ test_expect_success "$desc enables colors for color.ui" '
+ git -c color.ui=always log --format=$color -1 >actual &&
+ has_color actual
+ '
-test_expect_success TTY '%C(auto,...) respects --color=auto (stdout is tty)' '
- test_terminal env TERM=vt100 \
- git log --format=$AUTO_COLOR -1 --color=auto >actual &&
- has_color actual
-'
+ test_expect_success "$desc respects --color" '
+ git log --format=$color -1 --color >actual &&
+ has_color actual
+ '
-test_expect_success '%C(auto,...) respects --color=auto (stdout not tty)' '
- (
- TERM=vt100 && export TERM &&
- git log --format=$AUTO_COLOR -1 --color=auto >actual &&
+ test_expect_success "$desc respects --no-color" '
+ git -c color.ui=always log --format=$color -1 --no-color >actual &&
has_no_color actual
- )
+ '
+
+ test_expect_success TTY "$desc respects --color=auto (stdout is tty)" '
+ test_terminal env TERM=vt100 \
+ git log --format=$color -1 --color=auto >actual &&
+ has_color actual
+ '
+
+ test_expect_success "$desc respects --color=auto (stdout not tty)" '
+ (
+ TERM=vt100 && export TERM &&
+ git log --format=$color -1 --color=auto >actual &&
+ has_no_color actual
+ )
+ '
+done
+
+test_expect_success '%C(always,...) enables color even without tty' '
+ git log --format=$ALWAYS_COLOR -1 >actual &&
+ has_color actual
'
test_expect_success '%C(auto) respects --color' '
- git log --color --format="%C(auto)%H" -1 >actual &&
- printf "\\033[33m%s\\033[m\\n" $(git rev-parse HEAD) >expect &&
+ git log --color --format="%C(auto)%H" -1 >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ echo "<YELLOW>$(git rev-parse HEAD)<RESET>" >expect &&
test_cmp expect actual
'
@@ -235,6 +261,17 @@ test_expect_success '%C(auto) respects --no-color' '
test_cmp expect actual
'
+test_expect_success 'rev-list %C(auto,...) respects --color' '
+ git rev-list --color --format="%C(auto,green)foo%C(auto,reset)" \
+ -1 HEAD >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ cat >expect <<-EOF &&
+ commit $(git rev-parse HEAD)
+ <GREEN>foo<RESET>
+ EOF
+ test_cmp expect actual
+'
+
iconv -f utf-8 -t $test_encoding > commit-msg <<EOF
Test printing of complex bodies
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 381f35ed16..d3453c583c 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -255,27 +255,19 @@ test_expect_success 'rev-list accumulates multiple --exclude' '
compare rev-list "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches
'
-
-# "git rev-list<ENTER>" is likely to be a bug in the calling script and may
-# deserve an error message, but do cases where set of refs programmatically
-# given using globbing and/or --stdin need to fail with the same error, or
-# are we better off reporting a success with no output? The following few
-# tests document the current behaviour to remind us that we might want to
-# think about this issue.
-
-test_expect_failure 'rev-list may want to succeed with empty output on no input (1)' '
+test_expect_failure 'rev-list should succeed with empty output on empty stdin' '
>expect &&
git rev-list --stdin <expect >actual &&
test_cmp expect actual
'
-test_expect_failure 'rev-list may want to succeed with empty output on no input (2)' '
+test_expect_success 'rev-list should succeed with empty output with all refs excluded' '
>expect &&
git rev-list --exclude=* --all >actual &&
test_cmp expect actual
'
-test_expect_failure 'rev-list may want to succeed with empty output on no input (3)' '
+test_expect_success 'rev-list should succeed with empty output with empty --all' '
(
test_create_repo empty &&
cd empty &&
@@ -285,6 +277,12 @@ test_expect_failure 'rev-list may want to succeed with empty output on no input
)
'
+test_expect_success 'rev-list should succeed with empty output with empty glob' '
+ >expect &&
+ git rev-list --glob=does-not-match-anything >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'shortlog accepts --glob/--tags/--remotes' '
compare shortlog "subspace/one subspace/two" --branches=subspace &&
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 97a07655a0..be78cc4fad 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -188,35 +188,29 @@ test_expect_success 'fail to track annotated tags' '
test_must_fail git checkout heavytrack
'
-test_expect_success 'setup tracking with branch --set-upstream on existing branch' '
+test_expect_success '--set-upstream-to does not change branch' '
git branch from-master master &&
- test_must_fail git config branch.from-master.merge > actual &&
- git branch --set-upstream from-master master &&
- git config branch.from-master.merge > actual &&
- grep -q "^refs/heads/master$" actual
-'
-
-test_expect_success '--set-upstream does not change branch' '
+ git branch --set-upstream-to master from-master &&
git branch from-master2 master &&
test_must_fail git config branch.from-master2.merge > actual &&
git rev-list from-master2 &&
git update-ref refs/heads/from-master2 from-master2^ &&
git rev-parse from-master2 >expect2 &&
- git branch --set-upstream from-master2 master &&
+ git branch --set-upstream-to master from-master2 &&
git config branch.from-master.merge > actual &&
git rev-parse from-master2 >actual2 &&
grep -q "^refs/heads/master$" actual &&
cmp expect2 actual2
'
-test_expect_success '--set-upstream @{-1}' '
- git checkout from-master &&
+test_expect_success '--set-upstream-to @{-1}' '
+ git checkout follower &&
git checkout from-master2 &&
git config branch.from-master2.merge > expect2 &&
- git branch --set-upstream @{-1} follower &&
+ git branch --set-upstream-to @{-1} from-master &&
git config branch.from-master.merge > actual &&
git config branch.from-master2.merge > actual2 &&
- git branch --set-upstream from-master follower &&
+ git branch --set-upstream-to follower from-master &&
git config branch.from-master.merge > expect &&
test_cmp expect2 actual2 &&
test_cmp expect actual
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 834a9ed168..2274a4b733 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -7,6 +7,7 @@ test_description='for-each-ref test'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
# Mon Jul 3 23:18:43 2006 +0000
datestamp=1151968723
@@ -412,21 +413,33 @@ test_expect_success 'Check for invalid refname format' '
test_must_fail git for-each-ref --format="%(refname:INVALID)"
'
-get_color ()
-{
- git config --get-color no.such.slot "$1"
-}
+test_expect_success 'set up color tests' '
+ cat >expected.color <<-EOF &&
+ $(git rev-parse --short refs/heads/master) <GREEN>master<RESET>
+ $(git rev-parse --short refs/remotes/origin/master) <GREEN>origin/master<RESET>
+ $(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>
+ $(git rev-parse --short refs/tags/two) <GREEN>two<RESET>
+ EOF
+ sed "s/<[^>]*>//g" <expected.color >expected.bare &&
+ color_format="%(objectname:short) %(color:green)%(refname:short)"
+'
-cat >expected <<EOF
-$(git rev-parse --short refs/heads/master) $(get_color green)master$(get_color reset)
-$(git rev-parse --short refs/remotes/origin/master) $(get_color green)origin/master$(get_color reset)
-$(git rev-parse --short refs/tags/testtag) $(get_color green)testtag$(get_color reset)
-$(git rev-parse --short refs/tags/two) $(get_color green)two$(get_color reset)
-EOF
+test_expect_success TTY '%(color) shows color with a tty' '
+ test_terminal env TERM=vt100 \
+ git for-each-ref --format="$color_format" >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expected.color actual
+'
-test_expect_success 'Check %(color:...) ' '
- git for-each-ref --format="%(objectname:short) %(color:green)%(refname:short)" >actual &&
- test_cmp expected actual
+test_expect_success '%(color) does not show color without tty' '
+ TERM=vt100 git for-each-ref --format="$color_format" >actual &&
+ test_cmp expected.bare actual
+'
+
+test_expect_success 'color.ui=always can override tty check' '
+ git -c color.ui=always for-each-ref --format="$color_format" >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expected.color actual
'
cat >expected <<\EOF
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 0ef7b94394..dbcd6f623c 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -9,6 +9,7 @@ Tests for operations with tags.'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
# creating and listing lightweight tags:
@@ -1887,7 +1888,7 @@ EOF"
run_with_limited_stack git tag --contains HEAD >actual &&
test_cmp expect actual &&
run_with_limited_stack git tag --no-contains HEAD >actual &&
- test_line_count ">" 10 actual
+ test_line_count "-gt" 10 actual
'
test_expect_success '--format should list tags as per format given' '
@@ -1900,6 +1901,30 @@ test_expect_success '--format should list tags as per format given' '
test_cmp expect actual
'
+test_expect_success "set up color tests" '
+ echo "<RED>v1.0<RESET>" >expect.color &&
+ echo "v1.0" >expect.bare &&
+ color_args="--format=%(color:red)%(refname:short) --list v1.0"
+'
+
+test_expect_success '%(color) omitted without tty' '
+ TERM=vt100 git tag $color_args >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect.bare actual
+'
+
+test_expect_success TTY '%(color) present with tty' '
+ test_terminal env TERM=vt100 git tag $color_args >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect.color actual
+'
+
+test_expect_success 'color.ui=always overrides auto-color' '
+ git -c color.ui=always tag $color_args >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect.color actual
+'
+
test_expect_success 'setup --merged test tags' '
git tag mergetest-1 HEAD~2 &&
git tag mergetest-2 HEAD~1 &&
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index 20b4d83c28..9128ec5acd 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -134,6 +134,86 @@ test_expect_success TTY 'configuration can enable pager (from subdir)' '
}
'
+test_expect_success TTY 'git tag -l defaults to paging' '
+ rm -f paginated.out &&
+ test_terminal git tag -l &&
+ test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -l respects pager.tag' '
+ rm -f paginated.out &&
+ test_terminal git -c pager.tag=false tag -l &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -l respects --no-pager' '
+ rm -f paginated.out &&
+ test_terminal git -c pager.tag --no-pager tag -l &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag with no args defaults to paging' '
+ # no args implies -l so this should page like -l
+ rm -f paginated.out &&
+ test_terminal git tag &&
+ test -e paginated.out
+'
+
+test_expect_success TTY 'git tag with no args respects pager.tag' '
+ # no args implies -l so this should page like -l
+ rm -f paginated.out &&
+ test_terminal git -c pager.tag=false tag &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag --contains defaults to paging' '
+ # --contains implies -l so this should page like -l
+ rm -f paginated.out &&
+ test_terminal git tag --contains &&
+ test -e paginated.out
+'
+
+test_expect_success TTY 'git tag --contains respects pager.tag' '
+ # --contains implies -l so this should page like -l
+ rm -f paginated.out &&
+ test_terminal git -c pager.tag=false tag --contains &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -a defaults to not paging' '
+ test_when_finished "git tag -d newtag" &&
+ rm -f paginated.out &&
+ test_terminal git tag -am message newtag &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -a ignores pager.tag' '
+ test_when_finished "git tag -d newtag" &&
+ rm -f paginated.out &&
+ test_terminal git -c pager.tag tag -am message newtag &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -a respects --paginate' '
+ test_when_finished "git tag -d newtag" &&
+ rm -f paginated.out &&
+ test_terminal git --paginate tag -am message newtag &&
+ test -e paginated.out
+'
+
+test_expect_success TTY 'git tag as alias ignores pager.tag with -a' '
+ test_when_finished "git tag -d newtag" &&
+ rm -f paginated.out &&
+ test_terminal git -c pager.tag -c alias.t=tag t -am message newtag &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag as alias respects pager.tag with -l' '
+ rm -f paginated.out &&
+ test_terminal git -c pager.tag=false -c alias.t=tag t -l &&
+ ! test -e paginated.out
+'
+
# A colored commit log will begin with an appropriate ANSI escape
# for the first color; the text "commit" comes later.
colorful() {
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index 3ae394e934..556e1850e2 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -472,4 +472,14 @@ test_expect_success 'git clean -id with prefix and path (ask)' '
'
+test_expect_success 'git clean -i paints the header in HEADER color' '
+ >a.out &&
+ echo q |
+ git -c color.ui=always clean -i |
+ test_decode_color |
+ head -n 1 >header &&
+ # not i18ngrep
+ grep "^<BOLD>" header
+'
+
test_done
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index dcac364c5f..6f8337ffb5 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -46,16 +46,6 @@ test_expect_success 'submodule update aborts on missing gitmodules url' '
test_must_fail git submodule init
'
-test_expect_success 'configuration parsing' '
- test_when_finished "rm -f .gitmodules" &&
- cat >.gitmodules <<-\EOF &&
- [submodule "s"]
- path
- ignore
- EOF
- test_must_fail git status
-'
-
test_expect_success 'setup - repository in init subdirectory' '
mkdir init &&
(
@@ -1289,4 +1279,10 @@ test_expect_success 'init properly sets the config' '
test_must_fail git -C multisuper_clone config --get submodule.sub1.active
'
+test_expect_success 'recursive clone respects -q' '
+ test_when_finished "rm -rf multisuper_clone" &&
+ git clone -q --recurse-submodules multisuper multisuper_clone >actual &&
+ test_must_be_empty actual
+'
+
test_done
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
index eea36f1dbe..46c09c7765 100755
--- a/t/t7411-submodule-config.sh
+++ b/t/t7411-submodule-config.sh
@@ -31,6 +31,21 @@ test_expect_success 'submodule config cache setup' '
)
'
+test_expect_success 'configuration parsing with error' '
+ test_when_finished "rm -rf repo" &&
+ test_create_repo repo &&
+ cat >repo/.gitmodules <<-\EOF &&
+ [submodule "s"]
+ path
+ ignore
+ EOF
+ (
+ cd repo &&
+ test_must_fail test-submodule-config "" s 2>actual &&
+ test_i18ngrep "bad config" actual
+ )
+'
+
cat >super/expect <<EOF
Submodule name: 'a' for path 'a'
Submodule name: 'a' for path 'b'
@@ -107,78 +122,6 @@ test_expect_success 'using different treeishs works' '
)
'
-cat >super/expect_url <<EOF
-Submodule url: 'git@somewhere.else.net:a.git' for path 'b'
-Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
-EOF
-
-cat >super/expect_local_path <<EOF
-Submodule name: 'a' for path 'c'
-Submodule name: 'submodule' for path 'submodule'
-EOF
-
-test_expect_success 'reading of local configuration' '
- (cd super &&
- old_a=$(git config submodule.a.url) &&
- old_submodule=$(git config submodule.submodule.url) &&
- git config submodule.a.url git@somewhere.else.net:a.git &&
- git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
- test-submodule-config --url \
- "" b \
- "" submodule \
- >actual &&
- test_cmp expect_url actual &&
- git config submodule.a.path c &&
- test-submodule-config \
- "" c \
- "" submodule \
- >actual &&
- test_cmp expect_local_path actual &&
- git config submodule.a.url "$old_a" &&
- git config submodule.submodule.url "$old_submodule" &&
- git config --unset submodule.a.path c
- )
-'
-
-cat >super/expect_url <<EOF
-Submodule url: '../submodule' for path 'b'
-Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
-EOF
-
-test_expect_success 'reading of local configuration for uninitialized submodules' '
- (
- cd super &&
- git submodule deinit -f b &&
- old_submodule=$(git config submodule.submodule.url) &&
- git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
- test-submodule-config --url \
- "" b \
- "" submodule \
- >actual &&
- test_cmp expect_url actual &&
- git config submodule.submodule.url "$old_submodule" &&
- git submodule init b
- )
-'
-
-cat >super/expect_fetchrecurse_die.err <<EOF
-fatal: bad submodule.submodule.fetchrecursesubmodules argument: blabla
-EOF
-
-test_expect_success 'local error in fetchrecursesubmodule dies early' '
- (cd super &&
- git config submodule.submodule.fetchrecursesubmodules blabla &&
- test_must_fail test-submodule-config \
- "" b \
- "" submodule \
- >actual.out 2>actual.err &&
- touch expect_fetchrecurse_die.out &&
- test_cmp expect_fetchrecurse_die.out actual.out &&
- test_cmp expect_fetchrecurse_die.err actual.err &&
- git config --unset submodule.submodule.fetchrecursesubmodules
- )
-'
-
test_expect_success 'error in history in fetchrecursesubmodule lets continue' '
(cd super &&
git config -f .gitmodules \
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 0c6f91c433..164719d1c9 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -681,6 +681,36 @@ test_expect_success 'using "where = before"' '
test_cmp expected actual
'
+test_expect_success 'overriding configuration with "--where after"' '
+ git config trailer.ack.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --where after --trailer "ack: Peff" \
+ complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" with "--no-where"' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Peff
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --where after --no-where --trailer "ack: Peff" \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'using "where = after"' '
git config trailer.ack.where "after" &&
cat complex_message_body >expected &&
@@ -947,6 +977,23 @@ test_expect_success 'using "ifExists = add" with "where = after"' '
test_cmp expected actual
'
+test_expect_success 'overriding configuration with "--if-exists replace"' '
+ git config trailer.fix.key "Fixes: " &&
+ git config trailer.fix.ifExists "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Fixes: 22
+ EOF
+ git interpret-trailers --if-exists replace --trailer "review:" \
+ --trailer "fix=53" --trailer "fix=22" --trailer "bug: 42" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'using "ifExists = replace"' '
git config trailer.fix.key "Fixes: " &&
git config trailer.fix.ifExists "replace" &&
@@ -1026,6 +1073,25 @@ test_expect_success 'the default is "ifMissing = add"' '
test_cmp expected actual
'
+test_expect_success 'overriding configuration with "--if-missing doNothing"' '
+ git config trailer.ifmissing "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --if-missing doNothing \
+ --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'when default "ifMissing" is "doNothing"' '
git config trailer.ifmissing "doNothing" &&
cat complex_message_body >expected &&
@@ -1275,4 +1341,80 @@ test_expect_success 'with cut line' '
test_cmp expected actual
'
+test_expect_success 'only trailers' '
+ git config trailer.sign.command "echo config-value" &&
+ cat >expected <<-\EOF &&
+ existing: existing-value
+ sign: config-value
+ added: added-value
+ EOF
+ git interpret-trailers \
+ --trailer added:added-value \
+ --only-trailers >actual <<-\EOF &&
+ my subject
+
+ my body
+
+ existing: existing-value
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'only-trailers omits non-trailer in middle of block' '
+ git config trailer.sign.command "echo config-value" &&
+ cat >expected <<-\EOF &&
+ Signed-off-by: nobody <nobody@nowhere>
+ Signed-off-by: somebody <somebody@somewhere>
+ sign: config-value
+ EOF
+ git interpret-trailers --only-trailers >actual <<-\EOF &&
+ subject
+
+ it is important that the trailers below are signed-off-by
+ so that they meet the "25% trailers Git knows about" heuristic
+
+ Signed-off-by: nobody <nobody@nowhere>
+ this is not a trailer
+ Signed-off-by: somebody <somebody@somewhere>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'only input' '
+ git config trailer.sign.command "echo config-value" &&
+ cat >expected <<-\EOF &&
+ existing: existing-value
+ EOF
+ git interpret-trailers \
+ --only-trailers --only-input >actual <<-\EOF &&
+ my subject
+
+ my body
+
+ existing: existing-value
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'unfold' '
+ cat >expected <<-\EOF &&
+ foo: continued across several lines
+ EOF
+ # pass through tr to make leading and trailing whitespace more obvious
+ tr _ " " <<-\EOF |
+ my subject
+
+ my body
+
+ foo:_
+ __continued
+ ___across
+ ____several
+ _____lines
+ ___
+ EOF
+ git interpret-trailers --only-trailers --only-input --unfold >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 2ebda509ac..80194b79f9 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -774,4 +774,19 @@ test_expect_success 'merge can be completed with --continue' '
verify_parents $c0 $c1
'
+write_script .git/FAKE_EDITOR <<EOF
+# kill -TERM command added below.
+EOF
+
+test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue' '
+ git reset --hard c0 &&
+ ! "$SHELL_PATH" -c '\''
+ echo kill -TERM $$ >> .git/FAKE_EDITOR
+ GIT_EDITOR=.git/FAKE_EDITOR
+ export GIT_EDITOR
+ exec git merge --no-ff --edit c1'\'' &&
+ git merge --continue &&
+ verify_parents $c0 $c1
+'
+
test_done
diff --git a/t/t7614-merge-signoff.sh b/t/t7614-merge-signoff.sh
new file mode 100755
index 0000000000..c1b8446f49
--- /dev/null
+++ b/t/t7614-merge-signoff.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git merge --signoff
+
+This test runs git merge --signoff and makes sure that it works.
+'
+
+. ./test-lib.sh
+
+# Setup test files
+test_setup() {
+ # Expected commit message after merge --signoff
+ cat >expected-signed <<EOF &&
+Merge branch 'master' into other-branch
+
+Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
+EOF
+
+ # Expected commit message after merge without --signoff (or with --no-signoff)
+ cat >expected-unsigned <<EOF &&
+Merge branch 'master' into other-branch
+EOF
+
+ # Initial commit and feature branch to merge master into it.
+ git commit --allow-empty -m "Initial empty commit" &&
+ git checkout -b other-branch &&
+ test_commit other-branch file1 1
+}
+
+# Setup repository, files & feature branch
+# This step must be run if You want to test 2,3 or 4
+# Order of 2,3,4 is not important, but 1 must be run before
+# For example `-r 1,4` or `-r 1,4,2 -v` etc
+# But not `-r 2` or `-r 4,3,2,1`
+test_expect_success 'setup' '
+ test_setup
+'
+
+# Test with --signoff flag
+test_expect_success 'git merge --signoff adds a sign-off line' '
+ git checkout master &&
+ test_commit master-branch-2 file2 2 &&
+ git checkout other-branch &&
+ git merge master --signoff --no-edit &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ test_cmp expected-signed actual
+'
+
+# Test without --signoff flag
+test_expect_success 'git merge does not add a sign-off line' '
+ git checkout master &&
+ test_commit master-branch-3 file3 3 &&
+ git checkout other-branch &&
+ git merge master --no-edit &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ test_cmp expected-unsigned actual
+'
+
+# Test for --no-signoff flag
+test_expect_success 'git merge --no-signoff flag cancels --signoff flag' '
+ git checkout master &&
+ test_commit master-branch-4 file4 4 &&
+ git checkout other-branch &&
+ git merge master --no-edit --signoff --no-signoff &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ test_cmp expected-unsigned actual
+'
+
+test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index f106387820..2a6679c2f5 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -374,6 +374,11 @@ test_expect_success 'grep -L -C' '
test_cmp expected actual
'
+test_expect_success 'grep --files-without-match --quiet' '
+ git grep --files-without-match --quiet nonexistent_string >actual &&
+ test_cmp /dev/null actual
+'
+
cat >expected <<EOF
file:foo mmap bar_mmap
EOF
diff --git a/t/t8008-blame-formats.sh b/t/t8008-blame-formats.sh
index 92c8e792d1..ae4b579d24 100755
--- a/t/t8008-blame-formats.sh
+++ b/t/t8008-blame-formats.sh
@@ -12,22 +12,25 @@ test_expect_success 'setup' '
echo c >>file &&
echo d >>file &&
test_tick &&
- git commit -a -m two
+ git commit -a -m two &&
+ ID1=$(git rev-parse HEAD^) &&
+ shortID1="^$(git rev-parse HEAD^ |cut -c 1-17)" &&
+ ID2=$(git rev-parse HEAD) &&
+ shortID2="$(git rev-parse HEAD |cut -c 1-18)"
'
-cat >expect <<'EOF'
-^baf5e0b (A U Thor 2005-04-07 15:13:13 -0700 1) a
-8825379d (A U Thor 2005-04-07 15:14:13 -0700 2) b
-8825379d (A U Thor 2005-04-07 15:14:13 -0700 3) c
-8825379d (A U Thor 2005-04-07 15:14:13 -0700 4) d
+cat >expect <<EOF
+$shortID1 (A U Thor 2005-04-07 15:13:13 -0700 1) a
+$shortID2 (A U Thor 2005-04-07 15:14:13 -0700 2) b
+$shortID2 (A U Thor 2005-04-07 15:14:13 -0700 3) c
+$shortID2 (A U Thor 2005-04-07 15:14:13 -0700 4) d
EOF
test_expect_success 'normal blame output' '
- git blame file >actual &&
+ git blame --abbrev=17 file >actual &&
test_cmp expect actual
'
-ID1=baf5e0b3869e0b2b2beb395a3720c7b51eac94fc
-COMMIT1='author A U Thor
+COMMIT1="author A U Thor
author-mail <author@example.com>
author-time 1112911993
author-tz -0700
@@ -37,9 +40,8 @@ committer-time 1112911993
committer-tz -0700
summary one
boundary
-filename file'
-ID2=8825379dfb8a1267b58e8e5bcf69eec838f685ec
-COMMIT2='author A U Thor
+filename file"
+COMMIT2="author A U Thor
author-mail <author@example.com>
author-time 1112912053
author-tz -0700
@@ -48,8 +50,8 @@ committer-mail <committer@example.com>
committer-time 1112912053
committer-tz -0700
summary two
-previous baf5e0b3869e0b2b2beb395a3720c7b51eac94fc file
-filename file'
+previous $ID1 file
+filename file"
cat >expect <<EOF
$ID1 1 1 1
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 50a9a1d1c4..1701fe2a06 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -42,6 +42,7 @@ test_decode_color () {
function name(n) {
if (n == 0) return "RESET";
if (n == 1) return "BOLD";
+ if (n == 7) return "REVERSE";
if (n == 30) return "BLACK";
if (n == 31) return "RED";
if (n == 32) return "GREEN";
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1b6e53f78a..5fbd8d4a90 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -99,7 +99,6 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
my $ok = join("|", qw(
TRACE
DEBUG
- USE_LOOKUP
TEST
.*_TEST
PROVE
@@ -991,9 +990,6 @@ case $uname_s in
find () {
/usr/bin/find "$@"
}
- sum () {
- md5sum "$@"
- }
# git sees Windows-style pwd
pwd () {
builtin pwd -W
diff --git a/tag.c b/tag.c
index 47f60ae151..7e10acfb6e 100644
--- a/tag.c
+++ b/tag.c
@@ -33,7 +33,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
return ret;
}
-int gpg_verify_tag(const unsigned char *sha1, const char *name_to_report,
+int gpg_verify_tag(const struct object_id *oid, const char *name_to_report,
unsigned flags)
{
enum object_type type;
@@ -41,20 +41,20 @@ int gpg_verify_tag(const unsigned char *sha1, const char *name_to_report,
unsigned long size;
int ret;
- type = sha1_object_info(sha1, NULL);
+ type = sha1_object_info(oid->hash, NULL);
if (type != OBJ_TAG)
return error("%s: cannot verify a non-tag object of type %s.",
name_to_report ?
name_to_report :
- find_unique_abbrev(sha1, DEFAULT_ABBREV),
+ find_unique_abbrev(oid->hash, DEFAULT_ABBREV),
typename(type));
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf)
return error("%s: unable to read file.",
name_to_report ?
name_to_report :
- find_unique_abbrev(sha1, DEFAULT_ABBREV));
+ find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
ret = run_gpg_verify(buf, size, flags);
diff --git a/tag.h b/tag.h
index fdfcb4a84a..d469534e82 100644
--- a/tag.h
+++ b/tag.h
@@ -17,7 +17,7 @@ extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long si
extern int parse_tag(struct tag *item);
extern struct object *deref_tag(struct object *, const char *, int);
extern struct object *deref_tag_noverify(struct object *);
-extern int gpg_verify_tag(const unsigned char *sha1,
+extern int gpg_verify_tag(const struct object_id *oid,
const char *name_to_report, unsigned flags);
#endif /* TAG_H */
diff --git a/templates/hooks--prepare-commit-msg.sample b/templates/hooks--prepare-commit-msg.sample
index 86b8f227ec..318afe3fd8 100755
--- a/templates/hooks--prepare-commit-msg.sample
+++ b/templates/hooks--prepare-commit-msg.sample
@@ -9,8 +9,8 @@
#
# To enable this hook, rename this file to "prepare-commit-msg".
-# This hook includes three examples. The first comments out the
-# "Conflicts:" part of a merge commit.
+# This hook includes three examples. The first one removes the
+# "# Please enter the commit message..." help message.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output. It is
@@ -20,17 +20,23 @@
# The third example adds a Signed-off-by line to the message, that can
# still be edited. This is rarely a good idea.
-case "$2,$3" in
- merge,)
- @PERL_PATH@ -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
-# ,|template,)
-# @PERL_PATH@ -i.bak -pe '
-# print "\n" . `git diff --cached --name-status -r`
-# if /^#/ && $first++ == 0' "$1" ;;
+@PERL_PATH@ -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
- *) ;;
-esac
+# case "$COMMIT_SOURCE,$SHA1" in
+# ,|template,)
+# @PERL_PATH@ -i.bak -pe '
+# print "\n" . `git diff --cached --name-status -r`
+# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
+# *) ;;
+# esac
-# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
-# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
+# if test -z "$COMMIT_SOURCE"
+# then
+# @PERL_PATH@ -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
+# fi
diff --git a/trailer.c b/trailer.c
index 751b56c009..c30e3a0c04 100644
--- a/trailer.c
+++ b/trailer.c
@@ -10,18 +10,13 @@
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
-enum action_where { WHERE_END, WHERE_AFTER, WHERE_BEFORE, WHERE_START };
-enum action_if_exists { EXISTS_ADD_IF_DIFFERENT_NEIGHBOR, EXISTS_ADD_IF_DIFFERENT,
- EXISTS_ADD, EXISTS_REPLACE, EXISTS_DO_NOTHING };
-enum action_if_missing { MISSING_ADD, MISSING_DO_NOTHING };
-
struct conf_info {
char *name;
char *key;
char *command;
- enum action_where where;
- enum action_if_exists if_exists;
- enum action_if_missing if_missing;
+ enum trailer_where where;
+ enum trailer_if_exists if_exists;
+ enum trailer_if_missing if_missing;
};
static struct conf_info default_conf_info;
@@ -63,7 +58,7 @@ static const char *git_generated_prefixes[] = {
pos != (head); \
pos = is_reverse ? pos->prev : pos->next)
-static int after_or_end(enum action_where where)
+static int after_or_end(enum trailer_where where)
{
return (where == WHERE_AFTER) || (where == WHERE_END);
}
@@ -164,13 +159,15 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)
fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
}
-static void print_all(FILE *outfile, struct list_head *head, int trim_empty)
+static void print_all(FILE *outfile, struct list_head *head,
+ const struct process_trailer_options *opts)
{
struct list_head *pos;
struct trailer_item *item;
list_for_each(pos, head) {
item = list_entry(pos, struct trailer_item, list);
- if (!trim_empty || strlen(item->value) > 0)
+ if ((!opts->trim_empty || strlen(item->value) > 0) &&
+ (!opts->only_trailers || item->token))
print_tok_val(outfile, item->token, item->value);
}
}
@@ -201,7 +198,7 @@ static int check_if_different(struct trailer_item *in_tok,
int check_all,
struct list_head *head)
{
- enum action_where where = arg_tok->conf.where;
+ enum trailer_where where = arg_tok->conf.where;
struct list_head *next_head;
do {
if (same_trailer(in_tok, arg_tok))
@@ -300,13 +297,16 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,
else
free_arg_item(arg_tok);
break;
+ default:
+ die("BUG: trailer.c: unhandled value %d",
+ arg_tok->conf.if_exists);
}
}
static void apply_arg_if_missing(struct list_head *head,
struct arg_item *arg_tok)
{
- enum action_where where;
+ enum trailer_where where;
struct trailer_item *to_add;
switch (arg_tok->conf.if_missing) {
@@ -321,6 +321,10 @@ static void apply_arg_if_missing(struct list_head *head,
list_add_tail(&to_add->list, head);
else
list_add(&to_add->list, head);
+ break;
+ default:
+ die("BUG: trailer.c: unhandled value %d",
+ arg_tok->conf.if_missing);
}
}
@@ -331,7 +335,7 @@ static int find_same_and_apply_arg(struct list_head *head,
struct trailer_item *in_tok;
struct trailer_item *on_tok;
- enum action_where where = arg_tok->conf.where;
+ enum trailer_where where = arg_tok->conf.where;
int middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);
int backwards = after_or_end(where);
struct trailer_item *start_tok;
@@ -373,44 +377,50 @@ static void process_trailers_lists(struct list_head *head,
}
}
-static int set_where(struct conf_info *item, const char *value)
+int trailer_set_where(enum trailer_where *item, const char *value)
{
- if (!strcasecmp("after", value))
- item->where = WHERE_AFTER;
+ if (!value)
+ *item = WHERE_DEFAULT;
+ else if (!strcasecmp("after", value))
+ *item = WHERE_AFTER;
else if (!strcasecmp("before", value))
- item->where = WHERE_BEFORE;
+ *item = WHERE_BEFORE;
else if (!strcasecmp("end", value))
- item->where = WHERE_END;
+ *item = WHERE_END;
else if (!strcasecmp("start", value))
- item->where = WHERE_START;
+ *item = WHERE_START;
else
return -1;
return 0;
}
-static int set_if_exists(struct conf_info *item, const char *value)
+int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
{
- if (!strcasecmp("addIfDifferent", value))
- item->if_exists = EXISTS_ADD_IF_DIFFERENT;
+ if (!value)
+ *item = EXISTS_DEFAULT;
+ else if (!strcasecmp("addIfDifferent", value))
+ *item = EXISTS_ADD_IF_DIFFERENT;
else if (!strcasecmp("addIfDifferentNeighbor", value))
- item->if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
+ *item = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
else if (!strcasecmp("add", value))
- item->if_exists = EXISTS_ADD;
+ *item = EXISTS_ADD;
else if (!strcasecmp("replace", value))
- item->if_exists = EXISTS_REPLACE;
+ *item = EXISTS_REPLACE;
else if (!strcasecmp("doNothing", value))
- item->if_exists = EXISTS_DO_NOTHING;
+ *item = EXISTS_DO_NOTHING;
else
return -1;
return 0;
}
-static int set_if_missing(struct conf_info *item, const char *value)
+int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)
{
- if (!strcasecmp("doNothing", value))
- item->if_missing = MISSING_DO_NOTHING;
+ if (!value)
+ *item = MISSING_DEFAULT;
+ else if (!strcasecmp("doNothing", value))
+ *item = MISSING_DO_NOTHING;
else if (!strcasecmp("add", value))
- item->if_missing = MISSING_ADD;
+ *item = MISSING_ADD;
else
return -1;
return 0;
@@ -470,15 +480,18 @@ static int git_trailer_default_config(const char *conf_key, const char *value, v
variable_name = strrchr(trailer_item, '.');
if (!variable_name) {
if (!strcmp(trailer_item, "where")) {
- if (set_where(&default_conf_info, value) < 0)
+ if (trailer_set_where(&default_conf_info.where,
+ value) < 0)
warning(_("unknown value '%s' for key '%s'"),
value, conf_key);
} else if (!strcmp(trailer_item, "ifexists")) {
- if (set_if_exists(&default_conf_info, value) < 0)
+ if (trailer_set_if_exists(&default_conf_info.if_exists,
+ value) < 0)
warning(_("unknown value '%s' for key '%s'"),
value, conf_key);
} else if (!strcmp(trailer_item, "ifmissing")) {
- if (set_if_missing(&default_conf_info, value) < 0)
+ if (trailer_set_if_missing(&default_conf_info.if_missing,
+ value) < 0)
warning(_("unknown value '%s' for key '%s'"),
value, conf_key);
} else if (!strcmp(trailer_item, "separators")) {
@@ -532,15 +545,15 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
conf->command = xstrdup(value);
break;
case TRAILER_WHERE:
- if (set_where(conf, value))
+ if (trailer_set_where(&conf->where, value))
warning(_("unknown value '%s' for key '%s'"), value, conf_key);
break;
case TRAILER_IF_EXISTS:
- if (set_if_exists(conf, value))
+ if (trailer_set_if_exists(&conf->if_exists, value))
warning(_("unknown value '%s' for key '%s'"), value, conf_key);
break;
case TRAILER_IF_MISSING:
- if (set_if_missing(conf, value))
+ if (trailer_set_if_missing(&conf->if_missing, value))
warning(_("unknown value '%s' for key '%s'"), value, conf_key);
break;
default:
@@ -555,6 +568,9 @@ static void ensure_configured(void)
return;
/* Default config must be setup first */
+ default_conf_info.where = WHERE_END;
+ default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
+ default_conf_info.if_missing = MISSING_ADD;
git_config(git_trailer_default_config, NULL);
git_config(git_trailer_config, NULL);
configured = 1;
@@ -658,19 +674,27 @@ static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
}
static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
- const struct conf_info *conf)
+ const struct conf_info *conf,
+ const struct new_trailer_item *new_trailer_item)
{
struct arg_item *new = xcalloc(sizeof(*new), 1);
new->token = tok;
new->value = val;
duplicate_conf(&new->conf, conf);
+ if (new_trailer_item) {
+ if (new_trailer_item->where != WHERE_DEFAULT)
+ new->conf.where = new_trailer_item->where;
+ if (new_trailer_item->if_exists != EXISTS_DEFAULT)
+ new->conf.if_exists = new_trailer_item->if_exists;
+ if (new_trailer_item->if_missing != MISSING_DEFAULT)
+ new->conf.if_missing = new_trailer_item->if_missing;
+ }
list_add_tail(&new->list, arg_head);
}
static void process_command_line_args(struct list_head *arg_head,
- struct string_list *trailers)
+ struct list_head *new_trailer_head)
{
- struct string_list_item *tr;
struct arg_item *item;
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
@@ -690,26 +714,29 @@ static void process_command_line_args(struct list_head *arg_head,
add_arg_item(arg_head,
xstrdup(token_from_item(item, NULL)),
xstrdup(""),
- &item->conf);
+ &item->conf, NULL);
}
/* Add an arg item for each trailer on the command line */
- for_each_string_list_item(tr, trailers) {
- int separator_pos = find_separator(tr->string, cl_separators);
+ list_for_each(pos, new_trailer_head) {
+ struct new_trailer_item *tr =
+ list_entry(pos, struct new_trailer_item, list);
+ int separator_pos = find_separator(tr->text, cl_separators);
+
if (separator_pos == 0) {
struct strbuf sb = STRBUF_INIT;
- strbuf_addstr(&sb, tr->string);
+ strbuf_addstr(&sb, tr->text);
strbuf_trim(&sb);
error(_("empty trailer token in trailer '%.*s'"),
(int) sb.len, sb.buf);
strbuf_release(&sb);
} else {
- parse_trailer(&tok, &val, &conf, tr->string,
+ parse_trailer(&tok, &val, &conf, tr->text,
separator_pos);
add_arg_item(arg_head,
strbuf_detach(&tok, NULL),
strbuf_detach(&val, NULL),
- conf);
+ conf, tr);
}
}
@@ -885,9 +912,37 @@ static int ends_with_blank_line(const char *buf, size_t len)
return is_blank_line(buf + ll);
}
+static void unfold_value(struct strbuf *val)
+{
+ struct strbuf out = STRBUF_INIT;
+ size_t i;
+
+ strbuf_grow(&out, val->len);
+ i = 0;
+ while (i < val->len) {
+ char c = val->buf[i++];
+ if (c == '\n') {
+ /* Collapse continuation down to a single space. */
+ while (i < val->len && isspace(val->buf[i]))
+ i++;
+ strbuf_addch(&out, ' ');
+ } else {
+ strbuf_addch(&out, c);
+ }
+ }
+
+ /* Empty lines may have left us with whitespace cruft at the edges */
+ strbuf_trim(&out);
+
+ /* output goes back to val as if we modified it in-place */
+ strbuf_swap(&out, val);
+ strbuf_release(&out);
+}
+
static int process_input_file(FILE *outfile,
const char *str,
- struct list_head *head)
+ struct list_head *head,
+ const struct process_trailer_options *opts)
{
struct trailer_info info;
struct strbuf tok = STRBUF_INIT;
@@ -897,9 +952,10 @@ static int process_input_file(FILE *outfile,
trailer_info_get(&info, str);
/* Print lines before the trailers as is */
- fwrite(str, 1, info.trailer_start - str, outfile);
+ if (!opts->only_trailers)
+ fwrite(str, 1, info.trailer_start - str, outfile);
- if (!info.blank_line_before_trailer)
+ if (!opts->only_trailers && !info.blank_line_before_trailer)
fprintf(outfile, "\n");
for (i = 0; i < info.trailer_nr; i++) {
@@ -911,10 +967,12 @@ static int process_input_file(FILE *outfile,
if (separator_pos >= 1) {
parse_trailer(&tok, &val, NULL, trailer,
separator_pos);
+ if (opts->unfold)
+ unfold_value(&val);
add_trailer_item(head,
strbuf_detach(&tok, NULL),
strbuf_detach(&val, NULL));
- } else {
+ } else if (!opts->only_trailers) {
strbuf_addstr(&val, trailer);
strbuf_strip_suffix(&val, "\n");
add_trailer_item(head,
@@ -968,10 +1026,11 @@ static FILE *create_in_place_tempfile(const char *file)
return outfile;
}
-void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
+void process_trailers(const char *file,
+ const struct process_trailer_options *opts,
+ struct list_head *new_trailer_head)
{
LIST_HEAD(head);
- LIST_HEAD(arg_head);
struct strbuf sb = STRBUF_INIT;
int trailer_end;
FILE *outfile = stdout;
@@ -980,24 +1039,27 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str
read_input_file(&sb, file);
- if (in_place)
+ if (opts->in_place)
outfile = create_in_place_tempfile(file);
/* Print the lines before the trailers */
- trailer_end = process_input_file(outfile, sb.buf, &head);
-
- process_command_line_args(&arg_head, trailers);
+ trailer_end = process_input_file(outfile, sb.buf, &head, opts);
- process_trailers_lists(&head, &arg_head);
+ if (!opts->only_input) {
+ LIST_HEAD(arg_head);
+ process_command_line_args(&arg_head, new_trailer_head);
+ process_trailers_lists(&head, &arg_head);
+ }
- print_all(outfile, &head, trim_empty);
+ print_all(outfile, &head, opts);
free_all(&head);
/* Print the lines after the trailers as is */
- fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
+ if (!opts->only_trailers)
+ fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
- if (in_place)
+ if (opts->in_place)
if (rename_tempfile(&trailers_tempfile, file))
die_errno(_("could not rename temporary file to %s"), file);
@@ -1054,3 +1116,49 @@ void trailer_info_release(struct trailer_info *info)
free(info->trailers[i]);
free(info->trailers);
}
+
+static void format_trailer_info(struct strbuf *out,
+ const struct trailer_info *info,
+ const struct process_trailer_options *opts)
+{
+ int i;
+
+ /* If we want the whole block untouched, we can take the fast path. */
+ if (!opts->only_trailers && !opts->unfold) {
+ strbuf_add(out, info->trailer_start,
+ info->trailer_end - info->trailer_start);
+ return;
+ }
+
+ for (i = 0; i < info->trailer_nr; i++) {
+ char *trailer = info->trailers[i];
+ int separator_pos = find_separator(trailer, separators);
+
+ if (separator_pos >= 1) {
+ struct strbuf tok = STRBUF_INIT;
+ struct strbuf val = STRBUF_INIT;
+
+ parse_trailer(&tok, &val, NULL, trailer, separator_pos);
+ if (opts->unfold)
+ unfold_value(&val);
+
+ strbuf_addf(out, "%s: %s\n", tok.buf, val.buf);
+ strbuf_release(&tok);
+ strbuf_release(&val);
+
+ } else if (!opts->only_trailers) {
+ strbuf_addstr(out, trailer);
+ }
+ }
+
+}
+
+void format_trailers_from_commit(struct strbuf *out, const char *msg,
+ const struct process_trailer_options *opts)
+{
+ struct trailer_info info;
+
+ trailer_info_get(&info, msg);
+ format_trailer_info(out, &info, opts);
+ trailer_info_release(&info);
+}
diff --git a/trailer.h b/trailer.h
index 65cc5d79c6..6d7f8c2a52 100644
--- a/trailer.h
+++ b/trailer.h
@@ -1,6 +1,33 @@
#ifndef TRAILER_H
#define TRAILER_H
+#include "list.h"
+
+enum trailer_where {
+ WHERE_DEFAULT,
+ WHERE_END,
+ WHERE_AFTER,
+ WHERE_BEFORE,
+ WHERE_START
+};
+enum trailer_if_exists {
+ EXISTS_DEFAULT,
+ EXISTS_ADD_IF_DIFFERENT_NEIGHBOR,
+ EXISTS_ADD_IF_DIFFERENT,
+ EXISTS_ADD,
+ EXISTS_REPLACE,
+ EXISTS_DO_NOTHING
+};
+enum trailer_if_missing {
+ MISSING_DEFAULT,
+ MISSING_ADD,
+ MISSING_DO_NOTHING
+};
+
+int trailer_set_where(enum trailer_where *item, const char *value);
+int trailer_set_if_exists(enum trailer_if_exists *item, const char *value);
+int trailer_set_if_missing(enum trailer_if_missing *item, const char *value);
+
struct trailer_info {
/*
* True if there is a blank line before the location pointed to by
@@ -22,11 +49,50 @@ struct trailer_info {
size_t trailer_nr;
};
-void process_trailers(const char *file, int in_place, int trim_empty,
- struct string_list *trailers);
+/*
+ * A list that represents newly-added trailers, such as those provided
+ * with the --trailer command line option of git-interpret-trailers.
+ */
+struct new_trailer_item {
+ struct list_head list;
+
+ const char *text;
+
+ enum trailer_where where;
+ enum trailer_if_exists if_exists;
+ enum trailer_if_missing if_missing;
+};
+
+struct process_trailer_options {
+ int in_place;
+ int trim_empty;
+ int only_trailers;
+ int only_input;
+ int unfold;
+};
+
+#define PROCESS_TRAILER_OPTIONS_INIT {0}
+
+void process_trailers(const char *file,
+ const struct process_trailer_options *opts,
+ struct list_head *new_trailer_head);
void trailer_info_get(struct trailer_info *info, const char *str);
void trailer_info_release(struct trailer_info *info);
+/*
+ * Format the trailers from the commit msg "msg" into the strbuf "out".
+ * Note two caveats about "opts":
+ *
+ * - this is primarily a helper for pretty.c, and not
+ * all of the flags are supported.
+ *
+ * - this differs from process_trailers slightly in that we always format
+ * only the trailer block itself, even if the "only_trailers" option is not
+ * set.
+ */
+void format_trailers_from_commit(struct strbuf *out, const char *msg,
+ const struct process_trailer_options *opts);
+
#endif /* TRAILER_H */
diff --git a/transport-helper.c b/transport-helper.c
index 33cff38cc0..8f68d69a86 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -927,7 +927,7 @@ static int push_refs_with_export(struct transport *transport,
struct object_id oid;
private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
- if (private && !get_sha1(private, oid.hash)) {
+ if (private && !get_oid(private, &oid)) {
strbuf_addf(&buf, "^%s", private);
string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
oidcpy(&ref->old_oid, &oid);
diff --git a/tree-diff.c b/tree-diff.c
index bd6d65a409..4bb93155bc 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -421,8 +421,8 @@ static struct combine_diff_path *ll_diff_tree_paths(
* diff_tree_oid(parent, commit) )
*/
for (i = 0; i < nparent; ++i)
- tptree[i] = fill_tree_descriptor(&tp[i], parents_oid[i]->hash);
- ttree = fill_tree_descriptor(&t, oid->hash);
+ tptree[i] = fill_tree_descriptor(&tp[i], parents_oid[i]);
+ ttree = fill_tree_descriptor(&t, oid);
/* Enable recursion indefinitely */
opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
diff --git a/tree-walk.c b/tree-walk.c
index 6a42e402b0..c99309069a 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -78,15 +78,16 @@ int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned l
return result;
}
-void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
+void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid)
{
unsigned long size = 0;
void *buf = NULL;
- if (sha1) {
- buf = read_object_with_reference(sha1, tree_type, &size, NULL);
+ if (oid) {
+ buf = read_object_with_reference(oid->hash, tree_type, &size,
+ NULL);
if (!buf)
- die("unable to read tree %s", sha1_to_hex(sha1));
+ die("unable to read tree %s", oid_to_hex(oid));
}
init_tree_desc(desc, buf, size);
return buf;
diff --git a/tree-walk.h b/tree-walk.h
index 68bb78b928..b6bd1b4ccf 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -42,7 +42,7 @@ int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long
int tree_entry(struct tree_desc *, struct name_entry *);
int tree_entry_gently(struct tree_desc *, struct name_entry *);
-void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
+void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid);
struct traverse_info;
typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
diff --git a/unpack-trees.c b/unpack-trees.c
index dd535bc849..68d34259c6 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,5 +1,6 @@
#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "dir.h"
#include "tree.h"
@@ -255,47 +256,41 @@ static int check_submodule_move_head(const struct cache_entry *ce,
{
unsigned flags = SUBMODULE_MOVE_HEAD_DRY_RUN;
const struct submodule *sub = submodule_from_ce(ce);
+
if (!sub)
return 0;
if (o->reset)
flags |= SUBMODULE_MOVE_HEAD_FORCE;
- switch (sub->update_strategy.type) {
- case SM_UPDATE_UNSPECIFIED:
- case SM_UPDATE_CHECKOUT:
- if (submodule_move_head(ce->name, old_id, new_id, flags))
- return o->gently ? -1 :
- add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
- return 0;
- case SM_UPDATE_NONE:
- return 0;
- case SM_UPDATE_REBASE:
- case SM_UPDATE_MERGE:
- case SM_UPDATE_COMMAND:
- default:
- warning(_("submodule update strategy not supported for submodule '%s'"), ce->name);
- return -1;
- }
+ if (submodule_move_head(ce->name, old_id, new_id, flags))
+ return o->gently ? -1 :
+ add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
+ return 0;
}
-static void reload_gitmodules_file(struct index_state *index,
- struct checkout *state)
+/*
+ * Preform the loading of the repository's gitmodules file. This function is
+ * used by 'check_update()' to perform loading of the gitmodules file in two
+ * differnt situations:
+ * (1) before removing entries from the working tree if the gitmodules file has
+ * been marked for removal. This situation is specified by 'state' == NULL.
+ * (2) before checking out entries to the working tree if the gitmodules file
+ * has been marked for update. This situation is specified by 'state' != NULL.
+ */
+static void load_gitmodules_file(struct index_state *index,
+ struct checkout *state)
{
- int i;
- for (i = 0; i < index->cache_nr; i++) {
- struct cache_entry *ce = index->cache[i];
- if (ce->ce_flags & CE_UPDATE) {
- int r = strcmp(ce->name, ".gitmodules");
- if (r < 0)
- continue;
- else if (r == 0) {
- submodule_free();
- checkout_entry(ce, state, NULL);
- gitmodules_config();
- git_config(submodule_config, NULL);
- } else
- break;
+ int pos = index_name_pos(index, GITMODULES_FILE, strlen(GITMODULES_FILE));
+
+ if (pos >= 0) {
+ struct cache_entry *ce = index->cache[pos];
+ if (!state && ce->ce_flags & CE_WT_REMOVE) {
+ repo_read_gitmodules(the_repository);
+ } else if (state && (ce->ce_flags & CE_UPDATE)) {
+ submodule_free();
+ checkout_entry(ce, state, NULL);
+ repo_read_gitmodules(the_repository);
}
}
}
@@ -308,19 +303,9 @@ static void unlink_entry(const struct cache_entry *ce)
{
const struct submodule *sub = submodule_from_ce(ce);
if (sub) {
- switch (sub->update_strategy.type) {
- case SM_UPDATE_UNSPECIFIED:
- case SM_UPDATE_CHECKOUT:
- case SM_UPDATE_REBASE:
- case SM_UPDATE_MERGE:
- /* state.force is set at the caller. */
- submodule_move_head(ce->name, "HEAD", NULL,
- SUBMODULE_MOVE_HEAD_FORCE);
- break;
- case SM_UPDATE_NONE:
- case SM_UPDATE_COMMAND:
- return; /* Do not touch the submodule. */
- }
+ /* state.force is set at the caller. */
+ submodule_move_head(ce->name, "HEAD", NULL,
+ SUBMODULE_MOVE_HEAD_FORCE);
}
if (!check_leading_path(ce->name, ce_namelen(ce)))
return;
@@ -343,8 +328,7 @@ static struct progress *get_progress(struct unpack_trees_options *o)
total++;
}
- return start_progress_delay(_("Checking out files"),
- total, 50, 1);
+ return start_delayed_progress(_("Checking out files"), total);
}
static int check_updates(struct unpack_trees_options *o)
@@ -365,6 +349,10 @@ static int check_updates(struct unpack_trees_options *o)
if (o->update)
git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
+
+ if (should_update_submodules() && o->update && !o->dry_run)
+ load_gitmodules_file(index, NULL);
+
for (i = 0; i < index->cache_nr; i++) {
const struct cache_entry *ce = index->cache[i];
@@ -378,8 +366,9 @@ static int check_updates(struct unpack_trees_options *o)
remove_scheduled_dirs();
if (should_update_submodules() && o->update && !o->dry_run)
- reload_gitmodules_file(index, &state);
+ load_gitmodules_file(index, &state);
+ enable_delayed_checkout(&state);
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
@@ -394,6 +383,7 @@ static int check_updates(struct unpack_trees_options *o)
}
}
}
+ errs |= finish_delayed_checkout(&state);
stop_progress(&progress);
if (o->update)
git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
@@ -660,10 +650,10 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
else if (i > 1 && are_same_oid(&names[i], &names[i - 2]))
t[i] = t[i - 2];
else {
- const unsigned char *sha1 = NULL;
+ const struct object_id *oid = NULL;
if (dirmask & 1)
- sha1 = names[i].oid->hash;
- buf[nr_buf++] = fill_tree_descriptor(t+i, sha1);
+ oid = names[i].oid;
+ buf[nr_buf++] = fill_tree_descriptor(t + i, oid);
}
}
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 5a89db30e3..3fd047a8b8 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -6,7 +6,6 @@
#include "cache.h"
#include "quote.h"
#include "fast_export.h"
-#include "repo_tree.h"
#include "strbuf.h"
#include "svndiff.h"
#include "sliding_window.h"
@@ -210,7 +209,7 @@ static long apply_delta(off_t len, struct line_buffer *input,
die("invalid cat-blob response: %s", response);
check_preimage_overflow(preimage.max_off, 1);
}
- if (old_mode == REPO_MODE_LNK) {
+ if (old_mode == S_IFLNK) {
strbuf_addstr(&preimage.buf, "link ");
check_preimage_overflow(preimage.max_off, strlen("link "));
preimage.max_off += strlen("link ");
@@ -244,7 +243,7 @@ void fast_export_buf_to_data(const struct strbuf *data)
void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input)
{
assert(len >= 0);
- if (mode == REPO_MODE_LNK) {
+ if (mode == S_IFLNK) {
/* svn symlink blobs start with "link " */
if (len < 5)
die("invalid dump: symlink too short for \"link\" prefix");
@@ -312,6 +311,40 @@ int fast_export_ls(const char *path, uint32_t *mode, struct strbuf *dataref)
return parse_ls_response(get_response_line(), mode, dataref);
}
+const char *fast_export_read_path(const char *path, uint32_t *mode_out)
+{
+ int err;
+ static struct strbuf buf = STRBUF_INIT;
+
+ strbuf_reset(&buf);
+ err = fast_export_ls(path, mode_out, &buf);
+ if (err) {
+ if (errno != ENOENT)
+ die_errno("BUG: unexpected fast_export_ls error");
+ /* Treat missing paths as directories. */
+ *mode_out = S_IFDIR;
+ return NULL;
+ }
+ return buf.buf;
+}
+
+void fast_export_copy(uint32_t revision, const char *src, const char *dst)
+{
+ int err;
+ uint32_t mode;
+ static struct strbuf data = STRBUF_INIT;
+
+ strbuf_reset(&data);
+ err = fast_export_ls_rev(revision, src, &mode, &data);
+ if (err) {
+ if (errno != ENOENT)
+ die_errno("BUG: unexpected fast_export_ls_rev error");
+ fast_export_delete(dst);
+ return;
+ }
+ fast_export_modify(dst, mode, data.buf);
+}
+
void fast_export_blob_delta(uint32_t mode,
uint32_t old_mode, const char *old_data,
off_t len, struct line_buffer *input)
@@ -320,7 +353,7 @@ void fast_export_blob_delta(uint32_t mode,
assert(len >= 0);
postimage_len = apply_delta(len, input, old_data, old_mode);
- if (mode == REPO_MODE_LNK) {
+ if (mode == S_IFLNK) {
buffer_skip_bytes(&postimage, strlen("link "));
postimage_len -= strlen("link ");
}
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index b9a3b71c99..60b79c35b9 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -28,4 +28,7 @@ int fast_export_ls_rev(uint32_t rev, const char *path,
int fast_export_ls(const char *path,
uint32_t *mode_out, struct strbuf *dataref_out);
+void fast_export_copy(uint32_t revision, const char *src, const char *dst);
+const char *fast_export_read_path(const char *path, uint32_t *mode_out);
+
#endif
diff --git a/vcs-svn/repo_tree.c b/vcs-svn/repo_tree.c
deleted file mode 100644
index 67d27f0b6c..0000000000
--- a/vcs-svn/repo_tree.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed under a two-clause BSD-style license.
- * See LICENSE for details.
- */
-
-#include "git-compat-util.h"
-#include "strbuf.h"
-#include "repo_tree.h"
-#include "fast_export.h"
-
-const char *repo_read_path(const char *path, uint32_t *mode_out)
-{
- int err;
- static struct strbuf buf = STRBUF_INIT;
-
- strbuf_reset(&buf);
- err = fast_export_ls(path, mode_out, &buf);
- if (err) {
- if (errno != ENOENT)
- die_errno("BUG: unexpected fast_export_ls error");
- /* Treat missing paths as directories. */
- *mode_out = REPO_MODE_DIR;
- return NULL;
- }
- return buf.buf;
-}
-
-void repo_copy(uint32_t revision, const char *src, const char *dst)
-{
- int err;
- uint32_t mode;
- static struct strbuf data = STRBUF_INIT;
-
- strbuf_reset(&data);
- err = fast_export_ls_rev(revision, src, &mode, &data);
- if (err) {
- if (errno != ENOENT)
- die_errno("BUG: unexpected fast_export_ls_rev error");
- fast_export_delete(dst);
- return;
- }
- fast_export_modify(dst, mode, data.buf);
-}
-
-void repo_delete(const char *path)
-{
- fast_export_delete(path);
-}
diff --git a/vcs-svn/repo_tree.h b/vcs-svn/repo_tree.h
deleted file mode 100644
index 889c6a3c95..0000000000
--- a/vcs-svn/repo_tree.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef REPO_TREE_H_
-#define REPO_TREE_H_
-
-struct strbuf;
-
-#define REPO_MODE_DIR 0040000
-#define REPO_MODE_BLB 0100644
-#define REPO_MODE_EXE 0100755
-#define REPO_MODE_LNK 0120000
-
-uint32_t next_blob_mark(void);
-void repo_copy(uint32_t revision, const char *src, const char *dst);
-void repo_add(const char *path, uint32_t mode, uint32_t blob_mark);
-const char *repo_read_path(const char *path, uint32_t *mode_out);
-void repo_delete(const char *path);
-void repo_commit(uint32_t revision, const char *author,
- const struct strbuf *log, const char *uuid, const char *url,
- long unsigned timestamp);
-void repo_diff(uint32_t r1, uint32_t r2);
-void repo_init(void);
-void repo_reset(void);
-
-#endif
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index 1846685a21..ec6b350611 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -8,7 +8,6 @@
*/
#include "cache.h"
-#include "repo_tree.h"
#include "fast_export.h"
#include "line_buffer.h"
#include "strbuf.h"
@@ -134,13 +133,13 @@ static void handle_property(const struct strbuf *key_buf,
die("invalid dump: sets type twice");
}
if (!val) {
- node_ctx.type = REPO_MODE_BLB;
+ node_ctx.type = S_IFREG | 0644;
return;
}
*type_set = 1;
node_ctx.type = keylen == strlen("svn:executable") ?
- REPO_MODE_EXE :
- REPO_MODE_LNK;
+ (S_IFREG | 0755) :
+ S_IFLNK;
}
}
@@ -219,45 +218,45 @@ static void handle_node(void)
*/
static const char *const empty_blob = "::empty::";
const char *old_data = NULL;
- uint32_t old_mode = REPO_MODE_BLB;
+ uint32_t old_mode = S_IFREG | 0644;
if (node_ctx.action == NODEACT_DELETE) {
if (have_text || have_props || node_ctx.srcRev)
die("invalid dump: deletion node has "
"copyfrom info, text, or properties");
- repo_delete(node_ctx.dst.buf);
+ fast_export_delete(node_ctx.dst.buf);
return;
}
if (node_ctx.action == NODEACT_REPLACE) {
- repo_delete(node_ctx.dst.buf);
+ fast_export_delete(node_ctx.dst.buf);
node_ctx.action = NODEACT_ADD;
}
if (node_ctx.srcRev) {
- repo_copy(node_ctx.srcRev, node_ctx.src.buf, node_ctx.dst.buf);
+ fast_export_copy(node_ctx.srcRev, node_ctx.src.buf, node_ctx.dst.buf);
if (node_ctx.action == NODEACT_ADD)
node_ctx.action = NODEACT_CHANGE;
}
- if (have_text && type == REPO_MODE_DIR)
+ if (have_text && type == S_IFDIR)
die("invalid dump: directories cannot have text attached");
/*
* Find old content (old_data) and decide on the new mode.
*/
if (node_ctx.action == NODEACT_CHANGE && !*node_ctx.dst.buf) {
- if (type != REPO_MODE_DIR)
+ if (type != S_IFDIR)
die("invalid dump: root of tree is not a regular file");
old_data = NULL;
} else if (node_ctx.action == NODEACT_CHANGE) {
uint32_t mode;
- old_data = repo_read_path(node_ctx.dst.buf, &mode);
- if (mode == REPO_MODE_DIR && type != REPO_MODE_DIR)
+ old_data = fast_export_read_path(node_ctx.dst.buf, &mode);
+ if (mode == S_IFDIR && type != S_IFDIR)
die("invalid dump: cannot modify a directory into a file");
- if (mode != REPO_MODE_DIR && type == REPO_MODE_DIR)
+ if (mode != S_IFDIR && type == S_IFDIR)
die("invalid dump: cannot modify a file into a directory");
node_ctx.type = mode;
old_mode = mode;
} else if (node_ctx.action == NODEACT_ADD) {
- if (type == REPO_MODE_DIR)
+ if (type == S_IFDIR)
old_data = NULL;
else if (have_text)
old_data = empty_blob;
@@ -280,7 +279,7 @@ static void handle_node(void)
/*
* Save the result.
*/
- if (type == REPO_MODE_DIR) /* directories are not tracked. */
+ if (type == S_IFDIR) /* directories are not tracked. */
return;
assert(old_data);
if (old_data == empty_blob)
@@ -385,9 +384,9 @@ void svndump_read(const char *url, const char *local_ref, const char *notes_ref)
continue;
strbuf_addf(&rev_ctx.note, "%s\n", t);
if (!strcmp(val, "dir"))
- node_ctx.type = REPO_MODE_DIR;
+ node_ctx.type = S_IFDIR;
else if (!strcmp(val, "file"))
- node_ctx.type = REPO_MODE_BLB;
+ node_ctx.type = S_IFREG | 0644;
else
fprintf(stderr, "Unknown node-kind: %s\n", val);
break;