Sync with 'maint'
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index a241a63..d0a78fc 100644
--- a/.github/workflows/check-whitespace.yml
+++ b/.github/workflows/check-whitespace.yml
@@ -26,66 +26,7 @@
- name: git log --check
id: check_out
run: |
- baseSha=${{github.event.pull_request.base.sha}}
- problems=()
- commit=
- commitText=
- commitTextmd=
- goodparent=
- while read dash sha etc
- do
- case "${dash}" in
- "---")
- if test -z "${commit}"
- then
- goodparent=${sha}
- fi
- commit="${sha}"
- commitText="${sha} ${etc}"
- commitTextmd="[${sha}](https://github.com/${{ github.repository }}/commit/${sha}) ${etc}"
- ;;
- "")
- ;;
- *)
- if test -n "${commit}"
- then
- problems+=("1) --- ${commitTextmd}")
- echo ""
- echo "--- ${commitText}"
- commit=
- fi
- case "${dash}" in
- *:[1-9]*:) # contains file and line number information
- dashend=${dash#*:}
- problems+=("[${dash}](https://github.com/${{ github.repository }}/blob/${{github.event.pull_request.head.ref}}/${dash%%:*}#L${dashend%:}) ${sha} ${etc}")
- ;;
- *)
- problems+=("\`${dash} ${sha} ${etc}\`")
- ;;
- esac
- echo "${dash} ${sha} ${etc}"
- ;;
- esac
- done <<< $(git log --check --pretty=format:"---% h% s" ${baseSha}..)
-
- if test ${#problems[*]} -gt 0
- then
- if test -z "${commit}"
- then
- goodparent=${baseSha: 0:7}
- fi
- echo "🛑 Please review the Summary output for further information."
- echo "### :x: A whitespace issue was found in one or more of the commits." >$GITHUB_STEP_SUMMARY
- echo "" >>$GITHUB_STEP_SUMMARY
- echo "Run these commands to correct the problem:" >>$GITHUB_STEP_SUMMARY
- echo "1. \`git rebase --whitespace=fix ${goodparent}\`" >>$GITHUB_STEP_SUMMARY
- echo "1. \`git push --force\`" >>$GITHUB_STEP_SUMMARY
- echo " " >>$GITHUB_STEP_SUMMARY
- echo "Errors:" >>$GITHUB_STEP_SUMMARY
- for i in "${problems[@]}"
- do
- echo "${i}" >>$GITHUB_STEP_SUMMARY
- done
-
- exit 2
- fi
+ ./ci/check-whitespace.sh \
+ "${{github.event.pull_request.base.sha}}" \
+ "$GITHUB_STEP_SUMMARY" \
+ "https://github.com/${{github.repository}}"
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index 53cf12f..48341e8 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -45,7 +45,7 @@
- run: ci/install-dependencies.sh
if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos')
env:
- runs_on_pool: ${{ matrix.os }}
+ distro: ${{ matrix.os }}
# The Coverity site says the tool is usually updated twice yearly, so the
# MD5 of download can be used to determine whether there's been an update.
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 8922bd7..13cc0fe 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -302,7 +302,7 @@
CC: ${{matrix.vector.cc}}
CC_PACKAGE: ${{matrix.vector.cc_package}}
jobname: ${{matrix.vector.jobname}}
- runs_on_pool: ${{matrix.vector.pool}}
+ distro: ${{matrix.vector.pool}}
runs-on: ${{matrix.vector.pool}}
steps:
- uses: actions/checkout@v4
@@ -341,12 +341,16 @@
vector:
- jobname: linux-musl
image: alpine
+ distro: alpine-latest
- jobname: linux32
image: daald/ubuntu32:xenial
+ distro: ubuntu32-16.04
- jobname: pedantic
image: fedora
+ distro: fedora-latest
env:
jobname: ${{matrix.vector.jobname}}
+ distro: ${{matrix.vector.distro}}
runs-on: ubuntu-latest
container: ${{matrix.vector.image}}
steps:
@@ -354,7 +358,7 @@
if: matrix.vector.jobname != 'linux32'
- uses: actions/checkout@v1 # cannot be upgraded because Node.js Actions aren't supported in this container
if: matrix.vector.jobname == 'linux32'
- - run: ci/install-docker-dependencies.sh
+ - run: ci/install-dependencies.sh
- run: ci/run-build-and-tests.sh
- name: print test failures
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
diff --git a/.gitignore b/.gitignore
index 612c0f6..8caf370 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,6 +126,7 @@
/git-rebase
/git-receive-pack
/git-reflog
+/git-refs
/git-remote
/git-remote-http
/git-remote-https
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c0fa2fe..37b991e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,8 +9,10 @@
test:linux:
image: $image
+ variables:
+ CUSTOM_PATH: "/custom"
before_script:
- - ./ci/install-docker-dependencies.sh
+ - ./ci/install-dependencies.sh
script:
- useradd builder --create-home
- chown -R builder "${CI_PROJECT_DIR}"
@@ -93,12 +95,39 @@
- t/failed-test-artifacts
when: on_failure
+test:fuzz-smoke-tests:
+ image: ubuntu:latest
+ variables:
+ CC: clang
+ before_script:
+ - ./ci/install-dependencies.sh
+ script:
+ - ./ci/run-build-and-minimal-fuzzers.sh
+
static-analysis:
image: ubuntu:22.04
variables:
jobname: StaticAnalysis
before_script:
- - ./ci/install-docker-dependencies.sh
+ - ./ci/install-dependencies.sh
script:
- ./ci/run-static-analysis.sh
- ./ci/check-directional-formatting.bash
+
+check-whitespace:
+ image: ubuntu:latest
+ before_script:
+ - ./ci/install-dependencies.sh
+ script:
+ - ./ci/check-whitespace.sh "$CI_MERGE_REQUEST_TARGET_BRANCH_SHA"
+ rules:
+ - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+
+documentation:
+ image: ubuntu:latest
+ variables:
+ jobname: Documentation
+ before_script:
+ - ./ci/install-dependencies.sh
+ script:
+ - ./ci/test-documentation.sh
diff --git a/Documentation/BreakingChanges.txt b/Documentation/BreakingChanges.txt
new file mode 100644
index 0000000..0532bfc
--- /dev/null
+++ b/Documentation/BreakingChanges.txt
@@ -0,0 +1,135 @@
+= Upcoming breaking changes
+
+The Git project aims to ensure backwards compatibility to the best extent
+possible. Minor releases will not break backwards compatibility unless there is
+a very strong reason to do so, like for example a security vulnerability.
+
+Regardless of that, due to the age of the Git project, it is only natural to
+accumulate a backlog of backwards-incompatible changes that will eventually be
+required to keep the project aligned with a changing world. These changes fall
+into several categories:
+
+* Changes to long established defaults.
+* Concepts that have been replaced with a superior design.
+* Concepts, commands, configuration or options that have been lacking in major
+ ways and that cannot be fixed and which will thus be removed without any
+ replacement.
+
+Explicitly not included in this list are fixes to minor bugs that may cause a
+change in user-visible behavior.
+
+The Git project irregularly releases breaking versions that deliberately break
+backwards compatibility with older versions. This is done to ensure that Git
+remains relevant, safe and maintainable going forward. The release cadence of
+breaking versions is typically measured in multiple years. We had the following
+major breaking releases in the past:
+
+* Git 1.6.0, released in August 2008.
+* Git 2.0, released in May 2014.
+
+We use <major>.<minor> release numbers these days, starting from Git 2.0. For
+future releases, our plan is to increment <major> in the release number when we
+make the next breaking release. Before Git 2.0, the release numbers were
+1.<major>.<minor> with the intention to increment <major> for "usual" breaking
+releases, reserving the jump to Git 2.0 for really large backward-compatibility
+breaking changes.
+
+The intent of this document is to track upcoming deprecations for future
+breaking releases. Furthermore, this document also tracks what will _not_ be
+deprecated. This is done such that the outcome of discussions document both
+when the discussion favors deprecation, but also when it rejects a deprecation.
+
+Items should have a clear summary of the reasons why we do or do not want to
+make the described change that can be easily understood without having to read
+the mailing list discussions. If there are alternatives to the changed feature,
+those alternatives should be pointed out to our users.
+
+All items should be accompanied by references to relevant mailing list threads
+where the deprecation was discussed. These references use message-IDs, which
+can visited via
+
+ https://lore.kernel.org/git/$message_id/
+
+to see the message and its surrounding discussion. Such a reference is there to
+make it easier for you to find how the project reached consensus on the
+described item back then.
+
+This is a living document as the environment surrounding the project changes
+over time. If circumstances change, an earlier decision to deprecate or change
+something may need to be revisited from time to time. So do not take items on
+this list to mean "it is settled, do not waste our time bringing it up again".
+
+== Git 3.0
+
+The following subsections document upcoming breaking changes for Git 3.0. There
+is no planned release date for this breaking version yet.
+
+Proposed changes and removals only include items which are "ready" to be done.
+In other words, this is not supposed to be a wishlist of features that should
+be changed to or replaced in case the alternative was implemented already.
+
+=== Changes
+
+* The default hash function for new repositories will be changed from "sha1"
+ to "sha256". SHA-1 has been deprecated by NIST in 2011 and is nowadays
+ recommended against in FIPS 140-2 and similar certifications. Furthermore,
+ there are practical attacks on SHA-1 that weaken its cryptographic properties:
++
+ ** The SHAppening (2015). The first demonstration of a practical attack
+ against SHA-1 with 2^57 operations.
+ ** SHAttered (2017). Generation of two valid PDF files with 2^63 operations.
+ ** Birthday-Near-Collision (2019). This attack allows for chosen prefix
+ attacks with 2^68 operations.
+ ** Shambles (2020). This attack allows for chosen prefix attacks with 2^63
+ operations.
++
+While we have protections in place against known attacks, it is expected
+that more attacks against SHA-1 will be found by future research. Paired
+with the ever-growing capability of hardware, it is only a matter of time
+before SHA-1 will be considered broken completely. We want to be prepared
+and will thus change the default hash algorithm to "sha256" for newly
+initialized repositories.
++
+An important requirement for this change is that the ecosystem is ready to
+support the "sha256" object format. This includes popular Git libraries,
+applications and forges.
++
+There is no plan to deprecate the "sha1" object format at this point in time.
++
+Cf. <2f5de416-04ba-c23d-1e0b-83bb655829a7@zombino.com>,
+<20170223155046.e7nxivfwqqoprsqj@LykOS.localdomain>,
+<CA+EOSBncr=4a4d8n9xS4FNehyebpmX8JiUwCsXD47EQDE+DiUQ@mail.gmail.com>.
+
+=== Removals
+
+* Support for grafting commits has long been superseded by git-replace(1).
+ Grafts are inferior to replacement refs:
++
+ ** Grafts are a local-only mechanism and cannot be shared across
+ repositories.
+ ** Grafts can lead to hard-to-diagnose problems when transferring objects
+ between repositories.
++
+The grafting mechanism has been marked as outdated since e650d0643b (docs: mark
+info/grafts as outdated, 2014-03-05) and will be removed.
++
+Cf. <20140304174806.GA11561@sigill.intra.peff.net>.
+
+== Superseded features that will not be deprecated
+
+Some features have gained newer replacements that aim to improve the design in
+certain ways. The fact that there is a replacement does not automatically mean
+that the old way of doing things will eventually be removed. This section tracks
+those features with newer alternatives.
+
+* The features git-checkout(1) offers are covered by the pair of commands
+ git-restore(1) and git-switch(1). Because the use of git-checkout(1) is still
+ widespread, and it is not expected that this will change anytime soon, all
+ three commands will stay.
++
+This decision may get revisited in case we ever figure out that there are
+almost no users of any of the commands anymore.
++
+Cf. <xmqqttjazwwa.fsf@gitster.g>,
+<xmqqleeubork.fsf@gitster.g>,
+<112b6568912a6de6672bf5592c3a718e@manjaro.org>.
diff --git a/Documentation/DecisionMaking.txt b/Documentation/DecisionMaking.txt
new file mode 100644
index 0000000..dbb4c1f
--- /dev/null
+++ b/Documentation/DecisionMaking.txt
@@ -0,0 +1,74 @@
+Decision-Making Process in the Git Project
+==========================================
+
+Introduction
+------------
+This document describes the current decision-making process in the Git
+project. It is a descriptive rather than prescriptive doc; that is, we want to
+describe how things work in practice rather than explicitly recommending any
+particular process or changes to the current process.
+
+Here we document how the project makes decisions for discussions
+(with or without patches), in scale larger than an individual patch
+series (which is fully covered by the SubmittingPatches document).
+
+
+Larger Discussions (with patches)
+---------------------------------
+As with discussions on an individual patch series, starting a larger-scale
+discussion often begins by sending a patch or series to the list. This might
+take the form of an initial design doc, with implementation following in later
+iterations of the series (for example,
+link:https://lore.kernel.org/git/0169ce6fb9ccafc089b74ae406db0d1a8ff8ac65.1688165272.git.steadmon@google.com/[adding unit tests] or
+link:https://lore.kernel.org/git/20200420235310.94493-1-emilyshaffer@google.com/[config-based hooks]),
+or it might include a full implementation from the beginning.
+In either case, discussion progresses the same way for an individual patch series,
+until consensus is reached or the topic is dropped.
+
+
+Larger Discussions (without patches)
+------------------------------------
+Occasionally, larger discussions might occur without an associated patch series.
+These may be very large-scale technical decisions that are beyond the scope of
+even a single large patch series, or they may be more open-ended,
+policy-oriented discussions (examples:
+link:https://lore.kernel.org/git/ZZ77NQkSuiRxRDwt@nand.local/[introducing Rust]
+or link:https://lore.kernel.org/git/YHofmWcIAidkvJiD@google.com/[improving submodule UX]).
+In either case, discussion progresses as described above for general patch series.
+
+For larger discussions without a patch series or other concrete implementation,
+it may be hard to judge when consensus has been reached, as there are not any
+official guidelines. If discussion stalls at this point, it may be helpful to
+restart discussion with an RFC patch series (such as a partial, unfinished
+implementation or proof of concept) that can be more easily debated.
+
+When consensus is reached that it is a good idea, the original
+proposer is expected to coordinate the effort to make it happen,
+with help from others who were involved in the discussion, as
+needed.
+
+For decisions that require code changes, it is often the case that the original
+proposer will follow up with a patch series, although it is also common for
+other interested parties to provide an implementation (or parts of the
+implementation, for very large changes).
+
+For non-technical decisions such as community norms or processes, it is up to
+the community as a whole to implement and sustain agreed-upon changes.
+The project leadership committe (PLC) may help the implementation of
+policy decisions.
+
+
+Other Discussion Venues
+-----------------------
+Occasionally decision proposals are presented off-list, e.g. at the semi-regular
+Contributors' Summit. While higher-bandwidth face-to-face discussion is often
+useful for quickly reaching consensus among attendees, generally we expect to
+summarize the discussion in notes that can later be presented on-list. For an
+example, see the thread
+link:https://lore.kernel.org/git/AC2EB721-2979-43FD-922D-C5076A57F24B@jramsay.com.au/[Notes
+from Git Contributor Summit, Los Angeles (April 5, 2020)] by James Ramsay.
+
+We prefer that "official" discussion happens on the list so that the full
+community has opportunity to engage in discussion. This also means that the
+mailing list archives contain a more-or-less complete history of project
+discussions and decisions.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 3f2383a..dc65759 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -51,6 +51,7 @@
MAN7_TXT += giteveryday.txt
MAN7_TXT += gitfaq.txt
MAN7_TXT += gitglossary.txt
+MAN7_TXT += gitpacking.txt
MAN7_TXT += gitnamespaces.txt
MAN7_TXT += gitremote-helpers.txt
MAN7_TXT += gitrevisions.txt
@@ -103,6 +104,7 @@
API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
SP_ARTICLES += $(API_DOCS)
+TECH_DOCS += DecisionMaking
TECH_DOCS += ReviewingGuidelines
TECH_DOCS += MyFirstContribution
TECH_DOCS += MyFirstObjectWalk
@@ -484,12 +486,16 @@
lint-docs-fsck-msgids: $(LINT_DOCS_FSCK_MSGIDS)
+lint-docs-manpages:
+ $(QUIET_GEN)./lint-manpages.sh
+
## Lint: list of targets above
.PHONY: lint-docs
lint-docs: lint-docs-fsck-msgids
lint-docs: lint-docs-gitlink
lint-docs: lint-docs-man-end-blurb
lint-docs: lint-docs-man-section-order
+lint-docs: lint-docs-manpages
ifeq ($(wildcard po/Makefile),po/Makefile)
doc-l10n install-l10n::
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index f06563e..e41654c 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -1116,6 +1116,15 @@
NOTE: Check `git help send-email` for some other options which you may find
valuable, such as changing the Reply-to address or adding more CC and BCC lines.
+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
+not part of the core `git` binary and must be called directly. Clone the Git +
+codebase and run `perl contrib/contacts/git-contacts`.]
+
+NOTE: If you're not sure whom to CC, running `contrib/contacts/git-contacts` can
+list potential reviewers. In addition, you can do `git send-email
+--cc-cmd='perl contrib/contacts/git-contacts' feature/*.patch`{contrib-scripts} to
+automatically pass this list of emails to `send-email`.
+
NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
please don't send your patchset from the tutorial to the real mailing list! For
now, you can send it to yourself, to make sure you understand how it will look.
diff --git a/Documentation/RelNotes/2.46.0.txt b/Documentation/RelNotes/2.46.0.txt
new file mode 100644
index 0000000..67bae07
--- /dev/null
+++ b/Documentation/RelNotes/2.46.0.txt
@@ -0,0 +1,352 @@
+Git v2.46 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+ (None at this moment)
+
+UI, Workflows & Features
+
+ * The "--rfc" option of "git format-patch" learned to take an
+ optional string value to be used in place of "RFC" to tweak the
+ "[PATCH]" on the subject header.
+
+ * The credential helper protocol, together with the HTTP layer, have
+ been enhanced to support authentication schemes different from
+ username & password pair, like Bearer and NTLM.
+
+ * Command line completion script (in contrib/) learned to complete
+ "git symbolic-ref" a bit better (you need to enable plumbing
+ commands to be completed with GIT_COMPLETION_SHOW_ALL_COMMANDS).
+
+ * When the user responds to a prompt given by "git add -p" with an
+ unsupported command, list of available commands were given, which
+ was too much if the user knew what they wanted to type but merely
+ made a typo. Now the user gets a much shorter error message.
+
+ * The color parsing code learned to handle 12-bit RGB colors, spelled
+ as "#RGB" (in addition to "#RRGGBB" that is already supported).
+
+ * The operation mode options (like "--get") the "git config" command
+ uses have been deprecated and replaced with subcommands (like "git
+ config get").
+
+ * "git tag" learned the "--trailer" option to futz with the trailers
+ in the same way as "git commit" does.
+
+ * A new global "--no-advice" option can be used to disable all advice
+ messages, which is meant to be used only in scripts.
+
+ * Updates to symbolic refs can now be made as a part of ref
+ transaction.
+
+ * The trailer API has been reshuffled a bit.
+
+ * Terminology to call various ref-like things are getting
+ straightened out.
+
+ * The command line completion script (in contrib/) has been adjusted
+ to the recent update to "git config" that adopted subcommand based
+ UI.
+
+ * The knobs to tweak how reftable files are written have been made
+ available as configuration variables.
+
+ * When "git push" notices that the commit at the tip of the ref on
+ the other side it is about to overwrite does not exist locally, it
+ used to first try fetching it if the local repository is a partial
+ clone. The command has been taught not to do so and immediately
+ fail instead.
+
+ * The promisor.quiet configuration knob can be set to true to make
+ lazy fetching from promisor remotes silent.
+
+ * The inter/range-diff output has been moved to the end of the patch
+ when format-patch adds it to a single patch, instead of writing it
+ before the patch text, to be consistent with what is done for a
+ cover letter for a multi-patch series.
+
+ * A new command has been added to migrate a repository that uses the
+ files backend for its ref storage to use the reftable backend, with
+ limitations.
+
+ * "git diff --exit-code --ext-diff" learned to take the exit status
+ of the external diff driver into account when deciding the exit
+ status of the overall "git diff" invocation when configured to do
+ so.
+
+ * "git update-ref --stdin" learned to handle transactional updates of
+ symbolic-refs.
+
+ * "git format-patch --interdiff" for multi-patch series learned to
+ turn on cover letters automatically (unless told never to enable
+ cover letter with "--no-cover-letter" and such).
+
+ * The "--heads" option of "ls-remote" and "show-ref" has been been
+ deprecated; "--branches" replaces "--heads".
+
+ * For over a year, setting add.interactive.useBuiltin configuration
+ variable did nothing but giving a "this does not do anything"
+ warning. The warning has been removed.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Advertise "git contacts", a tool for newcomers to find people to
+ ask review for their patches, a bit more in our developer
+ documentation.
+
+ * In addition to building the objects needed, try to link the objects
+ that are used in fuzzer tests, to make sure at least they build
+ without bitrot, in Linux CI runs.
+
+ * Code to write out reftable has seen some optimization and
+ simplification.
+
+ * Tests to ensure interoperability between reftable written by jgit
+ and our code have been added and enabled in CI.
+
+ * The singleton index_state instance "the_index" has been eliminated
+ by always instantiating "the_repository" and replacing references
+ to "the_index" with references to its .index member.
+
+ * Git-GUI has a new maintainer, Johannes Sixt.
+
+ * The "test-tool" has been taught to run testsuite tests in parallel,
+ bypassing the need to use the "prove" tool.
+
+ * The "whitespace check" task that was enabled for GitHub Actions CI
+ has been ported to GitLab CI.
+
+ * The refs API lost functions that implicitly assumes to work on the
+ primary ref_store by forcing the callers to pass a ref_store as an
+ argument.
+
+ * Code clean-up to reduce inter-function communication inside
+ builtin/config.c done via the use of global variables.
+
+ * The pack bitmap code saw some clean-up to prepare for a follow-up topic.
+
+ * Preliminary code clean-up for "git send-email".
+
+ * The default "creation-factor" used by "git format-patch" has been
+ raised to make it more aggressively find matching commits.
+
+ * Before discovering the repository details, We used to assume SHA-1
+ as the "default" hash function, which has been corrected. Hopefully
+ this will smoke out codepaths that rely on such an unwarranted
+ assumptions.
+
+ * The project decision making policy has been documented.
+
+ * The strcmp-offset tests have been rewritten using the unit test
+ framework.
+
+ * "git add -p" learned to complain when an answer with more than one
+ letter is given to a prompt that expects a single letter answer.
+
+ * The alias-expanded command lines are logged to the trace output.
+
+ * A new test was added to ensure git commands that are designed to
+ run outside repositories do work.
+
+ * Basic unit tests for reftable have been reimplemented under the
+ unit test framework.
+
+ * A pair of test helpers that essentially are unit tests on hash
+ algorithms have been rewritten using the unit-tests framework.
+
+ * A test helper that essentially is unit tests on the "decorate"
+ logic has been rewritten using the unit-tests framework.
+
+ * Many memory leaks in the sparse-checkout code paths have been
+ plugged.
+
+ * "make check-docs" noticed problems and reported to its output but
+ failed to signal its findings with its exit status, which has been
+ corrected.
+
+ * Building with "-Werror -Wwrite-strings" is now supported.
+
+ * To help developers, the build procedure now allows builders to use
+ CFLAGS_APPEND to specify additional CFLAGS.
+
+ * "oidtree" tests were rewritten to use the unit test framework.
+
+ * The structure of the document that records longer-term project
+ decisions to deprecate/remove/update various behaviour has been
+ outlined.
+
+ * The pseudo-merge reachability bitmap to help more efficient storage
+ of the reachability bitmap in a repository with too many refs has
+ been added.
+
+ * When "git merge" sees that the index cannot be refreshed (e.g. due
+ to another process doing the same in the background), it died but
+ after writing MERGE_HEAD etc. files, which was useless for the
+ purpose to recover from the failure.
+
+ * The output from "git cat-file --batch-check" and "--batch-command
+ (info)" should not be unbuffered, for which some tests have been
+ added.
+
+ * A CPP macro USE_THE_REPOSITORY_VARIABLE is introduced to help
+ transition the codebase to rely less on the availability of the
+ singleton the_repository instance.
+
+ * "git version --build-options" reports the version information of
+ OpenSSL and other libraries (if used) in the build.
+
+ * Memory ownership rules for the in-core representation of
+ remote.*.url configuration values have been straightened out, which
+ resulted in a few leak fixes and code clarification.
+
+
+Fixes since v2.45
+-----------------
+
+ * "git rebase --signoff" used to forget that it needs to add a
+ sign-off to the resulting commit when told to continue after a
+ conflict stops its operation.
+
+ * The procedure to build multi-pack-index got confused by the
+ replace-refs mechanism, which has been corrected by disabling the
+ latter.
+
+ * The "-k" and "--rfc" options of "format-patch" will now error out
+ when used together, as one tells us not to add anything to the
+ title of the commit, and the other one tells us to add "RFC" in
+ addition to "PATCH".
+
+ * "git stash -S" did not handle binary files correctly, which has
+ been corrected.
+
+ * A scheduled "git maintenance" job is expected to work on all
+ repositories it knows about, but it stopped at the first one that
+ errored out. Now it keeps going.
+
+ * zsh can pretend to be a normal shell pretty well except for some
+ glitches that we tickle in some of our scripts. Work them around
+ so that "vimdiff" and our test suite works well enough with it.
+
+ * Command line completion support for zsh (in contrib/) has been
+ updated to stop exposing internal state to end-user shell
+ interaction.
+
+ * Tests that try to corrupt in-repository files in chunked format did
+ not work well on macOS due to its broken "mv", which has been
+ worked around.
+
+ * The maximum size of attribute files is enforced more consistently.
+
+ * Unbreak CI jobs so that we do not attempt to use Python 2 that has
+ been removed from the platform.
+
+ * Git 2.43 started using the tree of HEAD as the source of attributes
+ in a bare repository, which has severe performance implications.
+ For now, revert the change, without ripping out a more explicit
+ support for the attr.tree configuration variable.
+
+ * The "--exit-code" option of "git diff" command learned to work with
+ the "--ext-diff" option.
+
+ * Windows CI running in GitHub Actions started complaining about the
+ order of arguments given to calloc(); the imported regex code uses
+ the wrong order almost consistently, which has been corrected.
+
+ * Expose "name conflict" error when a ref creation fails due to D/F
+ conflict in the ref namespace, to improve an error message given by
+ "git fetch".
+ (merge 9339fca23e it/refs-name-conflict later to maint).
+
+ * The SubmittingPatches document now refers folks to manpages
+ translation project.
+
+ * The documentation for "git diff --name-only" has been clarified
+ that it is about showing the names in the post-image tree.
+
+ * The credential helper that talks with osx keychain learned to avoid
+ storing back the authentication material it just got received from
+ the keychain.
+ (merge e1ab45b2da kn/osxkeychain-skip-idempotent-store later to maint).
+
+ * The chainlint script (invoked during "make test") did nothing when
+ it failed to detect the number of available CPUs. It now falls
+ back to 1 CPU to avoid the problem.
+
+ * Revert overly aggressive "layered defence" that went into 2.45.1
+ and friends, which broke "git-lfs", "git-annex", and other use
+ cases, so that we can rebuild necessary counterparts in the open.
+
+ * "git init" in an already created directory, when the user
+ configuration has includeif.onbranch, started to fail recently,
+ which has been corrected.
+
+ * Memory leaks in "git mv" has been plugged.
+
+ * The safe.directory configuration knob has been updated to
+ optionally allow leading path matches.
+
+ * An overly large ".gitignore" files are now rejected silently.
+
+ * Upon expiration event, the credential subsystem forgot to clear
+ in-core authentication material other than password (whose support
+ was added recently), which has been corrected.
+
+ * Fix for an embarrassing typo that prevented Python2 tests from running
+ anywhere.
+
+ * Varargs functions that are unannotated as printf-like or execl-like
+ have been annotated as such.
+
+ * "git am" has a safety feature to prevent it from starting a new
+ session when there already is a session going. It reliably
+ triggers when a mbox is given on the command line, but it has to
+ rely on the tty-ness of the standard input. Add an explicit way to
+ opt out of this safety with a command line option.
+ (merge 62c71ace44 jk/am-retry later to maint).
+
+ * A leak in "git imap-send" that somehow escapes LSan has been
+ plugged.
+
+ * Setting core.abbrev too early before the repository set-up
+ (typically in "git clone") caused segfault, which as been
+ corrected.
+
+ * When the user adds to "git rebase -i" instruction to "pick" a merge
+ commit, the error experience is not pleasant. Such an error is now
+ caught earlier in the process that parses the todo list.
+
+ * We forgot to normalize the result of getcwd() to NFC on macOS where
+ all other paths are normalized, which has been corrected. This still
+ does not address the case where core.precomposeUnicode configuration
+ is not defined globally.
+
+ * Earlier we stopped using the tree of HEAD as the default source of
+ attributes in a bare repository, but failed to document it. This
+ has been corrected.
+
+ * "git update-server-info" and "git commit-graph --write" have been
+ updated to use the tempfile API to avoid leaving cruft after
+ failing.
+
+ * An unused extern declaration for mingw has been removed to prevent
+ it from causing build failure.
+
+ * A helper function shared between two tests had a copy-paste bug,
+ which has been corrected.
+
+ * "git fetch-pack -k -k" without passing "--lock-pack" (which we
+ never do ourselves) did not work at all, which has been corrected.
+
+ * CI job to build minimum fuzzers learned to pass NO_CURL=NoThanks to
+ the build procedure, as its build environment does not offer, or
+ the rest of the build needs, anything cURL.
+ (merge 4e66b5a990 jc/fuzz-sans-curl later to maint).
+
+ * "git diff --no-ext-diff" when diff.external is configured ignored
+ the "--color-moved" option.
+ (merge 0f4b0d4cf0 rs/diff-color-moved-w-no-ext-diff-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 493fdae046 ew/object-convert-leakfix later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 625bdd0..d8a8caa 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -7,6 +7,73 @@
project. There is also a link:MyFirstContribution.html[step-by-step tutorial]
available which covers many of these same guidelines.
+[[patch-flow]]
+=== A typical life cycle of a patch series
+
+To help us understand the reason behind various guidelines given later
+in the document, first let's understand how the life cycle of a
+typical patch series for this project goes.
+
+. You come up with an itch. You code it up. You do not need any
+ pre-authorization from the project to do so.
++
+Your patches will be reviewed by other contributors on the mailing
+list, and the reviews will be done to assess the merit of various
+things, like the general idea behind your patch (including "is it
+solving a problem worth solving in the first place?"), the reason
+behind the design of the solution, and the actual implementation.
+The guidelines given here are there to help your patches by making
+them easier to understand by the reviewers.
+
+. You send the patches to the list and cc people who may need to know
+ about the change. Your goal is *not* necessarily to convince others
+ that what you are building is good. Your goal is to get help in
+ coming up with a solution for the "itch" that is better than what
+ you can build alone.
++
+The people who may need to know are the ones who worked on the code
+you are touching. These people happen to be the ones who are
+most likely to be knowledgeable enough to help you, but
+they have no obligation to help you (i.e. you ask them for help,
+you don't demand). +git log -p {litdd} _$area_you_are_modifying_+ would
+help you find out who they are.
+
+. You get comments and suggestions for improvements. You may even get
+ them in an "on top of your change" patch form. You are expected to
+ respond to them with "Reply-All" on the mailing list, while taking
+ them into account while preparing an updated set of patches.
+
+. Polish, refine, and re-send your patches to the list and to the people
+ who spent their time to improve your patch. Go back to step (2).
+
+. While the above iterations improve your patches, the maintainer may
+ pick the patches up from the list and queue them to the `seen`
+ branch, in order to make it easier for people to play with it
+ without having to pick up and apply the patches to their trees
+ themselves. Being in `seen` has no other meaning. Specifically, it
+ does not mean the patch was "accepted" in any way.
+
+. When the discussion reaches a consensus that the latest iteration of
+ the patches are in good enough shape, the maintainer includes the
+ topic in the "What's cooking" report that are sent out a few times a
+ week to the mailing list, marked as "Will merge to 'next'." This
+ decision is primarily made by the maintainer with help from those
+ who participated in the review discussion.
+
+. After the patches are merged to the 'next' branch, the discussion
+ can still continue to further improve them by adding more patches on
+ top, but by the time a topic gets merged to 'next', it is expected
+ that everybody agrees that the scope and the basic direction of the
+ topic are appropriate, so such an incremental updates are limited to
+ small corrections and polishing. After a topic cooks for some time
+ (like 7 calendar days) in 'next' without needing further tweaks on
+ top, it gets merged to the 'master' branch and wait to become part
+ of the next major release.
+
+In the following sections, many techniques and conventions are listed
+to help your patches get reviewed effectively in such a life cycle.
+
+
[[choose-starting-point]]
=== Choose a starting point.
@@ -192,8 +259,9 @@
which case, they can explain why they extend your code to cover
files, too).
-The goal of your log message is to convey the _why_ behind your
-change to help future developers.
+The goal of your log message is to convey the _why_ behind your change
+to help future developers. The reviewers will also make sure that
+your proposed log message will serve this purpose well.
The first line of the commit message should be a short description (50
characters is the soft limit, see DISCUSSION in linkgit:git-commit[1]),
@@ -397,17 +465,57 @@
[[send-patches]]
=== Sending your patches.
+==== Choosing your reviewers
+
:security-ml: footnoteref:[security-ml,The Git Security mailing list: git-security@googlegroups.com]
-Before sending any patches, please note that patches that may be
+NOTE: Patches that may be
security relevant should be submitted privately to the Git Security
mailing list{security-ml}, instead of the public mailing list.
-Learn to use format-patch and send-email if possible. These commands
+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
+not part of the core `git` binary and must be called directly. Clone the Git +
+codebase and run `perl contrib/contacts/git-contacts`.]
+
+Send your patch with "To:" set to the mailing list, with "cc:" listing
+people who are involved in the area you are touching (the `git-contacts`
+script in `contrib/contacts/`{contrib-scripts} can help to
+identify them), to solicit comments and reviews. Also, when you made
+trial merges of your topic to `next` and `seen`, you may have noticed
+work by others conflicting with your changes. There is a good possibility
+that these people may know the area you are touching well.
+
+If you are using `send-email`, you can feed it the output of `git-contacts` like
+this:
+
+....
+ git send-email --cc-cmd='perl contrib/contacts/git-contacts' feature/*.patch
+....
+
+:current-maintainer: footnote:[The current maintainer: gitster@pobox.com]
+:git-ml: footnote:[The mailing list: git@vger.kernel.org]
+
+After the list reached a consensus that it is a good idea to apply the
+patch, re-send it with "To:" set to the maintainer{current-maintainer}
+and "cc:" the list{git-ml} for inclusion. This is especially relevant
+when the maintainer did not heavily participate in the discussion and
+instead left the review to trusted others.
+
+Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
+`Tested-by:` lines as necessary to credit people who helped your
+patch, and "cc:" them when sending such a final version for inclusion.
+
+==== `format-patch` and `send-email`
+
+Learn to use `format-patch` and `send-email` if possible. These commands
are optimized for the workflow of sending patches, avoiding many ways
your existing e-mail client (often optimized for "multipart/*" MIME
type e-mails) might render your patches unusable.
+NOTE: Here we outline the procedure using `format-patch` and
+`send-email`, but you can instead use GitGitGadget to send in your
+patches (see link:MyFirstContribution.html[MyFirstContribution]).
+
People on the Git mailing list need to be able to read and
comment on the changes you are submitting. It is important for
a developer to be able to "quote" your changes, using standard
@@ -500,33 +608,84 @@
that starts with `-----BEGIN PGP SIGNED MESSAGE-----`. That is
not a text/plain, it's something else.
-:security-ml-ref: footnoteref:[security-ml]
+=== Handling Conflicts and Iterating Patches
-As mentioned at the beginning of the section, patches that may be
-security relevant should not be submitted to the public mailing list
-mentioned below, but should instead be sent privately to the Git
-Security mailing list{security-ml-ref}.
+When revising changes made to your patches, it's important to
+acknowledge the possibility of conflicts with other ongoing topics. To
+navigate these potential conflicts effectively, follow the recommended
+steps outlined below:
-Send your patch with "To:" set to the mailing list, with "cc:" listing
-people who are involved in the area you are touching (the `git
-contacts` command in `contrib/contacts/` can help to
-identify them), to solicit comments and reviews. Also, when you made
-trial merges of your topic to `next` and `seen`, you may have noticed
-work by others conflicting with your changes. There is a good possibility
-that these people may know the area you are touching well.
+. Build on a suitable base branch, see the <<choose-starting-point, section above>>,
+and format-patch the series. If you are doing "rebase -i" in-place to
+update from the previous round, this will reuse the previous base so
+(2) and (3) may become trivial.
-:current-maintainer: footnote:[The current maintainer: gitster@pobox.com]
-:git-ml: footnote:[The mailing list: git@vger.kernel.org]
+. Find the base of where the last round was queued
++
+ $ mine='kn/ref-transaction-symref'
+ $ git checkout "origin/seen^{/^Merge branch '$mine'}...master"
-After the list reached a consensus that it is a good idea to apply the
-patch, re-send it with "To:" set to the maintainer{current-maintainer}
-and "cc:" the list{git-ml} for inclusion. This is especially relevant
-when the maintainer did not heavily participate in the discussion and
-instead left the review to trusted others.
+. Apply your format-patch result. There are two cases
+.. Things apply cleanly and tests fine. Go to (4).
+.. Things apply cleanly but does not build or test fails, or things do
+not apply cleanly.
++
+In the latter case, you have textual or semantic conflicts coming from
+the difference between the old base and the base you used to build in
+(1). Identify what caused the breakages (e.g., a topic or two may have
+merged since the base used by (2) until the base used by (1)).
++
+Check out the latest 'origin/master' (which may be newer than the base
+used by (2)), "merge --no-ff" the topics you newly depend on in there,
+and use the result of the merge(s) as the base, rebuild the series and
+test again. Run format-patch from the last such merges to the tip of
+your topic. If you did
++
+ $ git checkout origin/master
+ $ git merge --no-ff --into-name kn/ref-transaction-symref fo/obar
+ $ git merge --no-ff --into-name kn/ref-transaction-symref ba/zqux
+ ... rebuild the topic ...
++
+Then you'd just format your topic above these "preparing the ground"
+merges, e.g.
++
+ $ git format-patch "HEAD^{/^Merge branch 'ba/zqux'}"..HEAD
++
+Do not forget to write in the cover letter you did this, including the
+topics you have in your base on top of 'master'. Then go to (4).
-Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
-`Tested-by:` lines as necessary to credit people who helped your
-patch, and "cc:" them when sending such a final version for inclusion.
+. Make a trial merge of your topic into 'next' and 'seen', e.g.
++
+ $ git checkout --detach 'origin/seen'
+ $ git revert -m 1 <the merge of the previous iteration into seen>
+ $ git merge kn/ref-transaction-symref
++
+The "revert" is needed if the previous iteration of your topic is
+already in 'seen' (like in this case). You could choose to rebuild
+master..origin/seen from scratch while excluding your previous
+iteration, which may emulate what happens on the maintainers end more
+closely.
++
+This trial merge may conflict. It is primarily to see what conflicts
+_other_ topics may have with your topic. In other words, you do not
+have to depend on it to make your topic work on 'master'. It may
+become the job of the other topic owners to resolve conflicts if your
+topic goes to 'next' before theirs.
++
+Make a note on what conflict you saw in the cover letter. You do not
+necessarily have to resolve them, but it would be a good opportunity to
+learn what others are doing in related areas.
++
+ $ git checkout --detach 'origin/next'
+ $ git merge kn/ref-transaction-symref
++
+This is to see what conflicts your topic has with other topics that are
+already cooking. This should not conflict if (3)-2 prepared a base on
+top of updated master plus dependent topics taken from 'next'. Unless
+the context is severe (one way to tell is try the same trial merge with
+your old iteration, which may conflict in a similar way), expect that it
+will be handled on maintainers end (if it gets unmanageable, I'll ask to
+rebase when I receive your patches).
== Subsystems with dedicated maintainers
@@ -557,55 +716,6 @@
https://github.com/jnavila/git-manpages-l10n/
-[[patch-flow]]
-== An ideal patch flow
-
-Here is an ideal patch flow for this project the current maintainer
-suggests to the contributors:
-
-. You come up with an itch. You code it up.
-
-. Send it to the list and cc people who may need to know about
- the change.
-+
-The people who may need to know are the ones whose code you
-are butchering. These people happen to be the ones who are
-most likely to be knowledgeable enough to help you, but
-they have no obligation to help you (i.e. you ask for help,
-don't demand). +git log -p {litdd} _$area_you_are_modifying_+ would
-help you find out who they are.
-
-. You get comments and suggestions for improvements. You may
- even get them in an "on top of your change" patch form.
-
-. Polish, refine, and re-send to the list and the people who
- spend their time to improve your patch. Go back to step (2).
-
-. The list forms consensus that the last round of your patch is
- good. Send it to the maintainer and cc the list.
-
-. A topic branch is created with the patch and is merged to `next`,
- and cooked further and eventually graduates to `master`.
-
-In any time between the (2)-(3) cycle, the maintainer may pick it up
-from the list and queue it to `seen`, in order to make it easier for
-people to play with it without having to pick up and apply the patch to
-their trees themselves.
-
-[[patch-status]]
-== Know the status of your patch after submission
-
-* You can use Git itself to find out when your patch is merged in
- master. `git pull --rebase` will automatically skip already-applied
- patches, and will let you know. This works only if you rebase on top
- of the branch in which your patch has been merged (i.e. it will not
- tell you if your patch is merged in `seen` if you rebase on top of
- master).
-
-* Read the Git mailing list, the maintainer regularly posts messages
- entitled "What's cooking in git.git" giving
- the status of various proposed changes.
-
== GitHub CI[[GHCI]]
With an account at GitHub, you can use GitHub CI to test your changes
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 70b448b..8c0b3ed 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -316,7 +316,8 @@
Colors may also be given as numbers between 0 and 255; these use ANSI
256-color mode (but note that not all terminals may support this). If
your terminal supports it, you may also specify 24-bit RGB values as
-hex, like `#ff0ab3`.
+hex, like `#ff0ab3`, or 12-bit RGB values like `#f1b`, which is
+equivalent to the 24-bit color `#ff11bb`.
+
The accepted attributes are `bold`, `dim`, `ul`, `blink`, `reverse`,
`italic`, and `strike` (for crossed-out or "strikethrough" letters).
@@ -383,6 +384,8 @@
include::config/attr.txt[]
+include::config/bitmap-pseudo-merge.txt[]
+
include::config/blame.txt[]
include::config/branch.txt[]
@@ -487,6 +490,8 @@
include::config/pretty.txt[]
+include::config/promisor.txt[]
+
include::config/protocol.txt[]
include::config/pull.txt[]
@@ -497,6 +502,8 @@
include::config/receive.txt[]
+include::config/reftable.txt[]
+
include::config/remote.txt[]
include::config/remotes.txt[]
diff --git a/Documentation/config/add.txt b/Documentation/config/add.txt
index e0354ce..4d753f0 100644
--- a/Documentation/config/add.txt
+++ b/Documentation/config/add.txt
@@ -5,9 +5,3 @@
option of linkgit:git-add[1]. `add.ignore-errors` is deprecated,
as it does not follow the usual naming convention for configuration
variables.
-
-add.interactive.useBuiltin::
- Unused configuration variable. Used in Git versions v2.25.0 to
- v2.36.0 to enable the built-in version of linkgit:git-add[1]'s
- interactive mode, which then became the default in Git
- versions v2.37.0 to v2.39.0.
diff --git a/Documentation/config/alias.txt b/Documentation/config/alias.txt
index 01df96f..2c5db0a 100644
--- a/Documentation/config/alias.txt
+++ b/Documentation/config/alias.txt
@@ -21,8 +21,23 @@
it will be treated as a shell command. For example, defining
`alias.new = !gitk --all --not ORIG_HEAD`, the invocation
`git new` is equivalent to running the shell command
-`gitk --all --not ORIG_HEAD`. Note that shell commands will be
-executed from the top-level directory of a repository, which may
-not necessarily be the current directory.
-`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
-from the original current directory. See linkgit:git-rev-parse[1].
+`gitk --all --not ORIG_HEAD`. Note:
++
+* Shell commands will be executed from the top-level directory of a
+ repository, which may not necessarily be the current directory.
+* `GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
+ from the original current directory. See linkgit:git-rev-parse[1].
+* Shell command aliases always receive any extra arguments provided to
+ the Git command-line as positional arguments.
+** Care should be taken if your shell alias is a "one-liner" script
+ with multiple commands (e.g. in a pipeline), references multiple
+ arguments, or is otherwise not able to handle positional arguments
+ added at the end. For example: `alias.cmd = "!echo $1 | grep $2"`
+ called as `git cmd 1 2` will be executed as 'echo $1 | grep $2
+ 1 2', which is not what you want.
+** A convenient way to deal with this is to write your script
+ operations in an inline function that is then called with any
+ arguments from the command-line. For example `alias.cmd = "!c() {
+ echo $1 | grep $2 ; }; c" will correctly execute the prior example.
+** Setting `GIT_TRACE=1` can help you debug the command being run for
+ your alias.
diff --git a/Documentation/config/bitmap-pseudo-merge.txt b/Documentation/config/bitmap-pseudo-merge.txt
new file mode 100644
index 0000000..1f264ec
--- /dev/null
+++ b/Documentation/config/bitmap-pseudo-merge.txt
@@ -0,0 +1,91 @@
+NOTE: The configuration options in `bitmapPseudoMerge.*` are considered
+EXPERIMENTAL and may be subject to change or be removed entirely in the
+future. For more information about the pseudo-merge bitmap feature, see
+the "Pseudo-merge bitmaps" section of linkgit:gitpacking[7].
+
+bitmapPseudoMerge.<name>.pattern::
+ Regular expression used to match reference names. Commits
+ pointed to by references matching this pattern (and meeting
+ the below criteria, like `bitmapPseudoMerge.<name>.sampleRate`
+ and `bitmapPseudoMerge.<name>.threshold`) will be considered
+ for inclusion in a pseudo-merge bitmap.
++
+Commits are grouped into pseudo-merge groups based on whether or not
+any reference(s) that point at a given commit match the pattern, which
+is an extended regular expression.
++
+Within a pseudo-merge group, commits may be further grouped into
+sub-groups based on the capture groups in the pattern. These
+sub-groupings are formed from the regular expressions by concatenating
+any capture groups from the regular expression, with a '-' dash in
+between.
++
+For example, if the pattern is `refs/tags/`, then all tags (provided
+they meet the below criteria) will be considered candidates for the
+same pseudo-merge group. However, if the pattern is instead
+`refs/remotes/([0-9])+/tags/`, then tags from different remotes will
+be grouped into separate pseudo-merge groups, based on the remote
+number.
+
+bitmapPseudoMerge.<name>.decay::
+ Determines the rate at which consecutive pseudo-merge bitmap
+ groups decrease in size. Must be non-negative. This parameter
+ can be thought of as `k` in the function `f(n) = C * n^-k`,
+ where `f(n)` is the size of the `n`th group.
++
+Setting the decay rate equal to `0` will cause all groups to be the
+same size. Setting the decay rate equal to `1` will cause the `n`th
+group to be `1/n` the size of the initial group. Higher values of the
+decay rate cause consecutive groups to shrink at an increasing rate.
+The default is `1`.
++
+If all groups are the same size, it is possible that groups containing
+newer commits will be able to be used less often than earlier groups,
+since it is more likely that the references pointing at newer commits
+will be updated more often than a reference pointing at an old commit.
+
+bitmapPseudoMerge.<name>.sampleRate::
+ Determines the proportion of non-bitmapped commits (among
+ reference tips) which are selected for inclusion in an
+ unstable pseudo-merge bitmap. Must be between `0` and `1`
+ (inclusive). The default is `1`.
+
+bitmapPseudoMerge.<name>.threshold::
+ Determines the minimum age of non-bitmapped commits (among
+ reference tips, as above) which are candidates for inclusion
+ in an unstable pseudo-merge bitmap. The default is
+ `1.week.ago`.
+
+bitmapPseudoMerge.<name>.maxMerges::
+ Determines the maximum number of pseudo-merge commits among
+ which commits may be distributed.
++
+For pseudo-merge groups whose pattern does not contain any capture
+groups, this setting is applied for all commits matching the regular
+expression. For patterns that have one or more capture groups, this
+setting is applied for each distinct capture group.
++
+For example, if your capture group is `refs/tags/`, then this setting
+will distribute all tags into a maximum of `maxMerges` pseudo-merge
+commits. However, if your capture group is, say,
+`refs/remotes/([0-9]+)/tags/`, then this setting will be applied to
+each remote's set of tags individually.
++
+Must be non-negative. The default value is 64.
+
+bitmapPseudoMerge.<name>.stableThreshold::
+ Determines the minimum age of commits (among reference tips,
+ as above, however stable commits are still considered
+ candidates even when they have been covered by a bitmap) which
+ are candidates for a stable a pseudo-merge bitmap. The default
+ is `1.month.ago`.
++
+Setting this threshold to a smaller value (e.g., 1.week.ago) will cause
+more stable groups to be generated (which impose a one-time generation
+cost) but those groups will likely become stale over time. Using a
+larger value incurs the opposite penalty (fewer stable groups which are
+more useful).
+
+bitmapPseudoMerge.<name>.stableSize::
+ Determines the size (in number of commits) of a stable
+ psuedo-merge bitmap. The default is `512`.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 5ce7b91..190bda1 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -79,6 +79,15 @@
you want to use an external diff program only on a subset of
your files, you might want to use linkgit:gitattributes[5] instead.
+diff.trustExitCode::
+ If this boolean value is set to true then the
+ `diff.external` command is expected to return exit code
+ 0 if it considers the input files to be equal or 1 if it
+ considers them to be different, like `diff(1)`.
+ If it is set to false, which is the default, then the command
+ is expected to return exit code 0 regardless of equality.
+ Any other exit code causes Git to report a fatal error.
+
diff.ignoreSubmodules::
Sets the default value of --ignore-submodules. Note that this
affects only 'git diff' Porcelain, and not lower level 'diff'
@@ -164,6 +173,15 @@
The custom diff driver command. See linkgit:gitattributes[5]
for details.
+diff.<driver>.trustExitCode::
+ If this boolean value is set to true then the
+ `diff.<driver>.command` command is expected to return exit code
+ 0 if it considers the input files to be equal or 1 if it
+ considers them to be different, like `diff(1)`.
+ If it is set to false, which is the default, then the command
+ is expected to return exit code 0 regardless of equality.
+ Any other exit code causes Git to report a fatal error.
+
diff.<driver>.xfuncname::
The regular expression that the diff driver should use to
recognize the hunk header. A built-in pattern may also be used.
diff --git a/Documentation/config/promisor.txt b/Documentation/config/promisor.txt
new file mode 100644
index 0000000..98c5cb2
--- /dev/null
+++ b/Documentation/config/promisor.txt
@@ -0,0 +1,3 @@
+promisor.quiet::
+ If set to "true" assume `--quiet` when fetching additional
+ objects for a partial clone.
diff --git a/Documentation/config/reftable.txt b/Documentation/config/reftable.txt
new file mode 100644
index 0000000..0515727
--- /dev/null
+++ b/Documentation/config/reftable.txt
@@ -0,0 +1,48 @@
+reftable.blockSize::
+ The size in bytes used by the reftable backend when writing blocks.
+ The block size is determined by the writer, and does not have to be a
+ power of 2. The block size must be larger than the longest reference
+ name or log entry used in the repository, as references cannot span
+ blocks.
++
+Powers of two that are friendly to the virtual memory system or
+filesystem (such as 4kB or 8kB) are recommended. Larger sizes (64kB) can
+yield better compression, with a possible increased cost incurred by
+readers during access.
++
+The largest block size is `16777215` bytes (15.99 MiB). The default value is
+`4096` bytes (4kB). A value of `0` will use the default value.
+
+reftable.restartInterval::
+ The interval at which to create restart points. The reftable backend
+ determines the restart points at file creation. Every 16 may be
+ more suitable for smaller block sizes (4k or 8k), every 64 for larger
+ block sizes (64k).
++
+More frequent restart points reduces prefix compression and increases
+space consumed by the restart table, both of which increase file size.
++
+Less frequent restart points makes prefix compression more effective,
+decreasing overall file size, with increased penalties for readers
+walking through more records after the binary search step.
++
+A maximum of `65535` restart points per block is supported.
++
+The default value is to create restart points every 16 records. A value of `0`
+will use the default value.
+
+reftable.indexObjects::
+ Whether the reftable backend shall write object blocks. Object blocks
+ are a reverse mapping of object ID to the references pointing to them.
++
+The default value is `true`.
+
+reftable.geometricFactor::
+ Whenever the reftable backend appends a new table to the stack, it
+ performs auto compaction to ensure that there is only a handful of
+ tables. The backend does this by ensuring that tables form a geometric
+ sequence regarding the respective sizes of each table.
++
+By default, the geometric sequence uses a factor of 2, meaning that for any
+table, the next-biggest table must at least be twice as big. A maximum factor
+of 256 is supported.
diff --git a/Documentation/config/remote.txt b/Documentation/config/remote.txt
index 0678b4b..8efc53e 100644
--- a/Documentation/config/remote.txt
+++ b/Documentation/config/remote.txt
@@ -5,10 +5,19 @@
remote.<name>.url::
The URL of a remote repository. See linkgit:git-fetch[1] or
- linkgit:git-push[1].
+ linkgit:git-push[1]. A configured remote can have multiple URLs;
+ in this case the first is used for fetching, and all are used
+ for pushing (assuming no `remote.<name>.pushurl` is defined).
+ Setting this key to the empty string clears the list of urls,
+ allowing you to override earlier config.
remote.<name>.pushurl::
The push URL of a remote repository. See linkgit:git-push[1].
+ If a `pushurl` option is present in a configured remote, it
+ is used for pushing instead of `remote.<name>.url`. A configured
+ remote can have multiple push URLs; in this case a push goes to
+ all of them. Setting this key to the empty string clears the
+ list of urls, allowing you to override earlier config.
remote.<name>.proxy::
For remotes that require curl (http, https and ftp), the URL to
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index c7df20e..cd0b81a 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -820,6 +820,11 @@
--quiet::
Disable all output of the program. Implies `--exit-code`.
+ Disables execution of external diff helpers whose exit code
+ is not trusted, i.e. their respective configuration option
+ `diff.trustExitCode` or `diff.<driver>.trustExitCode` or
+ environment variable `GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE` is
+ false.
endif::git-log[]
endif::git-format-patch[]
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 624a6e6..69d5cc9 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@
[--quoted-cr=<action>]
[--empty=(stop|drop|keep)]
[(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
+'git am' (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty)
DESCRIPTION
-----------
@@ -208,6 +208,12 @@
Abort the patching operation but keep HEAD and the index
untouched.
+--retry::
+ Try to apply the last conflicting patch again. This is generally
+ only useful for passing extra options to the retry attempt
+ (e.g., `--3way`), since otherwise you'll just see the same
+ failure again.
+
--show-current-patch[=(diff|raw)]::
Show the message at which `git am` has stopped due to
conflicts. If `raw` is specified, show the raw contents of
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index ac61113..65c645d 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -9,21 +9,14 @@
SYNOPSIS
--------
[verse]
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch <name> <URL>
-'git config' [<file-option>] [--fixed-value] --unset <name> [<value-pattern>]
-'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
-'git config' [<file-option>] --rename-section <old-name> <new-name>
-'git config' [<file-option>] --remove-section <name>
-'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
-'git config' [<file-option>] --get-color <name> [<default>]
+'git config list' [<file-option>] [<display-option>] [--includes]
+'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>
+'git config set' [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>
+'git config unset' [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>
+'git config rename-section' [<file-option>] <old-name> <new-name>
+'git config remove-section' [<file-option>] <name>
+'git config edit' [<file-option>]
'git config' [<file-option>] --get-colorbool <name> [<stdout-is-tty>]
-'git config' [<file-option>] -e | --edit
DESCRIPTION
-----------
@@ -31,7 +24,7 @@
actually the section and the key separated by a dot, and the value will be
escaped.
-Multiple lines can be added to an option by using the `--add` option.
+Multiple lines can be added to an option by using the `--append` option.
If you want to update or unset an option which can occur on multiple
lines, a `value-pattern` (which is an extended regular expression,
unless the `--fixed-value` option is given) needs to be given. Only the
@@ -74,6 +67,42 @@
A list of all available configuration variables can be obtained using the
`git help --config` command.
+COMMANDS
+--------
+
+list::
+ List all variables set in config file, along with their values.
+
+get::
+ Emits the value of the specified key. If key is present multiple times
+ in the configuration, emits the last value. If `--all` is specified,
+ emits all values associated with key. Returns error code 1 if key is
+ not present.
+
+set::
+ Set value for one or more config options. By default, this command
+ refuses to write multi-valued config options. Passing `--all` will
+ replace all multi-valued config options with the new value, whereas
+ `--value=` will replace all config options whose values match the given
+ pattern.
+
+unset::
+ Unset value for one or more config options. By default, this command
+ refuses to unset multi-valued keys. Passing `--all` will unset all
+ multi-valued config options, whereas `--value` will unset all config
+ options whose values match the given pattern.
+
+rename-section::
+ Rename the given section to a new name.
+
+remove-section::
+ Remove the given section from the configuration file.
+
+edit::
+ Opens an editor to modify the specified config file; either
+ `--system`, `--global`, `--local` (default), `--worktree`, or
+ `--file <config-file>`.
+
[[OPTIONS]]
OPTIONS
-------
@@ -82,10 +111,9 @@
Default behavior is to replace at most one line. This replaces
all lines matching the key (and optionally the `value-pattern`).
---add::
+--append::
Adds a new line to the option without altering any existing
- values. This is the same as providing '^$' as the `value-pattern`
- in `--replace-all`.
+ values. This is the same as providing '--value=^$' in `set`.
--comment <message>::
Append a comment at the end of new or modified lines.
@@ -99,22 +127,16 @@
not contain linefeed characters (no multi-line comments are
permitted).
---get::
- Get the value for a given key (optionally filtered by a regex
- matching the value). Returns error code 1 if the key was not
- found and the last value if multiple key values were found.
+--all::
+ With `get`, return all values for a multi-valued key.
---get-all::
- Like get, but returns all values for a multi-valued key.
+---regexp::
+ With `get`, interpret the name as a regular expression. Regular
+ expression matching is currently case-sensitive and done against a
+ canonicalized version of the key in which section and variable names
+ are lowercased, but subsection names are not.
---get-regexp::
- Like --get-all, but interprets the name as a regular expression and
- writes out the key names. Regular expression matching is currently
- case-sensitive and done against a canonicalized version of the key
- in which section and variable names are lowercased, but subsection
- names are not.
-
---get-urlmatch <name> <URL>::
+--url=<URL>::
When given a two-part <name> as <section>.<key>, the value for
<section>.<URL>.<key> whose <URL> part matches the best to the
given URL is returned (if no such key exists, the value for
@@ -178,22 +200,6 @@
section in linkgit:gitrevisions[7] for a more complete list of
ways to spell blob names.
---remove-section::
- Remove the given section from the configuration file.
-
---rename-section::
- Rename the given section to a new name.
-
---unset::
- Remove the line matching the key from config file.
-
---unset-all::
- Remove all lines matching the key from config file.
-
--l::
---list::
- List all variables set in config file, along with their values.
-
--fixed-value::
When used with the `value-pattern` argument, treat `value-pattern` as
an exact string instead of a regular expression. This will restrict
@@ -248,8 +254,8 @@
contain line breaks.
--name-only::
- Output only the names of config variables for `--list` or
- `--get-regexp`.
+ Output only the names of config variables for `list` or
+ `get`.
--show-origin::
Augment the output of all queried config options with the
@@ -273,23 +279,6 @@
When the color setting for `name` is undefined, the command uses
`color.ui` as fallback.
---get-color <name> [<default>]::
-
- Find the color configured for `name` (e.g. `color.diff.new`) and
- output it as the ANSI color escape sequence to the standard
- output. The optional `default` parameter is used instead, if
- there is no color configured for `name`.
-+
-`--type=color [--default=<default>]` is preferred over `--get-color`
-(but note that `--get-color` will omit the trailing newline printed by
-`--type=color`).
-
--e::
---edit::
- Opens an editor to modify the specified config file; either
- `--system`, `--global`, `--local` (default), `--worktree`, or
- `--file <config-file>`.
-
--[no-]includes::
Respect `include.*` directives in config files when looking up
values. Defaults to `off` when a specific file is given (e.g.,
@@ -297,14 +286,64 @@
config files.
--default <value>::
- When using `--get`, and the requested variable is not found, behave as if
+ When using `get`, and the requested variable is not found, behave as if
<value> were the value assigned to that variable.
+DEPRECATED MODES
+----------------
+
+The following modes have been deprecated in favor of subcommands. It is
+recommended to migrate to the new syntax.
+
+'git config <name>'::
+ Replaced by `git config get <name>`.
+
+'git config <name> <value> [<value-pattern>]'::
+ Replaced by `git config set [--value=<pattern>] <name> <value>`.
+
+-l::
+--list::
+ Replaced by `git config list`.
+
+--get <name> [<value-pattern>]::
+ Replaced by `git config get [--value=<pattern>] <name>`.
+
+--get-all <name> [<value-pattern>]::
+ Replaced by `git config get [--value=<pattern>] --all --show-names <name>`.
+
+--get-regexp <name-regexp>::
+ Replaced by `git config get --all --show-names --regexp <name-regexp>`.
+
+--get-urlmatch <name> <URL>::
+ Replaced by `git config get --all --show-names --url=<URL> <name>`.
+
+--get-color <name> [<default>]::
+ Replaced by `git config get --type=color [--default=<default>] <name>`.
+
+--add <name> <value>::
+ Replaced by `git config set --append <name> <value>`.
+
+--unset <name> [<value-pattern>]::
+ Replaced by `git config unset [--value=<pattern>] <name>`.
+
+--unset-all <name> [<value-pattern>]::
+ Replaced by `git config unset [--value=<pattern>] --all <name>`.
+
+--rename-section <old-name> <new-name>::
+ Replaced by `git config rename-section <old-name> <new-name>`.
+
+--remove-section <name>::
+ Replaced by `git config remove-section <name>`.
+
+-e::
+--edit::
+ Replaced by `git config edit`.
+
CONFIGURATION
-------------
`pager.config` is only respected when listing configuration, i.e., when
-using `--list` or any of the `--get-*` which may return multiple results.
-The default is to use a pager.
+using `list` or `get` which may return multiple results. The default is to use
+a pager.
[[FILES]]
FILES
@@ -346,8 +385,8 @@
values of a key from all files will be used.
By default, options are only written to the repository specific
-configuration file. Note that this also affects options like `--replace-all`
-and `--unset`. *'git config' will only ever change one file at a time*.
+configuration file. Note that this also affects options like `set`
+and `unset`. *'git config' will only ever change one file at a time*.
You can limit which configuration sources are read from or written to by
specifying the path of a file with the `--file` option, or by specifying a
@@ -482,7 +521,7 @@
you can set the filemode to true with
------------
-% git config core.filemode true
+% git config set core.filemode true
------------
The hypothetical proxy command entries actually have a postfix to discern
@@ -490,7 +529,7 @@
to "ssh".
------------
-% git config core.gitproxy '"ssh" for kernel.org' 'for kernel.org$'
+% git config set --value='for kernel.org$' core.gitproxy '"ssh" for kernel.org'
------------
This makes sure that only the key/value pair for kernel.org is replaced.
@@ -498,7 +537,7 @@
To delete the entry for renames, do
------------
-% git config --unset diff.renames
+% git config unset diff.renames
------------
If you want to delete an entry for a multivar (like core.gitproxy above),
@@ -507,51 +546,45 @@
To query the value for a given key, do
------------
-% git config --get core.filemode
-------------
-
-or
-
-------------
-% git config core.filemode
+% git config get core.filemode
------------
or, to query a multivar:
------------
-% git config --get core.gitproxy "for kernel.org$"
+% git config get --value="for kernel.org$" core.gitproxy
------------
If you want to know all the values for a multivar, do:
------------
-% git config --get-all core.gitproxy
+% git config get --all --show-names core.gitproxy
------------
If you like to live dangerously, you can replace *all* core.gitproxy by a
new one with
------------
-% git config --replace-all core.gitproxy ssh
+% git config set --all core.gitproxy ssh
------------
However, if you really only want to replace the line for the default proxy,
i.e. the one without a "for ..." postfix, do something like this:
------------
-% git config core.gitproxy ssh '! for '
+% git config set --value='! for ' core.gitproxy ssh
------------
To actually match only values with an exclamation mark, you have to
------------
-% git config section.key value '[!]'
+% git config set --value='[!]' section.key value
------------
To add a new proxy, without altering any of the existing ones, use
------------
-% git config --add core.gitproxy '"proxy-command" for example.com'
+% git config set --append core.gitproxy '"proxy-command" for example.com'
------------
An example to use customized color from the configuration in your
@@ -559,8 +592,8 @@
------------
#!/bin/sh
-WS=$(git config --get-color color.diff.whitespace "blue reverse")
-RESET=$(git config --get-color "" "reset")
+WS=$(git config get --type=color --default="blue reverse" color.diff.whitespace)
+RESET=$(git config get --type=color --default="reset" "")
echo "${WS}your whitespace color or blue reverse${RESET}"
------------
@@ -568,11 +601,11 @@
false, while it is set to `true` for all others:
------------
-% git config --type=bool --get-urlmatch http.sslverify https://good.example.com
+% git config get --type=bool --url=https://good.example.com http.sslverify
true
-% git config --type=bool --get-urlmatch http.sslverify https://weak.example.com
+% git config get --type=bool --url=https://weak.example.com http.sslverify
false
-% git config --get-urlmatch http https://weak.example.com
+% git config get --url=https://weak.example.com http
http.cookieFile /tmp/cookie.txt
http.sslverify false
------------
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 918a0aa..e414932 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
------------------
-'git credential' (fill|approve|reject)
+'git credential' (fill|approve|reject|capability)
------------------
DESCRIPTION
@@ -41,6 +41,9 @@
any configured credential helpers, which may erase any stored
credentials matching the description.
+If the action is `capability`, git-credential will announce any capabilities
+it supports to standard output.
+
If the action is `approve` or `reject`, no output should be emitted.
TYPICAL USE OF GIT CREDENTIAL
@@ -111,7 +114,9 @@
separated by an `=` (equals) sign, followed by a newline.
The key may contain any bytes except `=`, newline, or NUL. The value may
-contain any bytes except newline or NUL.
+contain any bytes except newline or NUL. A line, including the trailing
+newline, may not exceed 65535 bytes in order to allow implementations to
+parse efficiently.
Attributes with keys that end with C-style array brackets `[]` can have
multiple values. Each instance of a multi-valued attribute forms an
@@ -178,6 +183,61 @@
Components which are missing from the URL (e.g., there is no
username in the example above) will be left unset.
+`authtype`::
+ This indicates that the authentication scheme in question should be used.
+ Common values for HTTP and HTTPS include `basic`, `bearer`, and `digest`,
+ although the latter is insecure and should not be used. If `credential`
+ is used, this may be set to an arbitrary string suitable for the protocol in
+ question (usually HTTP).
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`credential`::
+ The pre-encoded credential, suitable for the protocol in question (usually
+ HTTP). If this key is sent, `authtype` is mandatory, and `username` and
+ `password` are not used. For HTTP, Git concatenates the `authtype` value and
+ this value with a single space to determine the `Authorization` header.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`ephemeral`::
+ This boolean value indicates, if true, that the value in the `credential`
+ field should not be saved by the credential helper because its usefulness is
+ limited in time. For example, an HTTP Digest `credential` value is computed
+ using a nonce and reusing it will not result in successful authentication.
+ This may also be used for situations with short duration (e.g., 24-hour)
+ credentials. The default value is false.
++
+The credential helper will still be invoked with `store` or `erase` so that it
+can determine whether the operation was successful.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`state[]`::
+ This value provides an opaque state that will be passed back to this helper
+ if it is called again. Each different credential helper may specify this
+ once. The value should include a prefix unique to the credential helper and
+ should ignore values that don't match its prefix.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`continue`::
+ This is a boolean value, which, if enabled, indicates that this
+ authentication is a non-final part of a multistage authentication step. This
+ is common in protocols such as NTLM and Kerberos, where two rounds of client
+ authentication are required, and setting this flag allows the credential
+ helper to implement the multistage authentication step. This flag should
+ only be sent if a further stage is required; that is, if another round of
+ authentication is expected.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input. This attribute is 'one-way' from a credential helper to
+pass information to Git (or other programs invoking `git credential`).
+
`wwwauth[]`::
When an HTTP response is received by Git that includes one or more
@@ -189,7 +249,45 @@
they appear in the HTTP response. This attribute is 'one-way' from Git
to pass additional information to credential helpers.
-Unrecognised attributes are silently discarded.
+`capability[]`::
+ This signals that Git, or the helper, as appropriate, supports the capability
+ in question. This can be used to provide better, more specific data as part
+ of the protocol. A `capability[]` directive must precede any value depending
+ on it and these directives _should_ be the first item announced in the
+ protocol.
++
+There are two currently supported capabilities. The first is `authtype`, which
+indicates that the `authtype`, `credential`, and `ephemeral` values are
+understood. The second is `state`, which indicates that the `state[]` and
+`continue` values are understood.
++
+It is not obligatory to use the additional features just because the capability
+is supported, but they should not be provided without the capability.
+
+Unrecognised attributes and capabilities are silently discarded.
+
+[[CAPA-IOFMT]]
+CAPABILITY INPUT/OUTPUT FORMAT
+------------------------------
+
+For `git credential capability`, the format is slightly different. First, a
+`version 0` announcement is made to indicate the current version of the
+protocol, and then each capability is announced with a line like `capability
+authtype`. Credential helpers may also implement this format, again with the
+`capability` argument. Additional lines may be added in the future; callers
+should ignore lines which they don't understand.
+
+Because this is a new part of the credential helper protocol, older versions of
+Git, as well as some credential helpers, may not support it. If a non-zero
+exit status is received, or if the first line doesn't start with the word
+`version` and a space, callers should assume that no capabilities are supported.
+
+The intention of this format is to differentiate it from the credential output
+in an unambiguous way. It is possible to use very simple credential helpers
+(e.g., inline shell scripts) which always produce identical output. Using a
+distinct format allows users to continue to use this syntax without having to
+worry about correctly implementing capability advertisements or accidentally
+confusing callers querying for capabilities.
GIT
---
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 728bb38..8708b31 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -20,7 +20,7 @@
[--in-reply-to=<message-id>] [--suffix=.<sfx>]
[--ignore-if-in-upstream] [--always]
[--cover-from-description=<mode>]
- [--rfc] [--subject-prefix=<subject-prefix>]
+ [--rfc[=<rfc>]] [--subject-prefix=<subject-prefix>]
[(--reroll-count|-v) <n>]
[--to=<email>] [--cc=<email>]
[--[no-]cover-letter] [--quiet]
@@ -238,10 +238,21 @@
value of the `format.filenameMaxLength` configuration
variable, or 64 if unconfigured.
---rfc::
- Prepends "RFC" to the subject prefix (producing "RFC PATCH" by
- default). RFC means "Request For Comments"; use this when sending
- an experimental patch for discussion rather than application.
+--rfc[=<rfc>]::
+ Prepends the string _<rfc>_ (defaults to "RFC") to
+ the subject prefix. As the subject prefix defaults to
+ "PATCH", you'll get "RFC PATCH" by default.
++
+RFC means "Request For Comments"; use this when sending
+an experimental patch for discussion rather than application.
+"--rfc=WIP" may also be a useful way to indicate that a patch
+is not complete yet ("WIP" stands for "Work In Progress").
++
+If the convention of the receiving community for a particular extra
+string is to have it _after_ the subject prefix, the string _<rfc>_
+can be prefixed with a dash ("`-`") to signal that the the rest of
+the _<rfc>_ string should be appended to the subject prefix instead,
+e.g., `--rfc='-(WIP)'` results in "PATCH (WIP)".
-v <n>::
--reroll-count=<n>::
@@ -346,6 +357,11 @@
between the previous and current series of patches by adjusting the
creation/deletion cost fudge factor. See linkgit:git-range-diff[1])
for details.
++
+Defaults to 999 (the linkgit:git-range-diff[1] uses 60), as the use
+case is to show comparison with an older iteration of the same
+topic and the tool should find more correspondence between the two
+sets of patches.
--notes[=<ref>]::
--no-notes::
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index 1c4f696..76c86c3 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git ls-remote' [--heads] [--tags] [--refs] [--upload-pack=<exec>]
+'git ls-remote' [--branches] [--tags] [--refs] [--upload-pack=<exec>]
[-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]
[--symref] [<repository> [<patterns>...]]
@@ -21,14 +21,16 @@
OPTIONS
-------
--h::
---heads::
+-b::
+--branches::
-t::
--tags::
- Limit to only refs/heads and refs/tags, respectively.
+ Limit to only local branches and local tags, respectively.
These options are _not_ mutually exclusive; when given
both, references stored in refs/heads and refs/tags are
- displayed. Note that `git ls-remote -h` used without
+ displayed. Note that `--heads` and `-h` are deprecated
+ synonyms for `--branches` and `-b` and may be removed in
+ the future. Also note that `git ls-remote -h` used without
anything else on the command line gives help, consistent
with other git subcommands.
diff --git a/Documentation/git-refs.txt b/Documentation/git-refs.txt
new file mode 100644
index 0000000..5b99e04
--- /dev/null
+++ b/Documentation/git-refs.txt
@@ -0,0 +1,61 @@
+git-refs(1)
+===========
+
+NAME
+----
+git-refs - Low-level access to refs
+
+
+SYNOPSIS
+--------
+[verse]
+'git refs migrate' --ref-format=<format> [--dry-run]
+
+DESCRIPTION
+-----------
+
+This command provides low-level access to refs.
+
+COMMANDS
+--------
+
+migrate::
+ Migrate ref store between different formats.
+
+OPTIONS
+-------
+
+The following options are specific to 'git refs migrate':
+
+--ref-format=<format>::
+ The ref format to migrate the ref store to. Can be one of:
++
+include::ref-storage-format.txt[]
+
+--dry-run::
+ Perform the migration, but do not modify the repository. The migrated
+ refs will be written into a separate directory that can be inspected
+ separately. The name of the directory will be reported on stdout. This
+ can be used to double check that the migration works as expected before
+ performing the actual migration.
+
+KNOWN LIMITATIONS
+-----------------
+
+The ref format migration has several known limitations in its current form:
+
+* It is not possible to migrate repositories that have reflogs.
+
+* It is not possible to migrate repositories that have worktrees.
+
+* There is no way to block concurrent writes to the repository during an
+ ongoing migration. Concurrent writes can lead to an inconsistent migrated
+ state. Users are expected to block writes on a higher level. If your
+ repository is registered for scheduled maintenance, it is recommended to
+ unregister it first with git-maintenance(1).
+
+These limitations may eventually be lifted.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
index ba75747..616d919 100644
--- a/Documentation/git-show-ref.txt
+++ b/Documentation/git-show-ref.txt
@@ -9,8 +9,8 @@
--------
[verse]
'git show-ref' [--head] [-d | --dereference]
- [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]
- [--heads] [--] [<pattern>...]
+ [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]
+ [--] [<pattern>...]
'git show-ref' --verify [-q | --quiet] [-d | --dereference]
[-s | --hash[=<n>]] [--abbrev[=<n>]]
[--] [<ref>...]
@@ -45,12 +45,14 @@
Show the HEAD reference, even if it would normally be filtered out.
---heads::
+--branches::
--tags::
- Limit to "refs/heads" and "refs/tags", respectively. These options
+ Limit to local branches and local tags, respectively. These options
are not mutually exclusive; when given both, references stored in
- "refs/heads" and "refs/tags" are displayed.
+ "refs/heads" and "refs/tags" are displayed. Note that `--heads`
+ is a deprecated synonym for `--branches` and may be removed
+ in the future.
-d::
--dereference::
@@ -139,7 +141,7 @@
For example,
-----------------------------------------------------------------------------
-$ git show-ref --heads --hash
+$ git show-ref --branches --hash
2e3ba0114a1f52b47df29743d6915d056be13278
185008ae97960c8d551adcd9e23565194651b5d1
03adf42c988195b50e1a1935ba5fcbc39b2b029b
@@ -183,8 +185,8 @@
actually want to show any results, and we want to use the full refname for it
in order to not trigger the problem with ambiguous partial matches).
-To show only tags, or only proper branch heads, use `--tags` and/or `--heads`
-respectively (using both means that it shows tags and heads, but not other
+To show only tags, or only proper branch heads, use `--tags` and/or `--branches`
+respectively (using both means that it shows tags and branches, but not other
random references under the refs/ subdirectory).
To do automatic tag object dereferencing, use the `-d` or `--dereference`
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 5fe519c..4494729 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,6 +10,7 @@
--------
[verse]
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]
+ [(--trailer <token>[(=|:)<value>])...]
<tagname> [<commit> | <object>]
'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
@@ -31,8 +32,8 @@
`-m <msg>` or `-F <file>` is given, an editor is started for the user to type
in the tag message.
-If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
-are absent, `-a` is implied.
+If `-m <msg>` or `-F <file>` or `--trailer <token>[=<value>]` is given
+and `-a`, `-s`, and `-u <key-id>` are absent, `-a` is implied.
Otherwise, a tag reference that points directly at the given object
(i.e., a lightweight tag) is created.
@@ -178,6 +179,17 @@
Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
is given.
+--trailer <token>[(=|:)<value>]::
+ Specify a (<token>, <value>) pair that should be applied as a
+ trailer. (e.g. `git tag --trailer "Custom-Key: value"`
+ will add a "Custom-Key" trailer to the tag message.)
+ The `trailer.*` configuration variables
+ (linkgit:git-interpret-trailers[1]) can be used to define if
+ a duplicated trailer is omitted, where in the run of trailers
+ each trailer would appear, and other details.
+ The trailers can be extracted in `git tag --list`, using
+ `--format="%(trailers)"` placeholder.
+
-e::
--edit::
The message taken from file with `-F` and command line with
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 374a2eb..afcf33c 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -65,6 +65,10 @@
create SP <ref> SP <new-oid> LF
delete SP <ref> [SP <old-oid>] LF
verify SP <ref> [SP <old-oid>] LF
+ symref-update SP <ref> SP <new-target> [SP (ref SP <old-target> | oid SP <old-oid>)] LF
+ symref-create SP <ref> SP <new-target> LF
+ symref-delete SP <ref> [SP <old-target>] LF
+ symref-verify SP <ref> [SP <old-target>] LF
option SP <opt> LF
start LF
prepare LF
@@ -86,6 +90,10 @@
create SP <ref> NUL <new-oid> NUL
delete SP <ref> NUL [<old-oid>] NUL
verify SP <ref> NUL [<old-oid>] NUL
+ symref-update SP <ref> NUL <new-target> [NUL (ref NUL <old-target> | oid NUL <old-oid>)] NUL
+ symref-create SP <ref> NUL <new-target> NUL
+ symref-delete SP <ref> [NUL <old-target>] NUL
+ symref-verify SP <ref> [NUL <old-target>] NUL
option SP <opt> NUL
start NUL
prepare NUL
@@ -113,10 +121,27 @@
Delete <ref> after verifying it exists with <old-oid>, if
given. If given, <old-oid> may not be zero.
+symref-update::
+ Set <ref> to <new-target> after verifying <old-target> or <old-oid>,
+ if given. Specify a zero <old-oid> to ensure that the ref does not
+ exist before the update.
+
verify::
Verify <ref> against <old-oid> but do not change it. If
<old-oid> is zero or missing, the ref must not exist.
+symref-create:
+ Create symbolic ref <ref> with <new-target> after verifying
+ it does not exist.
+
+symref-delete::
+ Delete <ref> after verifying it exists with <old-target>, if given.
+
+symref-verify::
+ Verify symbolic <ref> against <old-target> but do not change it.
+ If <old-target> is missing, the ref must not exist. Can only be
+ used in `no-deref` mode.
+
option::
Modify the behavior of the next command naming a <ref>.
The only valid option is `no-deref` to avoid dereferencing
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 024a01d..4489e22 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -11,9 +11,10 @@
[verse]
'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
- [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
- [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
- [--config-env=<name>=<envvar>] <command> [<args>]
+ [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
+ [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]
+ [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]
+ <command> [<args>]
DESCRIPTION
-----------
@@ -186,6 +187,13 @@
This is equivalent to setting the `GIT_NO_LAZY_FETCH`
environment variable to `1`.
+--no-optional-locks::
+ Do not perform optional operations that require locks. This is
+ equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
+
+--no-advice::
+ Disable all advice hints from being printed.
+
--literal-pathspecs::
Treat pathspecs literally (i.e. no globbing, no pathspec magic).
This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
@@ -207,10 +215,6 @@
Add "icase" magic to all pathspec. This is equivalent to setting
the `GIT_ICASE_PATHSPECS` environment variable to `1`.
---no-optional-locks::
- Do not perform optional operations that require locks. This is
- equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
-
--list-cmds=<group>[,<group>...]::
List commands by group. This is an internal/experimental
option and may change or be removed in the future. Supported
@@ -640,6 +644,16 @@
For each path `GIT_EXTERNAL_DIFF` is called, two environment variables,
`GIT_DIFF_PATH_COUNTER` and `GIT_DIFF_PATH_TOTAL` are set.
+`GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE`::
+ If this Boolean environment variable is set to true then the
+ `GIT_EXTERNAL_DIFF` command is expected to return exit code
+ 0 if it considers the input files to be equal or 1 if it
+ considers them to be different, like `diff(1)`.
+ If it is set to false, which is the default, then the command
+ is expected to return exit code 0 regardless of equality.
+ Any other exit code causes Git to report a fatal error.
+
+
`GIT_DIFF_PATH_COUNTER`::
A 1-based counter incremented by one for every path.
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 4338d02..80cae17 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -776,6 +776,11 @@
parameters, just like `GIT_EXTERNAL_DIFF` program is called.
See linkgit:git[1] for details.
+If the program is able to ignore certain changes (similar to
+`git diff --ignore-space-change`), then also set the option
+`trustExitCode` to true. It is then expected to return exit code 1 if
+it finds significant changes and 0 if it doesn't.
+
Setting the internal diff algorithm
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index ee9b92c..06e9971 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -486,7 +486,7 @@
This hook is invoked by any Git command that performs reference
updates. It executes whenever a reference transaction is prepared,
committed or aborted and may thus get called multiple times. The hook
-does not cover symbolic references (but that may change in the future).
+also supports symbolic reference updates.
The hook takes exactly one argument, which is the current state the
given reference transaction is in:
@@ -503,16 +503,20 @@
For each reference update that was added to the transaction, the hook
receives on standard input a line of the format:
- <old-oid> SP <new-oid> SP <ref-name> LF
+ <old-value> SP <new-value> SP <ref-name> LF
-where `<old-oid>` is the old object name passed into the reference
-transaction, `<new-oid>` is the new object name to be stored in the
+where `<old-value>` is the old object name passed into the reference
+transaction, `<new-value>` is the new object name to be stored in the
ref and `<ref-name>` is the full name of the ref. When force updating
the reference regardless of its current value or when the reference is
-to be created anew, `<old-oid>` is the all-zeroes object name. To
+to be created anew, `<old-value>` is the all-zeroes object name. To
distinguish these cases, you can inspect the current value of
`<ref-name>` via `git rev-parse`.
+For symbolic reference updates the `<old_value>` and `<new-value>`
+fields could denote references instead of objects. A reference will be
+denoted with a 'ref:' prefix, like `ref:<ref-target>`.
+
The exit status of the hook is ignored for any state except for the
"prepared" state. In the "prepared" state, a non-zero exit status will
cause the transaction to be aborted. The hook will not be called with
diff --git a/Documentation/gitpacking.txt b/Documentation/gitpacking.txt
new file mode 100644
index 0000000..4a6fcba
--- /dev/null
+++ b/Documentation/gitpacking.txt
@@ -0,0 +1,189 @@
+gitpacking(7)
+=============
+
+NAME
+----
+gitpacking - Advanced concepts related to packing in Git
+
+SYNOPSIS
+--------
+gitpacking
+
+DESCRIPTION
+-----------
+
+This document aims to describe some advanced concepts related to packing
+in Git.
+
+Many concepts are currently described scattered between manual pages of
+various Git commands, including linkgit:git-pack-objects[1],
+linkgit:git-repack[1], and others, as well as linkgit:gitformat-pack[5],
+and parts of the `Documentation/technical` tree.
+
+There are many aspects of packing in Git that are not covered in this
+document that instead live in the aforementioned areas. Over time, those
+scattered bits may coalesce into this document.
+
+== Pseudo-merge bitmaps
+
+NOTE: Pseudo-merge bitmaps are considered an experimental feature, so
+the configuration and many of the ideas are subject to change.
+
+=== Background
+
+Reachability bitmaps are most efficient when we have on-disk stored
+bitmaps for one or more of the starting points of a traversal. For this
+reason, Git prefers storing bitmaps for commits at the tips of refs,
+because traversals tend to start with those points.
+
+But if you have a large number of refs, it's not feasible to store a
+bitmap for _every_ ref tip. It takes up space, and just OR-ing all of
+those bitmaps together is expensive.
+
+One way we can deal with that is to create bitmaps that represent
+_groups_ of refs. When a traversal asks about the entire group, then we
+can use this single bitmap instead of considering each ref individually.
+Because these bitmaps represent the set of objects which would be
+reachable in a hypothetical merge of all of the commits, we call them
+pseudo-merge bitmaps.
+
+=== Overview
+
+A "pseudo-merge bitmap" is used to refer to a pair of bitmaps, as
+follows:
+
+Commit bitmap::
+
+ A bitmap whose set bits describe the set of commits included in the
+ pseudo-merge's "merge" bitmap (as below).
+
+Merge bitmap::
+
+ A bitmap whose set bits describe the reachability closure over the set
+ of commits in the pseudo-merge's "commits" bitmap (as above). An
+ identical bitmap would be generated for an octopus merge with the same
+ set of parents as described in the commits bitmap.
+
+Pseudo-merge bitmaps can accelerate bitmap traversals when all commits
+for a given pseudo-merge are listed on either side of the traversal,
+either directly (by explicitly asking for them as part of the `HAVES`
+or `WANTS`) or indirectly (by encountering them during a fill-in
+traversal).
+
+=== Use-cases
+
+For example, suppose there exists a pseudo-merge bitmap with a large
+number of commits, all of which are listed in the `WANTS` section of
+some bitmap traversal query. When pseudo-merge bitmaps are enabled, the
+bitmap machinery can quickly determine there is a pseudo-merge which
+satisfies some subset of the wanted objects on either side of the query.
+Then, we can inflate the EWAH-compressed bitmap, and `OR` it in to the
+resulting bitmap. By contrast, without pseudo-merge bitmaps, we would
+have to repeat the decompression and `OR`-ing step over a potentially
+large number of individual bitmaps, which can take proportionally more
+time.
+
+Another benefit of pseudo-merges arises when there is some combination
+of (a) a large number of references, with (b) poor bitmap coverage, and
+(c) deep, nested trees, making fill-in traversal relatively expensive.
+For example, suppose that there are a large enough number of tags where
+bitmapping each of the tags individually is infeasible. Without
+pseudo-merge bitmaps, computing the result of, say, `git rev-list
+--use-bitmap-index --count --objects --tags` would likely require a
+large amount of fill-in traversal. But when a large quantity of those
+tags are stored together in a pseudo-merge bitmap, the bitmap machinery
+can take advantage of the fact that we only care about the union of
+objects reachable from all of those tags, and answer the query much
+faster.
+
+=== Configuration
+
+Reference tips are grouped into different pseudo-merge groups according
+to two criteria. A reference name matches one or more of the defined
+pseudo-merge patterns, and optionally one or more capture groups within
+that pattern which further partition the group.
+
+Within a group, commits may be considered "stable", or "unstable"
+depending on their age. These are adjusted by setting the
+`bitmapPseudoMerge.<name>.stableThreshold` and
+`bitmapPseudoMerge.<name>.threshold` configuration values, respectively.
+
+All stable commits are grouped into pseudo-merges of equal size
+(`bitmapPseudoMerge.<name>.stableSize`). If the `stableSize`
+configuration is set to, say, 100, then the first 100 commits (ordered
+by committer date) which are older than the `stableThreshold` value will
+form one group, the next 100 commits will form another group, and so on.
+
+Among unstable commits, the pseudo-merge machinery will attempt to
+combine older commits into large groups as opposed to newer commits
+which will appear in smaller groups. This is based on the heuristic that
+references whose tip commit is older are less likely to be modified to
+point at a different commit than a reference whose tip commit is newer.
+
+The size of groups is determined by a power-law decay function, and the
+decay parameter roughly corresponds to "k" in `f(n) = C*n^(-k/100)`,
+where `f(n)` describes the size of the `n`-th pseudo-merge group. The
+sample rate controls what percentage of eligible commits are considered
+as candidates. The threshold parameter indicates the minimum age (so as
+to avoid including too-recent commits in a pseudo-merge group, making it
+less likely to be valid). The "maxMerges" parameter sets an upper-bound
+on the number of pseudo-merge commits an individual group
+
+The "stable"-related parameters control "stable" pseudo-merge groups,
+comprised of a fixed number of commits which are older than the
+configured "stable threshold" value and may be grouped together in
+chunks of "stableSize" in order of age.
+
+The exact configuration for pseudo-merges is as follows:
+
+include::config/bitmap-pseudo-merge.txt[]
+
+=== Examples
+
+Suppose that you have a repository with a large number of references,
+and you want a bare-bones configuration of pseudo-merge bitmaps that
+will enhance bitmap coverage of the `refs/` namespace. You may start
+wiht a configuration like so:
+
+ [bitmapPseudoMerge "all"]
+ pattern = "refs/"
+ threshold = now
+ stableThreshold = never
+ sampleRate = 100
+ maxMerges = 64
+
+This will create pseudo-merge bitmaps for all references, regardless of
+their age, and group them into 64 pseudo-merge commits.
+
+If you wanted to separate tags from branches when generating
+pseudo-merge commits, you would instead define the pattern with a
+capture group, like so:
+
+ [bitmapPseudoMerge "all"]
+ pattern = "refs/(heads/tags)/"
+
+Suppose instead that you are working in a fork-network repository, with
+each fork specified by some numeric ID, and whose refs reside in
+`refs/virtual/NNN/` (where `NNN` is the numeric ID corresponding to some
+fork) in the network. In this instance, you may instead write something
+like:
+
+ [bitmapPseudoMerge "all"]
+ pattern = "refs/virtual/([0-9]+)/(heads|tags)/"
+ threshold = now
+ stableThreshold = never
+ sampleRate = 100
+ maxMerges = 64
+
+Which would generate pseudo-merge group identifiers like "1234-heads",
+and "5678-tags" (for branches in fork "1234", and tags in remote "5678",
+respectively).
+
+SEE ALSO
+--------
+linkgit:git-pack-objects[1]
+linkgit:git-repack[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index d71b199..30b394a 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -497,20 +497,18 @@
unusual refs.
[[def_pseudoref]]pseudoref::
- Pseudorefs are a class of files under `$GIT_DIR` which behave
- like refs for the purposes of rev-parse, but which are treated
- specially by git. Pseudorefs both have names that are all-caps,
- and always start with a line consisting of a
- <<def_SHA1,SHA-1>> followed by whitespace. So, HEAD is not a
- pseudoref, because it is sometimes a symbolic ref. They might
- optionally contain some additional data. `MERGE_HEAD` and
- `CHERRY_PICK_HEAD` are examples. Unlike
- <<def_per_worktree_ref,per-worktree refs>>, these files cannot
- be symbolic refs, and never have reflogs. They also cannot be
- updated through the normal ref update machinery. Instead,
- they are updated by directly writing to the files. However,
- they can be read as if they were refs, so `git rev-parse
- MERGE_HEAD` will work.
+ A ref that has different semantics than normal refs. These refs can be
+ read via normal Git commands, but cannot be written to by commands like
+ linkgit:git-update-ref[1].
++
+The following pseudorefs are known to Git:
+
+ - `FETCH_HEAD` is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
+ may refer to multiple object IDs. Each object ID is annotated with metadata
+ indicating where it was fetched from and its fetch status.
+
+ - `MERGE_HEAD` is written by linkgit:git-merge[1] when resolving merge
+ conflicts. It contains all commit IDs which are being merged.
[[def_pull]]pull::
Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
@@ -552,20 +550,38 @@
to the result.
[[def_ref]]ref::
- A name that begins with `refs/` (e.g. `refs/heads/master`)
- that points to an <<def_object_name,object name>> or another
- ref (the latter is called a <<def_symref,symbolic ref>>).
+ A name that that points to an <<def_object_name,object name>> or
+ another ref (the latter is called a <<def_symref,symbolic ref>>).
For convenience, a ref can sometimes be abbreviated when used
as an argument to a Git command; see linkgit:gitrevisions[7]
for details.
Refs are stored in the <<def_repository,repository>>.
+
The ref namespace is hierarchical.
-Different subhierarchies are used for different purposes (e.g. the
-`refs/heads/` hierarchy is used to represent local branches).
+Ref names must either start with `refs/` or be located in the root of
+the hierarchy. For the latter, their name must follow these rules:
+
-There are a few special-purpose refs that do not begin with `refs/`.
-The most notable example is `HEAD`.
+ - The name consists of only upper-case characters or underscores.
+
+ - The name ends with "`_HEAD`" or is equal to "`HEAD`".
++
+There are some irregular refs in the root of the hierarchy that do not
+match these rules. The following list is exhaustive and shall not be
+extended in the future:
++
+ - `AUTO_MERGE`
+
+ - `BISECT_EXPECTED_REV`
+
+ - `NOTES_MERGE_PARTIAL`
+
+ - `NOTES_MERGE_REF`
+
+ - `MERGE_AUTOSTASH`
++
+Different subhierarchies are used for different purposes. For example,
+the `refs/heads/` hierarchy is used to represent local branches whereas
+the `refs/tags/` hierarchy is used to represent local tags..
[[def_reflog]]reflog::
A reflog shows the local "history" of a ref. In other words,
@@ -576,7 +592,8 @@
[[def_refspec]]refspec::
A "refspec" is used by <<def_fetch,fetch>> and
<<def_push,push>> to describe the mapping between remote
- <<def_ref,ref>> and local ref.
+ <<def_ref,ref>> and local ref. See linkgit:git-fetch[1] or
+ linkgit:git-push[1] for details.
[[def_remote]]remote repository::
A <<def_repository,repository>> which is used to track the same
@@ -638,20 +655,6 @@
An <<def_object,object>> used to temporarily store the contents of a
<<def_dirty,dirty>> working directory and the index for future reuse.
-[[def_special_ref]]special ref::
- A ref that has different semantics than normal refs. These refs can be
- accessed via normal Git commands but may not behave the same as a
- normal ref in some cases.
-+
-The following special refs are known to Git:
-
- - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
- may refer to multiple object IDs. Each object ID is annotated with metadata
- indicating where it was fetched from and its fetch status.
-
- - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
- conflicts. It contains all commit IDs which are being merged.
-
[[def_submodule]]submodule::
A <<def_repository,repository>> that holds the history of a
separate project inside another repository (the latter of
diff --git a/Documentation/lint-manpages.sh b/Documentation/lint-manpages.sh
new file mode 100755
index 0000000..92cfc0a
--- /dev/null
+++ b/Documentation/lint-manpages.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+extract_variable () {
+ (
+ cat ../Makefile
+ cat <<EOF
+print_variable:
+ @\$(foreach b,\$($1),echo XXX \$(b:\$X=) YYY;)
+EOF
+ ) |
+ make -C .. -f - print_variable 2>/dev/null |
+ sed -n -e 's/.*XXX \(.*\) YYY.*/\1/p'
+}
+
+check_missing_docs () (
+ ret=0
+
+ for v in $ALL_COMMANDS
+ do
+ case "$v" in
+ git-merge-octopus) continue;;
+ git-merge-ours) continue;;
+ git-merge-recursive) continue;;
+ git-merge-resolve) continue;;
+ git-merge-subtree) continue;;
+ git-fsck-objects) continue;;
+ git-init-db) continue;;
+ git-remote-*) continue;;
+ git-stage) continue;;
+ git-legacy-*) continue;;
+ git-?*--?* ) continue ;;
+ esac
+
+ if ! test -f "$v.txt"
+ then
+ echo "no doc: $v"
+ ret=1
+ fi
+
+ if ! sed -e '1,/^### command list/d' -e '/^#/d' ../command-list.txt |
+ grep -q "^$v[ ]"
+ then
+ case "$v" in
+ git)
+ ;;
+ *)
+ echo "no link: $v"
+ ret=1
+ ;;
+ esac
+ fi
+ done
+
+ exit $ret
+)
+
+check_extraneous_docs () {
+ (
+ sed -e '1,/^### command list/d' \
+ -e '/^#/d' \
+ -e '/guide$/d' \
+ -e '/interfaces$/d' \
+ -e 's/[ ].*//' \
+ -e 's/^/listed /' ../command-list.txt
+ make print-man1 |
+ grep '\.txt$' |
+ sed -e 's|^|documented |' \
+ -e 's/\.txt//'
+ ) | (
+ all_commands="$(printf "%s " "$ALL_COMMANDS" "$BUILT_INS" "$EXCLUDED_PROGRAMS" | tr '\n' ' ')"
+ ret=0
+
+ while read how cmd
+ do
+ case " $all_commands " in
+ *" $cmd "*) ;;
+ *)
+ echo "removed but $how: $cmd"
+ ret=1;;
+ esac
+ done
+
+ exit $ret
+ )
+}
+
+BUILT_INS="$(extract_variable BUILT_INS)"
+ALL_COMMANDS="$(extract_variable ALL_COMMANDS)"
+EXCLUDED_PROGRAMS="$(extract_variable EXCLUDED_PROGRAMS)"
+
+findings=$(
+ if ! check_missing_docs
+ then
+ ret=1
+ fi
+
+ if ! check_extraneous_docs
+ then
+ ret=1
+ fi
+
+ exit $ret
+)
+ret=$?
+
+printf "%s" "$findings" | sort
+
+exit $ret
diff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt
index f5d2009..bfb0ec7 100644
--- a/Documentation/technical/bitmap-format.txt
+++ b/Documentation/technical/bitmap-format.txt
@@ -255,3 +255,144 @@
xor_row (4 byte integer, network byte order): ::
The position of the triplet whose bitmap is used to compress
this one, or `0xffffffff` if no such bitmap exists.
+
+Pseudo-merge bitmaps
+--------------------
+
+If the `BITMAP_OPT_PSEUDO_MERGES` flag is set, a variable number of
+bytes (preceding the name-hash cache, commit lookup table, and trailing
+checksum) of the `.bitmap` file is used to store pseudo-merge bitmaps.
+
+For more information on what pseudo-merges are, why they are useful, and
+how to configure them, see the information in linkgit:gitpacking[7].
+
+=== File format
+
+If enabled, pseudo-merge bitmaps are stored in an optional section at
+the end of a `.bitmap` file. The format is as follows:
+
+....
++-------------------------------------------+
+| .bitmap File |
++-------------------------------------------+
+| |
+| Pseudo-merge bitmaps (Variable Length) |
+| +---------------------------+ |
+| | commits_bitmap (EWAH) | |
+| +---------------------------+ |
+| | merge_bitmap (EWAH) | |
+| +---------------------------+ |
+| |
++-------------------------------------------+
+| |
+| Lookup Table |
+| +---------------------------+ |
+| | commit_pos (4 bytes) | |
+| +---------------------------+ |
+| | offset (8 bytes) | |
+| +------------+--------------+ |
+| |
+| Offset Cases: |
+| ------------- |
+| |
+| 1. MSB Unset: single pseudo-merge bitmap |
+| + offset to pseudo-merge bitmap |
+| |
+| 2. MSB Set: multiple pseudo-merges |
+| + offset to extended lookup table |
+| |
++-------------------------------------------+
+| |
+| Extended Lookup Table (Optional) |
+| +----+----------+----------+----------+ |
+| | N | Offset 1 | .... | Offset N | |
+| +----+----------+----------+----------+ |
+| | | 8 bytes | .... | 8 bytes | |
+| +----+----------+----------+----------+ |
+| |
++-------------------------------------------+
+| |
+| Pseudo-merge position table |
+| +----+----------+----------+----------+ |
+| | N | Offset 1 | .... | Offset N | |
+| +----+----------+----------+----------+ |
+| | | 8 bytes | .... | 8 bytes | |
+| +----+----------+----------+----------+ |
+| |
++-------------------------------------------+
+| |
+| Pseudo-merge Metadata |
+| +-----------------------------------+ |
+| | # pseudo-merges (4 bytes) | |
+| +-----------------------------------+ |
+| | # commits (4 bytes) | |
+| +-----------------------------------+ |
+| | Lookup offset (8 bytes) | |
+| +-----------------------------------+ |
+| | Extension size (8 bytes) | |
+| +-----------------------------------+ |
+| |
++-------------------------------------------+
+....
+
+* One or more pseudo-merge bitmaps, each containing:
+
+ ** `commits_bitmap`, an EWAH-compressed bitmap describing the set of
+ commits included in the this psuedo-merge.
+
+ ** `merge_bitmap`, an EWAH-compressed bitmap describing the union of
+ the set of objects reachable from all commits listed in the
+ `commits_bitmap`.
+
+* A lookup table, mapping pseudo-merged commits to the pseudo-merges
+ they belong to. Entries appear in increasing order of each commit's
+ bit position. Each entry is 12 bytes wide, and is comprised of the
+ following:
+
+ ** `commit_pos`, a 4-byte unsigned value (in network byte-order)
+ containing the bit position for this commit.
+
+ ** `offset`, an 8-byte unsigned value (also in network byte-order)
+ containing either one of two possible offsets, depending on whether or
+ not the most-significant bit is set.
+
+ *** If unset (i.e. `offset & ((uint64_t)1<<63) == 0`), the offset
+ (relative to the beginning of the `.bitmap` file) at which the
+ pseudo-merge bitmap for this commit can be read. This indicates
+ only a single pseudo-merge bitmap contains this commit.
+
+ *** If set (i.e. `offset & ((uint64_t)1<<63) != 0`), the offset
+ (again relative to the beginning of the `.bitmap` file) at which
+ the extended offset table can be located describing the set of
+ pseudo-merge bitmaps which contain this commit. This indicates
+ that multiple pseudo-merge bitmaps contain this commit.
+
+* An (optional) extended lookup table (written if and only if there is
+ at least one commit which appears in more than one pseudo-merge).
+ There are as many entries as commits which appear in multiple
+ pseudo-merges. Each entry contains the following:
+
+ ** `N`, a 4-byte unsigned value equal to the number of pseudo-merges
+ which contain a given commit.
+
+ ** An array of `N` 8-byte unsigned values, each of which is
+ interpreted as an offset (relative to the beginning of the
+ `.bitmap` file) at which a pseudo-merge bitmap for this commit can
+ be read. These values occur in no particular order.
+
+* Positions for all pseudo-merges, each stored as an 8-byte unsigned
+ value (in network byte-order) containing the offset (relative to the
+ beginning of the `.bitmap` file) of each consecutive pseudo-merge.
+
+* A 4-byte unsigned value (in network byte-order) equal to the number of
+ pseudo-merges.
+
+* A 4-byte unsigned value (in network byte-order) equal to the number of
+ unique commits which appear in any pseudo-merge.
+
+* An 8-byte unsigned value (in network byte-order) equal to the number
+ of bytes between the start of the pseudo-merge section and the
+ beginning of the lookup table.
+
+* An 8-byte unsigned value (in network byte-order) equal to the number
+ of bytes in the pseudo-merge section (including this field).
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index eb53b0e..814cdcf 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.45.2
+DEF_VER=v2.45.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 1e31acc..3eab701 100644
--- a/Makefile
+++ b/Makefile
@@ -409,6 +409,9 @@
# to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c`
# that implements the `fsm_os_settings__*()` routines.
#
+# Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test
+# programs in oss-fuzz/.
+#
# === Optional library: libintl ===
#
# Define NO_GETTEXT if you don't want Git output to be translated.
@@ -752,23 +755,6 @@
ETAGS_TARGET = TAGS
-# If you add a new fuzzer, please also make sure to run it in
-# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
-# runs in the future.
-FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
-FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
-FUZZ_OBJS += oss-fuzz/fuzz-config.o
-FUZZ_OBJS += oss-fuzz/fuzz-date.o
-FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
-FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
-.PHONY: fuzz-objs
-fuzz-objs: $(FUZZ_OBJS)
-
-# Always build fuzz objects even if not testing, to prevent bit-rot.
-all:: $(FUZZ_OBJS)
-
-FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
-
# Empty...
EXTRA_PROGRAMS =
@@ -807,7 +793,7 @@
TEST_BUILTINS_OBJS += test-dump-split-index.o
TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
TEST_BUILTINS_OBJS += test-env-helper.o
-TEST_BUILTINS_OBJS += test-example-decorate.o
+TEST_BUILTINS_OBJS += test-example-tap.o
TEST_BUILTINS_OBJS += test-find-pack.o
TEST_BUILTINS_OBJS += test-fsmonitor-client.o
TEST_BUILTINS_OBJS += test-genrandom.o
@@ -824,7 +810,6 @@
TEST_BUILTINS_OBJS += test-mktemp.o
TEST_BUILTINS_OBJS += test-oid-array.o
TEST_BUILTINS_OBJS += test-oidmap.o
-TEST_BUILTINS_OBJS += test-oidtree.o
TEST_BUILTINS_OBJS += test-online-cpus.o
TEST_BUILTINS_OBJS += test-pack-mtimes.o
TEST_BUILTINS_OBJS += test-parse-options.o
@@ -852,7 +837,6 @@
TEST_BUILTINS_OBJS += test-sha256.o
TEST_BUILTINS_OBJS += test-sigchain.o
TEST_BUILTINS_OBJS += test-simple-ipc.o
-TEST_BUILTINS_OBJS += test-strcmp-offset.o
TEST_BUILTINS_OBJS += test-string-list.o
TEST_BUILTINS_OBJS += test-submodule-config.o
TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
@@ -1119,6 +1103,7 @@
LIB_OBJS += protocol.o
LIB_OBJS += protocol-caps.o
LIB_OBJS += prune-packed.o
+LIB_OBJS += pseudo-merge.o
LIB_OBJS += quote.o
LIB_OBJS += range-diff.o
LIB_OBJS += reachable.o
@@ -1296,6 +1281,7 @@
BUILTIN_OBJS += builtin/rebase.o
BUILTIN_OBJS += builtin/receive-pack.o
BUILTIN_OBJS += builtin/reflog.o
+BUILTIN_OBJS += builtin/refs.o
BUILTIN_OBJS += builtin/remote-ext.o
BUILTIN_OBJS += builtin/remote-fd.o
BUILTIN_OBJS += builtin/remote.o
@@ -1347,14 +1333,21 @@
THIRD_PARTY_SOURCES += sha1collisiondetection/%
THIRD_PARTY_SOURCES += sha1dc/%
-UNIT_TEST_PROGRAMS += t-basic
-UNIT_TEST_PROGRAMS += t-mem-pool
-UNIT_TEST_PROGRAMS += t-strbuf
UNIT_TEST_PROGRAMS += t-ctype
+UNIT_TEST_PROGRAMS += t-example-decorate
+UNIT_TEST_PROGRAMS += t-hash
+UNIT_TEST_PROGRAMS += t-mem-pool
+UNIT_TEST_PROGRAMS += t-oidtree
UNIT_TEST_PROGRAMS += t-prio-queue
+UNIT_TEST_PROGRAMS += t-reftable-basics
+UNIT_TEST_PROGRAMS += t-strbuf
+UNIT_TEST_PROGRAMS += t-strcmp-offset
+UNIT_TEST_PROGRAMS += t-strvec
+UNIT_TEST_PROGRAMS += t-trailer
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
+UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
# xdiff and reftable libs may in turn depend on what is in libgit.a
GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
@@ -1458,8 +1451,8 @@
ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X)
endif
-ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
-ALL_LDFLAGS = $(LDFLAGS)
+ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_APPEND)
+ALL_LDFLAGS = $(LDFLAGS) $(LDFLAGS_APPEND)
ifdef SANITIZE
SANITIZERS := $(foreach flag,$(subst $(comma),$(space),$(SANITIZE)),$(flag))
@@ -2373,6 +2366,29 @@
endif
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
+# If you add a new fuzzer, please also make sure to run it in
+# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
+# runs in the future.
+FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
+FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-config.o
+FUZZ_OBJS += oss-fuzz/fuzz-date.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
+.PHONY: fuzz-objs
+fuzz-objs: $(FUZZ_OBJS)
+
+# Always build fuzz objects even if not testing, to prevent bit-rot.
+all:: $(FUZZ_OBJS)
+
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
+
+# Build fuzz programs when possible, even without the necessary fuzzing support,
+# to prevent bit-rot.
+ifdef LINK_FUZZ_PROGRAMS
+all:: $(FUZZ_PROGRAMS)
+endif
+
please_set_SHELL_PATH_to_a_more_modern_shell:
@$$(:)
@@ -2656,20 +2672,17 @@
REFTABLE_OBJS += reftable/pq.o
REFTABLE_OBJS += reftable/reader.o
REFTABLE_OBJS += reftable/record.o
-REFTABLE_OBJS += reftable/refname.o
REFTABLE_OBJS += reftable/generic.o
REFTABLE_OBJS += reftable/stack.o
REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/writer.o
-REFTABLE_TEST_OBJS += reftable/basics_test.o
REFTABLE_TEST_OBJS += reftable/block_test.o
REFTABLE_TEST_OBJS += reftable/dump.o
REFTABLE_TEST_OBJS += reftable/merged_test.o
REFTABLE_TEST_OBJS += reftable/pq_test.o
REFTABLE_TEST_OBJS += reftable/record_test.o
REFTABLE_TEST_OBJS += reftable/readwrite_test.o
-REFTABLE_TEST_OBJS += reftable/refname_test.o
REFTABLE_TEST_OBJS += reftable/stack_test.o
REFTABLE_TEST_OBJS += reftable/test_framework.o
REFTABLE_TEST_OBJS += reftable/tree_test.o
@@ -3228,7 +3241,7 @@
.PRECIOUS: $(TEST_OBJS)
-t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
+t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) $(UNIT_TEST_DIR)/test-lib.o
t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
@@ -3749,42 +3762,6 @@
.PHONY: check-docs
check-docs::
$(MAKE) -C Documentation lint-docs
- @(for v in $(patsubst %$X,%,$(ALL_COMMANDS)); \
- do \
- case "$$v" in \
- git-merge-octopus | git-merge-ours | git-merge-recursive | \
- git-merge-resolve | git-merge-subtree | \
- git-fsck-objects | git-init-db | \
- git-remote-* | git-stage | git-legacy-* | \
- git-?*--?* ) continue ;; \
- esac ; \
- test -f "Documentation/$$v.txt" || \
- echo "no doc: $$v"; \
- sed -e '1,/^### command list/d' -e '/^#/d' command-list.txt | \
- grep -q "^$$v[ ]" || \
- case "$$v" in \
- git) ;; \
- *) echo "no link: $$v";; \
- esac ; \
- done; \
- ( \
- sed -e '1,/^### command list/d' \
- -e '/^#/d' \
- -e '/guide$$/d' \
- -e '/interfaces$$/d' \
- -e 's/[ ].*//' \
- -e 's/^/listed /' command-list.txt; \
- $(MAKE) -C Documentation print-man1 | \
- grep '\.txt$$' | \
- sed -e 's|^|documented |' \
- -e 's/\.txt//'; \
- ) | while read how cmd; \
- do \
- case " $(patsubst %$X,%,$(ALL_COMMANDS) $(BUILT_INS) $(EXCLUDED_PROGRAMS)) " in \
- *" $$cmd "*) ;; \
- *) echo "removed but $$how: $$cmd" ;; \
- esac; \
- done ) | sort
### Make sure built-ins do not have dups and listed in git.c
#
@@ -3858,28 +3835,31 @@
#
# An example command to build against libFuzzer from LLVM 11.0.0:
#
-# make CC=clang CXX=clang++ \
+# make CC=clang FUZZ_CXX=clang++ \
# CFLAGS="-fsanitize=fuzzer-no-link,address" \
# LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
# fuzz-all
#
+FUZZ_CXX ?= $(CC)
FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)
.PHONY: fuzz-all
+fuzz-all: $(FUZZ_PROGRAMS)
$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
- $(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
+ $(QUIET_LINK)$(FUZZ_CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
-Wl,--allow-multiple-definition \
$(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
-fuzz-all: $(FUZZ_PROGRAMS)
-
-$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS
+$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \
+ $(UNIT_TEST_DIR)/test-lib.o \
+ $(UNIT_TEST_DIR)/lib-oid.o \
+ $(GITLIBS) GIT-LDFLAGS
$(call mkdir_p_parent_template)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
$(filter %.o,$^) $(filter %.a,$^) $(LIBS)
.PHONY: build-unit-tests unit-tests
build-unit-tests: $(UNIT_TEST_PROGS)
-unit-tests: $(UNIT_TEST_PROGS)
+unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X
$(MAKE) -C t/ unit-tests
diff --git a/RelNotes b/RelNotes
index 36d6ed4..cc696fc 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.45.3.txt
\ No newline at end of file
+Documentation/RelNotes/2.46.0.txt
\ No newline at end of file
diff --git a/add-interactive.c b/add-interactive.c
index e17602b..49042b3 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "add-interactive.h"
#include "color.h"
@@ -532,8 +534,9 @@ static int get_modified_files(struct repository *r,
size_t *binary_count)
{
struct object_id head_oid;
- int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- &head_oid, NULL);
+ int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING,
+ &head_oid, NULL);
struct collection_status s = { 0 };
int i;
@@ -556,7 +559,7 @@ static int get_modified_files(struct repository *r,
s.skip_unseen = filter && i;
opt.def = is_initial ?
- empty_tree_oid_hex() : oid_to_hex(&head_oid);
+ empty_tree_oid_hex(the_repository->hash_algo) : oid_to_hex(&head_oid);
repo_init_revisions(r, &rev, NULL);
setup_revisions(0, NULL, &rev, &opt);
@@ -761,8 +764,10 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
size_t count, i, j;
struct object_id oid;
- int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
- NULL);
+ int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING,
+ &oid,
+ NULL);
struct lock_file index_lock;
const char **paths;
struct tree *tree;
@@ -990,8 +995,10 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
ssize_t count, i;
struct object_id oid;
- int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
- NULL);
+ int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING,
+ &oid,
+ NULL);
if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
return -1;
diff --git a/add-patch.c b/add-patch.c
index b2fbd8c..6e176cd 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "add-interactive.h"
#include "advice.h"
@@ -293,10 +295,9 @@ static void err(struct add_p_state *s, const char *fmt, ...)
va_list args;
va_start(args, fmt);
- fputs(s->s.error_color, stderr);
- vfprintf(stderr, fmt, args);
- fputs(s->s.reset_color, stderr);
- fputc('\n', stderr);
+ fputs(s->s.error_color, stdout);
+ vprintf(fmt, args);
+ puts(s->s.reset_color);
va_end(args);
}
@@ -422,7 +423,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
/* could be on an unborn branch */
!strcmp("HEAD", s->revision) &&
repo_get_oid(the_repository, "HEAD", &oid) ?
- empty_tree_oid_hex() : s->revision);
+ empty_tree_oid_hex(the_repository->hash_algo) : s->revision);
}
color_arg_index = args.nr;
/* Use `--no-color` explicitly, just in case `diff.color = always`. */
@@ -1229,6 +1230,7 @@ static int prompt_yesno(struct add_p_state *s, const char *prompt)
fflush(stdout);
if (read_single_character(s) == EOF)
return -1;
+ /* do not limit to 1-byte input to allow 'no' etc. */
switch (tolower(s->answer.buf[0])) {
case 'n': return 0;
case 'y': return 1;
@@ -1327,7 +1329,7 @@ static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
err(s, _("Nothing was applied.\n"));
} else
/* As a last resort, show the diff to the user */
- fwrite(diff->buf, diff->len, 1, stderr);
+ fwrite(diff->buf, diff->len, 1, stdout);
return 0;
}
@@ -1512,6 +1514,12 @@ static int patch_update_file(struct add_p_state *s,
if (!s->answer.len)
continue;
ch = tolower(s->answer.buf[0]);
+
+ /* 'g' takes a hunk number and '/' takes a regexp */
+ if (s->answer.len != 1 && (ch != 'g' && ch != '/')) {
+ err(s, _("Only one letter is expected, got '%s'"), s->answer.buf);
+ continue;
+ }
if (ch == 'y') {
hunk->use = USE_HUNK;
soft_increment:
@@ -1669,7 +1677,7 @@ static int patch_update_file(struct add_p_state *s,
}
} else if (s->answer.buf[0] == 'p') {
rendered_hunk_index = -1;
- } else {
+ } else if (s->answer.buf[0] == '?') {
const char *p = _(help_patch_remainder), *eol = p;
color_fprintf(stdout, s->s.help_color, "%s",
@@ -1693,6 +1701,9 @@ static int patch_update_file(struct add_p_state *s,
color_fprintf_ln(stdout, s->s.help_color,
"%.*s", (int)(eol - p), p);
}
+ } else {
+ err(s, _("Unknown command '%s' (use '?' for help)"),
+ s->answer.buf);
}
}
@@ -1779,9 +1790,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
break;
if (s.file_diff_nr == 0)
- fprintf(stderr, _("No changes.\n"));
+ err(&s, _("No changes."));
else if (binary_count == s.file_diff_nr)
- fprintf(stderr, _("Only binary files changed.\n"));
+ err(&s, _("Only binary files changed."));
add_p_state_clear(&s);
return 0;
diff --git a/advice.c b/advice.c
index e070e64..558a46f 100644
--- a/advice.c
+++ b/advice.c
@@ -2,6 +2,7 @@
#include "advice.h"
#include "config.h"
#include "color.h"
+#include "environment.h"
#include "gettext.h"
#include "help.h"
#include "string-list.h"
@@ -128,6 +129,12 @@ void advise(const char *advice, ...)
int advice_enabled(enum advice_type type)
{
int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
+ static int globally_enabled = -1;
+
+ if (globally_enabled < 0)
+ globally_enabled = git_env_bool(GIT_ADVICE_ENVIRONMENT, 1);
+ if (!globally_enabled)
+ return 0;
if (type == ADVICE_PUSH_UPDATE_REJECTED)
return enabled &&
diff --git a/alias.c b/alias.c
index 5a238f2..4daafd9 100644
--- a/alias.c
+++ b/alias.c
@@ -21,9 +21,11 @@ static int config_alias_cb(const char *key, const char *value,
return 0;
if (data->alias) {
- if (!strcasecmp(p, data->alias))
- return git_config_string((const char **)&data->v,
+ if (!strcasecmp(p, data->alias)) {
+ FREE_AND_NULL(data->v);
+ return git_config_string(&data->v,
key, value);
+ }
} else if (data->list) {
string_list_append(data->list, p);
}
diff --git a/apply.c b/apply.c
index 901b67e..ff939f9 100644
--- a/apply.c
+++ b/apply.c
@@ -7,6 +7,8 @@
*
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "base85.h"
@@ -3680,7 +3682,7 @@ static int try_threeway(struct apply_state *state,
if (status) {
patch->conflicted_threeway = 1;
if (patch->is_new)
- oidclr(&patch->threeway_stage[0]);
+ oidclr(&patch->threeway_stage[0], the_repository->hash_algo);
else
oidcpy(&patch->threeway_stage[0], &pre_oid);
oidcpy(&patch->threeway_stage[1], &our_oid);
diff --git a/apply.h b/apply.h
index 7cd38b1..b9f18ce 100644
--- a/apply.h
+++ b/apply.h
@@ -1,7 +1,7 @@
#ifndef APPLY_H
#define APPLY_H
-#include "hash-ll.h"
+#include "hash.h"
#include "lockfile.h"
#include "string-list.h"
#include "strmap.h"
diff --git a/archive-tar.c b/archive-tar.c
index 8ae3012..e7b3489 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2005, 2006 Rene Scharfe
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "gettext.h"
diff --git a/archive-zip.c b/archive-zip.c
index fd1d3f8..9f32730 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2006 Rene Scharfe
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "archive.h"
diff --git a/archive.c b/archive.c
index 5287fcd..7bd60d0 100644
--- a/archive.c
+++ b/archive.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
diff --git a/attr.c b/attr.c
index 33473bd..06b5b5e 100644
--- a/attr.c
+++ b/attr.c
@@ -6,6 +6,8 @@
* an insanely large number of attributes.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -25,7 +27,7 @@
#include "tree-walk.h"
#include "object-name.h"
-const char *git_attr_tree;
+char *git_attr_tree;
const char git_attr__true[] = "(builtin)true";
const char git_attr__false[] = "\0(builtin)false";
@@ -865,7 +867,8 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags);
} else {
buf = read_blob_data_from_index(istate, path, &size);
- stack = read_attr_from_buf(buf, size, path, flags);
+ if (buf)
+ stack = read_attr_from_buf(buf, size, path, flags);
}
return stack;
}
@@ -1205,15 +1208,16 @@ static void collect_some_attrs(struct index_state *istate,
}
static const char *default_attr_source_tree_object_name;
-static int ignore_bad_attr_tree;
void set_git_attr_source(const char *tree_object_name)
{
default_attr_source_tree_object_name = xstrdup(tree_object_name);
}
-static void compute_default_attr_source(struct object_id *attr_source)
+static int compute_default_attr_source(struct object_id *attr_source)
{
+ int ignore_bad_attr_tree = 0;
+
if (!default_attr_source_tree_object_name)
default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT);
@@ -1222,22 +1226,34 @@ static void compute_default_attr_source(struct object_id *attr_source)
ignore_bad_attr_tree = 1;
}
- if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
- return;
+ if (!default_attr_source_tree_object_name)
+ return 0;
+
+ if (!startup_info->have_repository) {
+ if (!ignore_bad_attr_tree)
+ die(_("cannot use --attr-source or GIT_ATTR_SOURCE without repo"));
+ return 0;
+ }
if (repo_get_oid_treeish(the_repository,
default_attr_source_tree_object_name,
- attr_source) && !ignore_bad_attr_tree)
- die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+ attr_source)) {
+ if (!ignore_bad_attr_tree)
+ die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+ return 0;
+ }
+
+ return 1;
}
static struct object_id *default_attr_source(void)
{
static struct object_id attr_source;
+ static int has_attr_source = -1;
- if (is_null_oid(&attr_source))
- compute_default_attr_source(&attr_source);
- if (is_null_oid(&attr_source))
+ if (has_attr_source < 0)
+ has_attr_source = compute_default_attr_source(&attr_source);
+ if (!has_attr_source)
return NULL;
return &attr_source;
}
@@ -1288,7 +1304,8 @@ static const char *builtin_object_mode_attr(struct index_state *istate, const ch
if (pos >= 0) {
if (S_ISGITLINK(istate->cache[pos]->ce_mode))
mode = istate->cache[pos]->ce_mode;
- } else if (resolve_gitlink_ref(path, "HEAD", &oid) == 0) {
+ } else if (repo_resolve_gitlink_ref(the_repository, path,
+ "HEAD", &oid) == 0) {
mode = S_IFGITLINK;
}
}
diff --git a/attr.h b/attr.h
index afa33ce..bb33b60 100644
--- a/attr.h
+++ b/attr.h
@@ -238,6 +238,6 @@ const char *git_attr_global_file(void);
/* Return whether the system gitattributes file is enabled and should be used. */
int git_attr_system_is_enabled(void);
-extern const char *git_attr_tree;
+extern char *git_attr_tree;
#endif /* ATTR_H */
diff --git a/bisect.c b/bisect.c
index 29aae87..135f94b 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
@@ -469,7 +471,8 @@ static int register_ref(const char *refname, const struct object_id *oid,
static int read_bisect_refs(void)
{
- return for_each_ref_in("refs/bisect/", register_ref, NULL);
+ return refs_for_each_ref_in(get_main_ref_store(the_repository),
+ "refs/bisect/", register_ref, NULL);
}
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
@@ -709,7 +712,7 @@ static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
static int is_expected_rev(const struct object_id *oid)
{
struct object_id expected_oid;
- if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected_oid))
return 0;
return oideq(oid, &expected_oid);
}
@@ -721,11 +724,14 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
struct pretty_print_context pp = {0};
struct strbuf commit_msg = STRBUF_INIT;
- update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), NULL,
+ "BISECT_EXPECTED_REV", bisect_rev, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
if (no_checkout) {
- update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), NULL,
+ "BISECT_HEAD", bisect_rev, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
} else {
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -1027,7 +1033,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
* If no_checkout is non-zero, the bisection process does not
* checkout the trial commit but instead simply updates BISECT_HEAD.
*/
- int no_checkout = ref_exists("BISECT_HEAD");
+ int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
+ "BISECT_HEAD");
unsigned bisect_flags = 0;
read_bisect_terms(&term_bad, &term_good);
@@ -1178,10 +1185,14 @@ int bisect_clean_state(void)
/* There may be some refs packed during bisection */
struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
- for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
+ refs_for_each_ref_in(get_main_ref_store(the_repository),
+ "refs/bisect", mark_for_removal,
+ (void *) &refs_for_removal);
string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
string_list_append(&refs_for_removal, xstrdup("BISECT_EXPECTED_REV"));
- result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
+ result = refs_delete_refs(get_main_ref_store(the_repository),
+ "bisect: remove", &refs_for_removal,
+ REF_NO_DEREF);
refs_for_removal.strdup_strings = 1;
string_list_clear(&refs_for_removal, 0);
unlink_or_warn(git_path_bisect_ancestors_ok());
diff --git a/blame.c b/blame.c
index 1a16d4e..d403c46 100644
--- a/blame.c
+++ b/blame.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "refs.h"
#include "object-store-ll.h"
@@ -1246,7 +1248,7 @@ static int fill_blob_sha1_and_mode(struct repository *r,
goto error_out;
return 0;
error_out:
- oidclr(&origin->blob_oid);
+ oidclr(&origin->blob_oid, the_repository->hash_algo);
origin->mode = S_IFINVALID;
return -1;
}
@@ -2700,7 +2702,7 @@ static struct commit *dwim_reverse_initial(struct rev_info *revs,
return NULL;
/* Do we have HEAD? */
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL))
return NULL;
head_commit = lookup_commit_reference_gently(revs->repo,
&head_oid, 1);
@@ -2803,7 +2805,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
if (sb->final) {
parent_oid = &sb->final->object.oid;
} else {
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL))
die("no such ref: HEAD");
parent_oid = &head_oid;
}
diff --git a/bloom.c b/bloom.c
index e529f76..bbf146f 100644
--- a/bloom.c
+++ b/bloom.c
@@ -6,6 +6,7 @@
#include "commit-graph.h"
#include "commit.h"
#include "commit-slab.h"
+#include "repository.h"
define_commit_slab(bloom_filter_slab, struct bloom_filter);
diff --git a/branch.c b/branch.c
index e4a738f..c887ea2 100644
--- a/branch.c
+++ b/branch.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
@@ -377,7 +379,7 @@ int validate_branchname(const char *name, struct strbuf *ref)
exit(code);
}
- return ref_exists(ref->buf);
+ return refs_ref_exists(get_main_ref_store(the_repository), ref->buf);
}
static int initialized_checked_out_branches;
@@ -623,11 +625,12 @@ void create_branch(struct repository *r,
msg = xstrfmt("branch: Reset to %s", start_name);
else
msg = xstrfmt("branch: Created from %s", start_name);
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf,
&oid, forcing ? NULL : null_oid(),
- 0, msg, &err) ||
+ NULL, NULL, 0, msg, &err) ||
ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
diff --git a/builtin.h b/builtin.h
index 2828063..14fa017 100644
--- a/builtin.h
+++ b/builtin.h
@@ -1,6 +1,14 @@
#ifndef BUILTIN_H
#define BUILTIN_H
+/*
+ * TODO: Almost all of our builtins access `the_repository` by necessity
+ * because they do not get passed a pointer to it. We should adapt the function
+ * signature of those main functions to accept a `struct repository *` and then
+ * remove the macro here.
+ */
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
/*
@@ -207,6 +215,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix);
int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
int cmd_receive_pack(int argc, const char **argv, const char *prefix);
int cmd_reflog(int argc, const char **argv, const char *prefix);
+int cmd_refs(int argc, const char **argv, const char *prefix);
int cmd_remote(int argc, const char **argv, const char *prefix);
int cmd_remote_ext(int argc, const char **argv, const char *prefix);
int cmd_remote_fd(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index b7d3ff1..40b61ef 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006 Linus Torvalds
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -40,20 +40,20 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
{
int i, ret = 0;
- for (i = 0; i < the_index.cache_nr; i++) {
- struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ struct cache_entry *ce = the_repository->index->cache[i];
int err;
if (!include_sparse &&
(ce_skip_worktree(ce) ||
- !path_in_sparse_checkout(ce->name, &the_index)))
+ !path_in_sparse_checkout(ce->name, the_repository->index)))
continue;
- if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+ if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
continue;
if (!show_only)
- err = chmod_index_entry(&the_index, ce, flip);
+ err = chmod_index_entry(the_repository->index, ce, flip);
else
err = S_ISREG(ce->ce_mode) ? 0 : -1;
@@ -68,20 +68,20 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
{
int i, retval = 0;
- for (i = 0; i < the_index.cache_nr; i++) {
- struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ struct cache_entry *ce = the_repository->index->cache[i];
if (!include_sparse &&
(ce_skip_worktree(ce) ||
- !path_in_sparse_checkout(ce->name, &the_index)))
+ !path_in_sparse_checkout(ce->name, the_repository->index)))
continue;
if (ce_stage(ce))
continue; /* do not touch unmerged paths */
if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
continue; /* do not touch non blobs */
- if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+ if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
continue;
- retval |= add_file_to_index(&the_index, ce->name,
+ retval |= add_file_to_index(the_repository->index, ce->name,
flags | ADD_CACHE_RENORMALIZE);
}
@@ -100,11 +100,11 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
i = dir->nr;
while (--i >= 0) {
struct dir_entry *entry = *src++;
- if (dir_path_match(&the_index, entry, pathspec, prefix, seen))
+ if (dir_path_match(the_repository->index, entry, pathspec, prefix, seen))
*dst++ = entry;
}
dir->nr = dst - dir->entries;
- add_pathspec_matches_against_index(pathspec, &the_index, seen,
+ add_pathspec_matches_against_index(pathspec, the_repository->index, seen,
PS_IGNORE_SKIP_WORKTREE);
return seen;
}
@@ -119,14 +119,14 @@ static int refresh(int verbose, const struct pathspec *pathspec)
(verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
seen = xcalloc(pathspec->nr, 1);
- refresh_index(&the_index, flags, pathspec, seen,
+ refresh_index(the_repository->index, flags, pathspec, seen,
_("Unstaged changes after refreshing the index:"));
for (i = 0; i < pathspec->nr; i++) {
if (!seen[i]) {
const char *path = pathspec->items[i].original;
if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
- !path_in_sparse_checkout(path, &the_index)) {
+ !path_in_sparse_checkout(path, the_repository->index)) {
string_list_append(&only_match_skip_worktree,
pathspec->items[i].original);
} else {
@@ -150,11 +150,7 @@ static int refresh(int verbose, const struct pathspec *pathspec)
int interactive_add(const char **argv, const char *prefix, int patch)
{
struct pathspec pathspec;
- int unused, ret;
-
- if (!git_config_get_bool("add.interactive.usebuiltin", &unused))
- warning(_("the add.interactive.useBuiltin setting has been removed!\n"
- "See its entry in 'git help config' for details."));
+ int ret;
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
@@ -338,12 +334,12 @@ static int add_files(struct dir_struct *dir, int flags)
for (i = 0; i < dir->nr; i++) {
if (!include_sparse &&
- !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
+ !path_in_sparse_checkout(dir->entries[i]->name, the_repository->index)) {
string_list_append(&matched_sparse_paths,
dir->entries[i]->name);
continue;
}
- if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
+ if (add_file_to_index(the_repository->index, dir->entries[i]->name, flags)) {
if (!ignore_add_errors)
die(_("adding files failed"));
exit_status = 1;
@@ -461,8 +457,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
die(_("index file corrupt"));
- die_in_unpopulated_submodule(&the_index, prefix);
- die_path_inside_submodule(&the_index, &pathspec);
+ die_in_unpopulated_submodule(the_repository->index, prefix);
+ die_path_inside_submodule(the_repository->index, &pathspec);
if (add_new_files) {
int baselen;
@@ -474,7 +470,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
}
/* This picks up the paths that are not tracked */
- baselen = fill_directory(&dir, &the_index, &pathspec);
+ baselen = fill_directory(&dir, the_repository->index, &pathspec);
if (pathspec.nr)
seen = prune_directory(&dir, &pathspec, baselen);
}
@@ -491,7 +487,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (!seen)
seen = find_pathspecs_matching_against_index(&pathspec,
- &the_index, PS_IGNORE_SKIP_WORKTREE);
+ the_repository->index, PS_IGNORE_SKIP_WORKTREE);
/*
* file_exists() assumes exact match
@@ -527,8 +523,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
!file_exists(path)) {
if (ignore_missing) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, &the_index, path, &dtype))
- dir_add_ignored(&dir, &the_index,
+ if (is_excluded(&dir, the_repository->index, path, &dtype))
+ dir_add_ignored(&dir, the_repository->index,
path, pathspec.items[i].len);
} else
die(_("pathspec '%s' did not match any files"),
@@ -569,7 +565,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
end_odb_transaction();
finish:
- if (write_locked_index(&the_index, &lock_file,
+ if (write_locked_index(the_repository->index, &lock_file,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new index file"));
diff --git a/builtin/am.c b/builtin/am.c
index 022cae2..8f9619e 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -3,7 +3,7 @@
*
* Based on git-am.sh by Junio C Hamano.
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -408,7 +408,7 @@ static void am_load(struct am_state *state)
read_commit_msg(state);
if (read_state_file(&sb, state, "original-commit", 1) < 0)
- oidclr(&state->orig_commit);
+ oidclr(&state->orig_commit, the_repository->hash_algo);
else if (get_oid_hex(sb.buf, &state->orig_commit) < 0)
die(_("could not parse %s"), am_path(state, "original-commit"));
@@ -1001,7 +1001,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
die_errno(_("failed to create directory '%s'"), state->dir);
- delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "REBASE_HEAD", NULL, REF_NO_DEREF);
if (split_mail(state, patch_format, paths, keep_cr) < 0) {
am_destroy(state);
@@ -1081,12 +1082,15 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
if (!repo_get_oid(the_repository, "HEAD", &curr_head)) {
write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
if (!state->rebasing)
- update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "am", "ORIG_HEAD", &curr_head, NULL,
+ 0,
+ UPDATE_REFS_DIE_ON_ERR);
} else {
write_state_text(state, "abort-safety", "");
if (!state->rebasing)
- delete_ref(NULL, "ORIG_HEAD", NULL, 0);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, "ORIG_HEAD", NULL, 0);
}
/*
@@ -1117,9 +1121,10 @@ static void am_next(struct am_state *state)
unlink(am_path(state, "author-script"));
unlink(am_path(state, "final-commit"));
- oidclr(&state->orig_commit);
+ oidclr(&state->orig_commit, the_repository->hash_algo);
unlink(am_path(state, "original-commit"));
- delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "REBASE_HEAD", NULL, REF_NO_DEREF);
if (!repo_get_oid(the_repository, "HEAD", &head))
write_state_text(state, "abort-safety", oid_to_hex(&head));
@@ -1466,8 +1471,9 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
oidcpy(&state->orig_commit, &commit_oid);
write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
- update_ref("am", "REBASE_HEAD", &commit_oid,
- NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), "am",
+ "REBASE_HEAD", &commit_oid,
+ NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
return 0;
}
@@ -1536,8 +1542,8 @@ static int run_apply(const struct am_state *state, const char *index_file)
if (index_file) {
/* Reload index as apply_all_patches() will have modified it. */
- discard_index(&the_index);
- read_index_from(&the_index, index_file, get_git_dir());
+ discard_index(the_repository->index);
+ read_index_from(the_repository->index, index_file, get_git_dir());
}
return 0;
@@ -1579,10 +1585,10 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
if (build_fake_ancestor(state, index_path))
return error("could not build fake ancestor");
- discard_index(&the_index);
- read_index_from(&the_index, index_path, get_git_dir());
+ discard_index(the_repository->index);
+ read_index_from(the_repository->index, index_path, get_git_dir());
- if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
+ if (write_index_as_tree(&orig_tree, the_repository->index, index_path, 0, NULL))
return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
say(state, stdout, _("Using index info to reconstruct a base tree..."));
@@ -1608,12 +1614,12 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
return error(_("Did you hand edit your patch?\n"
"It does not apply to blobs recorded in its index."));
- if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL))
+ if (write_index_as_tree(&their_tree, the_repository->index, index_path, 0, NULL))
return error("could not write tree");
say(state, stdout, _("Falling back to patching base and 3-way merge..."));
- discard_index(&the_index);
+ discard_index(the_repository->index);
repo_read_index(the_repository);
/*
@@ -1660,7 +1666,7 @@ static void do_commit(const struct am_state *state)
if (!state->no_verify && run_hooks("pre-applypatch"))
exit(1);
- if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
+ if (write_index_as_tree(&tree, the_repository->index, get_index_file(), 0, NULL))
die(_("git write-tree failed to write a tree"));
if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) {
@@ -1697,8 +1703,9 @@ static void do_commit(const struct am_state *state)
strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
state->msg);
- update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), sb.buf, "HEAD",
+ &commit, old_oid, 0,
+ UPDATE_REFS_DIE_ON_ERR);
if (state->rebasing) {
FILE *fp = xfopen(am_path(state, "rewritten"), "a");
@@ -1948,7 +1955,7 @@ static void am_resolve(struct am_state *state, int allow_empty)
}
}
- if (unmerged_index(&the_index)) {
+ if (unmerged_index(the_repository->index)) {
printf_ln(_("You still have unmerged paths in your index.\n"
"You should 'git add' each file with resolved conflicts to mark them as such.\n"
"You might run `git rm` on a file to accept \"deleted by them\" for it."));
@@ -1987,12 +1994,12 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.update = 1;
opts.merge = 1;
opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
@@ -2006,7 +2013,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
return -1;
}
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return 0;
@@ -2029,8 +2036,8 @@ static int merge_tree(struct tree *tree)
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.merge = 1;
opts.fn = oneway_merge;
init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
@@ -2040,7 +2047,7 @@ static int merge_tree(struct tree *tree)
return -1;
}
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return 0;
@@ -2068,7 +2075,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
if (fast_forward_to(head_tree, head_tree, 1))
return -1;
- if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
+ if (write_index_as_tree(&index, the_repository->index, get_index_file(), 0, NULL))
return -1;
index_tree = parse_tree_indirect(&index);
@@ -2144,11 +2151,11 @@ static int safe_to_abort(const struct am_state *state)
if (get_oid_hex(sb.buf, &abort_safety))
die(_("could not parse %s"), am_path(state, "abort-safety"));
} else
- oidclr(&abort_safety);
+ oidclr(&abort_safety, the_repository->hash_algo);
strbuf_release(&sb);
if (repo_get_oid(the_repository, "HEAD", &head))
- oidclr(&head);
+ oidclr(&head, the_repository->hash_algo);
if (oideq(&head, &abort_safety))
return 1;
@@ -2175,7 +2182,8 @@ static void am_abort(struct am_state *state)
am_rerere_clear();
- curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
+ curr_branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &curr_head, NULL);
has_curr_head = curr_branch && !is_null_oid(&curr_head);
if (!has_curr_head)
oidcpy(&curr_head, the_hash_algo->empty_tree);
@@ -2188,11 +2196,13 @@ static void am_abort(struct am_state *state)
die(_("failed to clean index"));
if (has_orig_head)
- update_ref("am --abort", "HEAD", &orig_head,
- has_curr_head ? &curr_head : NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "am --abort", "HEAD", &orig_head,
+ has_curr_head ? &curr_head : NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
else if (curr_branch)
- delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ curr_branch, NULL, REF_NO_DEREF);
free(curr_branch);
am_destroy(state);
@@ -2383,6 +2393,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
N_("show the patch being applied"),
PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW },
+ OPT_CMDMODE(0, "retry", &resume_mode,
+ N_("try to apply current patch again"),
+ RESUME_APPLY),
OPT_CMDMODE(0, "allow-empty", &resume_mode,
N_("record the empty patch as an empty commit"),
RESUME_ALLOW_EMPTY),
diff --git a/builtin/apply.c b/builtin/apply.c
index 861a019..d623c52 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1,6 +1,7 @@
#include "builtin.h"
#include "gettext.h"
#include "repository.h"
+#include "hash.h"
#include "apply.h"
static const char * const apply_usage[] = {
@@ -18,6 +19,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
if (init_apply_state(&state, the_repository, prefix))
exit(128);
+ /*
+ * We could to redo the "apply.c" machinery to make this
+ * arbitrary fallback unnecessary, but it is dubious that it
+ * is worth the effort.
+ * cf. https://lore.kernel.org/git/xmqqcypfcmn4.fsf@gitster.g/
+ */
+ if (!the_hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
argc = apply_parse_options(argc, argv,
&state, &force_apply, &options,
apply_usage);
diff --git a/builtin/archive.c b/builtin/archive.c
index 15ee1ec..7234607 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -31,9 +31,7 @@ static int run_remote_archiver(int argc, const char **argv,
struct packet_reader reader;
_remote = remote_get(remote);
- if (!_remote->url[0])
- die(_("git archive: Remote with no URL"));
- transport = transport_get(_remote, _remote->url[0]);
+ transport = transport_get(_remote, _remote->url.v[0]);
transport_connect(transport, "git-upload-archive", exec, fd);
/*
diff --git a/builtin/bisect.c b/builtin/bisect.c
index f69c3f7..dabce9b 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -243,7 +243,7 @@ static int bisect_reset(const char *commit)
strbuf_addstr(&branch, commit);
}
- if (branch.len && !ref_exists("BISECT_HEAD")) {
+ if (branch.len && !refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD")) {
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.git_cmd = 1;
@@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
return bisect_clean_state();
}
-static void log_commit(FILE *fp, char *fmt, const char *state,
+static void log_commit(FILE *fp,
+ const char *fmt, const char *state,
struct commit *commit)
{
struct pretty_print_context pp = {0};
@@ -302,8 +303,8 @@ static int bisect_write(const char *state, const char *rev,
goto finish;
}
- if (update_ref(NULL, tag.buf, &oid, NULL, 0,
- UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), NULL, tag.buf, &oid, NULL, 0,
+ UPDATE_REFS_MSG_ON_ERR)) {
res = -1;
goto finish;
}
@@ -416,11 +417,12 @@ static void bisect_status(struct bisect_state *state,
char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
char *good_glob = xstrfmt("%s-*", terms->term_good);
- if (ref_exists(bad_ref))
+ if (refs_ref_exists(get_main_ref_store(the_repository), bad_ref))
state->nr_bad = 1;
- for_each_glob_ref_in(inc_nr, good_glob, "refs/bisect/",
- (void *) &state->nr_good);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository), inc_nr,
+ good_glob, "refs/bisect/",
+ (void *) &state->nr_good);
free(good_glob);
free(bad_ref);
@@ -574,9 +576,11 @@ static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
reset_revision_walk();
repo_init_revisions(the_repository, revs, NULL);
setup_revisions(0, NULL, revs, NULL);
- for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ add_bisect_ref, bad, "refs/bisect/", &cb);
cb.object_flags = UNINTERESTING;
- for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ add_bisect_ref, good, "refs/bisect/", &cb);
if (prepare_revision_walk(revs))
res = error(_("revision walk setup failed\n"));
@@ -636,7 +640,7 @@ static int bisect_successful(struct bisect_terms *terms)
char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad);
int res;
- read_ref(bad_ref, &oid);
+ refs_read_ref(get_main_ref_store(the_repository), bad_ref, &oid);
commit = lookup_commit_reference_by_name(bad_ref);
repo_format_commit_message(the_repository, commit, "%s", &commit_name,
&pp);
@@ -779,7 +783,8 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
/*
* Verify HEAD
*/
- head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
+ head = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, &head_oid, &flags);
if (!head)
if (repo_get_oid(the_repository, "HEAD", &head_oid))
return error(_("bad HEAD - I need a HEAD"));
@@ -838,8 +843,8 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
res = error(_("invalid ref: '%s'"), start_head.buf);
goto finish;
}
- if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
- UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), NULL, "BISECT_HEAD", &oid, NULL, 0,
+ UPDATE_REFS_MSG_ON_ERR)) {
res = BISECT_FAILED;
goto finish;
}
@@ -972,7 +977,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
oid_array_append(&revs, &commit->object.oid);
}
- if (read_ref("BISECT_EXPECTED_REV", &expected))
+ if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected))
verify_expected = 0; /* Ignore invalid file contents */
for (i = 0; i < revs.nr; i++) {
@@ -982,7 +987,9 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
}
if (verify_expected && !oideq(&revs.oid[i], &expected)) {
unlink_or_warn(git_path_bisect_ancestors_ok());
- delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, "BISECT_EXPECTED_REV", NULL,
+ REF_NO_DEREF);
verify_expected = 0;
}
}
@@ -1179,13 +1186,15 @@ static int verify_good(const struct bisect_terms *terms, const char *command)
struct object_id good_rev;
struct object_id current_rev;
char *good_glob = xstrfmt("%s-*", terms->term_good);
- int no_checkout = ref_exists("BISECT_HEAD");
+ int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
+ "BISECT_HEAD");
- for_each_glob_ref_in(get_first_good, good_glob, "refs/bisect/",
- &good_rev);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ get_first_good, good_glob, "refs/bisect/",
+ &good_rev);
free(good_glob);
- if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", ¤t_rev))
+ if (refs_read_ref(get_main_ref_store(the_repository), no_checkout ? "BISECT_HEAD" : "HEAD", ¤t_rev))
return -1;
res = bisect_checkout(&good_rev, no_checkout);
diff --git a/builtin/blame.c b/builtin/blame.c
index 9aa7468..babc147 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -5,7 +5,7 @@
* See COPYING for licensing conditions
*/
-#include "git-compat-util.h"
+#include "builtin.h"
#include "config.h"
#include "color.h"
#include "builtin.h"
@@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what,
{
struct ident_split ident;
size_t len, maillen, namelen;
- char *tmp, *endp;
+ const char *tmp, *endp;
const char *namebuf, *mailbuf;
tmp = strstr(inbuf, what);
@@ -718,7 +718,7 @@ static int git_blame_config(const char *var, const char *value,
return 0;
}
if (!strcmp(var, "blame.ignorerevsfile")) {
- const char *str;
+ char *str;
int ret;
ret = git_config_pathname(&str, var, value);
@@ -852,6 +852,7 @@ static void build_ignorelist(struct blame_scoreboard *sb,
oidset_clear(&sb->ignore_list);
else
oidset_parse_file_carefully(&sb->ignore_list, i->string,
+ the_repository->hash_algo,
peel_to_commit_oid, sb);
}
for_each_string_list_item(i, ignore_rev_list) {
@@ -915,7 +916,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
struct range_set ranges;
unsigned int range_i;
long anchor;
- const int hexsz = the_hash_algo->hexsz;
long num_lines = 0;
const char *str_usage = cmd_is_annotate ? annotate_usage : blame_usage;
const char **opt_usage = cmd_is_annotate ? annotate_opt_usage : blame_opt_usage;
@@ -973,11 +973,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
} else if (show_progress < 0)
show_progress = isatty(2);
- if (0 < abbrev && abbrev < hexsz)
+ if (0 < abbrev && abbrev < (int)the_hash_algo->hexsz)
/* one more abbrev length is needed for the boundary commit */
abbrev++;
else if (!abbrev)
- abbrev = hexsz;
+ abbrev = the_hash_algo->hexsz;
if (revs_file && read_ancestry(revs_file))
die_errno("reading graft file '%s' failed", revs_file);
@@ -1093,8 +1093,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
struct commit *head_commit;
struct object_id head_oid;
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- &head_oid, NULL) ||
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING,
+ &head_oid, NULL) ||
!(head_commit = lookup_commit_reference_gently(revs.repo,
&head_oid, 1)))
die("no such ref: HEAD");
diff --git a/builtin/branch.c b/builtin/branch.c
index dd3e3a7..48cac74 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -148,8 +148,8 @@ static int branch_merged(int kind, const char *name,
if (upstream &&
(reference_name = reference_name_to_free =
- resolve_refdup(upstream, RESOLVE_REF_READING,
- &oid, NULL)) != NULL)
+ refs_resolve_refdup(get_main_ref_store(the_repository), upstream, RESOLVE_REF_READING,
+ &oid, NULL)) != NULL)
reference_rev = lookup_commit_reference(the_repository,
&oid);
}
@@ -272,21 +272,24 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
}
}
- target = resolve_refdup(name,
- RESOLVE_REF_READING
- | RESOLVE_REF_NO_RECURSE
- | RESOLVE_REF_ALLOW_BAD_NAME,
- &oid, &flags);
+ target = refs_resolve_refdup(get_main_ref_store(the_repository),
+ name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ &oid, &flags);
if (!target) {
if (remote_branch) {
error(_("remote-tracking branch '%s' not found"), bname.buf);
} else {
char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
- char *virtual_target = resolve_refdup(virtual_name,
- RESOLVE_REF_READING
- | RESOLVE_REF_NO_RECURSE
- | RESOLVE_REF_ALLOW_BAD_NAME,
- &oid, &flags);
+ char *virtual_target = refs_resolve_refdup(get_main_ref_store(the_repository),
+ virtual_name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ &oid,
+ &flags);
FREE_AND_NULL(virtual_name);
if (virtual_target)
@@ -317,13 +320,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(target);
}
- if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+ if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
ret = 1;
for_each_string_list_item(item, &refs_to_delete) {
char *describe_ref = item->util;
char *name = item->string;
- if (!ref_exists(name)) {
+ if (!refs_ref_exists(get_main_ref_store(the_repository), name)) {
char *refname = name + branch_name_pos;
if (!quiet)
printf(remote_branch
@@ -499,7 +502,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
static void print_current_branch_name(void)
{
int flags;
- const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+ const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &flags);
const char *shortname;
if (!refname)
die(_("could not resolve HEAD"));
@@ -555,7 +559,7 @@ static int replace_each_worktree_head_symref(struct worktree **worktrees,
continue;
refs = get_worktree_ref_store(worktrees[i]);
- if (refs_create_symref(refs, "HEAD", newref, logmsg))
+ if (refs_update_symref(refs, "HEAD", newref, logmsg))
ret = error(_("HEAD of working tree %s is not updated"),
worktrees[i]->path);
}
@@ -580,7 +584,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
* Bad name --- this could be an attempt to rename a
* ref that we used to allow to be created by accident.
*/
- if (ref_exists(oldref.buf))
+ if (refs_ref_exists(get_main_ref_store(the_repository), oldref.buf))
recovery = 1;
else {
int code = die_message(_("invalid branch name: '%s'"), oldname);
@@ -601,7 +605,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
}
}
- if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
+ if ((copy || !(oldref_usage & IS_HEAD)) && !refs_ref_exists(get_main_ref_store(the_repository), oldref.buf)) {
if (oldref_usage & IS_HEAD)
die(_("no commit on branch '%s' yet"), oldname);
else
@@ -632,9 +636,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
oldref.buf, newref.buf);
if (!copy && !(oldref_usage & IS_ORPHAN) &&
- rename_ref(oldref.buf, newref.buf, logmsg.buf))
+ refs_rename_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf))
die(_("branch rename failed"));
- if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
+ if (copy && refs_copy_existing_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf))
die(_("branch copy failed"));
if (recovery) {
@@ -786,7 +790,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ 0, &head_oid, NULL);
if (!head)
die(_("failed to resolve HEAD as a valid ref"));
if (!strcmp(head, "HEAD"))
@@ -891,7 +896,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
}
strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
- if (!ref_exists(branch_ref.buf))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), branch_ref.buf))
error((!argc || branch_checked_out(branch_ref.buf))
? _("no commit on branch '%s' yet")
: _("no branch named '%s'"),
@@ -936,7 +941,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
die(_("no such branch '%s'"), argv[0]);
}
- if (!ref_exists(branch->refname)) {
+ if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname)) {
if (!argc || branch_checked_out(branch->refname))
die(_("no commit on branch '%s' yet"), branch->name);
die(_("branch '%s' does not exist"), branch->name);
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 25f860a..b3cc77a 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
struct tm tm;
enum diagnose_mode diagnose = DIAGNOSE_NONE;
char *option_output = NULL;
- char *option_suffix = "%Y-%m-%d-%H%M";
+ const char *option_suffix = "%Y-%m-%d-%H%M";
const char *user_relative_path = NULL;
char *prefixed_filename;
size_t output_path_len;
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 3ad11dc..d5d41a8 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -140,6 +140,11 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
builtin_bundle_verify_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
+ if (!startup_info->have_repository) {
+ ret = error(_("need a repository to verify a bundle"));
+ goto cleanup;
+ }
+
if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) {
ret = 1;
goto cleanup;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 0c948f4..43a1d7a 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "convert.h"
@@ -77,7 +77,7 @@ static int filter_object(const char *path, unsigned mode,
struct checkout_metadata meta;
init_checkout_metadata(&meta, NULL, NULL, oid);
- if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) {
+ if (convert_to_working_tree(the_repository->index, path, *buf, *size, &strbuf, &meta)) {
free(*buf);
*size = strbuf.len;
*buf = strbuf_detach(&strbuf, NULL);
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index c1da1d1..9376810 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "attr.h"
@@ -71,9 +70,9 @@ static void check_attr(const char *prefix, struct attr_check *check,
prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
if (collect_all) {
- git_all_attrs(&the_index, full_path, check);
+ git_all_attrs(the_repository->index, full_path, check);
} else {
- git_check_attr(&the_index, full_path, check);
+ git_check_attr(the_repository->index, full_path, check);
}
output_attr(check, file);
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 906cd96..2bda6a1 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "dir.h"
@@ -36,8 +35,8 @@ static const struct option check_ignore_options[] = {
static void output_pattern(const char *path, struct path_pattern *pattern)
{
- char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : "";
- char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
+ const char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : "";
+ const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
if (!nul_term_line) {
if (!verbose) {
write_name_quoted(path, stdout, '\n');
@@ -95,21 +94,21 @@ static int check_ignore(struct dir_struct *dir,
PATHSPEC_KEEP_ORDER,
prefix, argv);
- die_path_inside_submodule(&the_index, &pathspec);
+ die_path_inside_submodule(the_repository->index, &pathspec);
/*
* look for pathspecs matching entries in the index, since these
* should not be ignored, in order to be consistent with
* 'git status', 'git add' etc.
*/
- seen = find_pathspecs_matching_against_index(&pathspec, &the_index,
+ seen = find_pathspecs_matching_against_index(&pathspec, the_repository->index,
PS_HEED_SKIP_WORKTREE);
for (i = 0; i < pathspec.nr; i++) {
full_path = pathspec.items[i].match;
pattern = NULL;
if (!seen[i]) {
int dtype = DT_UNKNOWN;
- pattern = last_matching_pattern(dir, &the_index,
+ pattern = last_matching_pattern(dir, the_repository->index,
full_path, &dtype);
if (!verbose && pattern &&
pattern->flags & PATTERN_FLAG_NEGATIVE)
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 2e086a2..29e744d 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -4,7 +4,7 @@
* Copyright (C) 2005 Linus Torvalds
*
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -69,7 +69,7 @@ static void write_tempfile_record(const char *name, const char *prefix)
static int checkout_file(const char *name, const char *prefix)
{
int namelen = strlen(name);
- int pos = index_name_pos(&the_index, name, namelen);
+ int pos = index_name_pos(the_repository->index, name, namelen);
int has_same_name = 0;
int is_file = 0;
int is_skipped = 1;
@@ -79,8 +79,8 @@ static int checkout_file(const char *name, const char *prefix)
if (pos < 0)
pos = -pos - 1;
- while (pos < the_index.cache_nr) {
- struct cache_entry *ce = the_index.cache[pos];
+ while (pos <the_repository->index->cache_nr) {
+ struct cache_entry *ce =the_repository->index->cache[pos];
if (ce_namelen(ce) != namelen ||
memcmp(ce->name, name, namelen))
break;
@@ -140,8 +140,8 @@ static int checkout_all(const char *prefix, int prefix_length)
int i, errs = 0;
struct cache_entry *last_ce = NULL;
- for (i = 0; i < the_index.cache_nr ; i++) {
- struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr ; i++) {
+ struct cache_entry *ce = the_repository->index->cache[i];
if (S_ISSPARSEDIR(ce->ce_mode)) {
if (!ce_skip_worktree(ce))
@@ -154,8 +154,8 @@ static int checkout_all(const char *prefix, int prefix_length)
* first entry inside the expanded sparse directory).
*/
if (ignore_skip_worktree) {
- ensure_full_index(&the_index);
- ce = the_index.cache[i];
+ ensure_full_index(the_repository->index);
+ ce = the_repository->index->cache[i];
}
}
@@ -260,7 +260,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
builtin_checkout_index_usage, 0);
- state.istate = &the_index;
+ state.istate = the_repository->index;
state.force = force;
state.quiet = quiet;
state.not_new = not_new;
@@ -280,7 +280,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
*/
if (index_opt && !state.base_dir_len && !to_tempfile) {
state.refresh_cache = 1;
- state.istate = &the_index;
+ state.istate = the_repository->index;
repo_hold_locked_index(the_repository, &lock_file,
LOCK_DIE_ON_ERROR);
}
@@ -339,7 +339,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
return 1;
if (is_lock_file_locked(&lock_file) &&
- write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die("Unable to write new index file");
return 0;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 71e6036..3cf44b4 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "advice.h"
#include "branch.h"
@@ -146,7 +145,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
return READ_TREE_RECURSIVE;
len = base->len + strlen(pathname);
- ce = make_empty_cache_entry(&the_index, len);
+ ce = make_empty_cache_entry(the_repository->index, len);
oidcpy(&ce->oid, oid);
memcpy(ce->name, base->buf, base->len);
memcpy(ce->name + base->len, pathname, len - base->len);
@@ -159,9 +158,9 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
* entry in place. Whether it is UPTODATE or not, checkout_entry will
* do the right thing.
*/
- pos = index_name_pos(&the_index, ce->name, ce->ce_namelen);
+ pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen);
if (pos >= 0) {
- struct cache_entry *old = the_index.cache[pos];
+ struct cache_entry *old = the_repository->index->cache[pos];
if (ce->ce_mode == old->ce_mode &&
!ce_intent_to_add(old) &&
oideq(&ce->oid, &old->oid)) {
@@ -171,7 +170,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
}
}
- add_index_entry(&the_index, ce,
+ add_index_entry(the_repository->index, ce,
ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
return 0;
}
@@ -190,8 +189,8 @@ static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
static int skip_same_name(const struct cache_entry *ce, int pos)
{
- while (++pos < the_index.cache_nr &&
- !strcmp(the_index.cache[pos]->name, ce->name))
+ while (++pos < the_repository->index->cache_nr &&
+ !strcmp(the_repository->index->cache[pos]->name, ce->name))
; /* skip */
return pos;
}
@@ -199,9 +198,9 @@ static int skip_same_name(const struct cache_entry *ce, int pos)
static int check_stage(int stage, const struct cache_entry *ce, int pos,
int overlay_mode)
{
- while (pos < the_index.cache_nr &&
- !strcmp(the_index.cache[pos]->name, ce->name)) {
- if (ce_stage(the_index.cache[pos]) == stage)
+ while (pos < the_repository->index->cache_nr &&
+ !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+ if (ce_stage(the_repository->index->cache[pos]) == stage)
return 0;
pos++;
}
@@ -218,8 +217,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
unsigned seen = 0;
const char *name = ce->name;
- while (pos < the_index.cache_nr) {
- ce = the_index.cache[pos];
+ while (pos < the_repository->index->cache_nr) {
+ ce = the_repository->index->cache[pos];
if (strcmp(name, ce->name))
break;
seen |= (1 << ce_stage(ce));
@@ -235,10 +234,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
const struct checkout *state, int *nr_checkouts,
int overlay_mode)
{
- while (pos < the_index.cache_nr &&
- !strcmp(the_index.cache[pos]->name, ce->name)) {
- if (ce_stage(the_index.cache[pos]) == stage)
- return checkout_entry(the_index.cache[pos], state,
+ while (pos < the_repository->index->cache_nr &&
+ !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+ if (ce_stage(the_repository->index->cache[pos]) == stage)
+ return checkout_entry(the_repository->index->cache[pos], state,
NULL, nr_checkouts);
pos++;
}
@@ -256,7 +255,7 @@ static int checkout_merged(int pos, const struct checkout *state,
int *nr_checkouts, struct mem_pool *ce_mem_pool,
int conflict_style)
{
- struct cache_entry *ce = the_index.cache[pos];
+ struct cache_entry *ce = the_repository->index->cache[pos];
const char *path = ce->name;
mmfile_t ancestor, ours, theirs;
enum ll_merge_result merge_status;
@@ -269,7 +268,7 @@ static int checkout_merged(int pos, const struct checkout *state,
int renormalize = 0;
memset(threeway, 0, sizeof(threeway));
- while (pos < the_index.cache_nr) {
+ while (pos < the_repository->index->cache_nr) {
int stage;
stage = ce_stage(ce);
if (!stage || strcmp(path, ce->name))
@@ -278,7 +277,7 @@ static int checkout_merged(int pos, const struct checkout *state,
if (stage == 2)
mode = create_ce_mode(ce->ce_mode);
pos++;
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
}
if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
return error(_("path '%s' does not have necessary versions"), path);
@@ -356,7 +355,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
* match_pathspec() for _all_ entries when
* opts->source_tree != NULL.
*/
- if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched))
+ if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched))
ce->ce_flags |= CE_MATCHED;
}
@@ -367,7 +366,7 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
ce->ce_flags &= ~CE_MATCHED;
if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
return;
- if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
+ if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched)) {
ce->ce_flags |= CE_MATCHED;
if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
/*
@@ -391,7 +390,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
state.force = 1;
state.refresh_cache = 1;
- state.istate = &the_index;
+ state.istate = the_repository->index;
mem_pool_init(&ce_mem_pool, 0);
get_parallel_checkout_configs(&pc_workers, &pc_threshold);
@@ -404,8 +403,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
if (pc_workers > 1)
init_parallel_checkout();
- for (pos = 0; pos < the_index.cache_nr; pos++) {
- struct cache_entry *ce = the_index.cache[pos];
+ for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+ struct cache_entry *ce = the_repository->index->cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce)) {
errs |= checkout_entry(ce, &state,
@@ -429,7 +428,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
NULL, NULL);
mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
- remove_marked_cache_entries(&the_index, 1);
+ remove_marked_cache_entries(the_repository->index, 1);
remove_scheduled_dirs();
errs |= finish_delayed_checkout(&state, opts->show_progress);
@@ -571,7 +570,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (opts->source_tree)
read_tree_some(opts->source_tree, &opts->pathspec);
if (opts->merge)
- unmerge_index(&the_index, &opts->pathspec, CE_MATCHED);
+ unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED);
ps_matched = xcalloc(opts->pathspec.nr, 1);
@@ -579,13 +578,13 @@ static int checkout_paths(const struct checkout_opts *opts,
* Make sure all pathspecs participated in locating the paths
* to be checked out.
*/
- for (pos = 0; pos < the_index.cache_nr; pos++)
+ for (pos = 0; pos < the_repository->index->cache_nr; pos++)
if (opts->overlay_mode)
- mark_ce_for_checkout_overlay(the_index.cache[pos],
+ mark_ce_for_checkout_overlay(the_repository->index->cache[pos],
ps_matched,
opts);
else
- mark_ce_for_checkout_no_overlay(the_index.cache[pos],
+ mark_ce_for_checkout_no_overlay(the_repository->index->cache[pos],
ps_matched,
opts);
@@ -596,8 +595,8 @@ static int checkout_paths(const struct checkout_opts *opts,
free(ps_matched);
/* Any unmerged paths? */
- for (pos = 0; pos < the_index.cache_nr; pos++) {
- const struct cache_entry *ce = the_index.cache[pos];
+ for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+ const struct cache_entry *ce = the_repository->index->cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce))
continue;
@@ -622,7 +621,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (opts->checkout_worktree)
errs |= checkout_worktree(opts, new_branch_info);
else
- remove_marked_cache_entries(&the_index, 1);
+ remove_marked_cache_entries(the_repository->index, 1);
/*
* Allow updating the index when checking out from the index.
@@ -634,7 +633,7 @@ static int checkout_paths(const struct checkout_opts *opts,
checkout_index = opts->checkout_index;
if (checkout_index) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
} else {
/*
@@ -646,7 +645,8 @@ static int checkout_paths(const struct checkout_opts *opts,
rollback_lock_file(&lock_file);
}
- read_ref_full("HEAD", 0, &rev, NULL);
+ refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0,
+ &rev, NULL);
head = lookup_commit_reference_gently(the_repository, &rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -703,8 +703,8 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.merge = 1;
opts.fn = oneway_merge;
opts.verbose_update = o->show_progress;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
init_checkout_metadata(&opts.meta, info->refname,
info->commit ? &info->commit->object.oid : null_oid(),
NULL);
@@ -756,12 +756,12 @@ static void init_topts(struct unpack_trees_options *topts, int merge,
{
memset(topts, 0, sizeof(*topts));
topts->head_idx = -1;
- topts->src_index = &the_index;
- topts->dst_index = &the_index;
+ topts->src_index = the_repository->index;
+ topts->dst_index = the_repository->index;
setup_unpack_trees_porcelain(topts, "checkout");
- topts->initial_checkout = is_index_unborn(&the_index);
+ topts->initial_checkout = is_index_unborn(the_repository->index);
topts->update = 1;
topts->merge = 1;
topts->quiet = merge && old_commit;
@@ -783,7 +783,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (repo_read_index_preload(the_repository, NULL, 0) < 0)
return error(_("index file corrupt"));
- resolve_undo_clear_index(&the_index);
+ resolve_undo_clear_index(the_repository->index);
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
if (new_branch_info->commit)
BUG("'switch --orphan' should never accept a commit as starting point");
@@ -807,9 +807,9 @@ static int merge_working_tree(const struct checkout_opts *opts,
struct unpack_trees_options topts;
const struct object_id *old_commit_oid;
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
- if (unmerged_index(&the_index)) {
+ if (unmerged_index(the_repository->index)) {
error(_("you need to resolve your current index first"));
return 1;
}
@@ -919,10 +919,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
}
}
- if (!cache_tree_fully_valid(the_index.cache_tree))
- cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+ if (!cache_tree_fully_valid(the_repository->index->cache_tree))
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
@@ -958,7 +958,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
int ret;
struct strbuf err = STRBUF_INIT;
- ret = safe_create_reflog(refname, &err);
+ ret = refs_create_reflog(get_main_ref_store(the_repository),
+ refname, &err);
if (ret) {
fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
opts->new_orphan_branch, err.buf);
@@ -999,8 +1000,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) {
/* Nothing to do. */
} else if (opts->force_detach || !new_branch_info->path) { /* No longer on any branch. */
- update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL,
- REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ "HEAD", &new_branch_info->commit->object.oid,
+ NULL,
+ REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
if (!opts->quiet) {
if (old_branch_info->path &&
advice_enabled(ADVICE_DETACHED_HEAD) && !opts->force_detach)
@@ -1008,7 +1011,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
}
} else if (new_branch_info->path) { /* Switch branches. */
- if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path, msg.buf) < 0)
die(_("unable to update HEAD"));
if (!opts->quiet) {
if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
@@ -1029,8 +1032,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
}
}
if (old_branch_info->path && old_branch_info->name) {
- if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path))
- delete_reflog(old_branch_info->path);
+ if (!refs_ref_exists(get_main_ref_store(the_repository), old_branch_info->path) && refs_reflog_exists(get_main_ref_store(the_repository), old_branch_info->path))
+ refs_delete_reflog(get_main_ref_store(the_repository),
+ old_branch_info->path);
}
}
remove_branch_state(the_repository, !opts->quiet);
@@ -1129,7 +1133,8 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
object->flags &= ~UNINTERESTING;
add_pending_object(&revs, object, oid_to_hex(&object->oid));
- for_each_ref(add_pending_uninteresting_ref, &revs);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_pending_uninteresting_ref, &revs);
if (new_commit)
add_pending_oid(&revs, "HEAD",
&new_commit->object.oid,
@@ -1159,7 +1164,8 @@ static int switch_branches(const struct checkout_opts *opts,
trace2_cmd_mode("branch");
memset(&old_branch_info, 0, sizeof(old_branch_info));
- old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag);
+ old_branch_info.path = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &rev, &flag);
if (old_branch_info.path)
old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
if (!(flag & REF_ISSYMREF))
@@ -1247,7 +1253,7 @@ static void setup_new_branch_info_and_source_tree(
setup_branch_path(new_branch_info);
if (!check_refname_format(new_branch_info->path, 0) &&
- !read_ref(new_branch_info->path, &branch_rev))
+ !refs_read_ref(get_main_ref_store(the_repository), new_branch_info->path, &branch_rev))
oidcpy(rev, &branch_rev);
else
/* not an existing branch */
@@ -1269,12 +1275,12 @@ static void setup_new_branch_info_and_source_tree(
}
}
-static const char *parse_remote_branch(const char *arg,
- struct object_id *rev,
- int could_be_checkout_paths)
+static char *parse_remote_branch(const char *arg,
+ struct object_id *rev,
+ int could_be_checkout_paths)
{
int num_matches = 0;
- const char *remote = unique_tracking_name(arg, rev, &num_matches);
+ char *remote = unique_tracking_name(arg, rev, &num_matches);
if (remote && could_be_checkout_paths) {
die(_("'%s' could be both a local file and a tracking branch.\n"
@@ -1310,6 +1316,7 @@ static int parse_branchname_arg(int argc, const char **argv,
const char **new_branch = &opts->new_branch;
int argcount = 0;
const char *arg;
+ char *remote = NULL;
int dash_dash_pos;
int has_dash_dash = 0;
int i;
@@ -1410,8 +1417,8 @@ static int parse_branchname_arg(int argc, const char **argv,
recover_with_dwim = 0;
if (recover_with_dwim) {
- const char *remote = parse_remote_branch(arg, rev,
- could_be_checkout_paths);
+ remote = parse_remote_branch(arg, rev,
+ could_be_checkout_paths);
if (remote) {
*new_branch = arg;
arg = remote;
@@ -1453,6 +1460,7 @@ static int parse_branchname_arg(int argc, const char **argv,
argc--;
}
+ free(remote);
return argcount;
}
@@ -1466,7 +1474,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
if (!opts->new_branch)
die(_("You are on a branch yet to be born"));
strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
- status = create_symref("HEAD", branch_ref.buf, "checkout -b");
+ status = refs_update_symref(get_main_ref_store(the_repository),
+ "HEAD", branch_ref.buf, "checkout -b");
strbuf_release(&branch_ref);
if (!opts->quiet)
fprintf(stderr, _("Switched to a new branch '%s'\n"),
@@ -1553,7 +1562,8 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
if (opts->ignore_other_worktrees)
return;
- head_ref = resolve_refdup("HEAD", 0, NULL, &flags);
+ head_ref = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &flags);
if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
die_if_checked_out(full_ref, 1);
free(head_ref);
@@ -1634,7 +1644,7 @@ static int checkout_branch(struct checkout_opts *opts,
struct object_id rev;
int flag;
- if (!read_ref_full("HEAD", 0, &rev, &flag) &&
+ if (!refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &rev, &flag) &&
(flag & REF_ISSYMREF) && is_null_oid(&rev))
return switch_unborn_to_new_branch(opts);
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 29efe84..ded5a91 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -6,7 +6,6 @@
* Based on git-clean.sh by Pavel Roskin
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "config.h"
@@ -714,7 +713,7 @@ static int filter_by_patterns_cmd(void)
for_each_string_list_item(item, &del_list) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, &the_index, item->string, &dtype)) {
+ if (is_excluded(&dir, the_repository->index, item->string, &dtype)) {
*item->string = '\0';
changed++;
}
@@ -1021,7 +1020,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
PATHSPEC_PREFER_CWD,
prefix, argv);
- fill_directory(&dir, &the_index, &pathspec);
+ fill_directory(&dir, the_repository->index, &pathspec);
correct_untracked_entries(&dir);
for (i = 0; i < dir.nr; i++) {
@@ -1029,7 +1028,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct stat st;
const char *rel;
- if (!index_name_is_other(&the_index, ent->name, ent->len))
+ if (!index_name_is_other(the_repository->index, ent->name, ent->len))
continue;
if (lstat(ent->name, &st))
diff --git a/builtin/clone.c b/builtin/clone.c
index 5fbe39f..80ef2bf 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -8,7 +8,6 @@
* Clone a repository into a different directory that does not yet exist.
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -72,7 +71,7 @@ static char *option_branch = NULL;
static struct string_list option_not = STRING_LIST_INIT_NODUP;
static const char *real_git_dir;
static const char *ref_format;
-static char *option_upload_pack = "git-upload-pack";
+static const char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
static int option_sparse_checkout;
@@ -178,8 +177,8 @@ static struct option builtin_clone_options[] = {
static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
{
- static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
- static char *bundle_suffix[] = { ".bundle", "" };
+ static const char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+ static const char *bundle_suffix[] = { ".bundle", "" };
size_t baselen = path->len;
struct stat st;
int i;
@@ -524,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
struct ref *local_refs = head;
struct ref **tail = head ? &head->next : &local_refs;
+ struct refspec_item tag_refspec;
+
+ refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
if (option_single_branch) {
struct ref *remote_head = NULL;
@@ -546,7 +548,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
&tail, 0);
/* if --branch=tag, pull the requested tag explicitly */
- get_fetch_map(remote_head, tag_refspec, &tail, 0);
+ get_fetch_map(remote_head, &tag_refspec, &tail, 0);
}
free_refs(remote_head);
} else {
@@ -556,8 +558,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
}
if (!option_mirror && !option_single_branch && !option_no_tags)
- get_fetch_map(refs, tag_refspec, &tail, 0);
+ get_fetch_map(refs, &tag_refspec, &tail, 0);
+ refspec_item_clear(&tag_refspec);
return local_refs;
}
@@ -568,7 +571,8 @@ static void write_remote_refs(const struct ref *local_refs)
struct ref_transaction *t;
struct strbuf err = STRBUF_INIT;
- t = ref_transaction_begin(&err);
+ t = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!t)
die("%s", err.buf);
@@ -576,7 +580,7 @@ static void write_remote_refs(const struct ref *local_refs)
if (!r->peer_ref)
continue;
if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
- 0, NULL, &err))
+ NULL, 0, NULL, &err))
die("%s", err.buf);
}
@@ -599,8 +603,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
OBJECT_INFO_QUICK |
OBJECT_INFO_SKIP_FETCH_OBJECT))
continue;
- update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg,
+ ref->name, &ref->old_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
}
}
@@ -652,9 +657,9 @@ static void update_remote_refs(const struct ref *refs,
struct strbuf head_ref = STRBUF_INIT;
strbuf_addstr(&head_ref, branch_top);
strbuf_addstr(&head_ref, "HEAD");
- if (create_symref(head_ref.buf,
- remote_head_points_at->peer_ref->name,
- msg) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), head_ref.buf,
+ remote_head_points_at->peer_ref->name,
+ msg) < 0)
die(_("unable to update %s"), head_ref.buf);
strbuf_release(&head_ref);
}
@@ -666,33 +671,36 @@ static void update_head(const struct ref *our, const struct ref *remote,
const char *head;
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
/* Local default branch link */
- if (create_symref("HEAD", our->name, NULL) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0)
die(_("unable to update HEAD"));
if (!option_bare) {
- update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ msg, "HEAD", &our->old_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
install_branch_config(0, head, remote_name, our->name);
}
} else if (our) {
struct commit *c = lookup_commit_reference(the_repository,
&our->old_oid);
/* --branch specifies a non-branch (i.e. tags), detach HEAD */
- update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg,
+ "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
+ UPDATE_REFS_DIE_ON_ERR);
} else if (remote) {
/*
* We know remote HEAD points to a non-branch, or
* HEAD points to a branch but we don't know which one.
* Detach HEAD in all these cases.
*/
- update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg,
+ "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
+ UPDATE_REFS_DIE_ON_ERR);
} else if (unborn && skip_prefix(unborn, "refs/heads/", &head)) {
/*
* Unborn head from remote; same as "our" case above except
* that we have no ref to update.
*/
- if (create_symref("HEAD", unborn, NULL) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", unborn, NULL) < 0)
die(_("unable to update HEAD"));
if (!option_bare)
install_branch_config(0, head, remote_name, unborn);
@@ -733,7 +741,8 @@ static int checkout(int submodule_progress, int filter_submodules)
if (option_no_checkout)
return 0;
- head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ RESOLVE_REF_READING, &oid, NULL);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
"unable to checkout"));
@@ -760,8 +769,8 @@ static int checkout(int submodule_progress, int filter_submodules)
opts.preserve_ignored = 0;
opts.fn = oneway_merge;
opts.verbose_update = (option_verbosity >= 0);
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
init_checkout_metadata(&opts.meta, head, &oid, NULL);
tree = parse_tree_indirect(&oid);
@@ -775,7 +784,7 @@ static int checkout(int submodule_progress, int filter_submodules)
free(head);
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
err |= run_hooks_l("post-checkout", oid_to_hex(null_oid()),
@@ -965,7 +974,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int submodule_progress;
int filter_submodules = 0;
int hash_algo;
- unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+ enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
const int do_not_override_repo_unix_permissions = -1;
struct transport_ls_refs_options transport_ls_refs_options =
@@ -1285,7 +1294,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
branch_top.buf);
- path = get_repo_path(remote->url[0], &is_bundle);
+ path = get_repo_path(remote->url.v[0], &is_bundle);
is_local = option_local != 0 && path && !is_bundle;
if (is_local) {
if (option_depth)
@@ -1307,7 +1316,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_local > 0 && !is_local)
warning(_("--local is ignored"));
- transport = transport_get(remote, path ? path : remote->url[0]);
+ transport = transport_get(remote, path ? path : remote->url.v[0]);
transport_set_verbosity(transport, option_verbosity, option_progress);
transport->family = family;
transport->cloning = 1;
@@ -1454,6 +1463,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
} else if (remote_head) {
our_head_points_at = NULL;
} else {
+ char *to_free = NULL;
const char *branch;
if (!mapped_refs) {
@@ -1466,7 +1476,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
"refs/heads/", &branch)) {
unborn_head = xstrdup(transport_ls_refs_options.unborn_head_target);
} else {
- branch = git_default_branch_name(0);
+ branch = to_free = repo_default_branch_name(the_repository, 0);
unborn_head = xstrfmt("refs/heads/%s", branch);
}
@@ -1482,6 +1492,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* a match.
*/
our_head_points_at = find_remote_branch(mapped_refs, branch);
+
+ free(to_free);
}
write_refspec_config(src_ref_prefix, our_head_points_at,
diff --git a/builtin/commit.c b/builtin/commit.c
index 6e14844..75c7411 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -5,7 +5,6 @@
* Based on git-commit.sh by Junio C Hamano and Linus Torvalds
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -38,6 +37,7 @@
#include "commit-reach.h"
#include "commit-graph.h"
#include "pretty.h"
+#include "trailer.h"
static const char * const builtin_commit_usage[] = {
N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
@@ -107,13 +107,13 @@ static enum {
} commit_style;
static const char *logfile, *force_author;
-static const char *template_file;
+static char *template_file;
/*
* The _message variables are commit names from which to take
* the commit message and/or authorship.
*/
static const char *author_message, *author_message_buffer;
-static char *edit_message, *use_message;
+static const char *edit_message, *use_message;
static char *fixup_message, *fixup_commit, *squash_message;
static const char *fixup_prefix;
static int all, also, interactive, patch_interactive, only, amend, signoff;
@@ -121,8 +121,8 @@ static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int config_commit_verbose = -1; /* unspecified */
static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit, *pathspec_from_file;
+static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static const char *sign_commit, *pathspec_from_file;
static struct strvec trailer_args = STRVEC_INIT;
/*
@@ -133,7 +133,7 @@ static struct strvec trailer_args = STRVEC_INIT;
* is specified explicitly.
*/
static enum commit_msg_cleanup_mode cleanup_mode;
-static const char *cleanup_arg;
+static char *cleanup_arg;
static enum commit_whence whence;
static int use_editor = 1, include_status = 1;
@@ -142,14 +142,6 @@ static struct strbuf message = STRBUF_INIT;
static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
-static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
-{
- BUG_ON_OPT_NEG(unset);
-
- strvec_pushl(opt->value, "--trailer", arg, NULL);
- return 0;
-}
-
static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
{
enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -266,19 +258,19 @@ static int list_paths(struct string_list *list, const char *with_tree,
if (with_tree) {
char *max_prefix = common_prefix(pattern);
- overlay_tree_on_index(&the_index, with_tree, max_prefix);
+ overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
free(max_prefix);
}
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < the_index.cache_nr; i++) {
- const struct cache_entry *ce = the_index.cache[i];
+ ensure_full_index(the_repository->index);
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ const struct cache_entry *ce = the_repository->index->cache[i];
struct string_list_item *item;
if (ce->ce_flags & CE_UPDATE)
continue;
- if (!ce_path_match(&the_index, ce, pattern, m))
+ if (!ce_path_match(the_repository->index, ce, pattern, m))
continue;
item = string_list_insert(list, ce->name);
if (ce_skip_worktree(ce))
@@ -302,10 +294,10 @@ static void add_remove_files(struct string_list *list)
continue;
if (!lstat(p->string, &st)) {
- if (add_to_index(&the_index, p->string, &st, 0))
+ if (add_to_index(the_repository->index, p->string, &st, 0))
die(_("updating files failed"));
} else
- remove_file_from_index(&the_index, p->string);
+ remove_file_from_index(the_repository->index, p->string);
}
}
@@ -316,7 +308,7 @@ static void create_base_index(const struct commit *current_head)
struct tree_desc t;
if (!current_head) {
- discard_index(&the_index);
+ discard_index(the_repository->index);
return;
}
@@ -324,8 +316,8 @@ static void create_base_index(const struct commit *current_head)
opts.head_idx = 1;
opts.index_only = 1;
opts.merge = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.fn = oneway_merge;
tree = parse_tree_indirect(¤t_head->object.oid);
@@ -344,7 +336,7 @@ static void refresh_cache_or_die(int refresh_flags)
* refresh_flags contains REFRESH_QUIET, so the only errors
* are for unmerged entries.
*/
- if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
+ if (refresh_index(the_repository->index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
die_resolve_conflict("commit");
}
@@ -393,7 +385,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
refresh_cache_or_die(refresh_flags);
- if (write_locked_index(&the_index, &index_lock, 0))
+ if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to create temporary index"));
old_repo_index_file = the_repository->index_file;
@@ -412,13 +404,13 @@ static const char *prepare_index(const char **argv, const char *prefix,
unsetenv(INDEX_ENVIRONMENT);
FREE_AND_NULL(old_index_env);
- discard_index(&the_index);
- read_index_from(&the_index, get_lock_file_path(&index_lock),
+ discard_index(the_repository->index);
+ read_index_from(the_repository->index, get_lock_file_path(&index_lock),
get_git_dir());
- if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
+ if (cache_tree_update(the_repository->index, WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
- if (write_locked_index(&the_index, &index_lock, 0))
+ if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to update temporary index"));
} else
warning(_("Failed to update main cache tree"));
@@ -450,8 +442,8 @@ static const char *prepare_index(const char **argv, const char *prefix,
exit(128);
refresh_cache_or_die(refresh_flags);
- cache_tree_update(&the_index, WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock, 0))
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+ if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to write new index file"));
commit_style = COMMIT_NORMAL;
ret = get_lock_file_path(&index_lock);
@@ -472,10 +464,10 @@ static const char *prepare_index(const char **argv, const char *prefix,
repo_hold_locked_index(the_repository, &index_lock,
LOCK_DIE_ON_ERROR);
refresh_cache_or_die(refresh_flags);
- if (the_index.cache_changed
- || !cache_tree_fully_valid(the_index.cache_tree))
- cache_tree_update(&the_index, WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock,
+ if (the_repository->index->cache_changed
+ || !cache_tree_fully_valid(the_repository->index->cache_tree))
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+ if (write_locked_index(the_repository->index, &index_lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new index file"));
commit_style = COMMIT_AS_IS;
@@ -516,15 +508,15 @@ static const char *prepare_index(const char **argv, const char *prefix,
if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
exit(1);
- discard_index(&the_index);
+ discard_index(the_repository->index);
if (repo_read_index(the_repository) < 0)
die(_("cannot read the index"));
repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
add_remove_files(&partial);
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
- cache_tree_update(&the_index, WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock, 0))
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+ if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to write new index file"));
hold_lock_file_for_update(&false_lock,
@@ -534,14 +526,14 @@ static const char *prepare_index(const char **argv, const char *prefix,
create_base_index(current_head);
add_remove_files(&partial);
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
- if (write_locked_index(&the_index, &false_lock, 0))
+ if (write_locked_index(the_repository->index, &false_lock, 0))
die(_("unable to write temporary index file"));
- discard_index(&the_index);
+ discard_index(the_repository->index);
ret = get_lock_file_path(&false_lock);
- read_index_from(&the_index, ret, get_git_dir());
+ read_index_from(the_repository->index, ret, get_git_dir());
out:
string_list_clear(&partial, 0);
clear_pathspec(&pathspec);
@@ -999,7 +991,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct object_id oid;
const char *parent = "HEAD";
- if (!the_index.initialized && repo_read_index(the_repository) < 0)
+ if (!the_repository->index->initialized && repo_read_index(the_repository) < 0)
die(_("Cannot read index"));
if (amend)
@@ -1009,11 +1001,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
int i, ita_nr = 0;
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < the_index.cache_nr; i++)
- if (ce_intent_to_add(the_index.cache[i]))
+ ensure_full_index(the_repository->index);
+ for (i = 0; i < the_repository->index->cache_nr; i++)
+ if (ce_intent_to_add(the_repository->index->cache[i]))
ita_nr++;
- committable = the_index.cache_nr - ita_nr > 0;
+ committable = the_repository->index->cache_nr - ita_nr > 0;
} else {
/*
* Unless the user did explicitly request a submodule
@@ -1038,14 +1030,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
fclose(s->fp);
if (trailer_args.nr) {
- struct child_process run_trailer = CHILD_PROCESS_INIT;
-
- strvec_pushl(&run_trailer.args, "interpret-trailers",
- "--in-place", "--no-divider",
- git_path_commit_editmsg(), NULL);
- strvec_pushv(&run_trailer.args, trailer_args.v);
- run_trailer.git_cmd = 1;
- if (run_command(&run_trailer))
+ if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args))
die(_("unable to pass trailers to --trailers"));
strvec_clear(&trailer_args);
}
@@ -1081,11 +1066,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
* and could have updated it. We must do this before we invoke
* the editor and after we invoke run_status above.
*/
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
- read_index_from(&the_index, index_file, get_git_dir());
+ read_index_from(the_repository->index, index_file, get_git_dir());
- if (cache_tree_update(&the_index, 0)) {
+ if (cache_tree_update(the_repository->index, 0)) {
error(_("Error building trees"));
return 0;
}
@@ -1586,7 +1571,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
status_format != STATUS_FORMAT_PORCELAIN_V2)
progress_flag = REFRESH_PROGRESS;
repo_read_index(the_repository);
- refresh_index(&the_index,
+ refresh_index(the_repository->index,
REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
&s.pathspec, NULL, NULL);
@@ -1673,7 +1658,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
- OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
+ OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG),
OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1856,7 +1841,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
append_merge_tag_headers(parents, &tail);
}
- if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid,
+ if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid,
parents, &oid, author_ident.buf, NULL,
sign_commit, extra)) {
rollback_index_files();
diff --git a/builtin/config.c b/builtin/config.c
index 0015620..20a0b64 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -16,59 +16,112 @@
#include "worktree.h"
static const char *const builtin_config_usage[] = {
- N_("git config [<options>]"),
+ N_("git config list [<file-option>] [<display-option>] [--includes]"),
+ N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
+ N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+ N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+ N_("git config rename-section [<file-option>] <old-name> <new-name>"),
+ N_("git config remove-section [<file-option>] <name>"),
+ N_("git config edit [<file-option>]"),
+ N_("git config [<file-option>] --get-colorbool <name> [<stdout-is-tty>]"),
NULL
};
-static char *key;
-static regex_t *key_regexp;
-static const char *value_pattern;
-static regex_t *regexp;
-static int show_keys;
-static int omit_values;
-static int use_key_regexp;
-static int do_all;
-static int do_not_match;
-static char delim = '=';
-static char key_delim = ' ';
-static char term = '\n';
+static const char *const builtin_config_list_usage[] = {
+ N_("git config list [<file-option>] [<display-option>] [--includes]"),
+ NULL
+};
-static int use_global_config, use_system_config, use_local_config;
-static int use_worktree_config;
-static struct git_config_source given_config_source;
-static int actions, type;
-static char *default_value;
-static int end_nul;
-static int respect_includes_opt = -1;
-static struct config_options config_options;
-static int show_origin;
-static int show_scope;
-static int fixed_value;
-static const char *comment;
+static const char *const builtin_config_get_usage[] = {
+ N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
+ NULL
+};
-#define ACTION_GET (1<<0)
-#define ACTION_GET_ALL (1<<1)
-#define ACTION_GET_REGEXP (1<<2)
-#define ACTION_REPLACE_ALL (1<<3)
-#define ACTION_ADD (1<<4)
-#define ACTION_UNSET (1<<5)
-#define ACTION_UNSET_ALL (1<<6)
-#define ACTION_RENAME_SECTION (1<<7)
-#define ACTION_REMOVE_SECTION (1<<8)
-#define ACTION_LIST (1<<9)
-#define ACTION_EDIT (1<<10)
-#define ACTION_SET (1<<11)
-#define ACTION_SET_ALL (1<<12)
-#define ACTION_GET_COLOR (1<<13)
-#define ACTION_GET_COLORBOOL (1<<14)
-#define ACTION_GET_URLMATCH (1<<15)
+static const char *const builtin_config_set_usage[] = {
+ N_("git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+ NULL
+};
-/*
- * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
- * one line of output and which should therefore be paged.
- */
-#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \
- ACTION_GET_REGEXP | ACTION_GET_URLMATCH)
+static const char *const builtin_config_unset_usage[] = {
+ N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+ NULL
+};
+
+static const char *const builtin_config_rename_section_usage[] = {
+ N_("git config rename-section [<file-option>] <old-name> <new-name>"),
+ NULL
+};
+
+static const char *const builtin_config_remove_section_usage[] = {
+ N_("git config remove-section [<file-option>] <name>"),
+ NULL
+};
+
+static const char *const builtin_config_edit_usage[] = {
+ N_("git config edit [<file-option>]"),
+ NULL
+};
+
+#define CONFIG_LOCATION_OPTIONS(opts) \
+ OPT_GROUP(N_("Config file location")), \
+ OPT_BOOL(0, "global", &opts.use_global_config, N_("use global config file")), \
+ OPT_BOOL(0, "system", &opts.use_system_config, N_("use system config file")), \
+ OPT_BOOL(0, "local", &opts.use_local_config, N_("use repository config file")), \
+ OPT_BOOL(0, "worktree", &opts.use_worktree_config, N_("use per-worktree config file")), \
+ OPT_STRING('f', "file", &opts.source.file, N_("file"), N_("use given config file")), \
+ OPT_STRING(0, "blob", &opts.source.blob, N_("blob-id"), N_("read config from given blob object"))
+
+struct config_location_options {
+ struct git_config_source source;
+ struct config_options options;
+ char *file_to_free;
+ int use_global_config;
+ int use_system_config;
+ int use_local_config;
+ int use_worktree_config;
+ int respect_includes_opt;
+};
+#define CONFIG_LOCATION_OPTIONS_INIT { \
+ .respect_includes_opt = -1, \
+}
+
+#define CONFIG_TYPE_OPTIONS(type) \
+ OPT_GROUP(N_("Type")), \
+ OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \
+ OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), \
+ OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), \
+ OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), \
+ OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR), \
+ OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), \
+ OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE)
+
+#define CONFIG_DISPLAY_OPTIONS(opts) \
+ OPT_GROUP(N_("Display options")), \
+ OPT_BOOL('z', "null", &opts.end_nul, N_("terminate values with NUL byte")), \
+ OPT_BOOL(0, "name-only", &opts.omit_values, N_("show variable names only")), \
+ OPT_BOOL(0, "show-origin", &opts.show_origin, N_("show origin of config (file, standard input, blob, command line)")), \
+ OPT_BOOL(0, "show-scope", &opts.show_scope, N_("show scope of config (worktree, local, global, system, command)")), \
+ OPT_BOOL(0, "show-names", &opts.show_keys, N_("show config keys in addition to their values")), \
+ CONFIG_TYPE_OPTIONS(opts.type)
+
+struct config_display_options {
+ int end_nul;
+ int omit_values;
+ int show_origin;
+ int show_scope;
+ int show_keys;
+ int type;
+ char *default_value;
+ /* Populated via `display_options_init()`. */
+ int term;
+ int delim;
+ int key_delim;
+};
+#define CONFIG_DISPLAY_OPTIONS_INIT { \
+ .term = '\n', \
+ .delim = '=', \
+ .key_delim = ' ', \
+}
#define TYPE_BOOL 1
#define TYPE_INT 2
@@ -82,8 +135,6 @@ static const char *comment;
{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
PARSE_OPT_NONEG, option_parse_type, (i) }
-static NORETURN void usage_builtin_config(void);
-
static int option_parse_type(const struct option *opt, const char *arg,
int unset)
{
@@ -128,61 +179,13 @@ static int option_parse_type(const struct option *opt, const char *arg,
* --type=int'.
*/
error(_("only one type at a time"));
- usage_builtin_config();
+ exit(129);
}
*to_type = new_type;
return 0;
}
-static struct option builtin_config_options[] = {
- OPT_GROUP(N_("Config file location")),
- OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
- OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
- OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
- OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
- OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
- OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
- OPT_GROUP(N_("Action")),
- OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
- OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
- OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
- OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
- OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
- OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
- OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
- OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
- OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
- OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
- OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
- OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
- OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
- OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
- OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
- OPT_GROUP(N_("Type")),
- OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
- OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
- OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
- OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
- OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
- OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
- OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
- OPT_GROUP(N_("Other")),
- OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
- OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
- OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
- OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
- OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
- OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
- OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
- OPT_END(),
-};
-
-static NORETURN void usage_builtin_config(void)
-{
- usage_with_options(builtin_config_usage, builtin_config_options);
-}
-
static void check_argc(int argc, int min, int max)
{
if (argc >= min && argc <= max)
@@ -192,27 +195,29 @@ static void check_argc(int argc, int min, int max)
else
error(_("wrong number of arguments, should be from %d to %d"),
min, max);
- usage_builtin_config();
+ exit(129);
}
-static void show_config_origin(const struct key_value_info *kvi,
+static void show_config_origin(const struct config_display_options *opts,
+ const struct key_value_info *kvi,
struct strbuf *buf)
{
- const char term = end_nul ? '\0' : '\t';
+ const char term = opts->end_nul ? '\0' : '\t';
strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
strbuf_addch(buf, ':');
- if (end_nul)
+ if (opts->end_nul)
strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
else
quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
strbuf_addch(buf, term);
}
-static void show_config_scope(const struct key_value_info *kvi,
+static void show_config_scope(const struct config_display_options *opts,
+ const struct key_value_info *kvi,
struct strbuf *buf)
{
- const char term = end_nul ? '\0' : '\t';
+ const char term = opts->end_nul ? '\0' : '\t';
const char *scope = config_scope_name(kvi->scope);
strbuf_addstr(buf, N_(scope));
@@ -221,24 +226,25 @@ static void show_config_scope(const struct key_value_info *kvi,
static int show_all_config(const char *key_, const char *value_,
const struct config_context *ctx,
- void *cb UNUSED)
+ void *cb)
{
+ const struct config_display_options *opts = cb;
const struct key_value_info *kvi = ctx->kvi;
- if (show_origin || show_scope) {
+ if (opts->show_origin || opts->show_scope) {
struct strbuf buf = STRBUF_INIT;
- if (show_scope)
- show_config_scope(kvi, &buf);
- if (show_origin)
- show_config_origin(kvi, &buf);
+ if (opts->show_scope)
+ show_config_scope(opts, kvi, &buf);
+ if (opts->show_origin)
+ show_config_origin(opts, kvi, &buf);
/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
fwrite(buf.buf, 1, buf.len, stdout);
strbuf_release(&buf);
}
- if (!omit_values && value_)
- printf("%s%c%s%c", key_, delim, value_, term);
+ if (!opts->omit_values && value_)
+ printf("%s%c%s%c", key_, opts->delim, value_, opts->term);
else
- printf("%s%c", key_, term);
+ printf("%s%c", key_, opts->term);
return 0;
}
@@ -248,26 +254,27 @@ struct strbuf_list {
int alloc;
};
-static int format_config(struct strbuf *buf, const char *key_,
+static int format_config(const struct config_display_options *opts,
+ struct strbuf *buf, const char *key_,
const char *value_, const struct key_value_info *kvi)
{
- if (show_scope)
- show_config_scope(kvi, buf);
- if (show_origin)
- show_config_origin(kvi, buf);
- if (show_keys)
+ if (opts->show_scope)
+ show_config_scope(opts, kvi, buf);
+ if (opts->show_origin)
+ show_config_origin(opts, kvi, buf);
+ if (opts->show_keys)
strbuf_addstr(buf, key_);
- if (!omit_values) {
- if (show_keys)
- strbuf_addch(buf, key_delim);
+ if (!opts->omit_values) {
+ if (opts->show_keys)
+ strbuf_addch(buf, opts->key_delim);
- if (type == TYPE_INT)
+ if (opts->type == TYPE_INT)
strbuf_addf(buf, "%"PRId64,
git_config_int64(key_, value_ ? value_ : "", kvi));
- else if (type == TYPE_BOOL)
+ else if (opts->type == TYPE_BOOL)
strbuf_addstr(buf, git_config_bool(key_, value_) ?
"true" : "false");
- else if (type == TYPE_BOOL_OR_INT) {
+ else if (opts->type == TYPE_BOOL_OR_INT) {
int is_bool, v;
v = git_config_bool_or_int(key_, value_, kvi,
&is_bool);
@@ -275,24 +282,24 @@ static int format_config(struct strbuf *buf, const char *key_,
strbuf_addstr(buf, v ? "true" : "false");
else
strbuf_addf(buf, "%d", v);
- } else if (type == TYPE_BOOL_OR_STR) {
+ } else if (opts->type == TYPE_BOOL_OR_STR) {
int v = git_parse_maybe_bool(value_);
if (v < 0)
strbuf_addstr(buf, value_);
else
strbuf_addstr(buf, v ? "true" : "false");
- } else if (type == TYPE_PATH) {
- const char *v;
+ } else if (opts->type == TYPE_PATH) {
+ char *v;
if (git_config_pathname(&v, key_, value_) < 0)
return -1;
strbuf_addstr(buf, v);
free((char *)v);
- } else if (type == TYPE_EXPIRY_DATE) {
+ } else if (opts->type == TYPE_EXPIRY_DATE) {
timestamp_t t;
if (git_config_expiry_date(&t, key_, value_) < 0)
return -1;
strbuf_addf(buf, "%"PRItime, t);
- } else if (type == TYPE_COLOR) {
+ } else if (opts->type == TYPE_COLOR) {
char v[COLOR_MAXLEN];
if (git_config_color(v, key_, value_) < 0)
return -1;
@@ -301,43 +308,73 @@ static int format_config(struct strbuf *buf, const char *key_,
strbuf_addstr(buf, value_);
} else {
/* Just show the key name; back out delimiter */
- if (show_keys)
+ if (opts->show_keys)
strbuf_setlen(buf, buf->len - 1);
}
}
- strbuf_addch(buf, term);
+ strbuf_addch(buf, opts->term);
return 0;
}
+#define GET_VALUE_ALL (1 << 0)
+#define GET_VALUE_KEY_REGEXP (1 << 1)
+
+struct collect_config_data {
+ const struct config_display_options *display_opts;
+ struct strbuf_list *values;
+ const char *value_pattern;
+ const char *key;
+ regex_t *regexp;
+ regex_t *key_regexp;
+ int do_not_match;
+ unsigned get_value_flags;
+ unsigned flags;
+};
+
static int collect_config(const char *key_, const char *value_,
const struct config_context *ctx, void *cb)
{
- struct strbuf_list *values = cb;
+ struct collect_config_data *data = cb;
+ struct strbuf_list *values = data->values;
const struct key_value_info *kvi = ctx->kvi;
- if (!use_key_regexp && strcmp(key_, key))
+ if (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
+ strcmp(key_, data->key))
return 0;
- if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+ if ((data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
+ regexec(data->key_regexp, key_, 0, NULL, 0))
return 0;
- if (fixed_value && strcmp(value_pattern, (value_?value_:"")))
+ if ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&
+ strcmp(data->value_pattern, (value_?value_:"")))
return 0;
- if (regexp != NULL &&
- (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
+ if (data->regexp &&
+ (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:""), 0, NULL, 0)))
return 0;
ALLOC_GROW(values->items, values->nr + 1, values->alloc);
strbuf_init(&values->items[values->nr], 0);
- return format_config(&values->items[values->nr++], key_, value_, kvi);
+ return format_config(data->display_opts, &values->items[values->nr++],
+ key_, value_, kvi);
}
-static int get_value(const char *key_, const char *regex_, unsigned flags)
+static int get_value(const struct config_location_options *opts,
+ const struct config_display_options *display_opts,
+ const char *key_, const char *regex_,
+ unsigned get_value_flags, unsigned flags)
{
int ret = CONFIG_GENERIC_ERROR;
struct strbuf_list values = {NULL};
+ struct collect_config_data data = {
+ .display_opts = display_opts,
+ .values = &values,
+ .get_value_flags = get_value_flags,
+ .flags = flags,
+ };
+ char *key = NULL;
int i;
- if (use_key_regexp) {
+ if (get_value_flags & GET_VALUE_KEY_REGEXP) {
char *tl;
/*
@@ -354,10 +391,10 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
for (tl = key; *tl && *tl != '.'; tl++)
*tl = tolower(*tl);
- key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
- if (regcomp(key_regexp, key, REG_EXTENDED)) {
+ data.key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
+ if (regcomp(data.key_regexp, key, REG_EXTENDED)) {
error(_("invalid key pattern: %s"), key_);
- FREE_AND_NULL(key_regexp);
+ FREE_AND_NULL(data.key_regexp);
ret = CONFIG_INVALID_PATTERN;
goto free_strings;
}
@@ -366,30 +403,32 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
ret = CONFIG_INVALID_KEY;
goto free_strings;
}
+
+ data.key = key;
}
if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))
- value_pattern = regex_;
+ data.value_pattern = regex_;
else if (regex_) {
if (regex_[0] == '!') {
- do_not_match = 1;
+ data.do_not_match = 1;
regex_++;
}
- regexp = (regex_t*)xmalloc(sizeof(regex_t));
- if (regcomp(regexp, regex_, REG_EXTENDED)) {
+ data.regexp = (regex_t*)xmalloc(sizeof(regex_t));
+ if (regcomp(data.regexp, regex_, REG_EXTENDED)) {
error(_("invalid pattern: %s"), regex_);
- FREE_AND_NULL(regexp);
+ FREE_AND_NULL(data.regexp);
ret = CONFIG_INVALID_PATTERN;
goto free_strings;
}
}
- config_with_options(collect_config, &values,
- &given_config_source, the_repository,
- &config_options);
+ config_with_options(collect_config, &data,
+ &opts->source, the_repository,
+ &opts->options);
- if (!values.nr && default_value) {
+ if (!values.nr && display_opts->default_value) {
struct key_value_info kvi = KVI_INIT;
struct strbuf *item;
@@ -397,16 +436,17 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
ALLOC_GROW(values.items, values.nr + 1, values.alloc);
item = &values.items[values.nr++];
strbuf_init(item, 0);
- if (format_config(item, key_, default_value, &kvi) < 0)
+ if (format_config(display_opts, item, key_,
+ display_opts->default_value, &kvi) < 0)
die(_("failed to format default config value: %s"),
- default_value);
+ display_opts->default_value);
}
ret = !values.nr;
for (i = 0; i < values.nr; i++) {
struct strbuf *buf = values.items + i;
- if (do_all || i == values.nr - 1)
+ if ((get_value_flags & GET_VALUE_ALL) || i == values.nr - 1)
fwrite(buf->buf, 1, buf->len, stdout);
strbuf_release(buf);
}
@@ -414,20 +454,20 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
free_strings:
free(key);
- if (key_regexp) {
- regfree(key_regexp);
- free(key_regexp);
+ if (data.key_regexp) {
+ regfree(data.key_regexp);
+ free(data.key_regexp);
}
- if (regexp) {
- regfree(regexp);
- free(regexp);
+ if (data.regexp) {
+ regfree(data.regexp);
+ free(data.regexp);
}
return ret;
}
static char *normalize_value(const char *key, const char *value,
- struct key_value_info *kvi)
+ int type, struct key_value_info *kvi)
{
if (!value)
return NULL;
@@ -478,97 +518,113 @@ static char *normalize_value(const char *key, const char *value,
BUG("cannot normalize type %d", type);
}
-static int get_color_found;
-static const char *get_color_slot;
-static const char *get_colorbool_slot;
-static char parsed_color[COLOR_MAXLEN];
+struct get_color_config_data {
+ int get_color_found;
+ const char *get_color_slot;
+ char parsed_color[COLOR_MAXLEN];
+};
static int git_get_color_config(const char *var, const char *value,
const struct config_context *ctx UNUSED,
- void *cb UNUSED)
+ void *cb)
{
- if (!strcmp(var, get_color_slot)) {
+ struct get_color_config_data *data = cb;
+
+ if (!strcmp(var, data->get_color_slot)) {
if (!value)
config_error_nonbool(var);
- if (color_parse(value, parsed_color) < 0)
+ if (color_parse(value, data->parsed_color) < 0)
return -1;
- get_color_found = 1;
+ data->get_color_found = 1;
}
return 0;
}
-static void get_color(const char *var, const char *def_color)
+static void get_color(const struct config_location_options *opts,
+ const char *var, const char *def_color)
{
- get_color_slot = var;
- get_color_found = 0;
- parsed_color[0] = '\0';
- config_with_options(git_get_color_config, NULL,
- &given_config_source, the_repository,
- &config_options);
+ struct get_color_config_data data = {
+ .get_color_slot = var,
+ .parsed_color[0] = '\0',
+ };
- if (!get_color_found && def_color) {
- if (color_parse(def_color, parsed_color) < 0)
+ config_with_options(git_get_color_config, &data,
+ &opts->source, the_repository,
+ &opts->options);
+
+ if (!data.get_color_found && def_color) {
+ if (color_parse(def_color, data.parsed_color) < 0)
die(_("unable to parse default color value"));
}
- fputs(parsed_color, stdout);
+ fputs(data.parsed_color, stdout);
}
-static int get_colorbool_found;
-static int get_diff_color_found;
-static int get_color_ui_found;
+struct get_colorbool_config_data {
+ int get_colorbool_found;
+ int get_diff_color_found;
+ int get_color_ui_found;
+ const char *get_colorbool_slot;
+};
+
static int git_get_colorbool_config(const char *var, const char *value,
const struct config_context *ctx UNUSED,
- void *data UNUSED)
+ void *cb)
{
- if (!strcmp(var, get_colorbool_slot))
- get_colorbool_found = git_config_colorbool(var, value);
+ struct get_colorbool_config_data *data = cb;
+
+ if (!strcmp(var, data->get_colorbool_slot))
+ data->get_colorbool_found = git_config_colorbool(var, value);
else if (!strcmp(var, "diff.color"))
- get_diff_color_found = git_config_colorbool(var, value);
+ data->get_diff_color_found = git_config_colorbool(var, value);
else if (!strcmp(var, "color.ui"))
- get_color_ui_found = git_config_colorbool(var, value);
+ data->get_color_ui_found = git_config_colorbool(var, value);
return 0;
}
-static int get_colorbool(const char *var, int print)
+static int get_colorbool(const struct config_location_options *opts,
+ const char *var, int print)
{
- get_colorbool_slot = var;
- get_colorbool_found = -1;
- get_diff_color_found = -1;
- get_color_ui_found = -1;
- config_with_options(git_get_colorbool_config, NULL,
- &given_config_source, the_repository,
- &config_options);
+ struct get_colorbool_config_data data = {
+ .get_colorbool_slot = var,
+ .get_colorbool_found = -1,
+ .get_diff_color_found = -1,
+ .get_color_ui_found = -1,
+ };
- if (get_colorbool_found < 0) {
- if (!strcmp(get_colorbool_slot, "color.diff"))
- get_colorbool_found = get_diff_color_found;
- if (get_colorbool_found < 0)
- get_colorbool_found = get_color_ui_found;
+ config_with_options(git_get_colorbool_config, &data,
+ &opts->source, the_repository,
+ &opts->options);
+
+ if (data.get_colorbool_found < 0) {
+ if (!strcmp(data.get_colorbool_slot, "color.diff"))
+ data.get_colorbool_found = data.get_diff_color_found;
+ if (data.get_colorbool_found < 0)
+ data.get_colorbool_found = data.get_color_ui_found;
}
- if (get_colorbool_found < 0)
+ if (data.get_colorbool_found < 0)
/* default value if none found in config */
- get_colorbool_found = GIT_COLOR_AUTO;
+ data.get_colorbool_found = GIT_COLOR_AUTO;
- get_colorbool_found = want_color(get_colorbool_found);
+ data.get_colorbool_found = want_color(data.get_colorbool_found);
if (print) {
- printf("%s\n", get_colorbool_found ? "true" : "false");
+ printf("%s\n", data.get_colorbool_found ? "true" : "false");
return 0;
} else
- return get_colorbool_found ? 0 : 1;
+ return data.get_colorbool_found ? 0 : 1;
}
-static void check_write(void)
+static void check_write(const struct git_config_source *source)
{
- if (!given_config_source.file && !startup_info->have_repository)
+ if (!source->file && !startup_info->have_repository)
die(_("not in a git directory"));
- if (given_config_source.use_stdin)
+ if (source->use_stdin)
die(_("writing to stdin is not supported"));
- if (given_config_source.blob)
+ if (source->blob)
die(_("writing config blobs is not supported"));
}
@@ -605,10 +661,13 @@ static int urlmatch_collect_fn(const char *var, const char *value,
return 0;
}
-static int get_urlmatch(const char *var, const char *url)
+static int get_urlmatch(const struct config_location_options *opts,
+ const struct config_display_options *_display_opts,
+ const char *var, const char *url)
{
int ret;
char *section_tail;
+ struct config_display_options display_opts = *_display_opts;
struct string_list_item *item;
struct urlmatch_config config = URLMATCH_CONFIG_INIT;
struct string_list values = STRING_LIST_INIT_DUP;
@@ -625,15 +684,15 @@ static int get_urlmatch(const char *var, const char *url)
if (section_tail) {
*section_tail = '\0';
config.key = section_tail + 1;
- show_keys = 0;
+ display_opts.show_keys = 0;
} else {
config.key = NULL;
- show_keys = 1;
+ display_opts.show_keys = 1;
}
config_with_options(urlmatch_config_entry, &config,
- &given_config_source, the_repository,
- &config_options);
+ &opts->source, the_repository,
+ &opts->options);
ret = !values.nr;
@@ -641,7 +700,7 @@ static int get_urlmatch(const char *var, const char *url)
struct urlmatch_current_candidate_value *matched = item->util;
struct strbuf buf = STRBUF_INIT;
- format_config(&buf, item->string,
+ format_config(&display_opts, &buf, item->string,
matched->value_is_null ? NULL : matched->value.buf,
&matched->kvi);
fwrite(buf.buf, 1, buf.len, stdout);
@@ -671,47 +730,39 @@ static char *default_user_config(void)
return strbuf_detach(&buf, NULL);
}
-int cmd_config(int argc, const char **argv, const char *prefix)
+static void location_options_init(struct config_location_options *opts,
+ const char *prefix)
{
- int nongit = !startup_info->have_repository;
- char *value = NULL;
- int flags = 0;
- int ret = 0;
- struct key_value_info default_kvi = KVI_INIT;
+ if (!opts->source.file)
+ opts->source.file = opts->file_to_free =
+ xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
- given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
-
- argc = parse_options(argc, argv, prefix, builtin_config_options,
- builtin_config_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
-
- if (use_global_config + use_system_config + use_local_config +
- use_worktree_config +
- !!given_config_source.file + !!given_config_source.blob > 1) {
+ if (opts->use_global_config + opts->use_system_config +
+ opts->use_local_config + opts->use_worktree_config +
+ !!opts->source.file + !!opts->source.blob > 1) {
error(_("only one config file at a time"));
- usage_builtin_config();
+ exit(129);
}
- if (nongit) {
- if (use_local_config)
+ if (!startup_info->have_repository) {
+ if (opts->use_local_config)
die(_("--local can only be used inside a git repository"));
- if (given_config_source.blob)
+ if (opts->source.blob)
die(_("--blob can only be used inside a git repository"));
- if (use_worktree_config)
+ if (opts->use_worktree_config)
die(_("--worktree can only be used inside a git repository"));
-
}
- if (given_config_source.file &&
- !strcmp(given_config_source.file, "-")) {
- given_config_source.file = NULL;
- given_config_source.use_stdin = 1;
- given_config_source.scope = CONFIG_SCOPE_COMMAND;
+ if (opts->source.file &&
+ !strcmp(opts->source.file, "-")) {
+ opts->source.file = NULL;
+ opts->source.use_stdin = 1;
+ opts->source.scope = CONFIG_SCOPE_COMMAND;
}
- if (use_global_config) {
- given_config_source.file = git_global_config();
- if (!given_config_source.file)
+ if (opts->use_global_config) {
+ opts->source.file = opts->file_to_free = git_global_config();
+ if (!opts->source.file)
/*
* It is unknown if HOME/.gitconfig exists, so
* we do not know if we should write to XDG
@@ -719,17 +770,18 @@ int cmd_config(int argc, const char **argv, const char *prefix)
* is set and points at a sane location.
*/
die(_("$HOME not set"));
- given_config_source.scope = CONFIG_SCOPE_GLOBAL;
- } else if (use_system_config) {
- given_config_source.file = git_system_config();
- given_config_source.scope = CONFIG_SCOPE_SYSTEM;
- } else if (use_local_config) {
- given_config_source.file = git_pathdup("config");
- given_config_source.scope = CONFIG_SCOPE_LOCAL;
- } else if (use_worktree_config) {
+ opts->source.scope = CONFIG_SCOPE_GLOBAL;
+ } else if (opts->use_system_config) {
+ opts->source.file = opts->file_to_free = git_system_config();
+ opts->source.scope = CONFIG_SCOPE_SYSTEM;
+ } else if (opts->use_local_config) {
+ opts->source.file = opts->file_to_free = git_pathdup("config");
+ opts->source.scope = CONFIG_SCOPE_LOCAL;
+ } else if (opts->use_worktree_config) {
struct worktree **worktrees = get_worktrees();
if (the_repository->repository_format_worktree_config)
- given_config_source.file = git_pathdup("config.worktree");
+ opts->source.file = opts->file_to_free =
+ git_pathdup("config.worktree");
else if (worktrees[0] && worktrees[1])
die(_("--worktree cannot be used with multiple "
"working trees unless the config\n"
@@ -737,76 +789,439 @@ int cmd_config(int argc, const char **argv, const char *prefix)
"Please read \"CONFIGURATION FILE\"\n"
"section in \"git help worktree\" for details"));
else
- given_config_source.file = git_pathdup("config");
- given_config_source.scope = CONFIG_SCOPE_LOCAL;
+ opts->source.file = opts->file_to_free =
+ git_pathdup("config");
+ opts->source.scope = CONFIG_SCOPE_LOCAL;
free_worktrees(worktrees);
- } else if (given_config_source.file) {
- if (!is_absolute_path(given_config_source.file) && prefix)
- given_config_source.file =
- prefix_filename(prefix, given_config_source.file);
- given_config_source.scope = CONFIG_SCOPE_COMMAND;
- } else if (given_config_source.blob) {
- given_config_source.scope = CONFIG_SCOPE_COMMAND;
+ } else if (opts->source.file) {
+ if (!is_absolute_path(opts->source.file) && prefix)
+ opts->source.file = opts->file_to_free =
+ prefix_filename(prefix, opts->source.file);
+ opts->source.scope = CONFIG_SCOPE_COMMAND;
+ } else if (opts->source.blob) {
+ opts->source.scope = CONFIG_SCOPE_COMMAND;
}
- if (respect_includes_opt == -1)
- config_options.respect_includes = !given_config_source.file;
+ if (opts->respect_includes_opt == -1)
+ opts->options.respect_includes = !opts->source.file;
else
- config_options.respect_includes = respect_includes_opt;
- if (!nongit) {
- config_options.commondir = get_git_common_dir();
- config_options.git_dir = get_git_dir();
+ opts->options.respect_includes = opts->respect_includes_opt;
+ if (startup_info->have_repository) {
+ opts->options.commondir = get_git_common_dir();
+ opts->options.git_dir = get_git_dir();
+ }
+}
+
+static void location_options_release(struct config_location_options *opts)
+{
+ free(opts->file_to_free);
+}
+
+static void display_options_init(struct config_display_options *opts)
+{
+ if (opts->end_nul) {
+ opts->term = '\0';
+ opts->delim = '\n';
+ opts->key_delim = '\n';
+ }
+}
+
+static int cmd_config_list(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ CONFIG_DISPLAY_OPTIONS(display_opts),
+ OPT_GROUP(N_("Other")),
+ OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+ N_("respect include directives on lookup")),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);
+ check_argc(argc, 0, 0);
+
+ location_options_init(&location_opts, prefix);
+ display_options_init(&display_opts);
+
+ setup_auto_pager("config", 1);
+
+ if (config_with_options(show_all_config, &display_opts,
+ &location_opts.source, the_repository,
+ &location_opts.options) < 0) {
+ if (location_opts.source.file)
+ die_errno(_("unable to read config file '%s'"),
+ location_opts.source.file);
+ else
+ die(_("error processing config file(s)"));
}
- if (end_nul) {
- term = '\0';
- delim = '\n';
- key_delim = '\n';
+ location_options_release(&location_opts);
+ return 0;
+}
+
+static int cmd_config_get(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+ const char *value_pattern = NULL, *url = NULL;
+ int flags = 0;
+ unsigned get_value_flags = 0;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_GROUP(N_("Filter options")),
+ OPT_BIT(0, "all", &get_value_flags, N_("return all values for multi-valued config options"), GET_VALUE_ALL),
+ OPT_BIT(0, "regexp", &get_value_flags, N_("interpret the name as a regular expression"), GET_VALUE_KEY_REGEXP),
+ OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+ OPT_STRING(0, "url", &url, N_("URL"), N_("show config matching the given URL")),
+ CONFIG_DISPLAY_OPTIONS(display_opts),
+ OPT_GROUP(N_("Other")),
+ OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+ N_("respect include directives on lookup")),
+ OPT_STRING(0, "default", &display_opts.default_value,
+ N_("value"), N_("use default value when missing entry")),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 1, 1);
+
+ if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+ die(_("--fixed-value only applies with 'value-pattern'"));
+ if (display_opts.default_value &&
+ ((get_value_flags & GET_VALUE_ALL) || url))
+ die(_("--default= cannot be used with --all or --url="));
+ if (url && ((get_value_flags & GET_VALUE_ALL) ||
+ (get_value_flags & GET_VALUE_KEY_REGEXP) ||
+ value_pattern))
+ die(_("--url= cannot be used with --all, --regexp or --value"));
+
+ location_options_init(&location_opts, prefix);
+ display_options_init(&display_opts);
+
+ setup_auto_pager("config", 1);
+
+ if (url)
+ ret = get_urlmatch(&location_opts, &display_opts, argv[0], url);
+ else
+ ret = get_value(&location_opts, &display_opts, argv[0], value_pattern,
+ get_value_flags, flags);
+
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int cmd_config_set(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ const char *value_pattern = NULL, *comment_arg = NULL;
+ char *comment = NULL;
+ int flags = 0, append = 0, type = 0;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ CONFIG_TYPE_OPTIONS(type),
+ OPT_GROUP(N_("Filter")),
+ OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
+ OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+ OPT_GROUP(N_("Other")),
+ OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
+ OPT_BOOL(0, "append", &append, N_("add a new line without altering any existing values")),
+ OPT_END(),
+ };
+ struct key_value_info default_kvi = KVI_INIT;
+ char *value;
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 2, 2);
+
+ if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+ die(_("--fixed-value only applies with --value=<pattern>"));
+ if (append && value_pattern)
+ die(_("--append cannot be used with --value=<pattern>"));
+ if (append)
+ value_pattern = CONFIG_REGEX_NONE;
+
+ comment = git_config_prepare_comment_string(comment_arg);
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ value = normalize_value(argv[0], argv[1], type, &default_kvi);
+
+ if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+ argv[0], value, value_pattern,
+ comment, flags);
+ } else {
+ ret = git_config_set_in_file_gently(location_opts.source.file,
+ argv[0], comment, value);
+ if (ret == CONFIG_NOTHING_SET)
+ error(_("cannot overwrite multiple values with a single value\n"
+ " Use a regexp, --add or --replace-all to change %s."), argv[0]);
}
- if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
+ location_options_release(&location_opts);
+ free(comment);
+ free(value);
+ return ret;
+}
+
+static int cmd_config_unset(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ const char *value_pattern = NULL;
+ int flags = 0;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_GROUP(N_("Filter")),
+ OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
+ OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 1, 1);
+
+ if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+ die(_("--fixed-value only applies with 'value-pattern'"));
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+ argv[0], NULL, value_pattern,
+ NULL, flags);
+ else
+ ret = git_config_set_in_file_gently(location_opts.source.file, argv[0],
+ NULL, NULL);
+
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 2, 2);
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ ret = git_config_rename_section_in_file(location_opts.source.file,
+ argv[0], argv[1]);
+ if (ret < 0)
+ goto out;
+ else if (!ret)
+ die(_("no such section: %s"), argv[0]);
+ ret = 0;
+
+out:
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ check_argc(argc, 1, 1);
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ ret = git_config_rename_section_in_file(location_opts.source.file,
+ argv[0], NULL);
+ if (ret < 0)
+ goto out;
+ else if (!ret)
+ die(_("no such section: %s"), argv[0]);
+ ret = 0;
+
+out:
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int show_editor(struct config_location_options *opts)
+{
+ char *config_file;
+
+ if (!opts->source.file && !startup_info->have_repository)
+ die(_("not in a git directory"));
+ if (opts->source.use_stdin)
+ die(_("editing stdin is not supported"));
+ if (opts->source.blob)
+ die(_("editing blobs is not supported"));
+ git_config(git_default_config, NULL);
+ config_file = opts->source.file ?
+ xstrdup(opts->source.file) :
+ git_pathdup("config");
+ if (opts->use_global_config) {
+ int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd >= 0) {
+ char *content = default_user_config();
+ write_str_in_full(fd, content);
+ free(content);
+ close(fd);
+ }
+ else if (errno != EEXIST)
+ die_errno(_("cannot create configuration file %s"), config_file);
+ }
+ launch_editor(config_file, NULL, NULL);
+ free(config_file);
+
+ return 0;
+}
+
+static int cmd_config_edit(int argc, const char **argv, const char *prefix)
+{
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);
+ check_argc(argc, 0, 0);
+
+ location_options_init(&location_opts, prefix);
+ check_write(&location_opts.source);
+
+ ret = show_editor(&location_opts);
+ location_options_release(&location_opts);
+ return ret;
+}
+
+static int cmd_config_actions(int argc, const char **argv, const char *prefix)
+{
+ enum {
+ ACTION_GET = (1<<0),
+ ACTION_GET_ALL = (1<<1),
+ ACTION_GET_REGEXP = (1<<2),
+ ACTION_REPLACE_ALL = (1<<3),
+ ACTION_ADD = (1<<4),
+ ACTION_UNSET = (1<<5),
+ ACTION_UNSET_ALL = (1<<6),
+ ACTION_RENAME_SECTION = (1<<7),
+ ACTION_REMOVE_SECTION = (1<<8),
+ ACTION_LIST = (1<<9),
+ ACTION_EDIT = (1<<10),
+ ACTION_SET = (1<<11),
+ ACTION_SET_ALL = (1<<12),
+ ACTION_GET_COLOR = (1<<13),
+ ACTION_GET_COLORBOOL = (1<<14),
+ ACTION_GET_URLMATCH = (1<<15),
+ };
+ struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+ struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+ const char *comment_arg = NULL;
+ int actions = 0;
+ unsigned flags = 0;
+ struct option opts[] = {
+ CONFIG_LOCATION_OPTIONS(location_opts),
+ OPT_GROUP(N_("Action")),
+ OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET),
+ OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL),
+ OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [<value-pattern>]"), ACTION_GET_REGEXP),
+ OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
+ OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [<value-pattern>]"), ACTION_REPLACE_ALL),
+ OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
+ OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [<value-pattern>]"), ACTION_UNSET),
+ OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [<value-pattern>]"), ACTION_UNSET_ALL),
+ OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
+ OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+ OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST),
+ OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
+ OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [<default>]"), ACTION_GET_COLOR),
+ OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [<stdout-is-tty>]"), ACTION_GET_COLORBOOL),
+ CONFIG_DISPLAY_OPTIONS(display_opts),
+ OPT_GROUP(N_("Other")),
+ OPT_STRING(0, "default", &display_opts.default_value,
+ N_("value"), N_("with --get, use default value when missing entry")),
+ OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+ OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+ N_("respect include directives on lookup")),
+ OPT_END(),
+ };
+ char *value = NULL, *comment = NULL;
+ int ret = 0;
+ struct key_value_info default_kvi = KVI_INIT;
+
+ argc = parse_options(argc, argv, prefix, opts,
+ builtin_config_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ location_options_init(&location_opts, prefix);
+ display_options_init(&display_opts);
+
+ if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && display_opts.type) {
error(_("--get-color and variable type are incoherent"));
- usage_builtin_config();
+ exit(129);
}
- if (HAS_MULTI_BITS(actions)) {
- error(_("only one action at a time"));
- usage_builtin_config();
- }
if (actions == 0)
switch (argc) {
case 1: actions = ACTION_GET; break;
case 2: actions = ACTION_SET; break;
case 3: actions = ACTION_SET_ALL; break;
default:
- usage_builtin_config();
+ error(_("no action specified"));
+ exit(129);
}
- if (omit_values &&
+ if (display_opts.omit_values &&
!(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
error(_("--name-only is only applicable to --list or --get-regexp"));
- usage_builtin_config();
+ exit(129);
}
- if (show_origin && !(actions &
+ if (display_opts.show_origin && !(actions &
(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
error(_("--show-origin is only applicable to --get, --get-all, "
"--get-regexp, and --list"));
- usage_builtin_config();
+ exit(129);
}
- if (default_value && !(actions & ACTION_GET)) {
+ if (display_opts.default_value && !(actions & ACTION_GET)) {
error(_("--default is only applicable to --get"));
- usage_builtin_config();
+ exit(129);
}
- if (comment &&
+ if (comment_arg &&
!(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
error(_("--comment is only applicable to add/set/replace operations"));
- usage_builtin_config();
+ exit(129);
}
/* check usage of --fixed-value */
- if (fixed_value) {
+ if (flags & CONFIG_FLAGS_FIXED_VALUE) {
int allowed_usage = 0;
switch (actions) {
@@ -835,148 +1250,125 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (!allowed_usage) {
error(_("--fixed-value only applies with 'value-pattern'"));
- usage_builtin_config();
+ exit(129);
}
-
- flags |= CONFIG_FLAGS_FIXED_VALUE;
}
- comment = git_config_prepare_comment_string(comment);
+ comment = git_config_prepare_comment_string(comment_arg);
- if (actions & PAGING_ACTIONS)
+ /*
+ * The following actions may produce more than one line of output and
+ * should therefore be paged.
+ */
+ if (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))
setup_auto_pager("config", 1);
if (actions == ACTION_LIST) {
check_argc(argc, 0, 0);
- if (config_with_options(show_all_config, NULL,
- &given_config_source, the_repository,
- &config_options) < 0) {
- if (given_config_source.file)
+ if (config_with_options(show_all_config, &display_opts,
+ &location_opts.source, the_repository,
+ &location_opts.options) < 0) {
+ if (location_opts.source.file)
die_errno(_("unable to read config file '%s'"),
- given_config_source.file);
+ location_opts.source.file);
else
die(_("error processing config file(s)"));
}
}
else if (actions == ACTION_EDIT) {
- char *config_file;
-
- check_argc(argc, 0, 0);
- if (!given_config_source.file && nongit)
- die(_("not in a git directory"));
- if (given_config_source.use_stdin)
- die(_("editing stdin is not supported"));
- if (given_config_source.blob)
- die(_("editing blobs is not supported"));
- git_config(git_default_config, NULL);
- config_file = given_config_source.file ?
- xstrdup(given_config_source.file) :
- git_pathdup("config");
- if (use_global_config) {
- int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (fd >= 0) {
- char *content = default_user_config();
- write_str_in_full(fd, content);
- free(content);
- close(fd);
- }
- else if (errno != EEXIST)
- die_errno(_("cannot create configuration file %s"), config_file);
- }
- launch_editor(config_file, NULL, NULL);
- free(config_file);
+ ret = show_editor(&location_opts);
}
else if (actions == ACTION_SET) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 2);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
+ value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+ ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);
if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s."), argv[0]);
}
else if (actions == ACTION_SET_ALL) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 3);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
argv[0], value, argv[2],
comment, flags);
}
else if (actions == ACTION_ADD) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 2);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
argv[0], value,
CONFIG_REGEX_NONE,
comment, flags);
}
else if (actions == ACTION_REPLACE_ALL) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 3);
- value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
argv[0], value, argv[2],
comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_GET) {
check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
+ ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+ 0, flags);
}
else if (actions == ACTION_GET_ALL) {
- do_all = 1;
check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
+ ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+ GET_VALUE_ALL, flags);
}
else if (actions == ACTION_GET_REGEXP) {
- show_keys = 1;
- use_key_regexp = 1;
- do_all = 1;
+ display_opts.show_keys = 1;
check_argc(argc, 1, 2);
- return get_value(argv[0], argv[1], flags);
+ ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+ GET_VALUE_ALL|GET_VALUE_KEY_REGEXP, flags);
}
else if (actions == ACTION_GET_URLMATCH) {
check_argc(argc, 2, 2);
- return get_urlmatch(argv[0], argv[1]);
+ ret = get_urlmatch(&location_opts, &display_opts, argv[0], argv[1]);
}
else if (actions == ACTION_UNSET) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 1, 2);
if (argc == 2)
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], NULL, argv[1],
- NULL, flags);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+ argv[0], NULL, argv[1],
+ NULL, flags);
else
- return git_config_set_in_file_gently(given_config_source.file,
- argv[0], NULL, NULL);
+ ret = git_config_set_in_file_gently(location_opts.source.file,
+ argv[0], NULL, NULL);
}
else if (actions == ACTION_UNSET_ALL) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 1, 2);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], NULL, argv[1],
- NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
+ ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+ argv[0], NULL, argv[1],
+ NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_RENAME_SECTION) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 2, 2);
- ret = git_config_rename_section_in_file(given_config_source.file,
+ ret = git_config_rename_section_in_file(location_opts.source.file,
argv[0], argv[1]);
if (ret < 0)
- return ret;
+ goto out;
else if (!ret)
die(_("no such section: %s"), argv[0]);
else
ret = 0;
}
else if (actions == ACTION_REMOVE_SECTION) {
- check_write();
+ check_write(&location_opts.source);
check_argc(argc, 1, 1);
- ret = git_config_rename_section_in_file(given_config_source.file,
+ ret = git_config_rename_section_in_file(location_opts.source.file,
argv[0], NULL);
if (ret < 0)
- return ret;
+ goto out;
else if (!ret)
die(_("no such section: %s"), argv[0]);
else
@@ -984,15 +1376,51 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
else if (actions == ACTION_GET_COLOR) {
check_argc(argc, 1, 2);
- get_color(argv[0], argv[1]);
+ get_color(&location_opts, argv[0], argv[1]);
}
else if (actions == ACTION_GET_COLORBOOL) {
check_argc(argc, 1, 2);
if (argc == 2)
color_stdout_is_tty = git_config_bool("command line", argv[1]);
- return get_colorbool(argv[0], argc == 2);
+ ret = get_colorbool(&location_opts, argv[0], argc == 2);
}
+out:
+ location_options_release(&location_opts);
+ free(comment);
free(value);
return ret;
}
+
+int cmd_config(int argc, const char **argv, const char *prefix)
+{
+ parse_opt_subcommand_fn *subcommand = NULL;
+ struct option subcommand_opts[] = {
+ OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
+ OPT_SUBCOMMAND("get", &subcommand, cmd_config_get),
+ OPT_SUBCOMMAND("set", &subcommand, cmd_config_set),
+ OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset),
+ OPT_SUBCOMMAND("rename-section", &subcommand, cmd_config_rename_section),
+ OPT_SUBCOMMAND("remove-section", &subcommand, cmd_config_remove_section),
+ OPT_SUBCOMMAND("edit", &subcommand, cmd_config_edit),
+ OPT_END(),
+ };
+
+ /*
+ * This is somewhat hacky: we first parse the command line while
+ * keeping all args intact in order to determine whether a subcommand
+ * has been specified. If so, we re-parse it a second time, but this
+ * time we drop KEEP_ARGV0. This is so that we don't munge the command
+ * line in case no subcommand was given, which would otherwise confuse
+ * us when parsing the legacy-style modes that don't use subcommands.
+ */
+ argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);
+ if (subcommand) {
+ argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);
+ return subcommand(argc, argv, prefix);
+ }
+
+ return cmd_config_actions(argc, argv, prefix);
+}
diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index 17f929d..4952b22 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -115,7 +115,9 @@ static int read_request(FILE *fh, struct credential *c,
return error("client sent bogus timeout line: %s", item.buf);
*timeout = atoi(p);
- if (credential_read(c, fh) < 0)
+ credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
+
+ if (credential_read(c, fh, CREDENTIAL_OP_HELPER) < 0)
return -1;
return 0;
}
@@ -131,8 +133,18 @@ static void serve_one_client(FILE *in, FILE *out)
else if (!strcmp(action.buf, "get")) {
struct credential_cache_entry *e = lookup_credential(&c);
if (e) {
- fprintf(out, "username=%s\n", e->item.username);
- fprintf(out, "password=%s\n", e->item.password);
+ e->item.capa_authtype.request_initial = 1;
+ e->item.capa_authtype.request_helper = 1;
+
+ fprintf(out, "capability[]=authtype\n");
+ if (e->item.username)
+ fprintf(out, "username=%s\n", e->item.username);
+ if (e->item.password)
+ fprintf(out, "password=%s\n", e->item.password);
+ if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.authtype)
+ fprintf(out, "authtype=%s\n", e->item.authtype);
+ if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.credential)
+ fprintf(out, "credential=%s\n", e->item.credential);
if (e->item.password_expiry_utc != TIME_MAX)
fprintf(out, "password_expiry_utc=%"PRItime"\n",
e->item.password_expiry_utc);
@@ -157,8 +169,10 @@ static void serve_one_client(FILE *in, FILE *out)
else if (!strcmp(action.buf, "store")) {
if (timeout < 0)
warning("cache client didn't specify a timeout");
- else if (!c.username || !c.password)
+ else if ((!c.username || !c.password) && (!c.authtype && !c.credential))
warning("cache client gave us a partial credential");
+ else if (c.ephemeral)
+ warning("not storing ephemeral credential");
else {
remove_credential(&c, 0);
cache_credential(&c, timeout);
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index bef120b..3db8df7 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "credential.h"
#include "gettext.h"
#include "parse-options.h"
#include "path.h"
@@ -127,6 +128,13 @@ static char *get_socket_path(void)
return socket;
}
+static void announce_capabilities(void)
+{
+ struct credential c = CREDENTIAL_INIT;
+ c.capa_authtype.request_initial = 1;
+ credential_announce_capabilities(&c, stdout);
+}
+
int cmd_credential_cache(int argc, const char **argv, const char *prefix)
{
char *socket_path = NULL;
@@ -163,6 +171,8 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix)
do_cache(socket_path, op, timeout, FLAG_RELAY);
else if (!strcmp(op, "store"))
do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN);
+ else if (!strcmp(op, "capability"))
+ announce_capabilities();
else
; /* ignore unknown operation */
diff --git a/builtin/credential-store.c b/builtin/credential-store.c
index 4a49241..494c809 100644
--- a/builtin/credential-store.c
+++ b/builtin/credential-store.c
@@ -205,7 +205,7 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix)
if (!fns.nr)
die("unable to set up default path; use --file");
- if (credential_read(&c, stdin) < 0)
+ if (credential_read(&c, stdin, CREDENTIAL_OP_HELPER) < 0)
die("unable to read credential");
if (!strcmp(op, "get"))
diff --git a/builtin/credential.c b/builtin/credential.c
index 7010752..b72e76d 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -17,18 +17,29 @@ int cmd_credential(int argc, const char **argv, const char *prefix UNUSED)
usage(usage_msg);
op = argv[1];
- if (credential_read(&c, stdin) < 0)
+ if (!strcmp(op, "capability")) {
+ credential_set_all_capabilities(&c, CREDENTIAL_OP_INITIAL);
+ credential_announce_capabilities(&c, stdout);
+ return 0;
+ }
+
+ if (credential_read(&c, stdin, CREDENTIAL_OP_INITIAL) < 0)
die("unable to read credential from stdin");
if (!strcmp(op, "fill")) {
- credential_fill(&c);
- credential_write(&c, stdout);
+ credential_fill(&c, 0);
+ credential_next_state(&c);
+ credential_write(&c, stdout, CREDENTIAL_OP_RESPONSE);
} else if (!strcmp(op, "approve")) {
+ credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
credential_approve(&c);
} else if (!strcmp(op, "reject")) {
+ credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
credential_reject(&c);
} else {
usage(usage_msg);
}
+
+ credential_clear(&c);
return 0;
}
diff --git a/builtin/describe.c b/builtin/describe.c
index d6c77a7..e5287ed 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "environment.h"
@@ -201,7 +200,7 @@ static int get_name(const char *path, const struct object_id *oid,
}
/* Is it annotated? */
- if (!peel_iterated_oid(oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, oid, &peeled)) {
is_annotated = !oideq(oid, &peeled);
} else {
oidcpy(&peeled, oid);
@@ -638,7 +637,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
}
hashmap_init(&names, commit_name_neq, NULL, 0);
- for_each_rawref(get_name, NULL);
+ refs_for_each_rawref(get_main_ref_store(the_repository), get_name,
+ NULL);
if (!hashmap_get_size(&names) && !always)
die(_("No names found, cannot describe anything."));
@@ -674,7 +674,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
repo_read_index(the_repository);
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
+ refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED,
NULL, NULL, NULL);
fd = repo_hold_locked_index(the_repository,
&index_lock, 0);
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 4f22eb2..4857a43 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
struct tm tm;
enum diagnose_mode mode = DIAGNOSE_STATS;
char *option_output = NULL;
- char *option_suffix = "%Y-%m-%d-%H%M";
+ const char *option_suffix = "%Y-%m-%d-%H%M";
char *prefixed_filename;
const struct option diagnose_options[] = {
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index a8e68ce..0d3c611 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "diff.h"
@@ -206,7 +205,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
opt->diffopt.rotate_to_strict = 0;
opt->diffopt.no_free = 1;
if (opt->diffopt.detect_rename) {
- if (!the_index.cache)
+ if (the_repository->index->cache)
repo_read_index(the_repository);
opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE;
}
diff --git a/builtin/diff.c b/builtin/diff.c
index 6e196e0..9b6cdab 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2006 Junio C Hamano
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "ewah/ewok.h"
@@ -239,9 +239,9 @@ static void refresh_index_quietly(void)
fd = repo_hold_locked_index(the_repository, &lock_file, 0);
if (fd < 0)
return;
- discard_index(&the_index);
+ discard_index(the_repository->index);
repo_read_index(the_repository);
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
+ refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
NULL);
repo_update_index_if_able(the_repository, &lock_file);
}
@@ -465,6 +465,15 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
no_index = DIFF_NO_INDEX_IMPLICIT;
}
+ /*
+ * When operating outside of a Git repository we need to have a hash
+ * algorithm at hand so that we can generate the blob hashes. We
+ * default to SHA1 here, but may eventually want to change this to be
+ * configurable via a command line option.
+ */
+ if (nongit)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
prefix = precompose_argv_prefix(argc, argv, prefix);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 06a6c14..a1794b7 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -11,7 +11,7 @@
*
* Copyright (C) 2016 Johannes Schindelin
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "abspath.h"
#include "config.h"
@@ -117,7 +117,7 @@ static int use_wt_file(const char *workdir, const char *name,
int fd = open(buf.buf, O_RDONLY);
if (fd >= 0 &&
- !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+ !index_fd(the_repository->index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
if (is_null_oid(oid)) {
oidcpy(oid, &wt_oid);
use = 1;
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 4693d18..4b6e8c6 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -415,7 +415,7 @@ static char *generate_fake_oid(void)
struct object_id oid;
char *hex = xmallocz(GIT_MAX_HEXSZ);
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
put_be32(oid.hash + hashsz - 4, counter++);
return oid_to_hex_r(hex, &oid);
}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index dc5a9d3..d21c405 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1279,8 +1279,10 @@ static void load_tree(struct tree_entry *root)
e->versions[0].mode = e->versions[1].mode;
e->name = to_atom(c, strlen(c));
c += e->name->str_len + 1;
- oidread(&e->versions[0].oid, (unsigned char *)c);
- oidread(&e->versions[1].oid, (unsigned char *)c);
+ oidread(&e->versions[0].oid, (unsigned char *)c,
+ the_repository->hash_algo);
+ oidread(&e->versions[1].oid, (unsigned char *)c,
+ the_repository->hash_algo);
c += the_hash_algo->rawsz;
}
free(buf);
@@ -1386,7 +1388,7 @@ static void tree_content_replace(
{
if (!S_ISDIR(mode))
die("Root cannot be a non-directory");
- oidclr(&root->versions[0].oid);
+ oidclr(&root->versions[0].oid, the_repository->hash_algo);
oidcpy(&root->versions[1].oid, oid);
if (root->tree)
release_tree_content_recursive(root->tree);
@@ -1445,7 +1447,7 @@ static int tree_content_set(
if (S_ISDIR(e->versions[0].mode))
e->versions[0].mode |= NO_DELTA;
- oidclr(&root->versions[1].oid);
+ oidclr(&root->versions[1].oid, the_repository->hash_algo);
return 1;
}
if (!S_ISDIR(e->versions[1].mode)) {
@@ -1455,7 +1457,7 @@ static int tree_content_set(
if (!e->tree)
load_tree(e);
if (tree_content_set(e, slash1 + 1, oid, mode, subtree)) {
- oidclr(&root->versions[1].oid);
+ oidclr(&root->versions[1].oid, the_repository->hash_algo);
return 1;
}
return 0;
@@ -1467,7 +1469,7 @@ static int tree_content_set(
e = new_tree_entry();
e->name = to_atom(p, n);
e->versions[0].mode = 0;
- oidclr(&e->versions[0].oid);
+ oidclr(&e->versions[0].oid, the_repository->hash_algo);
t->entries[t->entry_count++] = e;
if (*slash1) {
e->tree = new_tree_content(8);
@@ -1478,7 +1480,7 @@ static int tree_content_set(
e->versions[1].mode = mode;
oidcpy(&e->versions[1].oid, oid);
}
- oidclr(&root->versions[1].oid);
+ oidclr(&root->versions[1].oid, the_repository->hash_algo);
return 1;
}
@@ -1523,7 +1525,8 @@ static int tree_content_remove(
if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) {
for (n = 0; n < e->tree->entry_count; n++) {
if (e->tree->entries[n]->versions[1].mode) {
- oidclr(&root->versions[1].oid);
+ oidclr(&root->versions[1].oid,
+ the_repository->hash_algo);
return 1;
}
}
@@ -1542,8 +1545,8 @@ static int tree_content_remove(
release_tree_content_recursive(e->tree);
e->tree = NULL;
e->versions[1].mode = 0;
- oidclr(&e->versions[1].oid);
- oidclr(&root->versions[1].oid);
+ oidclr(&e->versions[1].oid, the_repository->hash_algo);
+ oidclr(&root->versions[1].oid, the_repository->hash_algo);
return 1;
}
@@ -1604,11 +1607,12 @@ static int update_branch(struct branch *b)
if (is_null_oid(&b->oid)) {
if (b->delete)
- delete_ref(NULL, b->name, NULL, 0);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, b->name, NULL, 0);
return 0;
}
- if (read_ref(b->name, &old_oid))
- oidclr(&old_oid);
+ if (refs_read_ref(get_main_ref_store(the_repository), b->name, &old_oid))
+ oidclr(&old_oid, the_repository->hash_algo);
if (!force_update && !is_null_oid(&old_oid)) {
struct commit *old_cmit, *new_cmit;
int ret;
@@ -1631,10 +1635,11 @@ static int update_branch(struct branch *b)
return -1;
}
}
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
- 0, msg, &err) ||
+ NULL, NULL, 0, msg, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
@@ -1665,7 +1670,8 @@ static void dump_tags(void)
struct strbuf err = STRBUF_INIT;
struct ref_transaction *transaction;
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
failure |= error("%s", err.buf);
goto cleanup;
@@ -1675,7 +1681,8 @@ static void dump_tags(void)
strbuf_addf(&ref_name, "refs/tags/%s", t->name);
if (ref_transaction_update(transaction, ref_name.buf,
- &t->oid, NULL, 0, msg, &err)) {
+ &t->oid, NULL, NULL, NULL,
+ 0, msg, &err)) {
failure |= error("%s", err.buf);
goto cleanup;
}
@@ -2354,7 +2361,9 @@ static void file_change_m(const char *p, struct branch *b)
parse_path_eol(&path, p, "path");
/* Git does not track empty, non-toplevel directories. */
- if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *path.buf) {
+ if (S_ISDIR(mode) &&
+ is_empty_tree_oid(&oid, the_repository->hash_algo) &&
+ *path.buf) {
tree_content_remove(&b->branch_tree, path.buf, NULL, 0);
return;
}
@@ -2546,8 +2555,8 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
static void file_change_deleteall(struct branch *b)
{
release_tree_content_recursive(b->branch_tree.tree);
- oidclr(&b->branch_tree.versions[0].oid);
- oidclr(&b->branch_tree.versions[1].oid);
+ oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo);
+ oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo);
load_tree(&b->branch_tree);
b->num_notes = 0;
}
@@ -2566,8 +2575,8 @@ static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
static void parse_from_existing(struct branch *b)
{
if (is_null_oid(&b->oid)) {
- oidclr(&b->branch_tree.versions[0].oid);
- oidclr(&b->branch_tree.versions[1].oid);
+ oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo);
+ oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo);
} else {
unsigned long size;
char *buf;
@@ -2890,9 +2899,9 @@ static void parse_reset_branch(const char *arg)
b = lookup_branch(arg);
if (b) {
- oidclr(&b->oid);
- oidclr(&b->branch_tree.versions[0].oid);
- oidclr(&b->branch_tree.versions[1].oid);
+ oidclr(&b->oid, the_repository->hash_algo);
+ oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo);
+ oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo);
if (b->branch_tree.tree) {
release_tree_content_recursive(b->branch_tree.tree);
b->branch_tree.tree = NULL;
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 44c05ee..af329e8 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -29,11 +29,11 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
; /* <oid>, leave oid as name */
} else {
/* <ref>, clear cruft from oid */
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
}
} else {
/* <ref>, clear cruft from get_oid_hex */
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
}
ref = alloc_ref(name);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d86..693f02b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -340,7 +340,8 @@ static void find_non_local_tags(const struct ref *refs,
refname_hash_init(&remote_refs);
create_fetch_oidset(head, &fetch_oids);
- for_each_ref(add_one_refname, &existing_refs);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_one_refname,
+ &existing_refs);
/*
* If we already have a transaction, then we need to filter out all
@@ -581,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
}
}
- if (tags == TAGS_SET)
+ if (tags == TAGS_SET) {
+ struct refspec_item tag_refspec;
+
/* also fetch all tags */
- get_fetch_map(remote_refs, tag_refspec, &tail, 0);
- else if (tags == TAGS_DEFAULT && *autotags)
+ refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+ get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
+ refspec_item_clear(&tag_refspec);
+ } else if (tags == TAGS_DEFAULT && *autotags) {
find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
+ }
/* Now append any refs to be updated opportunistically: */
*tail = orefs;
@@ -614,7 +620,9 @@ static struct ref *get_ref_map(struct remote *remote,
if (!existing_refs_populated) {
refname_hash_init(&existing_refs);
- for_each_ref(add_one_refname, &existing_refs);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_one_refname,
+ &existing_refs);
existing_refs_populated = 1;
}
@@ -659,7 +667,8 @@ static int s_update_ref(const char *action,
* lifecycle.
*/
if (!transaction) {
- transaction = our_transaction = ref_transaction_begin(&err);
+ transaction = our_transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
ret = STORE_REF_ERROR_OTHER;
goto out;
@@ -668,7 +677,7 @@ static int s_update_ref(const char *action,
ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
check_old ? &ref->old_oid : NULL,
- 0, msg, &err);
+ NULL, NULL, 0, msg, &err);
if (ret) {
ret = STORE_REF_ERROR_OTHER;
goto out;
@@ -1382,8 +1391,8 @@ static int prune_refs(struct display_state *display_state,
if (!dry_run) {
if (transaction) {
for (ref = stale_refs; ref; ref = ref->next) {
- result = ref_transaction_delete(transaction, ref->name, NULL, 0,
- "fetch: prune", &err);
+ result = ref_transaction_delete(transaction, ref->name, NULL,
+ NULL, 0, "fetch: prune", &err);
if (result)
goto cleanup;
}
@@ -1393,7 +1402,9 @@ static int prune_refs(struct display_state *display_state,
for (ref = stale_refs; ref; ref = ref->next)
string_list_append(&refnames, ref->name);
- result = delete_refs("fetch: prune", &refnames, 0);
+ result = refs_delete_refs(get_main_ref_store(the_repository),
+ "fetch: prune", &refnames,
+ 0);
string_list_clear(&refnames, 0);
}
}
@@ -1406,7 +1417,8 @@ static int prune_refs(struct display_state *display_state,
_("(none)"), ref->name,
&ref->new_oid, &ref->old_oid,
summary_width);
- warn_dangling_symref(stderr, dangling_msg, ref->name);
+ refs_warn_dangling_symref(get_main_ref_store(the_repository),
+ stderr, dangling_msg, ref->name);
}
}
@@ -1479,7 +1491,8 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
continue;
}
old_nr = oids->nr;
- for_each_glob_ref(add_oid, s, oids);
+ refs_for_each_glob_ref(get_main_ref_store(the_repository),
+ add_oid, s, oids);
if (old_nr == oids->nr)
warning("ignoring --negotiation-tip=%s because it does not match any refs",
s);
@@ -1655,7 +1668,8 @@ static int do_fetch(struct transport *transport,
config->display_format);
if (atomic_fetch) {
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
retcode = -1;
goto cleanup;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 919282e..5517a4a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -98,7 +98,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
}
if (include_root_refs)
- flags |= FILTER_REFS_ROOT_REFS;
+ flags |= FILTER_REFS_ROOT_REFS | FILTER_REFS_DETACHED_HEAD;
filter.match_as_path = 1;
filter_and_format_refs(&filter, flags, sorting, &format);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index f892487..d13a226 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -514,7 +514,9 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
struct strbuf refname = STRBUF_INIT;
strbuf_worktree_ref(cb_data, &refname, logname);
- for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname.buf, fsck_handle_reflog_ent,
+ refname.buf);
strbuf_release(&refname);
return 0;
}
@@ -563,7 +565,8 @@ static void get_default_heads(void)
const char *head_points_at;
struct object_id head_oid;
- for_each_rawref(fsck_handle_ref, NULL);
+ refs_for_each_rawref(get_main_ref_store(the_repository),
+ fsck_handle_ref, NULL);
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
@@ -712,7 +715,9 @@ static int fsck_head_link(const char *head_ref_name,
if (verbose)
fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
- *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
+ *head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ head_ref_name, 0, head_oid,
+ NULL);
if (!*head_points_at) {
errors_found |= ERROR_REFS;
return error(_("invalid %s"), head_ref_name);
diff --git a/builtin/gc.c b/builtin/gc.c
index d3b5ca9..72bac25 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -846,7 +846,7 @@ static int dfs_on_ref(const char *refname UNUSED,
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
return 0;
@@ -907,7 +907,8 @@ static int should_write_commit_graph(void)
if (data.limit < 0)
return 1;
- result = for_each_ref(dfs_on_ref, &data);
+ result = refs_for_each_ref(get_main_ref_store(the_repository),
+ dfs_on_ref, &data);
repo_clear_commit_marks(the_repository, SEEN);
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 82ca6d2..c767414 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -123,6 +123,9 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
else
prefix = setup_git_directory_gently(&nongit);
+ if (nongit && !the_hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
if (vpath && prefix) {
vpath_free = prefix_filename(prefix, vpath);
vpath = vpath_free;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 856428f..fd968d6 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -528,7 +528,8 @@ static void *unpack_raw_entry(struct object_entry *obj,
switch (obj->type) {
case OBJ_REF_DELTA:
- oidread(ref_oid, fill(the_hash_algo->rawsz));
+ oidread(ref_oid, fill(the_hash_algo->rawsz),
+ the_repository->hash_algo);
use(the_hash_algo->rawsz);
break;
case OBJ_OFS_DELTA:
@@ -1204,7 +1205,7 @@ static void parse_pack_objects(unsigned char *hash)
the_hash_algo->init_fn(&tmp_ctx);
the_hash_algo->clone_fn(&tmp_ctx, &input_ctx);
the_hash_algo->final_fn(hash, &tmp_ctx);
- if (!hasheq(fill(the_hash_algo->rawsz), hash))
+ if (!hasheq(fill(the_hash_algo->rawsz), hash, the_repository->hash_algo))
die(_("pack is corrupted (SHA1 mismatch)"));
use(the_hash_algo->rawsz);
@@ -1307,11 +1308,11 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
stop_progress_msg(&progress, msg.buf);
strbuf_release(&msg);
finalize_hashfile(f, tail_hash, FSYNC_COMPONENT_PACK, 0);
- hashcpy(read_hash, pack_hash);
+ hashcpy(read_hash, pack_hash, the_repository->hash_algo);
fixup_pack_header_footer(output_fd, pack_hash,
curr_pack, nr_objects,
read_hash, consumed_bytes-the_hash_algo->rawsz);
- if (!hasheq(read_hash, tail_hash))
+ if (!hasheq(read_hash, tail_hash, the_repository->hash_algo))
die(_("Unexpected tail checksum for %s "
"(disk corruption?)"), curr_pack);
}
@@ -1372,7 +1373,7 @@ static struct object_entry *append_obj_to_pack(struct hashfile *f,
obj[1].idx.offset += write_compressed(f, buf, size);
obj[0].idx.crc32 = crc32_end(f);
hashflush(f);
- oidread(&obj->idx.oid, sha1);
+ oidread(&obj->idx.oid, sha1, the_repository->hash_algo);
return obj;
}
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 0170469..582dcf2 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -81,7 +81,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *ref_format = NULL;
const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
- unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+ enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
int init_shared_repository = -1;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 8768bfe..1d96949 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -141,7 +141,7 @@ static void interpret_trailers(const struct process_trailer_options *opts,
LIST_HEAD(head);
struct strbuf sb = STRBUF_INIT;
struct strbuf trailer_block = STRBUF_INIT;
- struct trailer_info info;
+ struct trailer_info *info;
FILE *outfile = stdout;
trailer_config_init();
@@ -151,13 +151,13 @@ static void interpret_trailers(const struct process_trailer_options *opts,
if (opts->in_place)
outfile = create_in_place_tempfile(file);
- parse_trailers(opts, &info, sb.buf, &head);
+ info = parse_trailers(opts, sb.buf, &head);
/* Print the lines before the trailers */
if (!opts->only_trailers)
- fwrite(sb.buf, 1, info.trailer_block_start, outfile);
+ fwrite(sb.buf, 1, trailer_block_start(info), outfile);
- if (!opts->only_trailers && !info.blank_line_before_trailer)
+ if (!opts->only_trailers && !blank_line_before_trailer_block(info))
fprintf(outfile, "\n");
@@ -178,8 +178,8 @@ static void interpret_trailers(const struct process_trailer_options *opts,
/* Print the lines after the trailers as is */
if (!opts->only_trailers)
- fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
- trailer_info_release(&info);
+ fwrite(sb.buf + trailer_block_end(info), 1, sb.len - trailer_block_end(info), outfile);
+ trailer_info_release(info);
if (opts->in_place)
if (rename_tempfile(&trailers_tempfile, file))
diff --git a/builtin/log.c b/builtin/log.c
index 8bab30f..1b43b60 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -4,7 +4,7 @@
* (C) Copyright 2006 Linus Torvalds
* 2006 Junio Hamano
*/
-#include "git-compat-util.h"
+#include "builtin.h"
#include "abspath.h"
#include "config.h"
#include "environment.h"
@@ -48,22 +48,8 @@
#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
#define FORMAT_PATCH_NAME_MAX_DEFAULT 64
-/* Set a default date-time format for git log ("log.date" config variable) */
-static const char *default_date_mode = NULL;
-
-static int default_abbrev_commit;
-static int default_show_root = 1;
-static int default_follow;
-static int default_show_signature;
-static int default_encode_email_headers = 1;
-static int decoration_style;
-static int decoration_given;
-static int use_mailmap_config = 1;
static unsigned int force_in_body_from;
static int stdout_mboxrd;
-static const char *fmt_patch_subject_prefix = "PATCH";
-static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
-static const char *fmt_pretty;
static int format_no_prefix;
static const char * const builtin_log_usage[] = {
@@ -111,6 +97,39 @@ static int parse_decoration_style(const char *value)
return -1;
}
+struct log_config {
+ int default_abbrev_commit;
+ int default_show_root;
+ int default_follow;
+ int default_show_signature;
+ int default_encode_email_headers;
+ int decoration_style;
+ int decoration_given;
+ int use_mailmap_config;
+ char *fmt_patch_subject_prefix;
+ int fmt_patch_name_max;
+ char *fmt_pretty;
+ char *default_date_mode;
+};
+
+static void log_config_init(struct log_config *cfg)
+{
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->default_show_root = 1;
+ cfg->default_encode_email_headers = 1;
+ cfg->use_mailmap_config = 1;
+ cfg->fmt_patch_subject_prefix = xstrdup("PATCH");
+ cfg->fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
+ cfg->decoration_style = auto_decoration_style();
+}
+
+static void log_config_release(struct log_config *cfg)
+{
+ free(cfg->default_date_mode);
+ free(cfg->fmt_pretty);
+ free(cfg->fmt_patch_subject_prefix);
+}
+
static int use_default_decoration_filter = 1;
static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
@@ -127,20 +146,22 @@ static int clear_decorations_callback(const struct option *opt UNUSED,
return 0;
}
-static int decorate_callback(const struct option *opt UNUSED, const char *arg,
+static int decorate_callback(const struct option *opt, const char *arg,
int unset)
{
- if (unset)
- decoration_style = 0;
- else if (arg)
- decoration_style = parse_decoration_style(arg);
- else
- decoration_style = DECORATE_SHORT_REFS;
+ struct log_config *cfg = opt->value;
- if (decoration_style < 0)
+ if (unset)
+ cfg->decoration_style = 0;
+ else if (arg)
+ cfg->decoration_style = parse_decoration_style(arg);
+ else
+ cfg->decoration_style = DECORATE_SHORT_REFS;
+
+ if (cfg->decoration_style < 0)
die(_("invalid --decorate option: %s"), arg);
- decoration_given = 1;
+ cfg->decoration_given = 1;
return 0;
}
@@ -160,32 +181,26 @@ static int log_line_range_callback(const struct option *option, const char *arg,
return 0;
}
-static void init_log_defaults(void)
+static void cmd_log_init_defaults(struct rev_info *rev,
+ struct log_config *cfg)
{
- init_diff_ui_defaults();
-
- decoration_style = auto_decoration_style();
-}
-
-static void cmd_log_init_defaults(struct rev_info *rev)
-{
- if (fmt_pretty)
- get_commit_format(fmt_pretty, rev);
- if (default_follow)
+ if (cfg->fmt_pretty)
+ get_commit_format(cfg->fmt_pretty, rev);
+ if (cfg->default_follow)
rev->diffopt.flags.default_follow_renames = 1;
rev->verbose_header = 1;
init_diffstat_widths(&rev->diffopt);
rev->diffopt.flags.recursive = 1;
rev->diffopt.flags.allow_textconv = 1;
- rev->abbrev_commit = default_abbrev_commit;
- rev->show_root_diff = default_show_root;
- rev->subject_prefix = fmt_patch_subject_prefix;
- rev->patch_name_max = fmt_patch_name_max;
- rev->show_signature = default_show_signature;
- rev->encode_email_headers = default_encode_email_headers;
+ rev->abbrev_commit = cfg->default_abbrev_commit;
+ rev->show_root_diff = cfg->default_show_root;
+ rev->subject_prefix = cfg->fmt_patch_subject_prefix;
+ rev->patch_name_max = cfg->fmt_patch_name_max;
+ rev->show_signature = cfg->default_show_signature;
+ rev->encode_email_headers = cfg->default_encode_email_headers;
- if (default_date_mode)
- parse_date_format(default_date_mode, &rev->date_mode);
+ if (cfg->default_date_mode)
+ parse_date_format(cfg->default_date_mode, &rev->date_mode);
}
static void set_default_decoration_filter(struct decoration_filter *decoration_filter)
@@ -233,7 +248,8 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f
}
static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
- struct rev_info *rev, struct setup_revision_opt *opt)
+ struct rev_info *rev, struct setup_revision_opt *opt,
+ struct log_config *cfg)
{
struct userformat_want w;
int quiet = 0, source = 0, mailmap;
@@ -258,7 +274,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
N_("pattern"), N_("only decorate refs that match <pattern>")),
OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
N_("pattern"), N_("do not decorate refs that match <pattern>")),
- OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"),
+ OPT_CALLBACK_F(0, "decorate", cfg, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback),
OPT_CALLBACK('L', NULL, &line_cb, "range:file",
N_("trace the evolution of line range <start>,<end> or function :<funcname> in <file>"),
@@ -269,7 +285,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
line_cb.rev = rev;
line_cb.prefix = prefix;
- mailmap = use_mailmap_config;
+ mailmap = cfg->use_mailmap_config;
argc = parse_options(argc, argv, prefix,
builtin_log_options, builtin_log_usage,
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
@@ -314,8 +330,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
* "log --pretty=raw" is special; ignore UI oriented
* configuration variables such as decoration.
*/
- if (!decoration_given)
- decoration_style = 0;
+ if (!cfg->decoration_given)
+ cfg->decoration_style = 0;
if (!rev->abbrev_commit_given)
rev->abbrev_commit = 0;
}
@@ -326,24 +342,24 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
* Disable decoration loading if the format will not
* show them anyway.
*/
- decoration_style = 0;
- } else if (!decoration_style) {
+ cfg->decoration_style = 0;
+ } else if (!cfg->decoration_style) {
/*
* If we are going to show them, make sure we do load
* them here, but taking care not to override a
* specific style set by config or --decorate.
*/
- decoration_style = DECORATE_SHORT_REFS;
+ cfg->decoration_style = DECORATE_SHORT_REFS;
}
}
- if (decoration_style || rev->simplify_by_decoration) {
+ if (cfg->decoration_style || rev->simplify_by_decoration) {
set_default_decoration_filter(&decoration_filter);
- if (decoration_style)
+ if (cfg->decoration_style)
rev->show_decorations = 1;
- load_ref_decorations(&decoration_filter, decoration_style);
+ load_ref_decorations(&decoration_filter, cfg->decoration_style);
}
if (rev->line_level_traverse)
@@ -353,16 +369,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
}
static void cmd_log_init(int argc, const char **argv, const char *prefix,
- struct rev_info *rev, struct setup_revision_opt *opt)
+ struct rev_info *rev, struct setup_revision_opt *opt,
+ struct log_config *cfg)
{
- cmd_log_init_defaults(rev);
- cmd_log_init_finish(argc, argv, prefix, rev, opt);
-}
-
-static int cmd_log_deinit(int ret, struct rev_info *rev)
-{
- release_revisions(rev);
- return ret;
+ cmd_log_init_defaults(rev, cfg);
+ cmd_log_init_finish(argc, argv, prefix, rev, opt, cfg);
}
/*
@@ -566,30 +577,37 @@ static int cmd_log_walk(struct rev_info *rev)
static int git_log_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
+ struct log_config *cfg = cb;
const char *slot_name;
- if (!strcmp(var, "format.pretty"))
- return git_config_string(&fmt_pretty, var, value);
- if (!strcmp(var, "format.subjectprefix"))
- return git_config_string(&fmt_patch_subject_prefix, var, value);
+ if (!strcmp(var, "format.pretty")) {
+ FREE_AND_NULL(cfg->fmt_pretty);
+ return git_config_string(&cfg->fmt_pretty, var, value);
+ }
+ if (!strcmp(var, "format.subjectprefix")) {
+ FREE_AND_NULL(cfg->fmt_patch_subject_prefix);
+ return git_config_string(&cfg->fmt_patch_subject_prefix, var, value);
+ }
if (!strcmp(var, "format.filenamemaxlength")) {
- fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
+ cfg->fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
return 0;
}
if (!strcmp(var, "format.encodeemailheaders")) {
- default_encode_email_headers = git_config_bool(var, value);
+ cfg->default_encode_email_headers = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "log.abbrevcommit")) {
- default_abbrev_commit = git_config_bool(var, value);
+ cfg->default_abbrev_commit = git_config_bool(var, value);
return 0;
}
- if (!strcmp(var, "log.date"))
- return git_config_string(&default_date_mode, var, value);
+ if (!strcmp(var, "log.date")) {
+ FREE_AND_NULL(cfg->default_date_mode);
+ return git_config_string(&cfg->default_date_mode, var, value);
+ }
if (!strcmp(var, "log.decorate")) {
- decoration_style = parse_decoration_style(value);
- if (decoration_style < 0)
- decoration_style = 0; /* maybe warn? */
+ cfg->decoration_style = parse_decoration_style(value);
+ if (cfg->decoration_style < 0)
+ cfg->decoration_style = 0; /* maybe warn? */
return 0;
}
if (!strcmp(var, "log.diffmerges")) {
@@ -598,21 +616,21 @@ static int git_log_config(const char *var, const char *value,
return diff_merges_config(value);
}
if (!strcmp(var, "log.showroot")) {
- default_show_root = git_config_bool(var, value);
+ cfg->default_show_root = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "log.follow")) {
- default_follow = git_config_bool(var, value);
+ cfg->default_follow = git_config_bool(var, value);
return 0;
}
if (skip_prefix(var, "color.decorate.", &slot_name))
return parse_decorate_color_config(var, slot_name, value);
if (!strcmp(var, "log.mailmap")) {
- use_mailmap_config = git_config_bool(var, value);
+ cfg->use_mailmap_config = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "log.showsignature")) {
- default_show_signature = git_config_bool(var, value);
+ cfg->default_show_signature = git_config_bool(var, value);
return 0;
}
@@ -621,11 +639,14 @@ static int git_log_config(const char *var, const char *value,
int cmd_whatchanged(int argc, const char **argv, const char *prefix)
{
+ struct log_config cfg;
struct rev_info rev;
struct setup_revision_opt opt;
+ int ret;
- init_log_defaults();
- git_config(git_log_config, NULL);
+ log_config_init(&cfg);
+ init_diff_ui_defaults();
+ git_config(git_log_config, &cfg);
repo_init_revisions(the_repository, &rev, prefix);
git_config(grep_config, &rev.grep_filter);
@@ -635,10 +656,15 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
opt.revarg_opt = REVARG_COMMITTISH;
- cmd_log_init(argc, argv, prefix, &rev, &opt);
+ cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_RAW;
- return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+
+ ret = cmd_log_walk(&rev);
+
+ release_revisions(&rev);
+ log_config_release(&cfg);
+ return ret;
}
static void show_tagger(const char *buf, struct rev_info *rev)
@@ -733,14 +759,16 @@ static void show_setup_revisions_tweak(struct rev_info *rev)
int cmd_show(int argc, const char **argv, const char *prefix)
{
+ struct log_config cfg;
struct rev_info rev;
unsigned int i;
struct setup_revision_opt opt;
struct pathspec match_all;
int ret = 0;
- init_log_defaults();
- git_config(git_log_config, NULL);
+ log_config_init(&cfg);
+ init_diff_ui_defaults();
+ git_config(git_log_config, &cfg);
if (the_repository->gitdir) {
prepare_repo_settings(the_repository);
@@ -759,10 +787,14 @@ int cmd_show(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
opt.tweak = show_setup_revisions_tweak;
- cmd_log_init(argc, argv, prefix, &rev, &opt);
+ cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
- if (!rev.no_walk)
- return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+ if (!rev.no_walk) {
+ ret = cmd_log_walk(&rev);
+ release_revisions(&rev);
+ log_config_release(&cfg);
+ return ret;
+ }
rev.diffopt.no_free = 1;
for (i = 0; i < rev.pending.nr && !ret; i++) {
@@ -832,8 +864,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.diffopt.no_free = 0;
diff_free(&rev.diffopt);
+ release_revisions(&rev);
+ log_config_release(&cfg);
- return cmd_log_deinit(ret, &rev);
+ return ret;
}
/*
@@ -841,11 +875,14 @@ int cmd_show(int argc, const char **argv, const char *prefix)
*/
int cmd_log_reflog(int argc, const char **argv, const char *prefix)
{
+ struct log_config cfg;
struct rev_info rev;
struct setup_revision_opt opt;
+ int ret;
- init_log_defaults();
- git_config(git_log_config, NULL);
+ log_config_init(&cfg);
+ init_diff_ui_defaults();
+ git_config(git_log_config, &cfg);
repo_init_revisions(the_repository, &rev, prefix);
init_reflog_walk(&rev.reflog_info);
@@ -854,14 +891,18 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
rev.verbose_header = 1;
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
- cmd_log_init_defaults(&rev);
+ cmd_log_init_defaults(&rev, &cfg);
rev.abbrev_commit = 1;
rev.commit_format = CMIT_FMT_ONELINE;
rev.use_terminator = 1;
rev.always_show_header = 1;
- cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
+ cmd_log_init_finish(argc, argv, prefix, &rev, &opt, &cfg);
- return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+ ret = cmd_log_walk(&rev);
+
+ release_revisions(&rev);
+ log_config_release(&cfg);
+ return ret;
}
static void log_setup_revisions_tweak(struct rev_info *rev)
@@ -876,11 +917,14 @@ static void log_setup_revisions_tweak(struct rev_info *rev)
int cmd_log(int argc, const char **argv, const char *prefix)
{
+ struct log_config cfg;
struct rev_info rev;
struct setup_revision_opt opt;
+ int ret;
- init_log_defaults();
- git_config(git_log_config, NULL);
+ log_config_init(&cfg);
+ init_diff_ui_defaults();
+ git_config(git_log_config, &cfg);
repo_init_revisions(the_repository, &rev, prefix);
git_config(grep_config, &rev.grep_filter);
@@ -890,42 +934,17 @@ int cmd_log(int argc, const char **argv, const char *prefix)
opt.def = "HEAD";
opt.revarg_opt = REVARG_COMMITTISH;
opt.tweak = log_setup_revisions_tweak;
- cmd_log_init(argc, argv, prefix, &rev, &opt);
- return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+ cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
+
+ ret = cmd_log_walk(&rev);
+
+ release_revisions(&rev);
+ log_config_release(&cfg);
+ return ret;
}
/* format-patch */
-static const char *fmt_patch_suffix = ".patch";
-static int numbered = 0;
-static int auto_number = 1;
-
-static char *default_attach = NULL;
-
-static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
-static struct string_list extra_to = STRING_LIST_INIT_NODUP;
-static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
-
-static void add_header(const char *value)
-{
- struct string_list_item *item;
- int len = strlen(value);
- while (len && value[len - 1] == '\n')
- len--;
-
- if (!strncasecmp(value, "to: ", 4)) {
- item = string_list_append(&extra_to, value + 4);
- len -= 4;
- } else if (!strncasecmp(value, "cc: ", 4)) {
- item = string_list_append(&extra_cc, value + 4);
- len -= 4;
- } else {
- item = string_list_append(&extra_hdr, value);
- }
-
- item->string[len] = '\0';
-}
-
enum cover_setting {
COVER_UNSET,
COVER_OFF,
@@ -952,17 +971,61 @@ enum auto_base_setting {
AUTO_BASE_WHEN_ABLE
};
-static enum thread_level thread;
-static int do_signoff;
-static enum auto_base_setting auto_base;
-static char *from;
-static const char *signature = git_version_string;
-static const char *signature_file;
-static enum cover_setting config_cover_letter;
-static const char *config_output_directory;
-static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
-static int show_notes;
-static struct display_notes_opt notes_opt;
+struct format_config {
+ struct log_config log;
+ enum thread_level thread;
+ int do_signoff;
+ enum auto_base_setting auto_base;
+ char *base_commit;
+ char *from;
+ char *signature;
+ char *signature_file;
+ enum cover_setting config_cover_letter;
+ char *config_output_directory;
+ enum cover_from_description cover_from_description_mode;
+ int show_notes;
+ struct display_notes_opt notes_opt;
+ int numbered_cmdline_opt;
+ int numbered;
+ int auto_number;
+ char *default_attach;
+ struct string_list extra_hdr;
+ struct string_list extra_to;
+ struct string_list extra_cc;
+ int keep_subject;
+ int subject_prefix;
+ struct strbuf sprefix;
+ char *fmt_patch_suffix;
+};
+
+static void format_config_init(struct format_config *cfg)
+{
+ memset(cfg, 0, sizeof(*cfg));
+ log_config_init(&cfg->log);
+ cfg->cover_from_description_mode = COVER_FROM_MESSAGE;
+ cfg->auto_number = 1;
+ string_list_init_dup(&cfg->extra_hdr);
+ string_list_init_dup(&cfg->extra_to);
+ string_list_init_dup(&cfg->extra_cc);
+ strbuf_init(&cfg->sprefix, 0);
+ cfg->fmt_patch_suffix = xstrdup(".patch");
+}
+
+static void format_config_release(struct format_config *cfg)
+{
+ log_config_release(&cfg->log);
+ free(cfg->base_commit);
+ free(cfg->from);
+ free(cfg->signature);
+ free(cfg->signature_file);
+ free(cfg->config_output_directory);
+ free(cfg->default_attach);
+ string_list_clear(&cfg->extra_hdr, 0);
+ string_list_clear(&cfg->extra_to, 0);
+ string_list_clear(&cfg->extra_cc, 0);
+ strbuf_release(&cfg->sprefix);
+ free(cfg->fmt_patch_suffix);
+}
static enum cover_from_description parse_cover_from_description(const char *arg)
{
@@ -980,27 +1043,51 @@ static enum cover_from_description parse_cover_from_description(const char *arg)
die(_("%s: invalid cover from description mode"), arg);
}
+static void add_header(struct format_config *cfg, const char *value)
+{
+ struct string_list_item *item;
+ int len = strlen(value);
+ while (len && value[len - 1] == '\n')
+ len--;
+
+ if (!strncasecmp(value, "to: ", 4)) {
+ item = string_list_append(&cfg->extra_to, value + 4);
+ len -= 4;
+ } else if (!strncasecmp(value, "cc: ", 4)) {
+ item = string_list_append(&cfg->extra_cc, value + 4);
+ len -= 4;
+ } else {
+ item = string_list_append(&cfg->extra_hdr, value);
+ }
+
+ item->string[len] = '\0';
+}
+
static int git_format_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
+ struct format_config *cfg = cb;
+
if (!strcmp(var, "format.headers")) {
if (!value)
die(_("format.headers without value"));
- add_header(value);
+ add_header(cfg, value);
return 0;
}
- if (!strcmp(var, "format.suffix"))
- return git_config_string(&fmt_patch_suffix, var, value);
+ if (!strcmp(var, "format.suffix")) {
+ FREE_AND_NULL(cfg->fmt_patch_suffix);
+ return git_config_string(&cfg->fmt_patch_suffix, var, value);
+ }
if (!strcmp(var, "format.to")) {
if (!value)
return config_error_nonbool(var);
- string_list_append(&extra_to, value);
+ string_list_append(&cfg->extra_to, value);
return 0;
}
if (!strcmp(var, "format.cc")) {
if (!value)
return config_error_nonbool(var);
- string_list_append(&extra_cc, value);
+ string_list_append(&cfg->extra_cc, value);
return 0;
}
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") ||
@@ -1009,69 +1096,76 @@ static int git_format_config(const char *var, const char *value,
}
if (!strcmp(var, "format.numbered")) {
if (value && !strcasecmp(value, "auto")) {
- auto_number = 1;
+ cfg->auto_number = 1;
return 0;
}
- numbered = git_config_bool(var, value);
- auto_number = auto_number && numbered;
+ cfg->numbered = git_config_bool(var, value);
+ cfg->auto_number = cfg->auto_number && cfg->numbered;
return 0;
}
if (!strcmp(var, "format.attach")) {
- if (value && *value)
- default_attach = xstrdup(value);
- else if (value && !*value)
- FREE_AND_NULL(default_attach);
- else
- default_attach = xstrdup(git_version_string);
+ if (value && *value) {
+ FREE_AND_NULL(cfg->default_attach);
+ cfg->default_attach = xstrdup(value);
+ } else if (value && !*value) {
+ FREE_AND_NULL(cfg->default_attach);
+ } else {
+ FREE_AND_NULL(cfg->default_attach);
+ cfg->default_attach = xstrdup(git_version_string);
+ }
return 0;
}
if (!strcmp(var, "format.thread")) {
if (value && !strcasecmp(value, "deep")) {
- thread = THREAD_DEEP;
+ cfg->thread = THREAD_DEEP;
return 0;
}
if (value && !strcasecmp(value, "shallow")) {
- thread = THREAD_SHALLOW;
+ cfg->thread = THREAD_SHALLOW;
return 0;
}
- thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
+ cfg->thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
return 0;
}
if (!strcmp(var, "format.signoff")) {
- do_signoff = git_config_bool(var, value);
+ cfg->do_signoff = git_config_bool(var, value);
return 0;
}
- if (!strcmp(var, "format.signature"))
- return git_config_string(&signature, var, value);
- if (!strcmp(var, "format.signaturefile"))
- return git_config_pathname(&signature_file, var, value);
+ if (!strcmp(var, "format.signature")) {
+ FREE_AND_NULL(cfg->signature);
+ return git_config_string(&cfg->signature, var, value);
+ }
+ if (!strcmp(var, "format.signaturefile")) {
+ FREE_AND_NULL(cfg->signature_file);
+ return git_config_pathname(&cfg->signature_file, var, value);
+ }
if (!strcmp(var, "format.coverletter")) {
if (value && !strcasecmp(value, "auto")) {
- config_cover_letter = COVER_AUTO;
+ cfg->config_cover_letter = COVER_AUTO;
return 0;
}
- config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
+ cfg->config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
return 0;
}
- if (!strcmp(var, "format.outputdirectory"))
- return git_config_string(&config_output_directory, var, value);
+ if (!strcmp(var, "format.outputdirectory")) {
+ FREE_AND_NULL(cfg->config_output_directory);
+ return git_config_string(&cfg->config_output_directory, var, value);
+ }
if (!strcmp(var, "format.useautobase")) {
if (value && !strcasecmp(value, "whenAble")) {
- auto_base = AUTO_BASE_WHEN_ABLE;
+ cfg->auto_base = AUTO_BASE_WHEN_ABLE;
return 0;
}
- auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER;
+ cfg->auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER;
return 0;
}
if (!strcmp(var, "format.from")) {
int b = git_parse_maybe_bool(value);
- free(from);
+ FREE_AND_NULL(cfg->from);
if (b < 0)
- from = xstrdup(value);
+ cfg->from = xstrdup(value);
else if (b)
- from = xstrdup(git_committer_info(IDENT_NO_DATE));
- else
- from = NULL;
+ cfg->from = xstrdup(git_committer_info(IDENT_NO_DATE));
return 0;
}
if (!strcmp(var, "format.forceinbodyfrom")) {
@@ -1081,15 +1175,15 @@ static int git_format_config(const char *var, const char *value,
if (!strcmp(var, "format.notes")) {
int b = git_parse_maybe_bool(value);
if (b < 0)
- enable_ref_display_notes(¬es_opt, &show_notes, value);
+ enable_ref_display_notes(&cfg->notes_opt, &cfg->show_notes, value);
else if (b)
- enable_default_display_notes(¬es_opt, &show_notes);
+ enable_default_display_notes(&cfg->notes_opt, &cfg->show_notes);
else
- disable_display_notes(¬es_opt, &show_notes);
+ disable_display_notes(&cfg->notes_opt, &cfg->show_notes);
return 0;
}
if (!strcmp(var, "format.coverfromdescription")) {
- cover_from_description_mode = parse_cover_from_description(value);
+ cfg->cover_from_description_mode = parse_cover_from_description(value);
return 0;
}
if (!strcmp(var, "format.mboxrd")) {
@@ -1110,7 +1204,7 @@ static int git_format_config(const char *var, const char *value,
if (!strcmp(var, "diff.noprefix"))
return 0;
- return git_log_config(var, value, ctx, cb);
+ return git_log_config(var, value, ctx, &cfg->log);
}
static const char *output_directory = NULL;
@@ -1189,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
o2->flags = flags2;
}
-static void gen_message_id(struct rev_info *info, char *base)
+static void gen_message_id(struct rev_info *info, const char *base)
{
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
@@ -1198,7 +1292,7 @@ static void gen_message_id(struct rev_info *info, char *base)
info->message_id = strbuf_detach(&buf, NULL);
}
-static void print_signature(FILE *file)
+static void print_signature(const char *signature, FILE *file)
{
if (!signature || !*signature)
return;
@@ -1268,14 +1362,15 @@ static void prepare_cover_text(struct pretty_print_context *pp,
const char *branch_name,
struct strbuf *sb,
const char *encoding,
- int need_8bit_cte)
+ int need_8bit_cte,
+ const struct format_config *cfg)
{
const char *subject = "*** SUBJECT HERE ***";
const char *body = "*** BLURB HERE ***";
struct strbuf description_sb = STRBUF_INIT;
struct strbuf subject_sb = STRBUF_INIT;
- if (cover_from_description_mode == COVER_FROM_NONE)
+ if (cfg->cover_from_description_mode == COVER_FROM_NONE)
goto do_pp;
if (description_file && *description_file)
@@ -1285,13 +1380,13 @@ static void prepare_cover_text(struct pretty_print_context *pp,
if (!description_sb.len)
goto do_pp;
- if (cover_from_description_mode == COVER_FROM_SUBJECT ||
- cover_from_description_mode == COVER_FROM_AUTO)
+ if (cfg->cover_from_description_mode == COVER_FROM_SUBJECT ||
+ cfg->cover_from_description_mode == COVER_FROM_AUTO)
body = format_subject(&subject_sb, description_sb.buf, " ");
- if (cover_from_description_mode == COVER_FROM_MESSAGE ||
- (cover_from_description_mode == COVER_FROM_AUTO &&
- subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
+ if (cfg->cover_from_description_mode == COVER_FROM_MESSAGE ||
+ (cfg->cover_from_description_mode == COVER_FROM_AUTO &&
+ subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
body = description_sb.buf;
else
subject = subject_sb.buf;
@@ -1328,7 +1423,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
int nr, struct commit **list,
const char *description_file,
const char *branch_name,
- int quiet)
+ int quiet,
+ const struct format_config *cfg)
{
const char *committer;
struct shortlog log;
@@ -1367,7 +1463,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
pp.encode_email_headers = rev->encode_email_headers;
pp_user_info(&pp, NULL, &sb, committer, encoding);
prepare_cover_text(&pp, description_file, branch_name, &sb,
- encoding, need_8bit_cte);
+ encoding, need_8bit_cte, cfg);
fprintf(rev->diffopt.file, "%s\n", sb.buf);
free(pp.after_subject);
@@ -1468,41 +1564,54 @@ static const char * const builtin_format_patch_usage[] = {
NULL
};
-static int keep_subject = 0;
+struct keep_callback_data {
+ struct format_config *cfg;
+ struct rev_info *revs;
+};
static int keep_callback(const struct option *opt, const char *arg, int unset)
{
+ struct keep_callback_data *data = opt->value;
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
- ((struct rev_info *)opt->value)->total = -1;
- keep_subject = 1;
+ data->revs->total = -1;
+ data->cfg->keep_subject = 1;
return 0;
}
-static int subject_prefix = 0;
-
static int subject_prefix_callback(const struct option *opt, const char *arg,
int unset)
{
- struct strbuf *sprefix;
+ struct format_config *cfg = opt->value;
BUG_ON_OPT_NEG(unset);
- sprefix = opt->value;
- subject_prefix = 1;
- strbuf_reset(sprefix);
- strbuf_addstr(sprefix, arg);
+ cfg->subject_prefix = 1;
+ strbuf_reset(&cfg->sprefix);
+ strbuf_addstr(&cfg->sprefix, arg);
return 0;
}
-static int numbered_cmdline_opt = 0;
+static int rfc_callback(const struct option *opt, const char *arg,
+ int unset)
+{
+ const char **rfc = opt->value;
+
+ *rfc = opt->value;
+ if (unset)
+ *rfc = NULL;
+ else
+ *rfc = arg ? arg : "RFC";
+ return 0;
+}
static int numbered_callback(const struct option *opt, const char *arg,
int unset)
{
+ struct format_config *cfg = opt->value;
BUG_ON_OPT_ARG(arg);
- *(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1;
+ cfg->numbered = cfg->numbered_cmdline_opt = unset ? 0 : 1;
if (unset)
- auto_number = 0;
+ cfg->auto_number = 0;
return 0;
}
@@ -1526,13 +1635,14 @@ static int output_directory_callback(const struct option *opt, const char *arg,
static int thread_callback(const struct option *opt, const char *arg, int unset)
{
- enum thread_level *thread = (enum thread_level *)opt->value;
+ struct format_config *cfg = opt->value;
+
if (unset)
- *thread = THREAD_UNSET;
+ cfg->thread = THREAD_UNSET;
else if (!arg || !strcmp(arg, "shallow"))
- *thread = THREAD_SHALLOW;
+ cfg->thread = THREAD_SHALLOW;
else if (!strcmp(arg, "deep"))
- *thread = THREAD_DEEP;
+ cfg->thread = THREAD_DEEP;
/*
* Please update _git_formatpatch() in git-completion.bash
* when you add new options.
@@ -1568,15 +1678,17 @@ static int inline_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
-static int header_callback(const struct option *opt UNUSED, const char *arg,
+static int header_callback(const struct option *opt, const char *arg,
int unset)
{
+ struct format_config *cfg = opt->value;
+
if (unset) {
- string_list_clear(&extra_hdr, 0);
- string_list_clear(&extra_to, 0);
- string_list_clear(&extra_cc, 0);
+ string_list_clear(&cfg->extra_hdr, 0);
+ string_list_clear(&cfg->extra_to, 0);
+ string_list_clear(&cfg->extra_cc, 0);
} else {
- add_header(arg);
+ add_header(cfg, arg);
}
return 0;
}
@@ -1598,17 +1710,17 @@ static int from_callback(const struct option *opt, const char *arg, int unset)
static int base_callback(const struct option *opt, const char *arg, int unset)
{
- const char **base_commit = opt->value;
+ struct format_config *cfg = opt->value;
if (unset) {
- auto_base = AUTO_BASE_NEVER;
- *base_commit = NULL;
+ cfg->auto_base = AUTO_BASE_NEVER;
+ FREE_AND_NULL(cfg->base_commit);
} else if (!strcmp(arg, "auto")) {
- auto_base = AUTO_BASE_ALWAYS;
- *base_commit = NULL;
+ cfg->auto_base = AUTO_BASE_ALWAYS;
+ FREE_AND_NULL(cfg->base_commit);
} else {
- auto_base = AUTO_BASE_NEVER;
- *base_commit = arg;
+ cfg->auto_base = AUTO_BASE_NEVER;
+ cfg->base_commit = xstrdup(arg);
}
return 0;
}
@@ -1619,7 +1731,7 @@ struct base_tree_info {
struct object_id *patch_id;
};
-static struct commit *get_base_commit(const char *base_commit,
+static struct commit *get_base_commit(const struct format_config *cfg,
struct commit **list,
int total)
{
@@ -1627,9 +1739,9 @@ static struct commit *get_base_commit(const char *base_commit,
struct commit **rev;
int i = 0, rev_nr = 0, auto_select, die_on_failure, ret;
- switch (auto_base) {
+ switch (cfg->auto_base) {
case AUTO_BASE_NEVER:
- if (base_commit) {
+ if (cfg->base_commit) {
auto_select = 0;
die_on_failure = 1;
} else {
@@ -1639,11 +1751,11 @@ static struct commit *get_base_commit(const char *base_commit,
break;
case AUTO_BASE_ALWAYS:
case AUTO_BASE_WHEN_ABLE:
- if (base_commit) {
+ if (cfg->base_commit) {
BUG("requested automatic base selection but a commit was provided");
} else {
auto_select = 1;
- die_on_failure = auto_base == AUTO_BASE_ALWAYS;
+ die_on_failure = cfg->auto_base == AUTO_BASE_ALWAYS;
}
break;
default:
@@ -1651,9 +1763,9 @@ static struct commit *get_base_commit(const char *base_commit,
}
if (!auto_select) {
- base = lookup_commit_reference_by_name(base_commit);
+ base = lookup_commit_reference_by_name(cfg->base_commit);
if (!base)
- die(_("unknown commit %s"), base_commit);
+ die(_("unknown commit %s"), cfg->base_commit);
} else {
struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL);
@@ -1826,7 +1938,7 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
free(bases->patch_id);
bases->nr_patch_id = 0;
bases->alloc_patch_id = 0;
- oidclr(&bases->base_commit);
+ oidclr(&bases->base_commit, the_repository->hash_algo);
}
static const char *diff_title(struct strbuf *sb,
@@ -1871,6 +1983,7 @@ static void infer_range_diff_ranges(struct strbuf *r1,
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
+ struct format_config cfg;
struct commit *commit;
struct commit **list = NULL;
struct rev_info rev;
@@ -1895,7 +2008,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
char *cover_from_description_arg = NULL;
char *description_file = NULL;
char *branch_name = NULL;
- char *base_commit = NULL;
struct base_tree_info bases;
struct commit *base;
int show_progress = 0;
@@ -1906,18 +2018,24 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
struct strbuf rdiff1 = STRBUF_INIT;
struct strbuf rdiff2 = STRBUF_INIT;
struct strbuf rdiff_title = STRBUF_INIT;
- struct strbuf sprefix = STRBUF_INIT;
+ const char *rfc = NULL;
int creation_factor = -1;
- int rfc = 0;
+ const char *signature = git_version_string;
+ const char *signature_file_arg = NULL;
+ struct keep_callback_data keep_callback_data = {
+ .cfg = &cfg,
+ .revs = &rev,
+ };
+ const char *fmt_patch_suffix = NULL;
const struct option builtin_format_patch_options[] = {
- OPT_CALLBACK_F('n', "numbered", &numbered, NULL,
+ OPT_CALLBACK_F('n', "numbered", &cfg, NULL,
N_("use [PATCH n/m] even with a single patch"),
PARSE_OPT_NOARG, numbered_callback),
- OPT_CALLBACK_F('N', "no-numbered", &numbered, NULL,
+ OPT_CALLBACK_F('N', "no-numbered", &cfg, NULL,
N_("use [PATCH] even with multiple patches"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback),
- OPT_BOOL('s', "signoff", &do_signoff, N_("add a Signed-off-by trailer")),
+ OPT_BOOL('s', "signoff", &cfg.do_signoff, N_("add a Signed-off-by trailer")),
OPT_BOOL(0, "stdout", &use_stdout,
N_("print patches to standard out")),
OPT_BOOL(0, "cover-letter", &cover_letter,
@@ -1930,21 +2048,23 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("start numbering patches at <n> instead of 1")),
OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
N_("mark the series as Nth re-roll")),
- OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
+ OPT_INTEGER(0, "filename-max-length", &cfg.log.fmt_patch_name_max,
N_("max length of output filename")),
- OPT_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")),
+ OPT_CALLBACK_F(0, "rfc", &rfc, N_("rfc"),
+ N_("add <rfc> (default 'RFC') before 'PATCH'"),
+ PARSE_OPT_OPTARG, rfc_callback),
OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
N_("cover-from-description-mode"),
N_("generate parts of a cover letter based on a branch's description")),
OPT_FILENAME(0, "description-file", &description_file,
N_("use branch description from file")),
- OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"),
+ OPT_CALLBACK_F(0, "subject-prefix", &cfg, N_("prefix"),
N_("use [<prefix>] instead of [PATCH]"),
PARSE_OPT_NONEG, subject_prefix_callback),
OPT_CALLBACK_F('o', "output-directory", &output_directory,
N_("dir"), N_("store resulting files in <dir>"),
PARSE_OPT_NONEG, output_directory_callback),
- OPT_CALLBACK_F('k', "keep-subject", &rev, NULL,
+ OPT_CALLBACK_F('k', "keep-subject", &keep_callback_data, NULL,
N_("don't strip/add [PATCH]"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback),
OPT_BOOL(0, "no-binary", &no_binary_diff,
@@ -1957,11 +2077,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("show patch format instead of default (patch + stat)"),
1, PARSE_OPT_NONEG),
OPT_GROUP(N_("Messaging")),
- OPT_CALLBACK(0, "add-header", NULL, N_("header"),
+ OPT_CALLBACK(0, "add-header", &cfg, N_("header"),
N_("add email header"), header_callback),
- OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")),
- OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")),
- OPT_CALLBACK_F(0, "from", &from, N_("ident"),
+ OPT_STRING_LIST(0, "to", &cfg.extra_to, N_("email"), N_("add To: header")),
+ OPT_STRING_LIST(0, "cc", &cfg.extra_cc, N_("email"), N_("add Cc: header")),
+ OPT_CALLBACK_F(0, "from", &cfg.from, N_("ident"),
N_("set From address to <ident> (or committer ident if absent)"),
PARSE_OPT_OPTARG, from_callback),
OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
@@ -1973,15 +2093,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("inline the patch"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
inline_callback),
- OPT_CALLBACK_F(0, "thread", &thread, N_("style"),
+ OPT_CALLBACK_F(0, "thread", &cfg, N_("style"),
N_("enable message threading, styles: shallow, deep"),
PARSE_OPT_OPTARG, thread_callback),
OPT_STRING(0, "signature", &signature, N_("signature"),
N_("add a signature")),
- OPT_CALLBACK_F(0, "base", &base_commit, N_("base-commit"),
+ OPT_CALLBACK_F(0, "base", &cfg, N_("base-commit"),
N_("add prerequisite tree info to the patch series"),
0, base_callback),
- OPT_FILENAME(0, "signature-file", &signature_file,
+ OPT_FILENAME(0, "signature-file", &signature_file_arg,
N_("add a signature from a file")),
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
OPT_BOOL(0, "progress", &show_progress,
@@ -1998,20 +2118,17 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
OPT_END()
};
- extra_hdr.strdup_strings = 1;
- extra_to.strdup_strings = 1;
- extra_cc.strdup_strings = 1;
-
- init_log_defaults();
- init_display_notes(¬es_opt);
- git_config(git_format_config, NULL);
+ format_config_init(&cfg);
+ init_diff_ui_defaults();
+ init_display_notes(&cfg.notes_opt);
+ git_config(git_format_config, &cfg);
repo_init_revisions(the_repository, &rev, prefix);
git_config(grep_config, &rev.grep_filter);
- rev.show_notes = show_notes;
- memcpy(&rev.notes_opt, ¬es_opt, sizeof(notes_opt));
+ rev.show_notes = cfg.show_notes;
+ memcpy(&rev.notes_opt, &cfg.notes_opt, sizeof(cfg.notes_opt));
rev.commit_format = CMIT_FMT_EMAIL;
- rev.encode_email_headers = default_encode_email_headers;
+ rev.encode_email_headers = cfg.log.default_encode_email_headers;
rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
rev.diff = 1;
@@ -2022,12 +2139,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
s_r_opt.def = "HEAD";
s_r_opt.revarg_opt = REVARG_COMMITTISH;
- strbuf_addstr(&sprefix, fmt_patch_subject_prefix);
+ strbuf_addstr(&cfg.sprefix, cfg.log.fmt_patch_subject_prefix);
if (format_no_prefix)
diff_set_noprefix(&rev.diffopt);
- if (default_attach) {
- rev.mime_boundary = default_attach;
+ if (cfg.default_attach) {
+ rev.mime_boundary = cfg.default_attach;
rev.no_inline = 1;
}
@@ -2043,57 +2160,63 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.force_in_body_from = force_in_body_from;
+ if (!fmt_patch_suffix)
+ fmt_patch_suffix = cfg.fmt_patch_suffix;
+
/* Make sure "0000-$sub.patch" gives non-negative length for $sub */
- if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
- fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
+ if (cfg.log.fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
+ cfg.log.fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
if (cover_from_description_arg)
- cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
+ cfg.cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
- if (rfc) {
- strbuf_insertstr(&sprefix, 0, "RFC ");
- subject_prefix = 1;
+ if (rfc && rfc[0]) {
+ cfg.subject_prefix = 1;
+ if (rfc[0] == '-')
+ strbuf_addf(&cfg.sprefix, " %s", rfc + 1);
+ else
+ strbuf_insertf(&cfg.sprefix, 0, "%s ", rfc);
}
if (reroll_count) {
- strbuf_addf(&sprefix, " v%s", reroll_count);
+ strbuf_addf(&cfg.sprefix, " v%s", reroll_count);
rev.reroll_count = reroll_count;
}
- rev.subject_prefix = sprefix.buf;
+ rev.subject_prefix = cfg.sprefix.buf;
- for (i = 0; i < extra_hdr.nr; i++) {
- strbuf_addstr(&buf, extra_hdr.items[i].string);
+ for (i = 0; i < cfg.extra_hdr.nr; i++) {
+ strbuf_addstr(&buf, cfg.extra_hdr.items[i].string);
strbuf_addch(&buf, '\n');
}
- if (extra_to.nr)
+ if (cfg.extra_to.nr)
strbuf_addstr(&buf, "To: ");
- for (i = 0; i < extra_to.nr; i++) {
+ for (i = 0; i < cfg.extra_to.nr; i++) {
if (i)
strbuf_addstr(&buf, " ");
- strbuf_addstr(&buf, extra_to.items[i].string);
- if (i + 1 < extra_to.nr)
+ strbuf_addstr(&buf, cfg.extra_to.items[i].string);
+ if (i + 1 < cfg.extra_to.nr)
strbuf_addch(&buf, ',');
strbuf_addch(&buf, '\n');
}
- if (extra_cc.nr)
+ if (cfg.extra_cc.nr)
strbuf_addstr(&buf, "Cc: ");
- for (i = 0; i < extra_cc.nr; i++) {
+ for (i = 0; i < cfg.extra_cc.nr; i++) {
if (i)
strbuf_addstr(&buf, " ");
- strbuf_addstr(&buf, extra_cc.items[i].string);
- if (i + 1 < extra_cc.nr)
+ strbuf_addstr(&buf, cfg.extra_cc.items[i].string);
+ if (i + 1 < cfg.extra_cc.nr)
strbuf_addch(&buf, ',');
strbuf_addch(&buf, '\n');
}
rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
- if (from) {
- if (split_ident_line(&rev.from_ident, from, strlen(from)))
- die(_("invalid ident line: %s"), from);
+ if (cfg.from) {
+ if (split_ident_line(&rev.from_ident, cfg.from, strlen(cfg.from)))
+ die(_("invalid ident line: %s"), cfg.from);
}
if (start_number < 0)
@@ -2104,14 +2227,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* and it would conflict with --keep-subject (-k) from the
* command line, reset "numbered".
*/
- if (numbered && keep_subject && !numbered_cmdline_opt)
- numbered = 0;
+ if (cfg.numbered && cfg.keep_subject && !cfg.numbered_cmdline_opt)
+ cfg.numbered = 0;
- if (numbered && keep_subject)
+ if (cfg.numbered && cfg.keep_subject)
die(_("options '%s' and '%s' cannot be used together"), "-n", "-k");
- if (keep_subject && subject_prefix)
+ if (cfg.keep_subject && cfg.subject_prefix)
die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k");
- rev.preserve_subject = keep_subject;
+ rev.preserve_subject = cfg.keep_subject;
argc = setup_revisions(argc, argv, &rev, &s_r_opt);
if (argc > 1)
@@ -2138,7 +2261,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.always_show_header = 1;
rev.zero_commit = zero_commit;
- rev.patch_name_max = fmt_patch_name_max;
+ rev.patch_name_max = cfg.log.fmt_patch_name_max;
if (!rev.diffopt.flags.text && !no_binary_diff)
rev.diffopt.flags.binary = 1;
@@ -2159,7 +2282,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int saved;
if (!output_directory)
- output_directory = config_output_directory;
+ output_directory = cfg.config_output_directory;
output_directory = set_outdir(prefix, output_directory);
if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
@@ -2208,8 +2331,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
const char *ref, *v;
- ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- NULL, NULL);
+ ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (ref && skip_prefix(ref, "refs/heads/", &v))
branch_name = xstrdup(v);
else
@@ -2255,14 +2380,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
goto done;
total = nr;
if (cover_letter == -1) {
- if (config_cover_letter == COVER_AUTO)
+ if (cfg.config_cover_letter == COVER_AUTO)
cover_letter = (total > 1);
+ else if ((idiff_prev.nr || rdiff_prev) && (total > 1))
+ cover_letter = (cfg.config_cover_letter != COVER_OFF);
else
- cover_letter = (config_cover_letter == COVER_ON);
+ cover_letter = (cfg.config_cover_letter == COVER_ON);
}
- if (!keep_subject && auto_number && (total > 1 || cover_letter))
- numbered = 1;
- if (numbered)
+ if (!cfg.keep_subject && cfg.auto_number && (total > 1 || cover_letter))
+ cfg.numbered = 1;
+ if (cfg.numbered)
rev.total = total + start_number - 1;
if (idiff_prev.nr) {
@@ -2276,7 +2403,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
}
if (creation_factor < 0)
- creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
+ creation_factor = CREATION_FACTOR_FOR_THE_SAME_SERIES;
else if (!rdiff_prev)
die(_("the option '%s' requires '%s'"), "--creation-factor", "--range-diff");
@@ -2294,27 +2421,40 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
_("Range-diff against v%d:"));
}
+ /*
+ * The order of precedence is:
+ *
+ * 1. The `--signature` and `--no-signature` options.
+ * 2. The `--signature-file` option.
+ * 3. The `format.signature` config.
+ * 4. The `format.signatureFile` config.
+ * 5. Default `git_version_string`.
+ */
if (!signature) {
; /* --no-signature inhibits all signatures */
} else if (signature && signature != git_version_string) {
; /* non-default signature already set */
- } else if (signature_file) {
+ } else if (signature_file_arg || (cfg.signature_file && !cfg.signature)) {
struct strbuf buf = STRBUF_INIT;
+ const char *signature_file = signature_file_arg ?
+ signature_file_arg : cfg.signature_file;
if (strbuf_read_file(&buf, signature_file, 128) < 0)
die_errno(_("unable to read signature file '%s'"), signature_file);
signature = strbuf_detach(&buf, NULL);
+ } else if (cfg.signature) {
+ signature = cfg.signature;
}
memset(&bases, 0, sizeof(bases));
- base = get_base_commit(base_commit, list, nr);
+ base = get_base_commit(&cfg, list, nr);
if (base) {
reset_revision_walk();
clear_object_flags(UNINTERESTING);
prepare_bases(&bases, base, list, nr);
}
- if (in_reply_to || thread || cover_letter) {
+ if (in_reply_to || cfg.thread || cover_letter) {
rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
string_list_init_dup(rev.ref_message_ids);
}
@@ -2325,19 +2465,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.numbered_files = just_numbers;
rev.patch_suffix = fmt_patch_suffix;
if (cover_letter) {
- if (thread)
+ if (cfg.thread)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, !!output_directory,
- origin, nr, list, description_file, branch_name, quiet);
+ origin, nr, list, description_file, branch_name, quiet, &cfg);
print_bases(&bases, rev.diffopt.file);
- print_signature(rev.diffopt.file);
+ print_signature(signature, rev.diffopt.file);
total++;
start_number--;
/* interdiff/range-diff in cover-letter; omit from patches */
rev.idiff_oid1 = NULL;
rev.rdiff1 = NULL;
}
- rev.add_signoff = do_signoff;
+ rev.add_signoff = cfg.do_signoff;
if (show_progress)
progress = start_delayed_progress(_("Generating patches"), total);
@@ -2347,7 +2487,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
commit = list[nr];
rev.nr = total - nr + (start_number - 1);
/* Make the second and subsequent mails replies to the first */
- if (thread) {
+ if (cfg.thread) {
/* Have we already had a message ID? */
if (rev.message_id) {
/*
@@ -2371,7 +2511,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* letter is a reply to the
* --in-reply-to, if specified.
*/
- if (thread == THREAD_SHALLOW
+ if (cfg.thread == THREAD_SHALLOW
&& rev.ref_message_ids->nr > 0
&& (!cover_letter || rev.nr > 1))
free(rev.message_id);
@@ -2404,7 +2544,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
mime_boundary_leader,
rev.mime_boundary);
else
- print_signature(rev.diffopt.file);
+ print_signature(signature, rev.diffopt.file);
}
if (output_directory)
fclose(rev.diffopt.file);
@@ -2412,9 +2552,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
stop_progress(&progress);
free(list);
free(branch_name);
- string_list_clear(&extra_to, 0);
- string_list_clear(&extra_cc, 0);
- string_list_clear(&extra_hdr, 0);
if (ignore_if_in_upstream)
free_patch_ids(&ids);
@@ -2424,13 +2561,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
strbuf_release(&rdiff1);
strbuf_release(&rdiff2);
strbuf_release(&rdiff_title);
- strbuf_release(&sprefix);
free(to_free);
free(rev.message_id);
if (rev.ref_message_ids)
string_list_clear(rev.ref_message_ids, 0);
free(rev.ref_message_ids);
- return cmd_log_deinit(0, &rev);
+ release_revisions(&rev);
+ format_config_release(&cfg);
+ return 0;
}
static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index e8d65eb..debf2d4 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -9,7 +9,7 @@
#include "wildmatch.h"
static const char * const ls_remote_usage[] = {
- N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
+ N_("git ls-remote [--branches] [--tags] [--refs] [--upload-pack=<exec>]\n"
" [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
" [--symref] [<repository> [<patterns>...]]"),
NULL
@@ -68,7 +68,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
N_("path of git-upload-pack on the remote host"),
PARSE_OPT_HIDDEN },
OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS),
- OPT_BIT('h', "heads", &flags, N_("limit to heads"), REF_HEADS),
+ OPT_BIT('b', "branches", &flags, N_("limit to branches"), REF_BRANCHES),
+ OPT_BIT_F('h', "heads", &flags,
+ N_("deprecated synonym for --branches"), REF_BRANCHES,
+ PARSE_OPT_HIDDEN),
OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL),
OPT_BOOL(0, "get-url", &get_url,
N_("take url.<base>.insteadOf into account")),
@@ -100,7 +103,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
if (flags & REF_TAGS)
strvec_push(&transport_options.ref_prefixes, "refs/tags/");
- if (flags & REF_HEADS)
+ if (flags & REF_BRANCHES)
strvec_push(&transport_options.ref_prefixes, "refs/heads/");
remote = remote_get(dest);
@@ -109,11 +112,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
die("bad repository '%s'", dest);
die("No remote configured to list refs from.");
}
- if (!remote->url_nr)
- die("remote %s has no configured URL", dest);
if (get_url) {
- printf("%s\n", *remote->url);
+ printf("%s\n", remote->url.v[0]);
return 0;
}
@@ -130,7 +131,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
}
if (!dest && !quiet)
- fprintf(stderr, "From %s\n", *remote->url);
+ fprintf(stderr, "From %s\n", remote->url.v[0]);
for ( ; ref; ref = ref->next) {
struct ref_array_item *item;
if (!check_ref_type(ref, flags))
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 3af9ddb..fe6dbc5 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path)
DIR *dir;
struct dirent *dent;
char *name = NULL;
- char *subs[] = { "cur", "new", NULL };
- char **sub;
+ const char *subs[] = { "cur", "new", NULL };
+ const char **sub;
int ret = -1;
for (sub = subs; *sub; ++sub) {
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 270d5f6..0fabe3f 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "hex.h"
#include "read-cache-ll.h"
@@ -18,11 +17,11 @@ static int merge_entry(int pos, const char *path)
char ownbuf[4][60];
struct child_process cmd = CHILD_PROCESS_INIT;
- if (pos >= the_index.cache_nr)
+ if (pos >= the_repository->index->cache_nr)
die("git merge-index: %s not in the cache", path);
found = 0;
do {
- const struct cache_entry *ce = the_index.cache[pos];
+ const struct cache_entry *ce = the_repository->index->cache[pos];
int stage = ce_stage(ce);
if (strcmp(ce->name, path))
@@ -32,7 +31,7 @@ static int merge_entry(int pos, const char *path)
xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
arguments[stage] = hexbuf[stage];
arguments[stage + 4] = ownbuf[stage];
- } while (++pos < the_index.cache_nr);
+ } while (++pos < the_repository->index->cache_nr);
if (!found)
die("git merge-index: %s not in the cache", path);
@@ -51,7 +50,7 @@ static int merge_entry(int pos, const char *path)
static void merge_one_path(const char *path)
{
- int pos = index_name_pos(&the_index, path, strlen(path));
+ int pos = index_name_pos(the_repository->index, path, strlen(path));
/*
* If it already exists in the cache as stage0, it's
@@ -65,9 +64,9 @@ static void merge_all(void)
{
int i;
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < the_index.cache_nr; i++) {
- const struct cache_entry *ce = the_index.cache[i];
+ ensure_full_index(the_repository->index);
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ const struct cache_entry *ce = the_repository->index->cache[i];
if (!ce_stage(ce))
continue;
i += merge_entry(i, ce->name)-1;
@@ -89,7 +88,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED)
repo_read_index(the_repository);
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
+ ensure_full_index(the_repository->index);
i = 1;
if (!strcmp(argv[i], "-o")) {
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 8bdb439..1082d91 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "tree-walk.h"
#include "xdiff-interface.h"
@@ -364,7 +363,7 @@ static void trivial_merge_trees(struct tree_desc t[3], const char *base)
setup_traverse_info(&info, base);
info.fn = threeway_callback;
- traverse_trees(&the_index, 3, t, &info);
+ traverse_trees(the_repository->index, 3, t, &info);
}
static void *get_tree_descriptor(struct repository *r,
diff --git a/builtin/merge.c b/builtin/merge.c
index 6f4fec8..66a4fa7 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -6,7 +6,6 @@
* Based on git-merge.sh by Junio C Hamano.
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -101,7 +100,7 @@ static struct strategy all_strategy[] = {
{ "subtree", NO_FAST_FORWARD | NO_TRIVIAL },
};
-static const char *pull_twohead, *pull_octopus;
+static char *pull_twohead, *pull_octopus;
enum ff_type {
FF_NO,
@@ -111,7 +110,7 @@ enum ff_type {
static enum ff_type fast_forward = FF_ALLOW;
-static const char *cleanup_arg;
+static char *cleanup_arg;
static enum commit_msg_cleanup_mode cleanup_mode;
static int option_parse_message(const struct option *opt,
@@ -300,7 +299,7 @@ static int save_state(struct object_id *stash)
int rc = -1;
fd = repo_hold_locked_index(the_repository, &lock_file, 0);
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
if (0 <= fd)
repo_update_index_if_able(the_repository, &lock_file);
rollback_lock_file(&lock_file);
@@ -331,7 +330,8 @@ static void read_empty(const struct object_id *oid)
{
struct child_process cmd = CHILD_PROCESS_INIT;
- strvec_pushl(&cmd.args, "read-tree", "-m", "-u", empty_tree_oid_hex(),
+ strvec_pushl(&cmd.args, "read-tree", "-m", "-u",
+ empty_tree_oid_hex(the_repository->hash_algo),
oid_to_hex(oid), NULL);
cmd.git_cmd = 1;
@@ -372,7 +372,7 @@ static void restore_state(const struct object_id *head,
run_command(&cmd);
refresh_cache:
- discard_index(&the_index);
+ discard_index(the_repository->index);
if (repo_read_index(the_repository) < 0)
die(_("could not read index"));
}
@@ -449,8 +449,10 @@ static void finish(struct commit *head_commit,
if (verbosity >= 0 && !merge_msg.len)
printf(_("No merge message -- not updating HEAD\n"));
else {
- update_ref(reflog_message.buf, "HEAD", new_head, head,
- 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ reflog_message.buf, "HEAD", new_head,
+ head,
+ 0, UPDATE_REFS_DIE_ON_ERR);
/*
* We ignore errors in 'gc --auto', since the
* user should see them.
@@ -493,7 +495,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_branchname(&bname, remote, 0);
remote = bname.buf;
- oidclr(&branch_head);
+ oidclr(&branch_head, the_repository->hash_algo);
remote_head = get_merge_parent(remote);
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
@@ -547,7 +549,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
struct strbuf truname = STRBUF_INIT;
strbuf_addf(&truname, "refs/heads/%s", remote);
strbuf_setlen(&truname, truname.len - len);
- if (ref_exists(truname.buf)) {
+ if (refs_ref_exists(get_main_ref_store(the_repository), truname.buf)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
oid_to_hex(&remote_head->object.oid),
@@ -610,17 +612,19 @@ static int git_merge_config(const char *k, const char *v,
return 0;
}
- if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+ if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
show_diffstat = git_config_bool(k, v);
- else if (!strcmp(k, "merge.verifysignatures"))
+ } else if (!strcmp(k, "merge.verifysignatures")) {
verify_signatures = git_config_bool(k, v);
- else if (!strcmp(k, "pull.twohead"))
+ } else if (!strcmp(k, "pull.twohead")) {
+ FREE_AND_NULL(pull_twohead);
return git_config_string(&pull_twohead, k, v);
- else if (!strcmp(k, "pull.octopus"))
+ } else if (!strcmp(k, "pull.octopus")) {
+ FREE_AND_NULL(pull_octopus);
return git_config_string(&pull_octopus, k, v);
- else if (!strcmp(k, "commit.cleanup"))
+ } else if (!strcmp(k, "commit.cleanup")) {
return git_config_string(&cleanup_arg, k, v);
- else if (!strcmp(k, "merge.ff")) {
+ } else if (!strcmp(k, "merge.ff")) {
int boolval = git_parse_maybe_bool(v);
if (0 <= boolval) {
fast_forward = boolval ? FF_ALLOW : FF_NO;
@@ -657,8 +661,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
memset(&opts, 0, sizeof(opts));
opts.head_idx = 2;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.update = 1;
opts.verbose_update = 1;
opts.trivial_merges_only = 1;
@@ -674,7 +678,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
if (!trees[nr_trees++])
return -1;
opts.fn = threeway_merge;
- cache_tree_free(&the_index.cache_tree);
+ cache_tree_free(&the_repository->index->cache_tree);
for (i = 0; i < nr_trees; i++) {
parse_tree(trees[i]);
init_tree_desc(t+i, &trees[i]->object.oid,
@@ -687,7 +691,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
static void write_tree_trivial(struct object_id *oid)
{
- if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
+ if (write_index_as_tree(oid, the_repository->index, get_index_file(), 0, NULL))
die(_("git write-tree failed to write a tree"));
}
@@ -700,7 +704,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET,
SKIP_IF_UNCHANGED, 0, NULL, NULL,
NULL) < 0)
- return error(_("Unable to write index."));
+ die(_("Unable to write index."));
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree") ||
!strcmp(strategy, "ort")) {
@@ -745,7 +749,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
rollback_lock_file(&lock);
return 2;
}
- if (write_locked_index(&the_index, &lock,
+ if (write_locked_index(the_repository->index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write %s"), get_index_file());
return clean ? 0 : 1;
@@ -768,8 +772,8 @@ static int count_unmerged_entries(void)
{
int i, ret = 0;
- for (i = 0; i < the_index.cache_nr; i++)
- if (ce_stage(the_index.cache[i]))
+ for (i = 0; i < the_repository->index->cache_nr; i++)
+ if (ce_stage(the_repository->index->cache[i]))
ret++;
return ret;
@@ -843,9 +847,9 @@ static void prepare_to_commit(struct commit_list *remoteheads)
* the editor and after we invoke run_status above.
*/
if (invoked_hook)
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
- read_index_from(&the_index, index_file, get_git_dir());
+ read_index_from(the_repository->index, index_file, get_git_dir());
strbuf_addbuf(&msg, &merge_msg);
if (squash)
BUG("the control must not reach here under --squash");
@@ -957,7 +961,7 @@ static int suggest_conflicts(void)
* Thus, we will get the cleanup mode which is returned when we _are_
* using an editor.
*/
- append_conflicts_hint(&the_index, &msgbuf,
+ append_conflicts_hint(the_repository->index, &msgbuf,
get_cleanup_mode(cleanup_arg, 1));
fputs(msgbuf.buf, fp);
strbuf_release(&msgbuf);
@@ -1252,7 +1256,7 @@ static int merging_a_throwaway_tag(struct commit *commit)
*/
tag_ref = xstrfmt("refs/tags/%s",
((struct tag *)merge_remote_util(commit)->obj)->tag);
- if (!read_ref(tag_ref, &oid) &&
+ if (!refs_read_ref(get_main_ref_store(the_repository), tag_ref, &oid) &&
oideq(&oid, &merge_remote_util(commit)->obj->oid))
is_throwaway_tag = 0;
else
@@ -1284,14 +1288,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ branch = branch_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &head_oid,
+ NULL);
if (branch)
skip_prefix(branch, "refs/heads/", &branch);
if (!pull_twohead) {
char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
if (default_strategy && !strcmp(default_strategy, "ort"))
- pull_twohead = "ort";
+ pull_twohead = xstrdup("ort");
}
init_diff_ui_defaults();
@@ -1325,8 +1331,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!file_exists(git_path_merge_head(the_repository)))
die(_("There is no merge to abort (MERGE_HEAD missing)."));
- if (!read_ref("MERGE_AUTOSTASH", &stash_oid))
- delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF);
+ if (!refs_read_ref(get_main_ref_store(the_repository), "MERGE_AUTOSTASH", &stash_oid))
+ refs_delete_ref(get_main_ref_store(the_repository),
+ "", "MERGE_AUTOSTASH", &stash_oid,
+ REF_NO_DEREF);
/* Invoke 'git reset --merge' */
ret = cmd_reset(nargc, nargv, prefix);
@@ -1379,14 +1387,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
die(_("You have not concluded your merge (MERGE_HEAD exists)."));
}
- if (ref_exists("CHERRY_PICK_HEAD")) {
+ if (refs_ref_exists(get_main_ref_store(the_repository), "CHERRY_PICK_HEAD")) {
if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
"Please, commit your changes before you merge."));
else
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
}
- resolve_undo_clear_index(&the_index);
+ resolve_undo_clear_index(the_repository->index);
if (option_edit < 0)
option_edit = default_edit_option();
@@ -1450,8 +1458,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
remote_head_oid = &remoteheads->item->object.oid;
read_empty(remote_head_oid);
- update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "initial pull", "HEAD", remote_head_oid, NULL,
+ 0,
+ UPDATE_REFS_DIE_ON_ERR);
goto done;
}
@@ -1531,8 +1541,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
free(list);
}
- update_ref("updating ORIG_HEAD", "ORIG_HEAD",
- &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "updating ORIG_HEAD", "ORIG_HEAD",
+ &head_commit->object.oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
if (remoteheads && !common) {
/* No common ancestors found. */
@@ -1595,7 +1607,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* We are not doing octopus, not fast-forward, and have
* only one common.
*/
- refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
if (allow_trivial && fast_forward != FF_ONLY) {
/*
* Must first ensure that index matches HEAD before
@@ -1681,7 +1693,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* index and working tree polluted.
*/
if (save_state(&stash))
- oidclr(&stash);
+ oidclr(&stash, the_repository->hash_algo);
for (i = 0; i < use_strategies_nr; i++) {
int ret, cnt;
@@ -1784,6 +1796,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
free(branch_to_free);
- discard_index(&the_index);
+ free(pull_twohead);
+ free(pull_octopus);
+ discard_index(the_repository->index);
return ret;
}
diff --git a/builtin/mv.c b/builtin/mv.c
index 22e64fc..6c69033 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006 Johannes Schindelin
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -20,6 +20,7 @@
#include "read-cache-ll.h"
#include "repository.h"
#include "setup.h"
+#include "strvec.h"
#include "submodule.h"
#include "entry.h"
@@ -38,45 +39,35 @@ enum update_mode {
#define DUP_BASENAME 1
#define KEEP_TRAILING_SLASH 2
-static const char **internal_prefix_pathspec(const char *prefix,
- const char **pathspec,
- int count, unsigned flags)
+static void internal_prefix_pathspec(struct strvec *out,
+ const char *prefix,
+ const char **pathspec,
+ int count, unsigned flags)
{
- int i;
- const char **result;
int prefixlen = prefix ? strlen(prefix) : 0;
- ALLOC_ARRAY(result, count + 1);
/* Create an intermediate copy of the pathspec based on the flags */
- for (i = 0; i < count; i++) {
- int length = strlen(pathspec[i]);
- int to_copy = length;
- char *it;
+ for (int i = 0; i < count; i++) {
+ size_t length = strlen(pathspec[i]);
+ size_t to_copy = length;
+ const char *maybe_basename;
+ char *trimmed, *prefixed_path;
+
while (!(flags & KEEP_TRAILING_SLASH) &&
to_copy > 0 && is_dir_sep(pathspec[i][to_copy - 1]))
to_copy--;
- it = xmemdupz(pathspec[i], to_copy);
- if (flags & DUP_BASENAME) {
- result[i] = xstrdup(basename(it));
- free(it);
- } else {
- result[i] = it;
- }
- }
- result[count] = NULL;
+ trimmed = xmemdupz(pathspec[i], to_copy);
+ maybe_basename = (flags & DUP_BASENAME) ? basename(trimmed) : trimmed;
+ prefixed_path = prefix_path(prefix, prefixlen, maybe_basename);
+ strvec_push(out, prefixed_path);
- /* Prefix the pathspec and free the old intermediate strings */
- for (i = 0; i < count; i++) {
- const char *match = prefix_path(prefix, prefixlen, result[i]);
- free((char *) result[i]);
- result[i] = match;
+ free(prefixed_path);
+ free(trimmed);
}
-
- return result;
}
-static const char *add_slash(const char *path)
+static char *add_slash(const char *path)
{
size_t len = strlen(path);
if (len && path[len - 1] != '/') {
@@ -86,46 +77,48 @@ static const char *add_slash(const char *path)
with_slash[len] = 0;
return with_slash;
}
- return path;
+ return xstrdup(path);
}
#define SUBMODULE_WITH_GITDIR ((const char *)1)
-static void prepare_move_submodule(const char *src, int first,
- const char **submodule_gitfile)
+static const char *submodule_gitfile_path(const char *src, int first)
{
struct strbuf submodule_dotgit = STRBUF_INIT;
- if (!S_ISGITLINK(the_index.cache[first]->ce_mode))
+ const char *path;
+
+ if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode))
die(_("Directory %s is in index and no submodule?"), src);
- if (!is_staging_gitmodules_ok(&the_index))
+ if (!is_staging_gitmodules_ok(the_repository->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);
- if (*submodule_gitfile)
- *submodule_gitfile = xstrdup(*submodule_gitfile);
- else
- *submodule_gitfile = SUBMODULE_WITH_GITDIR;
+
+ path = read_gitfile(submodule_dotgit.buf);
strbuf_release(&submodule_dotgit);
+ if (path)
+ return path;
+ return SUBMODULE_WITH_GITDIR;
}
static int index_range_of_same_dir(const char *src, int length,
int *first_p, int *last_p)
{
- const char *src_w_slash = add_slash(src);
+ char *src_w_slash = add_slash(src);
int first, last, len_w_slash = length + 1;
- first = index_name_pos(&the_index, src_w_slash, len_w_slash);
+ first = index_name_pos(the_repository->index, src_w_slash, len_w_slash);
if (first >= 0)
die(_("%.*s is in index"), len_w_slash, src_w_slash);
first = -1 - first;
- for (last = first; last < the_index.cache_nr; last++) {
- const char *path = the_index.cache[last]->name;
+ for (last = first; last < the_repository->index->cache_nr; last++) {
+ const char *path = the_repository->index->cache[last]->name;
if (strncmp(path, src_w_slash, len_w_slash))
break;
}
- if (src_w_slash != src)
- free((char *)src_w_slash);
+
+ free(src_w_slash);
*first_p = first;
*last_p = last;
return last - first;
@@ -141,17 +134,17 @@ static int index_range_of_same_dir(const char *src, int length,
static int empty_dir_has_sparse_contents(const char *name)
{
int ret = 0;
- const char *with_slash = add_slash(name);
+ char *with_slash = add_slash(name);
int length = strlen(with_slash);
- int pos = index_name_pos(&the_index, with_slash, length);
+ int pos = index_name_pos(the_repository->index, with_slash, length);
const struct cache_entry *ce;
if (pos < 0) {
pos = -pos - 1;
- if (pos >= the_index.cache_nr)
+ if (pos >= the_repository->index->cache_nr)
goto free_return;
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (strncmp(with_slash, ce->name, length))
goto free_return;
if (ce_skip_worktree(ce))
@@ -159,11 +152,32 @@ static int empty_dir_has_sparse_contents(const char *name)
}
free_return:
- if (with_slash != name)
- free((char *)with_slash);
+ free(with_slash);
return ret;
}
+static void remove_empty_src_dirs(const char **src_dir, size_t src_dir_nr)
+{
+ size_t i;
+ struct strbuf a_src_dir = STRBUF_INIT;
+
+ for (i = 0; i < src_dir_nr; i++) {
+ int dummy;
+ strbuf_addstr(&a_src_dir, src_dir[i]);
+ /*
+ * if entries under a_src_dir are all moved away,
+ * recursively remove a_src_dir to cleanup
+ */
+ if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
+ &dummy, &dummy) < 1) {
+ remove_dir_recursively(&a_src_dir, 0);
+ }
+ strbuf_reset(&a_src_dir);
+ }
+
+ strbuf_release(&a_src_dir);
+}
+
int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, flags, gitmodules_modified = 0;
@@ -177,18 +191,21 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_END(),
};
- const char **source, **destination, **dest_path, **submodule_gitfile;
- const char *dst_w_slash;
- const char **src_dir = NULL;
- int src_dir_nr = 0, src_dir_alloc = 0;
- struct strbuf a_src_dir = STRBUF_INIT;
+ struct strvec sources = STRVEC_INIT;
+ struct strvec dest_paths = STRVEC_INIT;
+ struct strvec destinations = STRVEC_INIT;
+ struct strvec submodule_gitfiles_to_free = STRVEC_INIT;
+ const char **submodule_gitfiles;
+ char *dst_w_slash = NULL;
+ struct strvec src_dir = STRVEC_INIT;
enum update_mode *modes, dst_mode = 0;
struct stat st, dest_st;
- struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
+ struct string_list src_for_dst = STRING_LIST_INIT_DUP;
struct lock_file lock_file = LOCK_INIT;
struct cache_entry *ce;
- struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
- struct string_list dirty_paths = STRING_LIST_INIT_NODUP;
+ struct string_list only_match_skip_worktree = STRING_LIST_INIT_DUP;
+ struct string_list dirty_paths = STRING_LIST_INIT_DUP;
+ int ret;
git_config(git_default_config, NULL);
@@ -201,7 +218,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
- source = internal_prefix_pathspec(prefix, argv, argc, 0);
+ internal_prefix_pathspec(&sources, prefix, argv, argc, 0);
CALLOC_ARRAY(modes, argc);
/*
@@ -212,45 +229,39 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
flags = KEEP_TRAILING_SLASH;
if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1]))
flags = 0;
- dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags);
- dst_w_slash = add_slash(dest_path[0]);
- submodule_gitfile = xcalloc(argc, sizeof(char *));
+ internal_prefix_pathspec(&dest_paths, prefix, argv + argc, 1, flags);
+ dst_w_slash = add_slash(dest_paths.v[0]);
+ submodule_gitfiles = xcalloc(argc, sizeof(char *));
- if (dest_path[0][0] == '\0')
+ if (dest_paths.v[0][0] == '\0')
/* special case: "." was normalized to "" */
- destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
- else if (!lstat(dest_path[0], &st) &&
- S_ISDIR(st.st_mode)) {
- destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
+ internal_prefix_pathspec(&destinations, dest_paths.v[0], argv, argc, DUP_BASENAME);
+ else if (!lstat(dest_paths.v[0], &st) && S_ISDIR(st.st_mode)) {
+ internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME);
+ } else if (!path_in_sparse_checkout(dst_w_slash, the_repository->index) &&
+ empty_dir_has_sparse_contents(dst_w_slash)) {
+ internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME);
+ dst_mode = SKIP_WORKTREE_DIR;
+ } else if (argc != 1) {
+ die(_("destination '%s' is not a directory"), dest_paths.v[0]);
} else {
- if (!path_in_sparse_checkout(dst_w_slash, &the_index) &&
- empty_dir_has_sparse_contents(dst_w_slash)) {
- destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
- dst_mode = SKIP_WORKTREE_DIR;
- } else if (argc != 1) {
- die(_("destination '%s' is not a directory"), dest_path[0]);
- } else {
- destination = dest_path;
- /*
- * <destination> is a file outside of sparse-checkout
- * cone. Insist on cone mode here for backward
- * compatibility. We don't want dst_mode to be assigned
- * for a file when the repo is using no-cone mode (which
- * is deprecated at this point) sparse-checkout. As
- * SPARSE here is only considering cone-mode situation.
- */
- if (!path_in_cone_mode_sparse_checkout(destination[0], &the_index))
- dst_mode = SPARSE;
- }
- }
- if (dst_w_slash != dest_path[0]) {
- free((char *)dst_w_slash);
- dst_w_slash = NULL;
+ strvec_pushv(&destinations, dest_paths.v);
+
+ /*
+ * <destination> is a file outside of sparse-checkout
+ * cone. Insist on cone mode here for backward
+ * compatibility. We don't want dst_mode to be assigned
+ * for a file when the repo is using no-cone mode (which
+ * is deprecated at this point) sparse-checkout. As
+ * SPARSE here is only considering cone-mode situation.
+ */
+ if (!path_in_cone_mode_sparse_checkout(destinations.v[0], the_repository->index))
+ dst_mode = SPARSE;
}
/* Checking */
for (i = 0; i < argc; i++) {
- const char *src = source[i], *dst = destination[i];
+ const char *src = sources.v[i], *dst = destinations.v[i];
int length;
const char *bad = NULL;
int skip_sparse = 0;
@@ -263,20 +274,22 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
int pos;
const struct cache_entry *ce;
- pos = index_name_pos(&the_index, src, length);
+ pos = index_name_pos(the_repository->index, src, length);
if (pos < 0) {
- const char *src_w_slash = add_slash(src);
- if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
+ char *src_w_slash = add_slash(src);
+ if (!path_in_sparse_checkout(src_w_slash, the_repository->index) &&
empty_dir_has_sparse_contents(src)) {
+ free(src_w_slash);
modes[i] |= SKIP_WORKTREE_DIR;
goto dir_check;
}
+ free(src_w_slash);
/* only error if existence is expected. */
if (!(modes[i] & SPARSE))
bad = _("bad source");
goto act_on_entry;
}
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (!ce_skip_worktree(ce)) {
bad = _("bad source");
goto act_on_entry;
@@ -286,7 +299,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
goto act_on_entry;
}
/* Check if dst exists in index */
- if (index_name_pos(&the_index, dst, strlen(dst)) < 0) {
+ if (index_name_pos(the_repository->index, dst, strlen(dst)) < 0) {
modes[i] |= SPARSE;
goto act_on_entry;
}
@@ -310,12 +323,16 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
dir_check:
if (S_ISDIR(st.st_mode)) {
- int j, dst_len, n;
- int first = index_name_pos(&the_index, src, length), last;
+ char *dst_with_slash;
+ size_t dst_with_slash_len;
+ int j, n;
+ int first = index_name_pos(the_repository->index, src, length), last;
if (first >= 0) {
- prepare_move_submodule(src, first,
- submodule_gitfile + i);
+ const char *path = submodule_gitfile_path(src, first);
+ if (path != SUBMODULE_WITH_GITDIR)
+ path = strvec_push(&submodule_gitfiles_to_free, path);
+ submodule_gitfiles[i] = path;
goto act_on_entry;
} else if (index_range_of_same_dir(src, length,
&first, &last) < 1) {
@@ -326,32 +343,35 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
/* last - first >= 1 */
modes[i] |= WORKING_DIRECTORY;
- ALLOC_GROW(src_dir, src_dir_nr + 1, src_dir_alloc);
- src_dir[src_dir_nr++] = src;
+ strvec_push(&src_dir, src);
n = argc + last - first;
- REALLOC_ARRAY(source, n);
- REALLOC_ARRAY(destination, n);
REALLOC_ARRAY(modes, n);
- REALLOC_ARRAY(submodule_gitfile, n);
+ REALLOC_ARRAY(submodule_gitfiles, n);
- dst = add_slash(dst);
- dst_len = strlen(dst);
+ dst_with_slash = add_slash(dst);
+ dst_with_slash_len = strlen(dst_with_slash);
for (j = 0; j < last - first; j++) {
- const struct cache_entry *ce = the_index.cache[first + j];
+ const struct cache_entry *ce = the_repository->index->cache[first + j];
const char *path = ce->name;
- source[argc + j] = path;
- destination[argc + j] =
- prefix_path(dst, dst_len, path + length + 1);
+ char *prefixed_path = prefix_path(dst_with_slash, dst_with_slash_len, path + length + 1);
+
+ strvec_push(&sources, path);
+ strvec_push(&destinations, prefixed_path);
+
memset(modes + argc + j, 0, sizeof(enum update_mode));
modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX;
- submodule_gitfile[argc + j] = NULL;
+ submodule_gitfiles[argc + j] = NULL;
+
+ free(prefixed_path);
}
+
+ free(dst_with_slash);
argc += last - first;
goto act_on_entry;
}
- if (!(ce = index_file_exists(&the_index, src, length, 0))) {
+ if (!(ce = index_file_exists(the_repository->index, src, length, 0))) {
bad = _("not under version control");
goto act_on_entry;
}
@@ -387,7 +407,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
if (ignore_sparse &&
(dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
- index_entry_exists(&the_index, dst, strlen(dst))) {
+ index_entry_exists(the_repository->index, dst, strlen(dst))) {
bad = _("destination exists in the index");
if (force) {
if (verbose)
@@ -404,12 +424,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
* option as a way to have a successful run.
*/
if (!ignore_sparse &&
- !path_in_sparse_checkout(src, &the_index)) {
+ !path_in_sparse_checkout(src, the_repository->index)) {
string_list_append(&only_match_skip_worktree, src);
skip_sparse = 1;
}
if (!ignore_sparse &&
- !path_in_sparse_checkout(dst, &the_index)) {
+ !path_in_sparse_checkout(dst, the_repository->index)) {
string_list_append(&only_match_skip_worktree, dst);
skip_sparse = 1;
}
@@ -428,28 +448,30 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
remove_entry:
if (--argc > 0) {
int n = argc - i;
- MOVE_ARRAY(source + i, source + i + 1, n);
- MOVE_ARRAY(destination + i, destination + i + 1, n);
+ strvec_remove(&sources, i);
+ strvec_remove(&destinations, i);
MOVE_ARRAY(modes + i, modes + i + 1, n);
- MOVE_ARRAY(submodule_gitfile + i,
- submodule_gitfile + i + 1, n);
+ MOVE_ARRAY(submodule_gitfiles + i,
+ submodule_gitfiles + i + 1, n);
i--;
}
}
if (only_match_skip_worktree.nr) {
advise_on_updating_sparse_paths(&only_match_skip_worktree);
- if (!ignore_errors)
- return 1;
+ if (!ignore_errors) {
+ ret = 1;
+ goto out;
+ }
}
for (i = 0; i < argc; i++) {
- const char *src = source[i], *dst = destination[i];
+ const char *src = sources.v[i], *dst = destinations.v[i];
enum update_mode mode = modes[i];
int pos;
int sparse_and_dirty = 0;
struct checkout state = CHECKOUT_INIT;
- state.istate = &the_index;
+ state.istate = the_repository->index;
if (force)
state.force = 1;
@@ -464,26 +486,26 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
continue;
die_errno(_("renaming '%s' failed"), src);
}
- if (submodule_gitfile[i]) {
+ if (submodule_gitfiles[i]) {
if (!update_path_in_gitmodules(src, dst))
gitmodules_modified = 1;
- if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
+ if (submodule_gitfiles[i] != SUBMODULE_WITH_GITDIR)
connect_work_tree_and_git_dir(dst,
- submodule_gitfile[i],
+ submodule_gitfiles[i],
1);
}
if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
continue;
- pos = index_name_pos(&the_index, src, strlen(src));
+ pos = index_name_pos(the_repository->index, src, strlen(src));
assert(pos >= 0);
if (!(mode & SPARSE) && !lstat(src, &st))
- sparse_and_dirty = ie_modified(&the_index,
- the_index.cache[pos],
+ sparse_and_dirty = ie_modified(the_repository->index,
+ the_repository->index->cache[pos],
&st,
0);
- rename_index_entry_at(&the_index, pos, dst);
+ rename_index_entry_at(the_repository->index, pos, dst);
if (ignore_sparse &&
core_apply_sparse_checkout &&
@@ -495,11 +517,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
* should be added in a future patch.
*/
if ((mode & SPARSE) &&
- path_in_sparse_checkout(dst, &the_index)) {
+ path_in_sparse_checkout(dst, the_repository->index)) {
/* from out-of-cone to in-cone */
- int dst_pos = index_name_pos(&the_index, dst,
+ int dst_pos = index_name_pos(the_repository->index, dst,
strlen(dst));
- struct cache_entry *dst_ce = the_index.cache[dst_pos];
+ struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
@@ -507,11 +529,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
die(_("cannot checkout %s"), dst_ce->name);
} else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
!(mode & SPARSE) &&
- !path_in_sparse_checkout(dst, &the_index)) {
+ !path_in_sparse_checkout(dst, the_repository->index)) {
/* from in-cone to out-of-cone */
- int dst_pos = index_name_pos(&the_index, dst,
+ int dst_pos = index_name_pos(the_repository->index, dst,
strlen(dst));
- struct cache_entry *dst_ce = the_index.cache[dst_pos];
+ struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
/*
* if src is clean, it will suffice to remove it
@@ -535,41 +557,31 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
}
}
- /*
- * cleanup the empty src_dirs
- */
- for (i = 0; i < src_dir_nr; i++) {
- int dummy;
- strbuf_addstr(&a_src_dir, src_dir[i]);
- /*
- * if entries under a_src_dir are all moved away,
- * recursively remove a_src_dir to cleanup
- */
- if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
- &dummy, &dummy) < 1) {
- remove_dir_recursively(&a_src_dir, 0);
- }
- strbuf_reset(&a_src_dir);
- }
-
- strbuf_release(&a_src_dir);
- free(src_dir);
+ remove_empty_src_dirs(src_dir.v, src_dir.nr);
if (dirty_paths.nr)
advise_on_moving_dirty_path(&dirty_paths);
if (gitmodules_modified)
- stage_updated_gitmodules(&the_index);
+ stage_updated_gitmodules(the_repository->index);
- if (write_locked_index(&the_index, &lock_file,
+ if (write_locked_index(the_repository->index, &lock_file,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("Unable to write new index file"));
+ ret = 0;
+
+out:
+ strvec_clear(&src_dir);
+ free(dst_w_slash);
string_list_clear(&src_for_dst, 0);
string_list_clear(&dirty_paths, 0);
- UNLEAK(source);
- UNLEAK(dest_path);
- free(submodule_gitfile);
+ string_list_clear(&only_match_skip_worktree, 0);
+ strvec_clear(&sources);
+ strvec_clear(&dest_paths);
+ strvec_clear(&destinations);
+ strvec_clear(&submodule_gitfiles_to_free);
+ free(submodule_gitfiles);
free(modes);
- return 0;
+ return ret;
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index ad9930c..70e9ec4 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -296,7 +296,8 @@ static void add_to_tip_table(const struct object_id *oid, const char *refname,
char *short_refname = NULL;
if (shorten_unambiguous)
- short_refname = shorten_unambiguous_ref(refname, 0);
+ short_refname = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname, 0);
else if (skip_prefix(refname, "refs/heads/", &refname))
; /* refname already advanced */
else
@@ -647,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
adjust_cutoff_timestamp_for_slop();
- for_each_ref(name_ref, &data);
+ refs_for_each_ref(get_main_ref_store(the_repository), name_ref, &data);
name_tips(&string_pool);
if (annotate_stdin) {
diff --git a/builtin/notes.c b/builtin/notes.c
index cb01130..d9c356e 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -794,9 +794,9 @@ static int merge_abort(struct notes_merge_options *o)
* notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
*/
- if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL"));
- if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
ret += error(_("failed to delete ref NOTES_MERGE_REF"));
if (notes_merge_abort(o))
ret += error(_("failed to remove 'git notes merge' worktree"));
@@ -828,13 +828,14 @@ static int merge_commit(struct notes_merge_options *o)
if (partial->parents)
oidcpy(&parent_oid, &partial->parents->item->object.oid);
else
- oidclr(&parent_oid);
+ oidclr(&parent_oid, the_repository->hash_algo);
CALLOC_ARRAY(t, 1);
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
o->local_ref = local_ref_to_free =
- resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL);
+ refs_resolve_refdup(get_main_ref_store(the_repository),
+ "NOTES_MERGE_REF", 0, &oid, NULL);
if (!o->local_ref)
die(_("failed to resolve NOTES_MERGE_REF"));
@@ -847,9 +848,10 @@ static int merge_commit(struct notes_merge_options *o)
&pretty_ctx);
strbuf_trim(&msg);
strbuf_insertstr(&msg, 0, "notes: ");
- update_ref(msg.buf, o->local_ref, &oid,
- is_null_oid(&parent_oid) ? NULL : &parent_oid,
- 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ o->local_ref, &oid,
+ is_null_oid(&parent_oid) ? NULL : &parent_oid,
+ 0, UPDATE_REFS_DIE_ON_ERR);
free_notes(t);
strbuf_release(&msg);
@@ -961,14 +963,16 @@ static int merge(int argc, const char **argv, const char *prefix)
if (result >= 0) /* Merge resulted (trivially) in result_oid */
/* Update default notes ref with new commit */
- update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ default_notes_ref(), &result_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
else { /* Merge has unresolved conflicts */
struct worktree **worktrees;
const struct worktree *wt;
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
- update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
- 0, UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ "NOTES_MERGE_PARTIAL", &result_oid, NULL,
+ 0, UPDATE_REFS_DIE_ON_ERR);
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
worktrees = get_worktrees();
wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
@@ -977,7 +981,7 @@ static int merge(int argc, const char **argv, const char *prefix)
die(_("a notes merge into %s is already in-progress at %s"),
default_notes_ref(), wt->path);
free_worktrees(worktrees);
- if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
+ if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", default_notes_ref(), NULL))
die(_("failed to store link to current notes ref (%s)"),
default_notes_ref());
fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index baf0090..f395488 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -779,7 +779,7 @@ static int mark_tagged(const char *path UNUSED, const struct object_id *oid,
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, oid, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -939,7 +939,8 @@ static struct object_entry **compute_write_order(void)
/*
* Mark objects that are at the tip of tags.
*/
- for_each_tag_ref(mark_tagged, NULL);
+ refs_for_each_tag_ref(get_main_ref_store(the_repository), mark_tagged,
+ NULL);
if (use_delta_islands) {
max_layers = compute_pack_layers(&to_pack);
@@ -1314,6 +1315,7 @@ static void write_pack_file(void)
if (!pack_to_stdout) {
struct stat st;
struct strbuf tmpname = STRBUF_INIT;
+ struct bitmap_writer bitmap_writer;
char *idx_tmp_name = NULL;
/*
@@ -1339,8 +1341,10 @@ static void write_pack_file(void)
hash_to_hex(hash));
if (write_bitmap_index) {
- bitmap_writer_set_checksum(hash);
- bitmap_writer_build_type_index(
+ bitmap_writer_init(&bitmap_writer,
+ the_repository);
+ bitmap_writer_set_checksum(&bitmap_writer, hash);
+ bitmap_writer_build_type_index(&bitmap_writer,
&to_pack, written_list, nr_written);
}
@@ -1358,12 +1362,17 @@ static void write_pack_file(void)
strbuf_addstr(&tmpname, "bitmap");
stop_progress(&progress_state);
- bitmap_writer_show_progress(progress);
- bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
- if (bitmap_writer_build(&to_pack) < 0)
+ bitmap_writer_show_progress(&bitmap_writer,
+ progress);
+ bitmap_writer_select_commits(&bitmap_writer,
+ indexed_commits,
+ indexed_commits_nr);
+ if (bitmap_writer_build(&bitmap_writer, &to_pack) < 0)
die(_("failed to write bitmap index"));
- bitmap_writer_finish(written_list, nr_written,
+ bitmap_writer_finish(&bitmap_writer,
+ written_list, nr_written,
tmpname.buf, write_bitmap_options);
+ bitmap_writer_free(&bitmap_writer);
write_bitmap_index = 0;
strbuf_setlen(&tmpname, tmpname_len);
}
@@ -2070,7 +2079,8 @@ static void check_object(struct object_entry *entry, uint32_t object_index)
oidread(&base_ref,
use_pack(p, &w_curs,
entry->in_pack_offset + used,
- NULL));
+ NULL),
+ the_repository->hash_algo);
have_base = 1;
}
entry->in_pack_header_size = used + the_hash_algo->rawsz;
@@ -3124,7 +3134,7 @@ static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid,
{
struct object_id peeled;
- if (!peel_iterated_oid(oid, &peeled) && obj_is_packed(&peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
add_tag_chain(oid);
return 0;
}
@@ -4073,7 +4083,7 @@ static int mark_bitmap_preferred_tip(const char *refname,
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
object = parse_object_or_die(oid, refname);
@@ -4093,7 +4103,9 @@ static void mark_bitmap_preferred_tips(void)
return;
for_each_string_list_item(item, preferred_tips) {
- for_each_ref_in(item->string, mark_bitmap_preferred_tip, NULL);
+ refs_for_each_ref_in(get_main_ref_store(the_repository),
+ item->string, mark_bitmap_preferred_tip,
+ NULL);
}
}
@@ -4588,7 +4600,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
cleanup_preferred_base();
if (include_tag && nr_result)
- for_each_tag_ref(add_ref_tag, NULL);
+ refs_for_each_tag_ref(get_main_ref_store(the_repository),
+ add_ref_tag, NULL);
stop_progress(&progress_state);
trace2_region_leave("pack-objects", "enumerate-objects",
the_repository);
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 4c735ba..dd9bf35 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -100,7 +100,7 @@ static inline struct llist_item *llist_insert(struct llist *list,
const unsigned char *oid)
{
struct llist_item *new_item = llist_item_get();
- oidread(&new_item->oid, oid);
+ oidread(&new_item->oid, oid, the_repository->hash_algo);
new_item->next = NULL;
if (after) {
@@ -155,7 +155,7 @@ static inline struct llist_item * llist_sorted_remove(struct llist *list, const
l = (hint == NULL) ? list->front : hint;
prev = NULL;
while (l) {
- const int cmp = hashcmp(l->oid.hash, oid);
+ const int cmp = hashcmp(l->oid.hash, oid, the_repository->hash_algo);
if (cmp > 0) /* not in list, since sorted */
return prev;
if (!cmp) { /* found */
@@ -258,7 +258,8 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
while (p1_off < p1->pack->num_objects * p1_step &&
p2_off < p2->pack->num_objects * p2_step)
{
- const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
+ const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off,
+ the_repository->hash_algo);
/* cmp ~ p1 - p2 */
if (cmp == 0) {
p1_hint = llist_sorted_remove(p1->unique_objects,
@@ -296,7 +297,8 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
while (p1_off < p1->num_objects * p1_step &&
p2_off < p2->num_objects * p2_step)
{
- int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
+ int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off,
+ the_repository->hash_algo);
/* cmp ~ p1 - p2 */
if (cmp == 0) {
ret++;
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 3894d2b..d790ae6 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -5,6 +5,7 @@
#include "hash.h"
#include "hex.h"
#include "parse-options.h"
+#include "setup.h"
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
{
@@ -69,7 +70,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
git_hash_ctx ctx;
the_hash_algo->init_fn(&ctx);
- oidclr(result);
+ oidclr(result, the_repository->hash_algo);
while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
char *line = line_buf->buf;
@@ -165,7 +166,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
}
if (!found_next)
- oidclr(next_oid);
+ oidclr(next_oid, the_repository->hash_algo);
flush_one_hunk(result, &ctx);
@@ -178,7 +179,7 @@ static void generate_id_list(int stable, int verbatim)
int patchlen;
struct strbuf line_buf = STRBUF_INIT;
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
while (!feof(stdin)) {
patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim);
flush_current_id(patchlen, &oid, &result);
@@ -237,6 +238,18 @@ int cmd_patch_id(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, builtin_patch_id_options,
patch_id_usage, 0);
+ /*
+ * We rely on `the_hash_algo` to compute patch IDs. This is dubious as
+ * it means that the hash algorithm now depends on the object hash of
+ * the repository, even though git-patch-id(1) clearly defines that
+ * patch IDs always use SHA1.
+ *
+ * NEEDSWORK: This hack should be removed in favor of converting
+ * the code that computes patch IDs to always use SHA1.
+ */
+ if (!the_hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
generate_id_list(opts ? opts > 1 : config.stable,
opts ? opts == 3 : config.verbatim);
return 0;
diff --git a/builtin/pull.c b/builtin/pull.c
index 72cbb76..4c54d81 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -5,7 +5,7 @@
*
* Fetch one or more remote refs and merge it/them into the current HEAD.
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -71,48 +71,48 @@ static const char * const pull_usage[] = {
/* Shared options */
static int opt_verbosity;
-static char *opt_progress;
+static const char *opt_progress;
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
/* Options passed to git-merge or git-rebase */
static enum rebase_type opt_rebase = -1;
-static char *opt_diffstat;
-static char *opt_log;
-static char *opt_signoff;
-static char *opt_squash;
-static char *opt_commit;
-static char *opt_edit;
-static char *cleanup_arg;
-static char *opt_ff;
-static char *opt_verify_signatures;
-static char *opt_verify;
+static const char *opt_diffstat;
+static const char *opt_log;
+static const char *opt_signoff;
+static const char *opt_squash;
+static const char *opt_commit;
+static const char *opt_edit;
+static const char *cleanup_arg;
+static const char *opt_ff;
+static const char *opt_verify_signatures;
+static const char *opt_verify;
static int opt_autostash = -1;
static int config_autostash;
static int check_trust_level = 1;
static struct strvec opt_strategies = STRVEC_INIT;
static struct strvec opt_strategy_opts = STRVEC_INIT;
-static char *opt_gpg_sign;
+static const char *opt_gpg_sign;
static int opt_allow_unrelated_histories;
/* Options passed to git-fetch */
-static char *opt_all;
-static char *opt_append;
-static char *opt_upload_pack;
+static const char *opt_all;
+static const char *opt_append;
+static const char *opt_upload_pack;
static int opt_force;
-static char *opt_tags;
-static char *opt_prune;
-static char *max_children;
+static const char *opt_tags;
+static const char *opt_prune;
+static const char *max_children;
static int opt_dry_run;
-static char *opt_keep;
-static char *opt_depth;
-static char *opt_unshallow;
-static char *opt_update_shallow;
-static char *opt_refmap;
-static char *opt_ipv4;
-static char *opt_ipv6;
+static const char *opt_keep;
+static const char *opt_depth;
+static const char *opt_unshallow;
+static const char *opt_update_shallow;
+static const char *opt_refmap;
+static const char *opt_ipv4;
+static const char *opt_ipv6;
static int opt_show_forced_updates = -1;
-static char *set_upstream;
+static const char *set_upstream;
static struct strvec opt_fetch = STRVEC_INIT;
static struct option pull_options[] = {
@@ -611,7 +611,7 @@ static int pull_into_void(const struct object_id *merge_head,
merge_head, 0))
return 1;
- if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
+ if (refs_update_ref(get_main_ref_store(the_repository), "initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
return 1;
return 0;
@@ -1038,13 +1038,13 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
die_conclude_merge();
if (repo_get_oid(the_repository, "HEAD", &orig_head))
- oidclr(&orig_head);
+ oidclr(&orig_head, the_repository->hash_algo);
if (opt_rebase) {
if (opt_autostash == -1)
opt_autostash = config_autostash;
- if (is_null_oid(&orig_head) && !is_index_unborn(&the_index))
+ if (is_null_oid(&orig_head) && !is_index_unborn(the_repository->index))
die(_("Updating an unborn branch with changes added to the index."));
if (!opt_autostash)
@@ -1053,7 +1053,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
_("Please commit or stash them."), 1, 0);
if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
- oidclr(&rebase_fork_point);
+ oidclr(&rebase_fork_point, the_repository->hash_algo);
}
if (run_fetch(repo, refspecs))
@@ -1063,7 +1063,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
return 0;
if (repo_get_oid(the_repository, "HEAD", &curr_head))
- oidclr(&curr_head);
+ oidclr(&curr_head, the_repository->hash_algo);
if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
!oideq(&orig_head, &curr_head)) {
diff --git a/builtin/push.c b/builtin/push.c
index 2fbb31c..8260c6e 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -141,16 +141,6 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
free_refs(local_refs);
}
-static int push_url_of_remote(struct remote *remote, const char ***url_p)
-{
- if (remote->pushurl_nr) {
- *url_p = remote->pushurl;
- return remote->pushurl_nr;
- }
- *url_p = remote->url;
- return remote->url_nr;
-}
-
static NORETURN void die_push_simple(struct branch *branch,
struct remote *remote)
{
@@ -434,8 +424,7 @@ static int do_push(int flags,
struct remote *remote)
{
int i, errs;
- const char **url;
- int url_nr;
+ struct strvec *url;
struct refspec *push_refspec = &rs;
if (push_options->nr)
@@ -448,19 +437,10 @@ static int do_push(int flags,
setup_default_push_refspecs(&flags, remote);
}
errs = 0;
- url_nr = push_url_of_remote(remote, &url);
- if (url_nr) {
- for (i = 0; i < url_nr; i++) {
- struct transport *transport =
- transport_get(remote, url[i]);
- if (flags & TRANSPORT_PUSH_OPTIONS)
- transport->push_options = push_options;
- if (push_with_options(transport, push_refspec, flags))
- errs++;
- }
- } else {
+ url = push_url_of_remote(remote);
+ for (i = 0; i < url->nr; i++) {
struct transport *transport =
- transport_get(remote, NULL);
+ transport_get(remote, url->v[i]);
if (flags & TRANSPORT_PUSH_OPTIONS)
transport->push_options = push_options;
if (push_with_options(transport, push_refspec, flags))
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 6f89cec..a8cf850 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -4,7 +4,6 @@
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -159,8 +158,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
git_config(git_read_tree_config, NULL);
@@ -197,7 +196,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
die(_("You need to resolve your current index first"));
stage = opts.merge = 1;
}
- resolve_undo_clear_index(&the_index);
+ resolve_undo_clear_index(the_repository->index);
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
@@ -225,7 +224,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
setup_work_tree();
if (opts.skip_sparse_checkout)
- ensure_full_index(&the_index);
+ ensure_full_index(the_repository->index);
if (opts.merge) {
switch (stage - 1) {
@@ -237,7 +236,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
break;
case 2:
opts.fn = twoway_merge;
- opts.initial_checkout = is_index_unborn(&the_index);
+ opts.initial_checkout = is_index_unborn(the_repository->index);
break;
case 3:
default:
@@ -258,7 +257,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (nr_trees == 1 && !opts.prefix)
opts.skip_cache_tree_update = 1;
- cache_tree_free(&the_index.cache_tree);
+ cache_tree_free(&the_repository->index->cache_tree);
for (i = 0; i < nr_trees; i++) {
struct tree *tree = trees[i];
if (parse_tree(tree) < 0)
@@ -282,7 +281,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
the_repository->index,
trees[0]);
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die("unable to write new index file");
return 0;
}
diff --git a/builtin/rebase.c b/builtin/rebase.c
index a33a2ed..e3a8e74 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -4,7 +4,6 @@
* Copyright (c) 2018 Pratik Karki
*/
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "environment.h"
@@ -84,7 +83,7 @@ static const char *action_names[] = {
struct rebase_options {
enum rebase_type type;
enum empty_type empty;
- const char *default_backend;
+ char *default_backend;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -136,7 +135,7 @@ struct rebase_options {
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
.keep_empty = 1, \
- .default_backend = "merge", \
+ .default_backend = xstrdup("merge"), \
.flags = REBASE_NO_QUIET, \
.git_am_opts = STRVEC_INIT, \
.exec = STRING_LIST_INIT_NODUP, \
@@ -152,6 +151,19 @@ struct rebase_options {
.strategy_opts = STRING_LIST_INIT_NODUP,\
}
+static void rebase_options_release(struct rebase_options *opts)
+{
+ free(opts->default_backend);
+ free(opts->reflog_action);
+ free(opts->head_name);
+ strvec_clear(&opts->git_am_opts);
+ free(opts->gpg_sign_opt);
+ string_list_clear(&opts->exec, 0);
+ free(opts->strategy);
+ string_list_clear(&opts->strategy_opts, 0);
+ strbuf_release(&opts->git_format_patch_opt);
+}
+
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
{
struct replay_opts replay = REPLAY_OPTS_INIT;
@@ -253,7 +265,7 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,
if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
return error_errno(_("could not create temporary %s"), merge_dir());
- delete_reflog("REBASE_HEAD");
+ refs_delete_reflog(get_main_ref_store(the_repository), "REBASE_HEAD");
interactive = fopen(path_interactive(), "w");
if (!interactive)
@@ -296,7 +308,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
if (ret)
error(_("could not generate todo list"));
else {
- discard_index(&the_index);
+ discard_index(the_repository->index);
if (todo_list_parse_insn_buffer(the_repository, &replay,
todo_list.buf.buf, &todo_list))
BUG("unusable todo list");
@@ -519,8 +531,10 @@ static int finish_rebase(struct rebase_options *opts)
struct strbuf dir = STRBUF_INIT;
int ret = 0;
- delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
- delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "REBASE_HEAD", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "AUTO_MERGE", NULL, REF_NO_DEREF);
apply_autostash(state_dir_path("autostash", opts));
/*
* We ignore errors in 'git maintenance run --auto', since the
@@ -800,6 +814,7 @@ static int rebase_config(const char *var, const char *value,
}
if (!strcmp(var, "rebase.backend")) {
+ FREE_AND_NULL(opts->default_backend);
return git_config_string(&opts->default_backend, var, value);
}
@@ -1051,6 +1066,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
{
struct rebase_options options = REBASE_OPTIONS_INIT;
const char *branch_name;
+ const char *strategy_opt = NULL;
int ret, flags, total_argc, in_progress = 0;
int keep_base = 0;
int ok_to_skip_pre_rebase = 0;
@@ -1165,7 +1181,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
PARSE_OPT_OPTARG, parse_opt_rebase_merges),
OPT_BOOL(0, "fork-point", &options.fork_point,
N_("use 'merge-base --fork-point' to refine upstream")),
- OPT_STRING('s', "strategy", &options.strategy,
+ OPT_STRING('s', "strategy", &strategy_opt,
N_("strategy"), N_("use the given merge strategy")),
OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
N_("option"),
@@ -1474,13 +1490,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
}
- if (options.strategy_opts.nr && !options.strategy)
- options.strategy = "ort";
-
- if (options.strategy) {
- options.strategy = xstrdup(options.strategy);
+ if (strategy_opt)
+ options.strategy = xstrdup(strategy_opt);
+ else if (options.strategy_opts.nr && !options.strategy)
+ options.strategy = xstrdup("ort");
+ if (options.strategy)
imply_merge(&options, "--strategy");
- }
if (options.root && !options.onto_name)
imply_merge(&options, "--root without --onto");
@@ -1628,7 +1643,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/* Is it a local branch? */
strbuf_reset(&buf);
strbuf_addf(&buf, "refs/heads/%s", branch_name);
- if (!read_ref(buf.buf, &branch_oid)) {
+ if (!refs_read_ref(get_main_ref_store(the_repository), buf.buf, &branch_oid)) {
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
options.orig_head =
@@ -1645,8 +1660,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
} else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
options.head_name =
- xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
- &flags));
+ xstrdup_or_null(refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL,
+ &flags));
if (!options.head_name)
die(_("No such ref: %s"), "HEAD");
if (flags & REF_ISSYMREF) {
@@ -1740,7 +1755,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
else if (!strcmp(branch_name, "HEAD") &&
- resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+ refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag))
puts(_("HEAD is up to date."));
else
printf(_("Current branch %s is up to date.\n"),
@@ -1750,7 +1765,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
} else if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
else if (!strcmp(branch_name, "HEAD") &&
- resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+ refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag))
puts(_("HEAD is up to date, rebase forced."));
else
printf(_("Current branch %s is up to date, rebase "
@@ -1837,14 +1852,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
cleanup:
strbuf_release(&buf);
strbuf_release(&revisions);
- free(options.reflog_action);
- free(options.head_name);
- strvec_clear(&options.git_am_opts);
- free(options.gpg_sign_opt);
- string_list_clear(&options.exec, 0);
- free(options.strategy);
- string_list_clear(&options.strategy_opts, 0);
- strbuf_release(&options.git_format_patch_opt);
+ rebase_options_release(&options);
free(squash_onto_name);
free(keep_base_onto_name);
return !!ret;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e8d7df1..339524a 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -88,7 +88,7 @@ static struct strbuf push_cert = STRBUF_INIT;
static struct object_id push_cert_oid;
static struct signature_check sigcheck;
static const char *push_cert_nonce;
-static const char *cert_nonce_seed;
+static char *cert_nonce_seed;
static struct strvec hidden_refs = STRVEC_INIT;
static const char *NONCE_UNSOLICITED = "UNSOLICITED";
@@ -168,13 +168,13 @@ static int receive_pack_config(const char *var, const char *value,
}
if (strcmp(var, "receive.fsck.skiplist") == 0) {
- const char *path;
+ char *path;
if (git_config_pathname(&path, var, value))
return 1;
strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
fsck_msg_types.len ? ',' : '=', path);
- free((char *)path);
+ free(path);
return 0;
}
@@ -741,7 +741,7 @@ static void prepare_push_cert_sha1(struct child_process *proc)
already_done = 1;
if (write_object_file(push_cert.buf, push_cert.len, OBJ_BLOB,
&push_cert_oid))
- oidclr(&push_cert_oid);
+ oidclr(&push_cert_oid, the_repository->hash_algo);
memset(&sigcheck, '\0', sizeof(sigcheck));
@@ -1249,7 +1249,7 @@ static int run_proc_receive_hook(struct command *commands,
return code;
}
-static char *refuse_unconfigured_deny_msg =
+static const char *refuse_unconfigured_deny_msg =
N_("By default, updating the current branch in a non-bare repository\n"
"is denied, because it will make the index and work tree inconsistent\n"
"with what you pushed, and will require 'git reset --hard' to match\n"
@@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void)
rp_error("%s", _(refuse_unconfigured_deny_msg));
}
-static char *refuse_unconfigured_deny_delete_current_msg =
+static const char *refuse_unconfigured_deny_delete_current_msg =
N_("By default, deleting the current branch is denied, because the next\n"
"'git clone' won't result in any file checked out, causing confusion.\n"
"\n"
@@ -1371,7 +1371,7 @@ static const char *push_to_deploy(unsigned char *sha1,
strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
"--ignore-submodules",
/* diff-index with either HEAD or an empty tree */
- head_has_history() ? "HEAD" : empty_tree_oid_hex(),
+ head_has_history() ? "HEAD" : empty_tree_oid_hex(the_repository->hash_algo),
"--", NULL);
strvec_pushv(&child.env, env->v);
child.no_stdin = 1;
@@ -1566,7 +1566,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct strbuf err = STRBUF_INIT;
if (!parse_object(the_repository, old_oid)) {
old_oid = NULL;
- if (ref_exists(name)) {
+ if (refs_ref_exists(get_main_ref_store(the_repository), name)) {
rp_warning("allowing deletion of corrupt ref");
} else {
rp_warning("deleting a non-existent ref");
@@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (ref_transaction_delete(transaction,
namespaced_name,
old_oid,
- 0, "push", &err)) {
+ NULL, 0,
+ "push", &err)) {
rp_error("%s", err.buf);
ret = "failed to delete";
} else {
@@ -1595,6 +1596,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (ref_transaction_update(transaction,
namespaced_name,
new_oid, old_oid,
+ NULL, NULL,
0, "push",
&err)) {
rp_error("%s", err.buf);
@@ -1693,7 +1695,8 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
+ dst_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ buf.buf, 0, NULL, &flag);
check_aliased_update_internal(cmd, list, dst_name, flag);
strbuf_release(&buf);
}
@@ -1829,7 +1832,8 @@ static void execute_commands_non_atomic(struct command *commands,
if (!should_process_cmd(cmd) || cmd->run_proc_receive)
continue;
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
rp_error("%s", err.buf);
strbuf_reset(&err);
@@ -1857,7 +1861,8 @@ static void execute_commands_atomic(struct command *commands,
struct strbuf err = STRBUF_INIT;
const char *reported_error = "atomic push failure";
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
rp_error("%s", err.buf);
strbuf_reset(&err);
@@ -1983,7 +1988,9 @@ static void execute_commands(struct command *commands,
check_aliased_updates(commands);
free(head_name_to_free);
- head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
+ head_name = head_name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL,
+ NULL);
if (run_proc_receive &&
run_proc_receive_hook(commands, push_options))
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 060eb33..0d2ff95 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -364,11 +364,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
};
set_reflog_expiry_param(&cb.cmd, item->string);
- status |= reflog_expire(item->string, flags,
- reflog_expiry_prepare,
- should_prune_fn,
- reflog_expiry_cleanup,
- &cb);
+ status |= refs_reflog_expire(get_main_ref_store(the_repository),
+ item->string, flags,
+ reflog_expiry_prepare,
+ should_prune_fn,
+ reflog_expiry_cleanup,
+ &cb);
}
string_list_clear(&collected.reflogs, 0);
}
@@ -377,16 +378,17 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
char *ref;
struct expire_reflog_policy_cb cb = { .cmd = cmd };
- if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
+ if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
status |= error(_("%s points nowhere!"), argv[i]);
continue;
}
set_reflog_expiry_param(&cb.cmd, ref);
- status |= reflog_expire(ref, flags,
- reflog_expiry_prepare,
- should_prune_fn,
- reflog_expiry_cleanup,
- &cb);
+ status |= refs_reflog_expire(get_main_ref_store(the_repository),
+ ref, flags,
+ reflog_expiry_prepare,
+ should_prune_fn,
+ reflog_expiry_cleanup,
+ &cb);
free(ref);
}
return status;
@@ -437,7 +439,8 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
refname = argv[0];
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
die(_("invalid ref format: %s"), refname);
- return !reflog_exists(refname);
+ return !refs_reflog_exists(get_main_ref_store(the_repository),
+ refname);
}
/*
diff --git a/builtin/refs.c b/builtin/refs.c
new file mode 100644
index 0000000..46dcd15
--- /dev/null
+++ b/builtin/refs.c
@@ -0,0 +1,75 @@
+#include "builtin.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "repository.h"
+#include "strbuf.h"
+
+#define REFS_MIGRATE_USAGE \
+ N_("git refs migrate --ref-format=<format> [--dry-run]")
+
+static int cmd_refs_migrate(int argc, const char **argv, const char *prefix)
+{
+ const char * const migrate_usage[] = {
+ REFS_MIGRATE_USAGE,
+ NULL,
+ };
+ const char *format_str = NULL;
+ enum ref_storage_format format;
+ unsigned int flags = 0;
+ struct option options[] = {
+ OPT_STRING_F(0, "ref-format", &format_str, N_("format"),
+ N_("specify the reference format to convert to"),
+ PARSE_OPT_NONEG),
+ OPT_BIT(0, "dry-run", &flags,
+ N_("perform a non-destructive dry-run"),
+ REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN),
+ OPT_END(),
+ };
+ struct strbuf errbuf = STRBUF_INIT;
+ int err;
+
+ argc = parse_options(argc, argv, prefix, options, migrate_usage, 0);
+ if (argc)
+ usage(_("too many arguments"));
+ if (!format_str)
+ usage(_("missing --ref-format=<format>"));
+
+ format = ref_storage_format_by_name(format_str);
+ if (format == REF_STORAGE_FORMAT_UNKNOWN) {
+ err = error(_("unknown ref storage format '%s'"), format_str);
+ goto out;
+ }
+
+ if (the_repository->ref_storage_format == format) {
+ err = error(_("repository already uses '%s' format"),
+ ref_storage_format_to_name(format));
+ goto out;
+ }
+
+ if (repo_migrate_ref_storage_format(the_repository, format, flags, &errbuf) < 0) {
+ err = error("%s", errbuf.buf);
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ strbuf_release(&errbuf);
+ return err;
+}
+
+int cmd_refs(int argc, const char **argv, const char *prefix)
+{
+ const char * const refs_usage[] = {
+ REFS_MIGRATE_USAGE,
+ NULL,
+ };
+ parse_opt_subcommand_fn *fn = NULL;
+ struct option opts[] = {
+ OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, opts, refs_usage, 0);
+ return fn(argc, argv, prefix);
+}
diff --git a/builtin/remote.c b/builtin/remote.c
index 8412d12..0829249 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -240,7 +240,7 @@ static int add(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf2);
strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
- if (create_symref(buf.buf, buf2.buf, "remote add"))
+ if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add"))
return error(_("Could not setup master '%s'"), master);
}
@@ -376,7 +376,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
for (ref = fetch_map; ref; ref = ref->next) {
if (omit_name_by_refspec(ref->name, &states->remote->fetch))
string_list_append(&states->skipped, abbrev_branch(ref->name));
- else if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
+ else if (!ref->peer_ref || !refs_ref_exists(get_main_ref_store(the_repository), ref->peer_ref->name))
string_list_append(&states->new_refs, abbrev_branch(ref->name));
else
string_list_append(&states->tracked, abbrev_branch(ref->name));
@@ -493,12 +493,13 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
{
struct ref *ref, *matches;
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
- struct refspec_item refspec;
+ struct refspec_item refspec = {
+ .force = 0,
+ .pattern = 1,
+ .src = (char *) "refs/heads/*",
+ .dst = (char *) "refs/heads/*",
+ };
- memset(&refspec, 0, sizeof(refspec));
- refspec.force = 0;
- refspec.pattern = 1;
- refspec.src = refspec.dst = "refs/heads/*";
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
@@ -507,7 +508,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
free_refs(fetch_map);
free_refs(matches);
-
return 0;
}
@@ -598,8 +598,9 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, refname);
- symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
- NULL, &flag);
+ symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname, RESOLVE_REF_READING,
+ NULL, &flag);
if (symref && (flag & REF_ISSYMREF)) {
item->util = xstrdup(symref);
rename->symrefs_nr++;
@@ -618,8 +619,8 @@ static int migrate_file(struct remote *remote)
int i;
strbuf_addf(&buf, "remote.%s.url", remote->name);
- for (i = 0; i < remote->url_nr; i++)
- git_config_set_multivar(buf.buf, remote->url[i], "^$", 0);
+ for (i = 0; i < remote->url.nr; i++)
+ git_config_set_multivar(buf.buf, remote->url.v[i], "^$", 0);
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.push", remote->name);
for (i = 0; i < remote->push.raw_nr; i++)
@@ -789,7 +790,8 @@ static int mv(int argc, const char **argv, const char *prefix)
* First remove symrefs, then rename the rest, finally create
* the new symrefs.
*/
- for_each_ref(read_remote_branches, &rename);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ read_remote_branches, &rename);
if (show_progress) {
/*
* Count symrefs twice, since "renaming" them is done by
@@ -805,7 +807,7 @@ static int mv(int argc, const char **argv, const char *prefix)
if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string,
&referent))
continue;
- if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, item->string, NULL, REF_NO_DEREF))
die(_("deleting '%s' failed"), item->string);
strbuf_release(&referent);
@@ -823,7 +825,7 @@ static int mv(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf2);
strbuf_addf(&buf2, "remote: renamed %s to %s",
item->string, buf.buf);
- if (rename_ref(item->string, buf.buf, buf2.buf))
+ if (refs_rename_ref(get_main_ref_store(the_repository), item->string, buf.buf, buf2.buf))
die(_("renaming '%s' failed"), item->string);
display_progress(progress, ++refs_renamed_nr);
}
@@ -843,7 +845,7 @@ static int mv(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf3);
strbuf_addf(&buf3, "remote: renamed %s to %s",
item->string, buf.buf);
- if (create_symref(buf.buf, buf2.buf, buf3.buf))
+ if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf))
die(_("creating '%s' failed"), buf.buf);
display_progress(progress, ++refs_renamed_nr);
}
@@ -917,11 +919,14 @@ static int rm(int argc, const char **argv, const char *prefix)
* refs, which are invalidated when deleting a branch.
*/
cb_data.remote = remote;
- result = for_each_ref(add_branch_for_removal, &cb_data);
+ result = refs_for_each_ref(get_main_ref_store(the_repository),
+ add_branch_for_removal, &cb_data);
strbuf_release(&buf);
if (!result)
- result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
+ result = refs_delete_refs(get_main_ref_store(the_repository),
+ "remote: remove", &branches,
+ REF_NO_DEREF);
string_list_clear(&branches, 0);
if (skipped.nr) {
@@ -997,8 +1002,7 @@ static int get_remote_ref_states(const char *name,
struct transport *transport;
const struct ref *remote_refs;
- transport = transport_get(states->remote, states->remote->url_nr > 0 ?
- states->remote->url[0] : NULL);
+ transport = transport_get(states->remote, states->remote->url.v[0]);
remote_refs = transport_get_remote_refs(transport, NULL);
states->queried = 1;
@@ -1010,7 +1014,8 @@ static int get_remote_ref_states(const char *name,
get_push_ref_states(remote_refs, states);
transport_disconnect(transport);
} else {
- for_each_ref(append_ref_to_tracked_list, states);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_ref_to_tracked_list, states);
string_list_sort(&states->tracked);
get_push_ref_states_noquery(states);
}
@@ -1207,15 +1212,15 @@ static int get_one_entry(struct remote *remote, void *priv)
{
struct string_list *list = priv;
struct strbuf remote_info_buf = STRBUF_INIT;
- const char **url;
- int i, url_nr;
+ struct strvec *url;
+ int i;
- if (remote->url_nr > 0) {
+ if (remote->url.nr > 0) {
struct strbuf promisor_config = STRBUF_INIT;
const char *partial_clone_filter = NULL;
strbuf_addf(&promisor_config, "remote.%s.partialclonefilter", remote->name);
- strbuf_addf(&remote_info_buf, "%s (fetch)", remote->url[0]);
+ strbuf_addf(&remote_info_buf, "%s (fetch)", remote->url.v[0]);
if (!git_config_get_string_tmp(promisor_config.buf, &partial_clone_filter))
strbuf_addf(&remote_info_buf, " [%s]", partial_clone_filter);
@@ -1224,16 +1229,10 @@ static int get_one_entry(struct remote *remote, void *priv)
strbuf_detach(&remote_info_buf, NULL);
} else
string_list_append(list, remote->name)->util = NULL;
- if (remote->pushurl_nr) {
- url = remote->pushurl;
- url_nr = remote->pushurl_nr;
- } else {
- url = remote->url;
- url_nr = remote->url_nr;
- }
- for (i = 0; i < url_nr; i++)
+ url = push_url_of_remote(remote);
+ for (i = 0; i < url->nr; i++)
{
- strbuf_addf(&remote_info_buf, "%s (push)", url[i]);
+ strbuf_addf(&remote_info_buf, "%s (push)", url->v[i]);
string_list_append(list, remote->name)->util =
strbuf_detach(&remote_info_buf, NULL);
}
@@ -1289,28 +1288,20 @@ static int show(int argc, const char **argv, const char *prefix)
for (; argc; argc--, argv++) {
int i;
- const char **url;
- int url_nr;
+ struct strvec *url;
get_remote_ref_states(*argv, &info.states, query_flag);
printf_ln(_("* remote %s"), *argv);
- printf_ln(_(" Fetch URL: %s"), info.states.remote->url_nr > 0 ?
- info.states.remote->url[0] : _("(no URL)"));
- if (info.states.remote->pushurl_nr) {
- url = info.states.remote->pushurl;
- url_nr = info.states.remote->pushurl_nr;
- } else {
- url = info.states.remote->url;
- url_nr = info.states.remote->url_nr;
- }
- for (i = 0; i < url_nr; i++)
+ printf_ln(_(" Fetch URL: %s"), info.states.remote->url.v[0]);
+ url = push_url_of_remote(info.states.remote);
+ for (i = 0; i < url->nr; i++)
/*
* TRANSLATORS: the colon ':' should align
* with the one in " Fetch URL: %s"
* translation.
*/
- printf_ln(_(" Push URL: %s"), url[i]);
+ printf_ln(_(" Push URL: %s"), url->v[i]);
if (!i)
printf_ln(_(" Push URL: %s"), _("(no URL)"));
if (no_query)
@@ -1407,7 +1398,7 @@ static int set_head(int argc, const char **argv, const char *prefix)
head_name = xstrdup(states.heads.items[0].string);
free_remote_ref_states(&states);
} else if (opt_d && !opt_a && argc == 1) {
- if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, buf.buf, NULL, REF_NO_DEREF))
result |= error(_("Could not delete %s"), buf.buf);
} else
usage_with_options(builtin_remote_sethead_usage, options);
@@ -1415,9 +1406,9 @@ static int set_head(int argc, const char **argv, const char *prefix)
if (head_name) {
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
/* make sure it's valid */
- if (!ref_exists(buf2.buf))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
result |= error(_("Not a valid ref: %s"), buf2.buf);
- else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
+ else if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
result |= error(_("Could not setup %s"), buf.buf);
else if (opt_a)
printf("%s/HEAD set to %s\n", argv[0], head_name);
@@ -1447,17 +1438,15 @@ static int prune_remote(const char *remote, int dry_run)
}
printf_ln(_("Pruning %s"), remote);
- printf_ln(_("URL: %s"),
- states.remote->url_nr
- ? states.remote->url[0]
- : _("(no URL)"));
+ printf_ln(_("URL: %s"), states.remote->url.v[0]);
for_each_string_list_item(item, &states.stale)
string_list_append(&refs_to_prune, item->util);
string_list_sort(&refs_to_prune);
if (!dry_run)
- result |= delete_refs("remote: prune", &refs_to_prune, 0);
+ result |= refs_delete_refs(get_main_ref_store(the_repository),
+ "remote: prune", &refs_to_prune, 0);
for_each_string_list_item(item, &states.stale) {
const char *refname = item->util;
@@ -1470,7 +1459,8 @@ static int prune_remote(const char *remote, int dry_run)
abbrev_ref(refname, "refs/remotes/"));
}
- warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune);
+ refs_warn_dangling_symrefs(get_main_ref_store(the_repository),
+ stdout, dangling_msg, &refs_to_prune);
string_list_clear(&refs_to_prune, 0);
free_remote_ref_states(&states);
@@ -1614,8 +1604,7 @@ static int get_url(int argc, const char **argv, const char *prefix)
int i, push_mode = 0, all_mode = 0;
const char *remotename = NULL;
struct remote *remote;
- const char **url;
- int url_nr;
+ struct strvec *url;
struct option options[] = {
OPT_BOOL('\0', "push", &push_mode,
N_("query push URLs rather than fetch URLs")),
@@ -1637,27 +1626,13 @@ static int get_url(int argc, const char **argv, const char *prefix)
exit(2);
}
- url_nr = 0;
- if (push_mode) {
- url = remote->pushurl;
- url_nr = remote->pushurl_nr;
- }
- /* else fetch mode */
-
- /* Use the fetch URL when no push URLs were found or requested. */
- if (!url_nr) {
- url = remote->url;
- url_nr = remote->url_nr;
- }
-
- if (!url_nr)
- die(_("no URLs configured for remote '%s'"), remotename);
+ url = push_mode ? push_url_of_remote(remote) : &remote->url;
if (all_mode) {
- for (i = 0; i < url_nr; i++)
- printf_ln("%s", url[i]);
+ for (i = 0; i < url->nr; i++)
+ printf_ln("%s", url->v[i]);
} else {
- printf_ln("%s", *url);
+ printf_ln("%s", url->v[0]);
}
return 0;
@@ -1672,8 +1647,7 @@ static int set_url(int argc, const char **argv, const char *prefix)
const char *oldurl = NULL;
struct remote *remote;
regex_t old_regex;
- const char **urlset;
- int urlset_nr;
+ struct strvec *urlset;
struct strbuf name_buf = STRBUF_INIT;
struct option options[] = {
OPT_BOOL('\0', "push", &push_mode,
@@ -1710,12 +1684,10 @@ static int set_url(int argc, const char **argv, const char *prefix)
if (push_mode) {
strbuf_addf(&name_buf, "remote.%s.pushurl", remotename);
- urlset = remote->pushurl;
- urlset_nr = remote->pushurl_nr;
+ urlset = &remote->pushurl;
} else {
strbuf_addf(&name_buf, "remote.%s.url", remotename);
- urlset = remote->url;
- urlset_nr = remote->url_nr;
+ urlset = &remote->url;
}
/* Special cases that add new entry. */
@@ -1732,8 +1704,8 @@ static int set_url(int argc, const char **argv, const char *prefix)
if (regcomp(&old_regex, oldurl, REG_EXTENDED))
die(_("Invalid old URL pattern: %s"), oldurl);
- for (i = 0; i < urlset_nr; i++)
- if (!regexec(&old_regex, urlset[i], 0, NULL, 0))
+ for (i = 0; i < urlset->nr; i++)
+ if (!regexec(&old_regex, urlset->v[i], 0, NULL, 0))
matches++;
else
negative_matches++;
diff --git a/builtin/repack.c b/builtin/repack.c
index 15e4ccc..f0317fa 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -48,10 +48,10 @@ static const char incremental_bitmap_conflict_error[] = N_(
);
struct pack_objects_args {
- const char *window;
- const char *window_memory;
- const char *depth;
- const char *threads;
+ char *window;
+ char *window_memory;
+ char *depth;
+ char *threads;
unsigned long max_pack_size;
int no_reuse_delta;
int no_reuse_object;
@@ -673,7 +673,7 @@ static int midx_snapshot_ref_one(const char *refname UNUSED,
struct midx_snapshot_ref_data *data = _data;
struct object_id peeled;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (oidset_insert(&data->seen, oid))
@@ -706,11 +706,14 @@ static void midx_snapshot_refs(struct tempfile *f)
data.preferred = 1;
for_each_string_list_item(item, preferred)
- for_each_ref_in(item->string, midx_snapshot_ref_one, &data);
+ refs_for_each_ref_in(get_main_ref_store(the_repository),
+ item->string,
+ midx_snapshot_ref_one, &data);
data.preferred = 0;
}
- for_each_ref(midx_snapshot_ref_one, &data);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ midx_snapshot_ref_one, &data);
if (close_tempfile_gently(f)) {
int save_errno = errno;
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600..1ef833c 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -43,11 +43,12 @@ enum replace_format {
};
struct show_data {
+ struct repository *repo;
const char *pattern;
enum replace_format format;
};
-static int show_reference(struct repository *r, const char *refname,
+static int show_reference(const char *refname,
const struct object_id *oid,
int flag UNUSED, void *cb_data)
{
@@ -62,11 +63,11 @@ static int show_reference(struct repository *r, const char *refname,
struct object_id object;
enum object_type obj_type, repl_type;
- if (repo_get_oid(r, refname, &object))
+ if (repo_get_oid(data->repo, refname, &object))
return error(_("failed to resolve '%s' as a valid ref"), refname);
- obj_type = oid_object_info(r, &object, NULL);
- repl_type = oid_object_info(r, oid, NULL);
+ obj_type = oid_object_info(data->repo, &object, NULL);
+ repl_type = oid_object_info(data->repo, oid, NULL);
printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
oid_to_hex(oid), type_name(repl_type));
@@ -80,6 +81,7 @@ static int list_replace_refs(const char *pattern, const char *format)
{
struct show_data data;
+ data.repo = the_repository;
if (!pattern)
pattern = "*";
data.pattern = pattern;
@@ -99,7 +101,8 @@ static int list_replace_refs(const char *pattern, const char *format)
"valid formats are 'short', 'medium' and 'long'"),
format);
- for_each_replace_ref(the_repository, show_reference, (void *)&data);
+ refs_for_each_replace_ref(get_main_ref_store(the_repository),
+ show_reference, (void *)&data);
return 0;
}
@@ -130,7 +133,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
strbuf_addstr(&ref, oid_to_hex(&oid));
full_hex = ref.buf + base_len;
- if (read_ref(ref.buf, &oid)) {
+ if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
error(_("replace ref '%s' not found"), full_hex);
had_error = 1;
continue;
@@ -145,7 +148,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
static int delete_replace_ref(const char *name, const char *ref,
const struct object_id *oid)
{
- if (delete_ref(NULL, ref, oid, 0))
+ if (refs_delete_ref(get_main_ref_store(the_repository), NULL, ref, oid, 0))
return 1;
printf_ln(_("Deleted replace ref '%s'"), name);
return 0;
@@ -163,8 +166,8 @@ static int check_ref_valid(struct object_id *object,
if (check_refname_format(ref->buf, 0))
return error(_("'%s' is not a valid ref name"), ref->buf);
- if (read_ref(ref->buf, prev))
- oidclr(prev);
+ if (refs_read_ref(get_main_ref_store(the_repository), ref->buf, prev))
+ oidclr(prev, the_repository->hash_algo);
else if (!force)
return error(_("replace ref '%s' already exists"), ref->buf);
return 0;
@@ -198,10 +201,11 @@ static int replace_object_oid(const char *object_ref,
return -1;
}
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, repl, &prev,
- 0, NULL, &err) ||
+ NULL, NULL, 0, NULL, &err) ||
ref_transaction_commit(transaction, &err))
res = error("%s", err.buf);
diff --git a/builtin/replay.c b/builtin/replay.c
index 6bc4b47..6bf0691 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -2,7 +2,6 @@
* "git replay" builtin command
*/
-#define USE_THE_INDEX_VARIABLE
#include "git-compat-util.h"
#include "builtin.h"
diff --git a/builtin/reset.c b/builtin/reset.c
index 1d62ff6..5f941fb 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -7,7 +7,7 @@
*
* Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -66,8 +66,8 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.fn = oneway_merge;
opts.merge = 1;
init_checkout_metadata(&opts.meta, ref, oid, NULL);
@@ -159,11 +159,11 @@ static void update_index_from_diff(struct diff_queue_struct *q,
struct cache_entry *ce;
if (!is_in_reset_tree && !intent_to_add) {
- remove_file_from_index(&the_index, one->path);
+ remove_file_from_index(the_repository->index, one->path);
continue;
}
- ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
+ ce = make_cache_entry(the_repository->index, one->mode, &one->oid, one->path,
0, 0);
/*
@@ -174,9 +174,9 @@ static void update_index_from_diff(struct diff_queue_struct *q,
* if this entry is outside the sparse cone - this is necessary
* to properly construct the reset sparse directory.
*/
- pos = index_name_pos(&the_index, one->path, strlen(one->path));
- if ((pos >= 0 && ce_skip_worktree(the_index.cache[pos])) ||
- (pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
+ pos = index_name_pos(the_repository->index, one->path, strlen(one->path));
+ if ((pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) ||
+ (pos < 0 && !path_in_sparse_checkout(one->path, the_repository->index)))
ce->ce_flags |= CE_SKIP_WORKTREE;
if (!ce)
@@ -186,7 +186,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
ce->ce_flags |= CE_INTENT_TO_ADD;
set_object_name_for_intent_to_add_entry(ce);
}
- add_index_entry(&the_index, ce,
+ add_index_entry(the_repository->index, ce,
ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
}
}
@@ -208,8 +208,8 @@ static int read_from_tree(const struct pathspec *pathspec,
opt.change = diff_change;
opt.add_remove = diff_addremove;
- if (pathspec->nr && pathspec_needs_expanded_index(&the_index, pathspec))
- ensure_full_index(&the_index);
+ if (pathspec->nr && pathspec_needs_expanded_index(the_repository->index, pathspec))
+ ensure_full_index(the_repository->index);
if (do_diff_cache(tree_oid, &opt))
return 1;
@@ -235,7 +235,7 @@ static void set_reflog_message(struct strbuf *sb, const char *action,
static void die_if_unmerged_cache(int reset_type)
{
- if (is_merge() || unmerged_index(&the_index))
+ if (is_merge() || unmerged_index(the_repository->index))
die(_("Cannot do a %s reset in the middle of a merge."),
_(reset_type_names[reset_type]));
@@ -307,13 +307,16 @@ static int reset_refs(const char *rev, const struct object_id *oid)
if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) {
orig = &oid_orig;
set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
- update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
- UPDATE_REFS_MSG_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+ "ORIG_HEAD", orig, old_orig, 0,
+ UPDATE_REFS_MSG_ON_ERR);
} else if (old_orig)
- delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+ refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ "ORIG_HEAD", old_orig, 0);
set_reflog_message(&msg, "updating HEAD", rev);
- update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0,
- UPDATE_REFS_MSG_ON_ERR);
+ update_ref_status = refs_update_ref(get_main_ref_store(the_repository),
+ msg.buf, "HEAD", oid, orig, 0,
+ UPDATE_REFS_MSG_ON_ERR);
strbuf_release(&msg);
return update_ref_status;
}
@@ -470,12 +473,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
update_ref_status = 1;
goto cleanup;
}
- the_index.updated_skipworktree = 1;
+ the_repository->index->updated_skipworktree = 1;
if (!no_refresh && get_git_work_tree()) {
uint64_t t_begin, t_delta_in_ms;
t_begin = getnanotime();
- refresh_index(&the_index, flags, NULL, NULL,
+ refresh_index(the_repository->index, flags, NULL, NULL,
_("Unstaged changes after reset:"));
t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
if (!quiet && advice_enabled(ADVICE_RESET_NO_REFRESH_WARNING) && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) {
@@ -501,7 +504,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
free(ref);
}
- if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK))
die(_("Could not write new index file."));
}
@@ -516,7 +519,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (!pathspec.nr)
remove_branch_state(the_repository, 0);
- discard_index(&the_index);
+ discard_index(the_repository->index);
cleanup:
clear_pathspec(&pathspec);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 624182e..1e2919f 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "abspath.h"
#include "config.h"
@@ -160,8 +160,9 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
case 1: /* happy */
if (abbrev_ref) {
char *old = full;
- full = shorten_unambiguous_ref(full,
- abbrev_ref_strict);
+ full = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ full,
+ abbrev_ref_strict);
free(old);
}
show_with_type(type, full);
@@ -599,9 +600,12 @@ static int opt_with_value(const char *arg, const char *opt, const char **value)
static void handle_ref_opt(const char *pattern, const char *prefix)
{
if (pattern)
- for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ show_reference, pattern, prefix,
+ NULL);
else
- for_each_ref_in(prefix, show_reference, NULL);
+ refs_for_each_ref_in(get_main_ref_store(the_repository),
+ prefix, show_reference, NULL);
clear_ref_exclusions(&ref_excludes);
}
@@ -687,7 +691,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
const char *name = NULL;
struct object_context unused;
struct strbuf buf = STRBUF_INIT;
- const int hexsz = the_hash_algo->hexsz;
int seen_end_of_options = 0;
enum format_type format = FORMAT_DEFAULT;
@@ -863,8 +866,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
abbrev = strtoul(arg, NULL, 10);
if (abbrev < MINIMUM_ABBREV)
abbrev = MINIMUM_ABBREV;
- else if (hexsz <= abbrev)
- abbrev = hexsz;
+ else if ((int)the_hash_algo->hexsz <= abbrev)
+ abbrev = the_hash_algo->hexsz;
continue;
}
if (!strcmp(arg, "--sq")) {
@@ -898,7 +901,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--all")) {
- for_each_ref(show_reference, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ show_reference, NULL);
clear_ref_exclusions(&ref_excludes);
continue;
}
@@ -908,8 +912,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--bisect")) {
- for_each_fullref_in("refs/bisect/bad", show_reference, NULL);
- for_each_fullref_in("refs/bisect/good", anti_reference, NULL);
+ refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/bisect/bad",
+ NULL, show_reference,
+ NULL);
+ refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/bisect/good",
+ NULL, anti_reference,
+ NULL);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
@@ -1049,8 +1059,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
if (!strcmp(arg, "--shared-index-path")) {
if (repo_read_index(the_repository) < 0)
die(_("Could not read the index"));
- if (the_index.split_index) {
- const struct object_id *oid = &the_index.split_index->base_oid;
+ if (the_repository->index->split_index) {
+ const struct object_id *oid = &the_repository->index->split_index->base_oid;
const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
print_path(path, prefix, format, DEFAULT_RELATIVE);
}
diff --git a/builtin/revert.c b/builtin/revert.c
index 53935d2..7bf2b4e 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
/* Check for incompatible command line arguments */
if (cmd) {
- char *this_operation;
+ const char *this_operation;
if (cmd == 'q')
this_operation = "--quit";
else if (cmd == 'c')
diff --git a/builtin/rm.c b/builtin/rm.c
index fd130ce..0e79cba 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds 2006
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -41,8 +41,8 @@ static int get_ours_cache_pos(const char *path, int pos)
{
int i = -pos - 1;
- while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) {
- if (ce_stage(the_index.cache[i]) == 2)
+ while ((i < the_repository->index->cache_nr) && !strcmp(the_repository->index->cache[i]->name, path)) {
+ if (ce_stage(the_repository->index->cache[i]) == 2)
return i;
i++;
}
@@ -78,13 +78,13 @@ static void submodules_absorb_gitdir_if_needed(void)
int pos;
const struct cache_entry *ce;
- pos = index_name_pos(&the_index, name, strlen(name));
+ pos = index_name_pos(the_repository->index, name, strlen(name));
if (pos < 0) {
pos = get_ours_cache_pos(name, pos);
if (pos < 0)
continue;
}
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (!S_ISGITLINK(ce->ce_mode) ||
!file_exists(ce->name) ||
@@ -122,7 +122,7 @@ static int check_local_mod(struct object_id *head, int index_only)
int local_changes = 0;
int staged_changes = 0;
- pos = index_name_pos(&the_index, name, strlen(name));
+ pos = index_name_pos(the_repository->index, name, strlen(name));
if (pos < 0) {
/*
* Skip unmerged entries except for populated submodules
@@ -132,11 +132,11 @@ static int check_local_mod(struct object_id *head, int index_only)
if (pos < 0)
continue;
- if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) ||
+ if (!S_ISGITLINK(the_repository->index->cache[pos]->ce_mode) ||
is_empty_dir(name))
continue;
}
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (lstat(ce->name, &st) < 0) {
if (!is_missing_file_error(errno))
@@ -173,7 +173,7 @@ static int check_local_mod(struct object_id *head, int index_only)
* Is the index different from the file in the work tree?
* If it's a submodule, is its work tree modified?
*/
- if (ie_match_stat(&the_index, ce, &st, 0) ||
+ if (ie_match_stat(the_repository->index, ce, &st, 0) ||
(S_ISGITLINK(ce->ce_mode) &&
bad_to_remove_submodule(ce->name,
SUBMODULE_REMOVAL_DIE_ON_ERROR |
@@ -301,27 +301,27 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
+ refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
seen = xcalloc(pathspec.nr, 1);
- if (pathspec_needs_expanded_index(&the_index, &pathspec))
- ensure_full_index(&the_index);
+ if (pathspec_needs_expanded_index(the_repository->index, &pathspec))
+ ensure_full_index(the_repository->index);
- for (i = 0; i < the_index.cache_nr; i++) {
- const struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ const struct cache_entry *ce = the_repository->index->cache[i];
if (!include_sparse &&
(ce_skip_worktree(ce) ||
- !path_in_sparse_checkout(ce->name, &the_index)))
+ !path_in_sparse_checkout(ce->name, the_repository->index)))
continue;
- if (!ce_path_match(&the_index, ce, &pathspec, seen))
+ if (!ce_path_match(the_repository->index, ce, &pathspec, seen))
continue;
ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
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(&the_index))
+ !is_staging_gitmodules_ok(the_repository->index))
die(_("please stage your changes to .gitmodules or stash them to proceed"));
}
@@ -377,7 +377,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!force) {
struct object_id oid;
if (repo_get_oid(the_repository, "HEAD", &oid))
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
if (check_local_mod(&oid, index_only))
exit(1);
}
@@ -391,7 +391,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!quiet)
printf("rm '%s'\n", path);
- if (remove_file_from_index(&the_index, path))
+ if (remove_file_from_index(the_repository->index, path))
die(_("git rm: unable to remove %s"), path);
}
@@ -432,10 +432,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
if (gitmodules_modified)
- stage_updated_gitmodules(&the_index);
+ stage_updated_gitmodules(the_repository->index);
}
- if (write_locked_index(&the_index, &lock_file,
+ if (write_locked_index(the_repository->index, &lock_file,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("Unable to write new index file"));
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 3df9eaa..17cae6b 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
/* stable plumbing output; do not modify or localize */
fprintf(stderr, "Everything up-to-date\n");
+ free_refs(remote_refs);
+ free_refs(local_refs);
return ret;
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 3c7cd2d..d4daf31 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -435,7 +435,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
usage_with_options(shortlog_usage, options);
}
- if (setup_revisions(argc, argv, &rev, NULL) != 1) {
+ if (!nongit && setup_revisions(argc, argv, &rev, NULL) != 1) {
error(_("unrecognized argument: %s"), argv[1]);
usage_with_options(shortlog_usage, options);
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index b01ec76..d72f4cb 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -479,13 +479,15 @@ static void snarf_refs(int head, int remotes)
if (head) {
int orig_cnt = ref_name_cnt;
- for_each_ref(append_head_ref, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_head_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
if (remotes) {
int orig_cnt = ref_name_cnt;
- for_each_ref(append_remote_ref, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_remote_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
}
@@ -549,7 +551,8 @@ static void append_one_rev(const char *av)
match_ref_pattern = av;
match_ref_slash = count_slashes(av);
- for_each_ref(append_matching_ref, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_matching_ref, NULL);
if (saved_matches == ref_name_cnt &&
ref_name_cnt < MAX_REVS)
error(_("no matching refs with %s"), av);
@@ -740,9 +743,11 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
- fake_av[0] = resolve_refdup("HEAD",
- RESOLVE_REF_READING, &oid,
- NULL);
+ fake_av[0] = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING,
+ &oid,
+ NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -815,8 +820,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
snarf_refs(all_heads, all_remotes);
}
- head = resolve_refdup("HEAD", RESOLVE_REF_READING,
- &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ RESOLVE_REF_READING,
+ &head_oid, NULL);
if (with_current_branch && head) {
int has_head = 0;
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 1c15421..839a5c2 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -11,8 +11,8 @@
static const char * const show_ref_usage[] = {
N_("git show-ref [--head] [-d | --dereference]\n"
- " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
- " [--heads] [--] [<pattern>...]"),
+ " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]\n"
+ " [--] [<pattern>...]"),
N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
" [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
" [--] [<ref>...]"),
@@ -50,7 +50,7 @@ static void show_one(const struct show_one_options *opts,
if (!opts->deref_tags)
return;
- if (!peel_iterated_oid(oid, &peeled)) {
+ if (!peel_iterated_oid(the_repository, oid, &peeled)) {
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
printf("%s %s^{}\n", hex, refname);
}
@@ -129,7 +129,8 @@ static int cmd_show_ref__exclude_existing(const struct exclude_existing_options
char buf[1024];
int patternlen = opts->pattern ? strlen(opts->pattern) : 0;
- for_each_ref(add_existing, &existing_refs);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_existing,
+ &existing_refs);
while (fgets(buf, sizeof(buf), stdin)) {
char *ref;
int len = strlen(buf);
@@ -173,7 +174,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
struct object_id oid;
if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
- !read_ref(*refs, &oid)) {
+ !refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
show_one(show_one_opts, *refs, &oid);
}
else if (!show_one_opts->quiet)
@@ -188,7 +189,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
struct patterns_options {
int show_head;
- int heads_only;
+ int branches_only;
int tags_only;
};
@@ -205,14 +206,20 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,
show_ref_data.patterns = patterns;
if (opts->show_head)
- head_ref(show_ref, &show_ref_data);
- if (opts->heads_only || opts->tags_only) {
- if (opts->heads_only)
- for_each_fullref_in("refs/heads/", show_ref, &show_ref_data);
+ refs_head_ref(get_main_ref_store(the_repository), show_ref,
+ &show_ref_data);
+ if (opts->branches_only || opts->tags_only) {
+ if (opts->branches_only)
+ refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/heads/", NULL,
+ show_ref, &show_ref_data);
if (opts->tags_only)
- for_each_fullref_in("refs/tags/", show_ref, &show_ref_data);
+ refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/tags/", NULL, show_ref,
+ &show_ref_data);
} else {
- for_each_ref(show_ref, &show_ref_data);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ show_ref, &show_ref_data);
}
if (!show_ref_data.found_match)
return 1;
@@ -286,8 +293,10 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
struct show_one_options show_one_opts = {0};
int verify = 0, exists = 0;
const struct option show_ref_options[] = {
- OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")),
- OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")),
+ OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with branches)")),
+ OPT_BOOL(0, "branches", &patterns_opts.branches_only, N_("only show branches (can be combined with tags)")),
+ OPT_HIDDEN_BOOL(0, "heads", &patterns_opts.branches_only,
+ N_("deprecated synonym for --branches")),
OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")),
OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
"requires exact ref path")),
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 0f52e25..3f2bfce 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -96,10 +96,11 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix)
printf("\n");
}
- return 0;
+ string_list_clear(&sl, 0);
+ } else {
+ write_patterns_to_file(stdout, &pl);
}
- write_patterns_to_file(stdout, &pl);
clear_pattern_list(&pl);
return 0;
@@ -205,11 +206,13 @@ static int update_working_directory(struct pattern_list *pl)
struct unpack_trees_options o;
struct lock_file lock_file = LOCK_INIT;
struct repository *r = the_repository;
+ struct pattern_list *old_pl;
/* If no branch has been checked out, there are no updates to make. */
if (is_index_unborn(r->index))
return UPDATE_SPARSITY_SUCCESS;
+ old_pl = r->index->sparse_checkout_patterns;
r->index->sparse_checkout_patterns = pl;
memset(&o, 0, sizeof(o));
@@ -241,7 +244,12 @@ static int update_working_directory(struct pattern_list *pl)
clean_tracked_sparse_directories(r);
- r->index->sparse_checkout_patterns = NULL;
+ if (r->index->sparse_checkout_patterns != pl) {
+ clear_pattern_list(r->index->sparse_checkout_patterns);
+ FREE_AND_NULL(r->index->sparse_checkout_patterns);
+ }
+ r->index->sparse_checkout_patterns = old_pl;
+
return result;
}
@@ -311,6 +319,8 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
fprintf(fp, "%s/\n", pattern);
free(pattern);
}
+
+ string_list_clear(&sl, 0);
}
static int write_patterns_and_update(struct pattern_list *pl)
@@ -440,7 +450,6 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
char *sparse_filename;
int res;
struct object_id oid;
- struct strbuf pattern = STRBUF_INIT;
static struct option builtin_sparse_checkout_init_options[] = {
OPT_BOOL(0, "cone", &init_opts.cone_mode,
@@ -471,6 +480,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
+ clear_pattern_list(&pl);
return update_working_directory(NULL);
}
@@ -491,10 +501,10 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
return 0;
}
- strbuf_addstr(&pattern, "/*");
- add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
- strbuf_addstr(&pattern, "!/*/");
- add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
+ free(sparse_filename);
+
+ add_pattern("/*", empty_base, 0, &pl, 0);
+ add_pattern("!/*/", empty_base, 0, &pl, 0);
pl.use_cone_patterns = init_opts.cone_mode;
return write_patterns_and_update(&pl);
@@ -513,6 +523,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
char *slash = strrchr(e->pattern, '/');
char *oldpattern = e->pattern;
size_t newlen;
+ struct pattern_entry *dup;
if (!slash || slash == e->pattern)
break;
@@ -523,8 +534,14 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
e->pattern = xstrndup(oldpattern, newlen);
hashmap_entry_init(&e->ent, fspathhash(e->pattern));
- if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
+ dup = hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL);
+ if (!dup) {
hashmap_add(&pl->parent_hashmap, &e->ent);
+ } else {
+ free(e->pattern);
+ free(e);
+ e = dup;
+ }
}
}
@@ -581,15 +598,15 @@ static void add_patterns_from_input(struct pattern_list *pl,
strbuf_to_cone_pattern(&line, pl);
}
}
+ strbuf_release(&line);
} else {
if (file) {
struct strbuf line = STRBUF_INIT;
- while (!strbuf_getline(&line, file)) {
- size_t len;
- char *buf = strbuf_detach(&line, &len);
- add_pattern(buf, empty_base, 0, pl, 0);
- }
+ while (!strbuf_getline(&line, file))
+ add_pattern(line.buf, empty_base, 0, pl, 0);
+
+ strbuf_release(&line);
} else {
for (i = 0; i < argc; i++)
add_pattern(argv[i], empty_base, 0, pl, 0);
@@ -891,7 +908,6 @@ static int sparse_checkout_disable(int argc, const char **argv,
OPT_END(),
};
struct pattern_list pl;
- struct strbuf match_all = STRBUF_INIT;
/*
* We do not exit early if !core_apply_sparse_checkout; due to the
@@ -917,8 +933,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
pl.use_cone_patterns = 0;
core_apply_sparse_checkout = 1;
- strbuf_addstr(&match_all, "/*");
- add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
+ add_pattern("/*", empty_base, 0, &pl, 0);
prepare_repo_settings(the_repository);
the_repository->settings.sparse_index = 0;
diff --git a/builtin/stash.c b/builtin/stash.c
index 7751bca..7859bc0 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "config.h"
@@ -196,7 +195,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
commit = argv[0];
if (!commit) {
- if (!ref_exists(ref_stash)) {
+ if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) {
fprintf_ln(stderr, _("No stash entries found."));
return -1;
}
@@ -244,7 +243,8 @@ static int do_clear_stash(void)
if (repo_get_oid(the_repository, ref_stash, &obj))
return 0;
- return delete_ref(NULL, ref_stash, &obj, 0);
+ return refs_delete_ref(get_main_ref_store(the_repository), NULL,
+ ref_stash, &obj, 0);
}
static int clear_stash(int argc, const char **argv, const char *prefix)
@@ -273,7 +273,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
struct lock_file lock_file = LOCK_INIT;
repo_read_index_preload(the_repository, NULL, 0);
- if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL))
+ if (refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL))
return -1;
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -287,8 +287,8 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = the_repository->index;
+ opts.dst_index = the_repository->index;
opts.merge = 1;
opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
opts.update = update;
@@ -299,7 +299,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
if (unpack_trees(nr_trees, t, &opts))
return -1;
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
return error(_("unable to write new index file"));
return 0;
@@ -430,7 +430,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
state.force = 1;
state.quiet = 1;
state.refresh_cache = 1;
- state.istate = &the_index;
+ state.istate = the_repository->index;
/*
* Step 1: get a difference between orig_tree (which corresponding
@@ -454,7 +454,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
/* Look up the path's position in the current index. */
p = diff_queued_diff.queue[i];
- pos = index_name_pos(&the_index, p->two->path,
+ pos = index_name_pos(the_repository->index, p->two->path,
strlen(p->two->path));
/*
@@ -465,10 +465,10 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
* path, but left it out of the working tree, then clear the
* SKIP_WORKTREE bit and write it to the working tree.
*/
- if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) {
+ if (pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) {
struct stat st;
- ce = the_index.cache[pos];
+ ce = the_repository->index->cache[pos];
if (!lstat(ce->name, &st)) {
/* Conflicting path present; relocate it */
struct strbuf new_path = STRBUF_INIT;
@@ -504,12 +504,12 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
if (pos < 0)
option = ADD_CACHE_OK_TO_ADD;
- ce = make_cache_entry(&the_index,
+ ce = make_cache_entry(the_repository->index,
p->one->mode,
&p->one->oid,
p->one->path,
0, 0);
- add_index_entry(&the_index, ce, option);
+ add_index_entry(the_repository->index, ce, option);
}
}
diff_flush(&diff_opts);
@@ -518,7 +518,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
* Step 4: write the new index to disk
*/
repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
- if (write_locked_index(&the_index, &lock,
+ if (write_locked_index(the_repository->index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("could not write index"));
}
@@ -539,7 +539,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
NULL, NULL, NULL))
return error(_("could not write index"));
- if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+ if (write_index_as_tree(&c_tree, the_repository->index, get_index_file(), 0,
NULL))
return error(_("cannot apply a stash in the middle of a merge"));
@@ -562,14 +562,14 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
return error(_("conflicts in index. "
"Try without --index."));
- discard_index(&the_index);
+ discard_index(the_repository->index);
repo_read_index(the_repository);
- if (write_index_as_tree(&index_tree, &the_index,
+ if (write_index_as_tree(&index_tree, the_repository->index,
get_index_file(), 0, NULL))
return error(_("could not save index tree"));
reset_head();
- discard_index(&the_index);
+ discard_index(the_repository->index);
repo_read_index(the_repository);
}
}
@@ -687,7 +687,8 @@ static int reject_reflog_ent(struct object_id *ooid UNUSED,
static int reflog_is_empty(const char *refname)
{
- return !for_each_reflog_ent(refname, reject_reflog_ent, NULL);
+ return !refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname, reject_reflog_ent, NULL);
}
static int do_drop_stash(struct stash_info *info, int quiet)
@@ -824,7 +825,7 @@ static int list_stash(int argc, const char **argv, const char *prefix)
git_stash_list_usage,
PARSE_OPT_KEEP_UNKNOWN_OPT);
- if (!ref_exists(ref_stash))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash))
return 0;
cp.git_cmd = 1;
@@ -875,8 +876,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
}
unpack_tree_opt.head_idx = -1;
- unpack_tree_opt.src_index = &the_index;
- unpack_tree_opt.dst_index = &the_index;
+ unpack_tree_opt.src_index = the_repository->index;
+ unpack_tree_opt.dst_index = the_repository->index;
unpack_tree_opt.merge = 1;
unpack_tree_opt.fn = stash_worktree_untracked_merge;
@@ -998,10 +999,10 @@ static int do_store_stash(const struct object_id *w_commit, const char *stash_ms
if (!stash_msg)
stash_msg = "Created via \"git stash store\".";
- if (update_ref(stash_msg, ref_stash, w_commit, NULL,
- REF_FORCE_CREATE_REFLOG,
- quiet ? UPDATE_REFS_QUIET_ON_ERR :
- UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), stash_msg, ref_stash, w_commit, NULL,
+ REF_FORCE_CREATE_REFLOG,
+ quiet ? UPDATE_REFS_QUIET_ON_ERR :
+ UPDATE_REFS_MSG_ON_ERR)) {
if (!quiet) {
fprintf_ln(stderr, _("Cannot update %s with %s"),
ref_stash, oid_to_hex(w_commit));
@@ -1384,7 +1385,8 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
goto done;
}
- branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+ branch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &flags);
if (flags & REF_ISSYMREF)
skip_prefix(branch_ref, "refs/heads/", &branch_name);
head_short_sha1 = repo_find_unique_abbrev(the_repository,
@@ -1395,7 +1397,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
commit_list_insert(head_commit, &parents);
- if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+ if (write_index_as_tree(&info->i_tree, the_repository->index, get_index_file(), 0,
NULL) ||
commit_tree(commit_tree_label.buf, commit_tree_label.len,
&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
@@ -1540,9 +1542,9 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
char *ps_matched = xcalloc(ps->nr, 1);
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
- for (i = 0; i < the_index.cache_nr; i++)
- ce_path_match(&the_index, the_index.cache[i], ps,
+ ensure_full_index(the_repository->index);
+ for (i = 0; i < the_repository->index->cache_nr; i++)
+ ce_path_match(the_repository->index, the_repository->index->cache[i], ps,
ps_matched);
if (report_path_error(ps_matched, ps)) {
@@ -1566,7 +1568,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
goto done;
}
- if (!reflog_exists(ref_stash) && do_clear_stash()) {
+ if (!refs_reflog_exists(get_main_ref_store(the_repository), ref_stash) && do_clear_stash()) {
ret = -1;
if (!quiet)
fprintf_ln(stderr, _("Cannot initialize stash"));
@@ -1612,7 +1614,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
goto done;
}
}
- discard_index(&the_index);
+ discard_index(the_repository->index);
if (ps->nr) {
struct child_process cp_add = CHILD_PROCESS_INIT;
struct child_process cp_diff = CHILD_PROCESS_INIT;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5a71b1e..880ab44 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
#include "environment.h"
@@ -207,18 +206,18 @@ static int module_list_compute(const char **argv,
if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
- for (i = 0; i < the_index.cache_nr; i++) {
- const struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ const struct cache_entry *ce = the_repository->index->cache[i];
- if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
+ if (!match_pathspec(the_repository->index, pathspec, ce->name, ce_namelen(ce),
0, ps_matched, 1) ||
!S_ISGITLINK(ce->ce_mode))
continue;
ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
list->entries[list->nr++] = ce;
- while (i + 1 < the_index.cache_nr &&
- !strcmp(ce->name, the_index.cache[i + 1]->name))
+ while (i + 1 < the_repository->index->cache_nr &&
+ !strcmp(ce->name, the_repository->index->cache[i + 1]->name))
/*
* Skip entries with the same name in different stages
* to make sure an entry is returned only once.
@@ -257,11 +256,9 @@ static void module_list_active(struct module_list *list)
static char *get_up_path(const char *path)
{
- int i;
struct strbuf sb = STRBUF_INIT;
- for (i = count_slashes(path); i; i--)
- strbuf_addstr(&sb, "../");
+ strbuf_addstrings(&sb, "../", count_slashes(path));
/*
* Check if 'path' ends with slash or not
@@ -680,7 +677,8 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
displaypath);
} else if (!(flags & OPT_CACHED)) {
struct object_id oid;
- struct ref_store *refs = get_submodule_ref_store(path);
+ struct ref_store *refs = repo_get_submodule_ref_store(the_repository,
+ path);
if (!refs) {
print_status(flags, '-', path, ce_oid, displaypath);
@@ -904,7 +902,8 @@ static void generate_submodule_summary(struct summary_cb *info,
if (!info->cached && oideq(&p->oid_dst, null_oid())) {
if (S_ISGITLINK(p->mod_dst)) {
- struct ref_store *refs = get_submodule_ref_store(p->sm_path);
+ struct ref_store *refs = repo_get_submodule_ref_store(the_repository,
+ p->sm_path);
if (refs)
refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
@@ -913,7 +912,7 @@ static void generate_submodule_summary(struct summary_cb *info,
int fd = open(p->sm_path, O_RDONLY);
if (fd < 0 || fstat(fd, &st) < 0 ||
- index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
+ index_fd(the_repository->index, &p->oid_dst, fd, &st, OBJ_BLOB,
p->sm_path, 0))
error(_("couldn't hash object from '%s'"), p->sm_path);
} else {
@@ -2457,7 +2456,9 @@ static int remote_submodule_branch(const char *path, const char **branch)
}
if (!strcmp(*branch, ".")) {
- const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+ const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL,
+ NULL);
if (!refname)
return die_message(_("No such ref: %s"), "HEAD");
@@ -2597,7 +2598,8 @@ static int update_submodule(struct update_data *update_data)
if (update_data->just_cloned)
oidcpy(&update_data->suboid, null_oid());
- else if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid))
+ else if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path,
+ "HEAD", &update_data->suboid))
return die_message(_("Unable to find current revision in submodule path '%s'"),
update_data->displaypath);
@@ -2624,7 +2626,8 @@ static int update_submodule(struct update_data *update_data)
update_data->sm_path);
}
- if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid))
+ if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path,
+ remote_ref, &update_data->oid))
return die_message(_("Unable to find %s revision in submodule path '%s'"),
remote_ref, update_data->sm_path);
@@ -2875,7 +2878,8 @@ static int push_check(int argc, const char **argv, const char *prefix UNUSED)
argv++;
argc--;
/* Get the submodule's head ref and determine if it is detached */
- head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ 0, &head_oid, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD"))
@@ -3322,21 +3326,21 @@ static void die_on_index_match(const char *path, int force)
char *ps_matched = xcalloc(ps.nr, 1);
/* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
+ ensure_full_index(the_repository->index);
/*
* Since there is only one pathspec, we just need to
* check ps_matched[0] to know if a cache entry matched.
*/
- for (i = 0; i < the_index.cache_nr; i++) {
- ce_path_match(&the_index, the_index.cache[i], &ps,
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ ce_path_match(the_repository->index, the_repository->index->cache[i], &ps,
ps_matched);
if (ps_matched[0]) {
if (!force)
die(_("'%s' already exists in the index"),
path);
- if (!S_ISGITLINK(the_index.cache[i]->ce_mode))
+ if (!S_ISGITLINK(the_repository->index->cache[i]->ce_mode))
die(_("'%s' already exists in the index "
"and is not a submodule"), path);
break;
@@ -3353,7 +3357,7 @@ static void die_on_repo_without_commits(const char *path)
strbuf_addstr(&sb, path);
if (is_nonbare_repository_dir(&sb)) {
struct object_id oid;
- if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid) < 0)
die(_("'%s' does not have a commit checked out"), path);
}
strbuf_release(&sb);
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index c9defe4..81abdd1 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -18,7 +18,8 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i
const char *refname;
resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE);
- refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag);
+ refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ HEAD, resolve_flags, NULL, &flag);
if (!refname)
die("No such ref: %s", HEAD);
@@ -31,7 +32,9 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i
if (print) {
char *to_free = NULL;
if (shorten)
- refname = to_free = shorten_unambiguous_ref(refname, 0);
+ refname = to_free = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname,
+ 0);
puts(refname);
free(to_free);
}
@@ -66,7 +69,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
die("Cannot delete %s, not a symbolic ref", argv[0]);
if (!strcmp(argv[0], "HEAD"))
die("deleting '%s' is not allowed", argv[0]);
- return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF);
+ return refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, argv[0], NULL, REF_NO_DEREF);
}
switch (argc) {
@@ -79,7 +83,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
die("Refusing to point HEAD outside of refs/");
if (check_refname_format(argv[1], REFNAME_ALLOW_ONELEVEL) < 0)
die("Refusing to set '%s' to invalid ref '%s'", argv[0], argv[1]);
- ret = !!create_symref(argv[0], argv[1], msg);
+ ret = !!refs_update_symref(get_main_ref_store(the_repository),
+ argv[0], argv[1], msg);
break;
default:
usage_with_options(git_symbolic_ref_usage, options);
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb5..a1fb218 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -28,9 +28,11 @@
#include "date.h"
#include "write-or-die.h"
#include "object-file-convert.h"
+#include "trailer.h"
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+ " [(--trailer <token>[(=|:)<value>])...]\n"
" <tagname> [<commit> | <object>]"),
N_("git tag -d <tagname>..."),
N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
@@ -87,7 +89,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
for (p = argv; *p; p++) {
strbuf_reset(&ref);
strbuf_addf(&ref, "refs/tags/%s", *p);
- if (read_ref(ref.buf, &oid)) {
+ if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
error(_("tag '%s' not found."), *p);
had_error = 1;
continue;
@@ -116,13 +118,13 @@ static int delete_tags(const char **argv)
struct string_list_item *item;
result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete);
- if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+ if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
result = 1;
for_each_string_list_item(item, &refs_to_delete) {
const char *name = item->string;
struct object_id *oid = item->util;
- if (!ref_exists(name))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), name))
printf(_("Deleted tag '%s' (was %s)\n"),
item->string + 10,
repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
@@ -290,10 +292,12 @@ static const char message_advice_nested_tag[] =
static void create_tag(const struct object_id *object, const char *object_ref,
const char *tag,
struct strbuf *buf, struct create_tag_options *opt,
- struct object_id *prev, struct object_id *result, char *path)
+ struct object_id *prev, struct object_id *result,
+ struct strvec *trailer_args, char *path)
{
enum object_type type;
struct strbuf header = STRBUF_INIT;
+ int should_edit;
type = oid_object_info(the_repository, object, NULL);
if (type <= OBJ_NONE)
@@ -313,13 +317,15 @@ static void create_tag(const struct object_id *object, const char *object_ref,
tag,
git_committer_info(IDENT_STRICT));
- if (!opt->message_given || opt->use_editor) {
+ should_edit = opt->use_editor || !opt->message_given;
+ if (should_edit || trailer_args->nr) {
int fd;
/* write the template message before editing: */
fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (opt->message_given) {
+ if (opt->message_given && buf->len) {
+ strbuf_complete(buf, '\n');
write_or_die(fd, buf->buf, buf->len);
strbuf_reset(buf);
} else if (!is_null_oid(prev)) {
@@ -338,10 +344,19 @@ static void create_tag(const struct object_id *object, const char *object_ref,
}
close(fd);
- if (launch_editor(path, buf, NULL)) {
- fprintf(stderr,
- _("Please supply the message using either -m or -F option.\n"));
- exit(1);
+ if (trailer_args->nr && amend_file_with_trailers(path, trailer_args))
+ die(_("unable to pass trailers to --trailers"));
+
+ if (should_edit) {
+ if (launch_editor(path, buf, NULL)) {
+ fprintf(stderr,
+ _("Please supply the message using either -m or -F option.\n"));
+ exit(1);
+ }
+ } else if (trailer_args->nr) {
+ strbuf_reset(buf);
+ if (strbuf_read_file(buf, path, 0) < 0)
+ die_errno(_("failed to read '%s'"), path);
}
}
@@ -463,6 +478,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
+ struct strvec trailer_args = STRVEC_INIT;
int icase = 0;
int edit_flag = 0;
struct option options[] = {
@@ -479,6 +495,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_CALLBACK_F('m', "message", &msg, N_("message"),
N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+ OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"),
+ N_("add custom trailer(s)"), PARSE_OPT_NONEG),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
OPT_CLEANUP(&cleanup_arg),
@@ -548,7 +566,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
opt.sign = 1;
set_signing_key(keyid);
}
- create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+ create_tag_object = (opt.sign || annotate || msg.given || msgfile ||
+ edit_flag || trailer_args.nr);
if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
@@ -630,8 +649,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (strbuf_check_tag_ref(&ref, tag))
die(_("'%s' is not a valid tag name."), tag);
- if (read_ref(ref.buf, &prev))
- oidclr(&prev);
+ if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &prev))
+ oidclr(&prev, the_repository->hash_algo);
else if (!force)
die(_("tag '%s' already exists"), tag);
@@ -654,12 +673,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
opt.sign = 1;
path = git_pathdup("TAG_EDITMSG");
create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
- path);
+ &trailer_args, path);
}
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, &object, &prev,
+ NULL, NULL,
create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
reflog_msg.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
@@ -686,6 +707,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
strbuf_release(&reflog_msg);
strbuf_release(&msg.buf);
strbuf_release(&err);
+ strvec_clear(&trailer_args);
free(msgfile);
return ret;
}
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index f1c85a0..08fa2a7 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -439,7 +439,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
struct object_id base_oid;
if (type == OBJ_REF_DELTA) {
- oidread(&base_oid, fill(the_hash_algo->rawsz));
+ oidread(&base_oid, fill(the_hash_algo->rawsz), the_repository->hash_algo);
use(the_hash_algo->rawsz);
delta_data = get_data(delta_size);
if (!delta_data)
@@ -451,7 +451,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
return; /* we are done */
else {
/* cannot resolve yet --- queue it */
- oidclr(&obj_list[nr].oid);
+ oidclr(&obj_list[nr].oid, the_repository->hash_algo);
add_delta_to_list(nr, &base_oid, 0, delta_data, delta_size);
return;
}
@@ -500,7 +500,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
* The delta base object is itself a delta that
* has not been resolved yet.
*/
- oidclr(&obj_list[nr].oid);
+ oidclr(&obj_list[nr].oid, the_repository->hash_algo);
add_delta_to_list(nr, null_oid(), base_offset,
delta_data, delta_size);
return;
@@ -674,7 +674,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED)
if (fsck_finish(&fsck_options))
die(_("fsck error in pack objects"));
}
- if (!hasheq(fill(the_hash_algo->rawsz), oid.hash))
+ if (!hasheq(fill(the_hash_algo->rawsz), oid.hash,
+ the_repository->hash_algo))
die("final sha1 did not match");
use(the_hash_algo->rawsz);
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 7bcaa14..d343416 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "bulk-checkin.h"
#include "config.h"
@@ -247,16 +247,16 @@ static int test_if_untracked_cache_is_supported(void)
static int mark_ce_flags(const char *path, int flag, int mark)
{
int namelen = strlen(path);
- int pos = index_name_pos(&the_index, path, namelen);
+ int pos = index_name_pos(the_repository->index, path, namelen);
if (0 <= pos) {
- mark_fsmonitor_invalid(&the_index, the_index.cache[pos]);
+ mark_fsmonitor_invalid(the_repository->index, the_repository->index->cache[pos]);
if (mark)
- the_index.cache[pos]->ce_flags |= flag;
+ the_repository->index->cache[pos]->ce_flags |= flag;
else
- the_index.cache[pos]->ce_flags &= ~flag;
- the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
- cache_tree_invalidate_path(&the_index, path);
- the_index.cache_changed |= CE_ENTRY_CHANGED;
+ the_repository->index->cache[pos]->ce_flags &= ~flag;
+ the_repository->index->cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+ cache_tree_invalidate_path(the_repository->index, path);
+ the_repository->index->cache_changed |= CE_ENTRY_CHANGED;
return 0;
}
return -1;
@@ -266,7 +266,7 @@ static int remove_one_path(const char *path)
{
if (!allow_remove)
return error("%s: does not exist and --remove not passed", path);
- if (remove_file_from_index(&the_index, path))
+ if (remove_file_from_index(the_repository->index, path))
return error("%s: cannot remove from the index", path);
return 0;
}
@@ -291,24 +291,24 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
struct cache_entry *ce;
/* Was the old index entry already up-to-date? */
- if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0))
+ if (old && !ce_stage(old) && !ie_match_stat(the_repository->index, old, st, 0))
return 0;
- ce = make_empty_cache_entry(&the_index, len);
+ ce = make_empty_cache_entry(the_repository->index, len);
memcpy(ce->name, path, len);
ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = len;
- fill_stat_cache_info(&the_index, ce, st);
+ fill_stat_cache_info(the_repository->index, ce, st);
ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
- if (index_path(&the_index, &ce->oid, path, st,
+ if (index_path(the_repository->index, &ce->oid, path, st,
info_only ? 0 : HASH_WRITE_OBJECT)) {
discard_cache_entry(ce);
return -1;
}
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
- if (add_index_entry(&the_index, ce, option)) {
+ if (add_index_entry(the_repository->index, ce, option)) {
discard_cache_entry(ce);
return error("%s: cannot add to the index - missing --add option?", path);
}
@@ -341,15 +341,16 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
static int process_directory(const char *path, int len, struct stat *st)
{
struct object_id oid;
- int pos = index_name_pos(&the_index, path, len);
+ int pos = index_name_pos(the_repository->index, path, len);
/* Exact match: file or existing gitlink */
if (pos >= 0) {
- const struct cache_entry *ce = the_index.cache[pos];
+ const struct cache_entry *ce = the_repository->index->cache[pos];
if (S_ISGITLINK(ce->ce_mode)) {
/* Do nothing to the index if there is no HEAD! */
- if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, path,
+ "HEAD", &oid) < 0)
return 0;
return add_one_path(ce, path, len, st);
@@ -360,8 +361,8 @@ static int process_directory(const char *path, int len, struct stat *st)
/* Inexact match: is there perhaps a subdirectory match? */
pos = -pos-1;
- while (pos < the_index.cache_nr) {
- const struct cache_entry *ce = the_index.cache[pos++];
+ while (pos < the_repository->index->cache_nr) {
+ const struct cache_entry *ce = the_repository->index->cache[pos++];
if (strncmp(ce->name, path, len))
break;
@@ -375,7 +376,7 @@ static int process_directory(const char *path, int len, struct stat *st)
}
/* No match - should we add it as a gitlink? */
- if (!resolve_gitlink_ref(path, "HEAD", &oid))
+ if (!repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid))
return add_one_path(NULL, path, len, st);
/* Error out. */
@@ -391,8 +392,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
if (has_symlink_leading_path(path, len))
return error("'%s' is beyond a symbolic link", path);
- pos = index_name_pos(&the_index, path, len);
- ce = pos < 0 ? NULL : the_index.cache[pos];
+ pos = index_name_pos(the_repository->index, path, len);
+ ce = pos < 0 ? NULL : the_repository->index->cache[pos];
if (ce && ce_skip_worktree(ce)) {
/*
* working directory version is assumed "good"
@@ -400,7 +401,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
* On the other hand, removing it from index should work
*/
if (!ignore_skip_worktree_entries && allow_remove &&
- remove_file_from_index(&the_index, path))
+ remove_file_from_index(the_repository->index, path))
return error("%s: cannot remove from the index", path);
return 0;
}
@@ -428,7 +429,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
return error("Invalid path '%s'", path);
len = strlen(path);
- ce = make_empty_cache_entry(&the_index, len);
+ ce = make_empty_cache_entry(the_repository->index, len);
oidcpy(&ce->oid, oid);
memcpy(ce->name, path, len);
@@ -439,7 +440,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
ce->ce_flags |= CE_VALID;
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
- if (add_index_entry(&the_index, ce, option))
+ if (add_index_entry(the_repository->index, ce, option))
return error("%s: cannot add to the index - missing --add option?",
path);
report("add '%s'", path);
@@ -451,11 +452,11 @@ static void chmod_path(char flip, const char *path)
int pos;
struct cache_entry *ce;
- pos = index_name_pos(&the_index, path, strlen(path));
+ pos = index_name_pos(the_repository->index, path, strlen(path));
if (pos < 0)
goto fail;
- ce = the_index.cache[pos];
- if (chmod_index_entry(&the_index, ce, flip) < 0)
+ ce = the_repository->index->cache[pos];
+ if (chmod_index_entry(the_repository->index, ce, flip) < 0)
goto fail;
report("chmod %cx '%s'", flip, path);
@@ -498,7 +499,7 @@ static void update_one(const char *path)
}
if (force_remove) {
- if (remove_file_from_index(&the_index, path))
+ if (remove_file_from_index(the_repository->index, path))
die("git update-index: unable to remove %s", path);
report("remove '%s'", path);
return;
@@ -581,7 +582,7 @@ static void read_index_info(int nul_term_line)
if (!mode) {
/* mode == 0 means there is no such path -- remove */
- if (remove_file_from_index(&the_index, path_name))
+ if (remove_file_from_index(the_repository->index, path_name))
die("git update-index: unable to remove %s",
ptr);
}
@@ -622,12 +623,12 @@ static struct cache_entry *read_one_ent(const char *which,
error("%s: not in %s branch.", path, which);
return NULL;
}
- if (!the_index.sparse_index && mode == S_IFDIR) {
+ if (!the_repository->index->sparse_index && mode == S_IFDIR) {
if (which)
error("%s: not a blob in %s branch.", path, which);
return NULL;
}
- ce = make_empty_cache_entry(&the_index, namelen);
+ ce = make_empty_cache_entry(the_repository->index, namelen);
oidcpy(&ce->oid, &oid);
memcpy(ce->name, path, namelen);
@@ -642,12 +643,12 @@ static int unresolve_one(const char *path)
struct string_list_item *item;
int res = 0;
- if (!the_index.resolve_undo)
+ if (!the_repository->index->resolve_undo)
return res;
- item = string_list_lookup(the_index.resolve_undo, path);
+ item = string_list_lookup(the_repository->index->resolve_undo, path);
if (!item)
return res; /* no resolve-undo record for the path */
- res = unmerge_index_entry(&the_index, path, item->util, 0);
+ res = unmerge_index_entry(the_repository->index, path, item->util, 0);
FREE_AND_NULL(item->util);
return res;
}
@@ -682,19 +683,19 @@ static int do_reupdate(const char **paths,
PATHSPEC_PREFER_CWD,
prefix, paths);
- if (read_ref("HEAD", &head_oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &head_oid))
/* If there is no HEAD, that means it is an initial
* commit. Update everything in the index.
*/
has_head = 0;
redo:
- for (pos = 0; pos < the_index.cache_nr; pos++) {
- const struct cache_entry *ce = the_index.cache[pos];
+ for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+ const struct cache_entry *ce = the_repository->index->cache[pos];
struct cache_entry *old = NULL;
int save_nr;
char *path;
- if (ce_stage(ce) || !ce_path_match(&the_index, ce, &pathspec, NULL))
+ if (ce_stage(ce) || !ce_path_match(the_repository->index, ce, &pathspec, NULL))
continue;
if (has_head)
old = read_one_ent(NULL, &head_oid,
@@ -710,7 +711,7 @@ static int do_reupdate(const char **paths,
* to process each path individually
*/
if (S_ISSPARSEDIR(ce->ce_mode)) {
- ensure_full_index(&the_index);
+ ensure_full_index(the_repository->index);
goto redo;
}
@@ -718,12 +719,12 @@ static int do_reupdate(const char **paths,
* path anymore, in which case, under 'allow_remove',
* or worse yet 'allow_replace', active_nr may decrease.
*/
- save_nr = the_index.cache_nr;
+ save_nr = the_repository->index->cache_nr;
path = xstrdup(ce->name);
update_one(path);
free(path);
discard_cache_entry(old);
- if (save_nr != the_index.cache_nr)
+ if (save_nr != the_repository->index->cache_nr)
goto redo;
}
clear_pathspec(&pathspec);
@@ -739,9 +740,9 @@ static int refresh(struct refresh_params *o, unsigned int flag)
{
setup_work_tree();
repo_read_index(the_repository);
- *o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL,
+ *o->has_errors |= refresh_index(the_repository->index, o->flags | flag, NULL,
NULL, NULL);
- if (has_racy_timestamp(&the_index)) {
+ if (has_racy_timestamp(the_repository->index)) {
/*
* Even if nothing else has changed, updating the file
* increases the chance that racy timestamps become
@@ -750,7 +751,7 @@ static int refresh(struct refresh_params *o, unsigned int flag)
* refresh_index() as these are no actual errors.
* cmd_status() does the same.
*/
- the_index.cache_changed |= SOMETHING_CHANGED;
+ the_repository->index->cache_changed |= SOMETHING_CHANGED;
}
return 0;
}
@@ -787,7 +788,7 @@ static int resolve_undo_clear_callback(const struct option *opt UNUSED,
{
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
- resolve_undo_clear_index(&the_index);
+ resolve_undo_clear_index(the_repository->index);
return 0;
}
@@ -888,7 +889,7 @@ static enum parse_opt_result unresolve_callback(
*has_errors = do_unresolve(ctx->argc, ctx->argv,
prefix, prefix ? strlen(prefix) : 0);
if (*has_errors)
- the_index.cache_changed = 0;
+ the_repository->index->cache_changed = 0;
ctx->argv += ctx->argc - 1;
ctx->argc = 1;
@@ -909,7 +910,7 @@ static enum parse_opt_result reupdate_callback(
setup_work_tree();
*has_errors = do_reupdate(ctx->argv + 1, prefix);
if (*has_errors)
- the_index.cache_changed = 0;
+ the_repository->index->cache_changed = 0;
ctx->argv += ctx->argc - 1;
ctx->argc = 1;
@@ -1056,7 +1057,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
if (entries < 0)
die("cache corrupted");
- the_index.updated_skipworktree = 1;
+ the_repository->index->updated_skipworktree = 1;
/*
* Custom copy of parse_options() because we want to handle
@@ -1111,18 +1112,18 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
if (preferred_index_format) {
if (preferred_index_format < 0) {
- printf(_("%d\n"), the_index.version);
+ printf(_("%d\n"), the_repository->index->version);
} else if (preferred_index_format < INDEX_FORMAT_LB ||
INDEX_FORMAT_UB < preferred_index_format) {
die("index-version %d not in range: %d..%d",
preferred_index_format,
INDEX_FORMAT_LB, INDEX_FORMAT_UB);
} else {
- if (the_index.version != preferred_index_format)
- the_index.cache_changed |= SOMETHING_CHANGED;
+ if (the_repository->index->version != preferred_index_format)
+ the_repository->index->cache_changed |= SOMETHING_CHANGED;
report(_("index-version: was %d, set to %d"),
- the_index.version, preferred_index_format);
- the_index.version = preferred_index_format;
+ the_repository->index->version, preferred_index_format);
+ the_repository->index->version = preferred_index_format;
}
}
@@ -1159,16 +1160,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
warning(_("core.splitIndex is set to false; "
"remove or change it, if you really want to "
"enable split index"));
- if (the_index.split_index)
- the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+ if (the_repository->index->split_index)
+ the_repository->index->cache_changed |= SPLIT_INDEX_ORDERED;
else
- add_split_index(&the_index);
+ add_split_index(the_repository->index);
} else if (!split_index) {
if (git_config_get_split_index() == 1)
warning(_("core.splitIndex is set to true; "
"remove or change it, if you really want to "
"disable split index"));
- remove_split_index(&the_index);
+ remove_split_index(the_repository->index);
}
prepare_repo_settings(r);
@@ -1180,7 +1181,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
warning(_("core.untrackedCache is set to true; "
"remove or change it, if you really want to "
"disable the untracked cache"));
- remove_untracked_cache(&the_index);
+ remove_untracked_cache(the_repository->index);
report(_("Untracked cache disabled"));
break;
case UC_TEST:
@@ -1192,7 +1193,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
warning(_("core.untrackedCache is set to false; "
"remove or change it, if you really want to "
"enable the untracked cache"));
- add_untracked_cache(&the_index);
+ add_untracked_cache(the_repository->index);
report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
break;
default:
@@ -1222,7 +1223,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
"set it if you really want to "
"enable fsmonitor"));
}
- add_fsmonitor(&the_index);
+ add_fsmonitor(the_repository->index);
report(_("fsmonitor enabled"));
} else if (!fsmonitor) {
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
@@ -1230,17 +1231,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
warning(_("core.fsmonitor is set; "
"remove it if you really want to "
"disable fsmonitor"));
- remove_fsmonitor(&the_index);
+ remove_fsmonitor(the_repository->index);
report(_("fsmonitor disabled"));
}
- if (the_index.cache_changed || force_write) {
+ if (the_repository->index->cache_changed || force_write) {
if (newfd < 0) {
if (refresh_args.flags & REFRESH_QUIET)
exit(128);
unable_to_lock_die(get_index_file(), lock_error);
}
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die("Unable to write new index file");
}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc..6a6a2ff 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -77,6 +77,65 @@ static char *parse_refname(const char **next)
}
/*
+ * Wrapper around parse_refname which skips the next delimiter.
+ */
+static char *parse_next_refname(const char **next)
+{
+ if (line_termination) {
+ /* Without -z, consume SP and use next argument */
+ if (!**next || **next == line_termination)
+ return NULL;
+ if (**next != ' ')
+ die("expected SP but got: %s", *next);
+ } else {
+ /* With -z, read the next NUL-terminated line */
+ if (**next)
+ return NULL;
+ }
+ /* Skip the delimiter */
+ (*next)++;
+
+ return parse_refname(next);
+}
+
+/*
+ * Wrapper around parse_arg which skips the next delimiter.
+ */
+static char *parse_next_arg(const char **next)
+{
+ struct strbuf arg = STRBUF_INIT;
+
+ if (line_termination) {
+ /* Without -z, consume SP and use next argument */
+ if (!**next || **next == line_termination)
+ return NULL;
+ if (**next != ' ')
+ die("expected SP but got: %s", *next);
+ } else {
+ /* With -z, read the next NUL-terminated line */
+ if (**next)
+ return NULL;
+ }
+ /* Skip the delimiter */
+ (*next)++;
+
+ if (line_termination) {
+ /* Without -z, use the next argument */
+ *next = parse_arg(*next, &arg);
+ } else {
+ /* With -z, use everything up to the next NUL */
+ strbuf_addstr(&arg, *next);
+ *next += arg.len;
+ }
+
+ if (arg.len)
+ return strbuf_detach(&arg, NULL);
+
+ strbuf_release(&arg);
+ return NULL;
+}
+
+/*
* The value being parsed is <old-oid> (as opposed to <new-oid>; the
* difference affects which error messages are generated):
*/
@@ -122,7 +181,7 @@ static int parse_next_oid(const char **next, const char *end,
goto invalid;
} else {
/* Without -z, an empty value means all zeros: */
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
}
} else {
/* With -z, read the next NUL-terminated line */
@@ -142,7 +201,7 @@ static int parse_next_oid(const char **next, const char *end,
/* With -z, treat an empty value as all zeros: */
warning("%s %s: missing <new-oid>, treating as zero",
command, refname);
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
} else {
/*
* With -z, an empty non-required value means
@@ -204,6 +263,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
if (ref_transaction_update(transaction, refname,
&new_oid, have_old ? &old_oid : NULL,
+ NULL, NULL,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@@ -213,6 +273,61 @@ static void parse_cmd_update(struct ref_transaction *transaction,
strbuf_release(&err);
}
+static void parse_cmd_symref_update(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ char *refname, *new_target, *old_arg;
+ char *old_target = NULL;
+ struct strbuf err = STRBUF_INIT;
+ struct object_id old_oid;
+ int have_old_oid = 0;
+
+ refname = parse_refname(&next);
+ if (!refname)
+ die("symref-update: missing <ref>");
+
+ new_target = parse_next_refname(&next);
+ if (!new_target)
+ die("symref-update %s: missing <new-target>", refname);
+
+ old_arg = parse_next_arg(&next);
+ if (old_arg) {
+ old_target = parse_next_arg(&next);
+ if (!old_target)
+ die("symref-update %s: expected old value", refname);
+
+ if (!strcmp(old_arg, "oid")) {
+ if (repo_get_oid(the_repository, old_target, &old_oid))
+ die("symref-update %s: invalid oid: %s", refname, old_target);
+
+ have_old_oid = 1;
+ } else if (!strcmp(old_arg, "ref")) {
+ if (check_refname_format(old_target, REFNAME_ALLOW_ONELEVEL))
+ die("symref-update %s: invalid ref: %s", refname, old_target);
+ } else {
+ die("symref-update %s: invalid arg '%s' for old value", refname, old_arg);
+ }
+ }
+
+ if (*next != line_termination)
+ die("symref-update %s: extra input: %s", refname, next);
+
+ if (ref_transaction_update(transaction, refname, NULL,
+ have_old_oid ? &old_oid : NULL,
+ new_target,
+ have_old_oid ? NULL : old_target,
+ update_flags | create_reflog_flag,
+ msg, &err))
+ die("%s", err.buf);
+
+ update_flags = default_flags;
+ free(refname);
+ free(old_arg);
+ free(old_target);
+ free(new_target);
+ strbuf_release(&err);
+}
+
static void parse_cmd_create(struct ref_transaction *transaction,
const char *next, const char *end)
{
@@ -233,7 +348,7 @@ static void parse_cmd_create(struct ref_transaction *transaction,
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
- if (ref_transaction_create(transaction, refname, &new_oid,
+ if (ref_transaction_create(transaction, refname, &new_oid, NULL,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@@ -243,6 +358,35 @@ static void parse_cmd_create(struct ref_transaction *transaction,
strbuf_release(&err);
}
+
+static void parse_cmd_symref_create(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf err = STRBUF_INIT;
+ char *refname, *new_target;
+
+ refname = parse_refname(&next);
+ if (!refname)
+ die("symref-create: missing <ref>");
+
+ new_target = parse_next_refname(&next);
+ if (!new_target)
+ die("symref-create %s: missing <new-target>", refname);
+
+ if (*next != line_termination)
+ die("symref-create %s: extra input: %s", refname, next);
+
+ if (ref_transaction_create(transaction, refname, NULL, new_target,
+ update_flags | create_reflog_flag,
+ msg, &err))
+ die("%s", err.buf);
+
+ update_flags = default_flags;
+ free(refname);
+ free(new_target);
+ strbuf_release(&err);
+}
+
static void parse_cmd_delete(struct ref_transaction *transaction,
const char *next, const char *end)
{
@@ -269,7 +413,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
if (ref_transaction_delete(transaction, refname,
have_old ? &old_oid : NULL,
- update_flags, msg, &err))
+ NULL, update_flags, msg, &err))
die("%s", err.buf);
update_flags = default_flags;
@@ -277,6 +421,36 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
strbuf_release(&err);
}
+
+static void parse_cmd_symref_delete(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf err = STRBUF_INIT;
+ char *refname, *old_target;
+
+ if (!(update_flags & REF_NO_DEREF))
+ die("symref-delete: cannot operate with deref mode");
+
+ refname = parse_refname(&next);
+ if (!refname)
+ die("symref-delete: missing <ref>");
+
+ old_target = parse_next_refname(&next);
+
+ if (*next != line_termination)
+ die("symref-delete %s: extra input: %s", refname, next);
+
+ if (ref_transaction_delete(transaction, refname, NULL,
+ old_target, update_flags, msg, &err))
+ die("%s", err.buf);
+
+ update_flags = default_flags;
+ free(refname);
+ free(old_target);
+ strbuf_release(&err);
+}
+
+
static void parse_cmd_verify(struct ref_transaction *transaction,
const char *next, const char *end)
{
@@ -290,13 +464,13 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
if (parse_next_oid(&next, end, &old_oid, "verify", refname,
PARSE_SHA1_OLD))
- oidclr(&old_oid);
+ oidclr(&old_oid, the_repository->hash_algo);
if (*next != line_termination)
die("verify %s: extra input: %s", refname, next);
if (ref_transaction_verify(transaction, refname, &old_oid,
- update_flags, &err))
+ NULL, update_flags, &err))
die("%s", err.buf);
update_flags = default_flags;
@@ -304,6 +478,42 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
strbuf_release(&err);
}
+static void parse_cmd_symref_verify(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf err = STRBUF_INIT;
+ struct object_id old_oid;
+ char *refname, *old_target;
+
+ if (!(update_flags & REF_NO_DEREF))
+ die("symref-verify: cannot operate with deref mode");
+
+ refname = parse_refname(&next);
+ if (!refname)
+ die("symref-verify: missing <ref>");
+
+ /*
+ * old_ref is optional, if not provided, we need to ensure that the
+ * ref doesn't exist.
+ */
+ old_target = parse_next_refname(&next);
+ if (!old_target)
+ oidcpy(&old_oid, null_oid());
+
+ if (*next != line_termination)
+ die("symref-verify %s: extra input: %s", refname, next);
+
+ if (ref_transaction_verify(transaction, refname,
+ old_target ? NULL : &old_oid,
+ old_target, update_flags, &err))
+ die("%s", err.buf);
+
+ update_flags = default_flags;
+ free(refname);
+ free(old_target);
+ strbuf_release(&err);
+}
+
static void report_ok(const char *command)
{
fprintf(stdout, "%s: ok\n", command);
@@ -379,15 +589,19 @@ static const struct parse_cmd {
unsigned args;
enum update_refs_state state;
} command[] = {
- { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
- { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
- { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
- { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
- { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
- { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
- { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
- { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
- { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
+ { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
+ { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
+ { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
+ { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
+ { "symref-update", parse_cmd_symref_update, 4, UPDATE_REFS_OPEN },
+ { "symref-create", parse_cmd_symref_create, 2, UPDATE_REFS_OPEN },
+ { "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN },
+ { "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN },
+ { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
+ { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
+ { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
+ { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
+ { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
};
static void update_refs_stdin(void)
@@ -397,7 +611,8 @@ static void update_refs_stdin(void)
struct ref_transaction *transaction;
int i, j;
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction)
die("%s", err.buf);
@@ -464,7 +679,8 @@ static void update_refs_stdin(void)
* get a "start".
*/
state = cmd->state;
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction)
die("%s", err.buf);
@@ -561,7 +777,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
* The empty string implies that the reference
* must not already exist:
*/
- oidclr(&oldoid);
+ oidclr(&oldoid, the_repository->hash_algo);
else if (repo_get_oid(the_repository, oldval, &oldoid))
die("%s: not a valid old SHA1", oldval);
}
@@ -571,11 +787,14 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
* For purposes of backwards compatibility, we treat
* NULL_SHA1 as "don't care" here:
*/
- return delete_ref(msg, refname,
- (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
- default_flags);
+ return refs_delete_ref(get_main_ref_store(the_repository),
+ msg, refname,
+ (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
+ default_flags);
else
- return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
- default_flags | create_reflog_flag,
- UPDATE_REFS_DIE_ON_ERR);
+ return refs_update_ref(get_main_ref_store(the_repository),
+ msg, refname, &oid,
+ oldval ? &oldoid : NULL,
+ default_flags | create_reflog_flag,
+ UPDATE_REFS_DIE_ON_ERR);
}
diff --git a/builtin/var.c b/builtin/var.c
index cf55672..5dc3848 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -46,7 +46,7 @@ static char *pager(int ident_flag UNUSED)
static char *default_branch(int ident_flag UNUSED)
{
- return xstrdup_or_null(git_default_branch_name(1));
+ return repo_default_branch_name(the_repository, 1);
}
static char *shell_path(int ident_flag UNUSED)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7c6c725..1d51e54 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -433,7 +433,7 @@ static int add_worktree(const char *path, const char *refname,
/* is 'refname' a branch or commit? */
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
- ref_exists(symref.buf)) {
+ refs_ref_exists(get_main_ref_store(the_repository), symref.buf)) {
is_branch = 1;
if (!opts->force)
die_if_checked_out(symref.buf, 0);
@@ -509,7 +509,7 @@ static int add_worktree(const char *path, const char *refname,
}
wt_refs = get_worktree_ref_store(wt);
- ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
+ ret = ref_store_create_on_disk(wt_refs, REF_STORE_CREATE_ON_DISK_IS_WORKTREE, &sb);
if (ret)
goto done;
@@ -517,7 +517,7 @@ static int add_worktree(const char *path, const char *refname,
ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
NULL, 0, UPDATE_REFS_MSG_ON_ERR);
else
- ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
+ ret = refs_update_symref(wt_refs, "HEAD", symref.buf, NULL);
if (ret)
goto done;
@@ -605,7 +605,7 @@ static void print_preparing_worktree_line(int detach,
} else {
struct strbuf s = STRBUF_INIT;
if (!detach && !strbuf_check_branch_ref(&s, branch) &&
- ref_exists(s.buf))
+ refs_ref_exists(get_main_ref_store(the_repository), s.buf))
fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
branch);
else {
@@ -647,9 +647,9 @@ static int first_valid_ref(const char *refname UNUSED,
*/
static int can_use_local_refs(const struct add_opts *opts)
{
- if (head_ref(first_valid_ref, NULL)) {
+ if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
return 1;
- } else if (for_each_branch_ref(first_valid_ref, NULL)) {
+ } else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
if (!opts->quiet) {
struct strbuf path = STRBUF_INIT;
struct strbuf contents = STRBUF_INIT;
@@ -689,7 +689,7 @@ static int can_use_remote_refs(const struct add_opts *opts)
{
if (!guess_remote) {
return 0;
- } else if (for_each_remote_ref(first_valid_ref, NULL)) {
+ } else if (refs_for_each_remote_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
return 1;
} else if (!opts->force && remote_get(NULL)) {
die(_("No local or remote refs exist despite at least one remote\n"
@@ -736,18 +736,17 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
return 1;
}
-static const char *dwim_branch(const char *path, const char **new_branch)
+static char *dwim_branch(const char *path, char **new_branch)
{
int n;
int branch_exists;
const char *s = worktree_basename(path, &n);
- const char *branchname = xstrndup(s, n);
+ char *branchname = xstrndup(s, n);
struct strbuf ref = STRBUF_INIT;
- UNLEAK(branchname);
-
branch_exists = !strbuf_check_branch_ref(&ref, branchname) &&
- ref_exists(ref.buf);
+ refs_ref_exists(get_main_ref_store(the_repository),
+ ref.buf);
strbuf_release(&ref);
if (branch_exists)
return branchname;
@@ -755,8 +754,7 @@ static const char *dwim_branch(const char *path, const char **new_branch)
*new_branch = branchname;
if (guess_remote) {
struct object_id oid;
- const char *remote =
- unique_tracking_name(*new_branch, &oid, NULL);
+ char *remote = unique_tracking_name(*new_branch, &oid, NULL);
return remote;
}
return NULL;
@@ -768,6 +766,8 @@ static int add(int ac, const char **av, const char *prefix)
const char *new_branch_force = NULL;
char *path;
const char *branch;
+ char *branch_to_free = NULL;
+ char *new_branch_to_free = NULL;
const char *new_branch = NULL;
const char *opt_track = NULL;
const char *lock_reason = NULL;
@@ -838,7 +838,7 @@ static int add(int ac, const char **av, const char *prefix)
if (!opts.force &&
!strbuf_check_branch_ref(&symref, new_branch) &&
- ref_exists(symref.buf))
+ refs_ref_exists(get_main_ref_store(the_repository), symref.buf))
die_if_checked_out(symref.buf, 0);
strbuf_release(&symref);
}
@@ -858,16 +858,17 @@ static int add(int ac, const char **av, const char *prefix)
opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
} else if (ac < 2) {
/* DWIM: Guess branch name from path. */
- const char *s = dwim_branch(path, &new_branch);
+ char *s = dwim_branch(path, &new_branch_to_free);
if (s)
- branch = s;
+ branch = branch_to_free = s;
+ new_branch = new_branch_to_free;
/* DWIM: Infer --orphan when repo has no refs. */
opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);
} else if (ac == 2) {
struct object_id oid;
struct commit *commit;
- const char *remote;
+ char *remote;
commit = lookup_commit_reference_by_name(branch);
if (!commit) {
@@ -922,6 +923,8 @@ static int add(int ac, const char **av, const char *prefix)
ret = add_worktree(path, branch, &opts);
free(path);
+ free(branch_to_free);
+ free(new_branch_to_free);
return ret;
}
@@ -974,7 +977,9 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
if (wt->is_detached)
strbuf_addstr(&sb, "(detached HEAD)");
else if (wt->head_ref) {
- char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
+ char *ref = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ wt->head_ref,
+ 0);
strbuf_addf(&sb, "[%s]", ref);
free(ref);
} else
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 66e83d0..8c75b46 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "environment.h"
@@ -44,8 +44,8 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
- tree_prefix);
+ ret = write_index_as_tree(&oid, the_repository->index, get_index_file(),
+ flags, tree_prefix);
switch (ret) {
case 0:
printf("%s\n", oid_to_hex(&oid));
diff --git a/bulk-checkin.c b/bulk-checkin.c
index eb46b88..da86731 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2011, Google Inc.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "bulk-checkin.h"
#include "environment.h"
diff --git a/bundle-uri.c b/bundle-uri.c
index ca32050..804fbcf 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "bundle-uri.h"
#include "bundle.h"
@@ -395,11 +397,13 @@ static int unbundle_from_file(struct repository *r, const char *file)
strbuf_setlen(&bundle_ref, bundle_prefix_len);
strbuf_addstr(&bundle_ref, branch_name);
- has_old = !read_ref(bundle_ref.buf, &old_oid);
- update_ref("fetched bundle", bundle_ref.buf, oid,
- has_old ? &old_oid : NULL,
- REF_SKIP_OID_VERIFICATION,
- UPDATE_REFS_MSG_ON_ERR);
+ has_old = !refs_read_ref(get_main_ref_store(the_repository),
+ bundle_ref.buf, &old_oid);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "fetched bundle", bundle_ref.buf, oid,
+ has_old ? &old_oid : NULL,
+ REF_SKIP_OID_VERIFICATION,
+ UPDATE_REFS_MSG_ON_ERR);
}
bundle_header_release(&header);
diff --git a/bundle.c b/bundle.c
index a9744da..82c285b 100644
--- a/bundle.c
+++ b/bundle.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "lockfile.h"
#include "bundle.h"
@@ -389,7 +391,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
&oid, &ref, 0) != 1)
goto skip_write_ref;
- if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), e->name, RESOLVE_REF_READING, &oid, &flag))
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
diff --git a/cache-tree.c b/cache-tree.c
index 387c0a3..50610c3 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "hex.h"
@@ -422,7 +424,7 @@ static int update_one(struct cache_tree *it,
/*
* "sub" can be an empty tree if all subentries are i-t-a.
*/
- if (contains_ita && is_empty_tree_oid(oid))
+ if (contains_ita && is_empty_tree_oid(oid, the_repository->hash_algo))
continue;
strbuf_grow(&buffer, entlen + 100);
@@ -578,7 +580,8 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
if (0 <= it->entry_count) {
if (size < rawsz)
goto free_return;
- oidread(&it->oid, (const unsigned char *)buf);
+ oidread(&it->oid, (const unsigned char *)buf,
+ the_repository->hash_algo);
buf += rawsz;
size -= rawsz;
}
diff --git a/checkout.c b/checkout.c
index 4256e71..0b1cf8b 100644
--- a/checkout.c
+++ b/checkout.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object-name.h"
#include "remote.h"
@@ -45,8 +47,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
return 0;
}
-const char *unique_tracking_name(const char *name, struct object_id *oid,
- int *dwim_remotes_matched)
+char *unique_tracking_name(const char *name, struct object_id *oid,
+ int *dwim_remotes_matched)
{
struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;
const char *default_remote = NULL;
diff --git a/checkout.h b/checkout.h
index 3c514a5..55920e7 100644
--- a/checkout.h
+++ b/checkout.h
@@ -1,15 +1,15 @@
#ifndef CHECKOUT_H
#define CHECKOUT_H
-#include "hash-ll.h"
+#include "hash.h"
/*
* Check if the branch name uniquely matches a branch name on a remote
* tracking branch. Return the name of the remote if such a branch
* exists, NULL otherwise.
*/
-const char *unique_tracking_name(const char *name,
- struct object_id *oid,
- int *dwim_remotes_matched);
+char *unique_tracking_name(const char *name,
+ struct object_id *oid,
+ int *dwim_remotes_matched);
#endif /* CHECKOUT_H */
diff --git a/chunk-format.c b/chunk-format.c
index cdc7f39..2dde24e 100644
--- a/chunk-format.c
+++ b/chunk-format.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "chunk-format.h"
#include "csum-file.h"
diff --git a/chunk-format.h b/chunk-format.h
index 14b7618..212a0a6 100644
--- a/chunk-format.h
+++ b/chunk-format.h
@@ -1,7 +1,7 @@
#ifndef CHUNK_FORMAT_H
#define CHUNK_FORMAT_H
-#include "hash-ll.h"
+#include "hash.h"
struct hashfile;
struct chunkfile;
diff --git a/ci/check-whitespace.sh b/ci/check-whitespace.sh
new file mode 100755
index 0000000..db39909
--- /dev/null
+++ b/ci/check-whitespace.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+#
+# Check that commits after a specified point do not contain new or modified
+# lines with whitespace errors. An optional formatted summary can be generated
+# by providing an output file path and url as additional arguments.
+#
+
+baseCommit=$1
+outputFile=$2
+url=$3
+
+if test "$#" -ne 1 && test "$#" -ne 3
+then
+ echo "USAGE: $0 <BASE_COMMIT> [<OUTPUT_FILE> <URL>]"
+ exit 1
+fi
+
+problems=()
+commit=
+commitText=
+commitTextmd=
+goodParent=
+
+while read dash sha etc
+do
+ case "${dash}" in
+ "---") # Line contains commit information.
+ if test -z "${goodParent}"
+ then
+ # Assume the commit has no whitespace errors until detected otherwise.
+ goodParent=${sha}
+ fi
+
+ commit="${sha}"
+ commitText="${sha} ${etc}"
+ commitTextmd="[${sha}](${url}/commit/${sha}) ${etc}"
+ ;;
+ "")
+ ;;
+ *) # Line contains whitespace error information for current commit.
+ if test -n "${goodParent}"
+ then
+ problems+=("1) --- ${commitTextmd}")
+ echo ""
+ echo "--- ${commitText}"
+ goodParent=
+ fi
+
+ case "${dash}" in
+ *:[1-9]*:) # contains file and line number information
+ dashend=${dash#*:}
+ problems+=("[${dash}](${url}/blob/${commit}/${dash%%:*}#L${dashend%:}) ${sha} ${etc}")
+ ;;
+ *)
+ problems+=("\`${dash} ${sha} ${etc}\`")
+ ;;
+ esac
+ echo "${dash} ${sha} ${etc}"
+ ;;
+ esac
+done <<< "$(git log --check --pretty=format:"---% h% s" "${baseCommit}"..)"
+
+if test ${#problems[*]} -gt 0
+then
+ if test -z "${goodParent}"
+ then
+ goodParent=${baseCommit: 0:7}
+ fi
+
+ echo "A whitespace issue was found in onen of more of the commits."
+ echo "Run the following command to resolve whitespace issues:"
+ echo "git rebase --whitespace=fix ${goodParent}"
+
+ # If target output file is provided, write formatted output.
+ if test -n "$outputFile"
+ then
+ echo "🛑 Please review the Summary output for further information."
+ (
+ echo "### :x: A whitespace issue was found in one or more of the commits."
+ echo ""
+ echo "Run these commands to correct the problem:"
+ echo "1. \`git rebase --whitespace=fix ${goodParent}\`"
+ echo "1. \`git push --force\`"
+ echo ""
+ echo "Errors:"
+
+ for i in "${problems[@]}"
+ do
+ echo "${i}"
+ done
+ ) >"$outputFile"
+ fi
+
+ exit 2
+fi
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index fa0c00c..6ec0f85 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -1,34 +1,69 @@
-#!/usr/bin/env bash
+#!/bin/sh
#
# Install dependencies required to build and test Git on Linux and macOS
#
. ${0%/*}/lib.sh
+begin_group "Install dependencies"
+
P4WHENCE=https://cdist2.perforce.com/perforce/r21.2
LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION
-UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev
- tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl
- libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl"
+JGITWHENCE=https://repo.eclipse.org/content/groups/releases//org/eclipse/jgit/org.eclipse.jgit.pgm/6.8.0.202311291450-r/org.eclipse.jgit.pgm-6.8.0.202311291450-r.sh
-case "$runs_on_pool" in
+# Make sudo a no-op and execute the command directly when running as root.
+# While using sudo would be fine on most platforms when we are root already,
+# some platforms like e.g. Alpine Linux do not have sudo available by default
+# and would thus break.
+if test "$(id -u)" -eq 0
+then
+ sudo () {
+ "$@"
+ }
+fi
+
+case "$distro" in
+alpine-*)
+ apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
+ pcre2-dev python3 musl-libintl perl-utils ncurses \
+ apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
+ bash cvs gnupg perl-cgi perl-dbd-sqlite perl-io-tty >/dev/null
+ ;;
+fedora-*)
+ dnf -yq update >/dev/null &&
+ dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
+ ;;
ubuntu-*)
+ # Required so that apt doesn't wait for user input on certain packages.
+ export DEBIAN_FRONTEND=noninteractive
+
sudo apt-get -q update
- sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \
- $UBUNTU_COMMON_PKGS $CC_PACKAGE $PYTHON_PACKAGE
- mkdir --parents "$P4_PATH"
- pushd "$P4_PATH"
- wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
- wget --quiet "$P4WHENCE/bin.linux26x86_64/p4"
- chmod u+x p4d
- chmod u+x p4
- popd
- mkdir --parents "$GIT_LFS_PATH"
- pushd "$GIT_LFS_PATH"
- wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
- tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
- cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
- popd
+ sudo apt-get -q -y install \
+ language-pack-is libsvn-perl apache2 cvs cvsps git gnupg subversion \
+ make libssl-dev libcurl4-openssl-dev libexpat-dev wget sudo default-jre \
+ tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \
+ libemail-valid-perl libio-pty-perl libio-socket-ssl-perl libnet-smtp-ssl-perl libdbd-sqlite3-perl libcgi-pm-perl \
+ ${CC_PACKAGE:-${CC:-gcc}} $PYTHON_PACKAGE
+
+ mkdir --parents "$CUSTOM_PATH"
+ wget --quiet --directory-prefix="$CUSTOM_PATH" \
+ "$P4WHENCE/bin.linux26x86_64/p4d" "$P4WHENCE/bin.linux26x86_64/p4"
+ chmod a+x "$CUSTOM_PATH/p4d" "$CUSTOM_PATH/p4"
+
+ wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+ tar -xzf "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz" \
+ -C "$CUSTOM_PATH" --strip-components=1 "git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs"
+ rm "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+
+ wget --quiet "$JGITWHENCE" --output-document="$CUSTOM_PATH/jgit"
+ chmod a+x "$CUSTOM_PATH/jgit"
+ ;;
+ubuntu32-*)
+ sudo linux32 --32bit i386 sh -c '
+ apt update >/dev/null &&
+ apt install -y build-essential libcurl4-openssl-dev \
+ libssl-dev libexpat-dev gettext python >/dev/null
+ '
;;
macos-*)
export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1
@@ -36,12 +71,11 @@
# brew install gnu-time
brew link --force gettext
- mkdir -p "$P4_PATH"
- pushd "$P4_PATH"
- wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
- tar -xf helix-core-server.tgz &&
- sudo xattr -d com.apple.quarantine p4 p4d 2>/dev/null || true
- popd
+ mkdir -p "$CUSTOM_PATH"
+ wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
+ tar -xf helix-core-server.tgz -C "$CUSTOM_PATH" p4 p4d &&
+ sudo xattr -d com.apple.quarantine "$CUSTOM_PATH/p4" "$CUSTOM_PATH/p4d" 2>/dev/null || true
+ rm helix-core-server.tgz
if test -n "$CC_PACKAGE"
then
@@ -70,10 +104,6 @@
test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
sudo gem install --version 1.5.8 asciidoctor
;;
-linux-gcc-default)
- sudo apt-get -q update
- sudo apt-get -q -y install $UBUNTU_COMMON_PKGS
- ;;
esac
if type p4d >/dev/null 2>&1 && type p4 >/dev/null 2>&1
@@ -85,6 +115,7 @@
else
echo >&2 "WARNING: perforce wasn't installed, see above for clues why"
fi
+
if type git-lfs >/dev/null 2>&1
then
echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)"
@@ -92,3 +123,13 @@
else
echo >&2 "WARNING: git-lfs wasn't installed, see above for clues why"
fi
+
+if type jgit >/dev/null 2>&1
+then
+ echo "$(tput setaf 6)JGit Version$(tput sgr0)"
+ jgit version
+else
+ echo >&2 "WARNING: JGit wasn't installed, see above for clues why"
+fi
+
+end_group "Install dependencies"
diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh
deleted file mode 100755
index eb2c9e1..0000000
--- a/ci/install-docker-dependencies.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-#
-# Install dependencies required to build and test Git inside container
-#
-
-. ${0%/*}/lib.sh
-
-begin_group "Install dependencies"
-
-case "$jobname" in
-linux32)
- linux32 --32bit i386 sh -c '
- apt update >/dev/null &&
- apt install -y build-essential libcurl4-openssl-dev \
- libssl-dev libexpat-dev gettext python >/dev/null
- '
- ;;
-linux-musl)
- apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
- pcre2-dev python3 musl-libintl perl-utils ncurses \
- apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
- bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
- ;;
-linux-*|StaticAnalysis)
- # Required so that apt doesn't wait for user input on certain packages.
- export DEBIAN_FRONTEND=noninteractive
-
- apt update -q &&
- apt install -q -y sudo git make language-pack-is libsvn-perl apache2 libssl-dev \
- libcurl4-openssl-dev libexpat-dev tcl tk gettext zlib1g-dev \
- perl-modules liberror-perl libauthen-sasl-perl libemail-valid-perl \
- libdbd-sqlite3-perl libio-socket-ssl-perl libnet-smtp-ssl-perl ${CC_PACKAGE:-${CC:-gcc}} \
- apache2 cvs cvsps gnupg libcgi-pm-perl subversion
-
- if test "$jobname" = StaticAnalysis
- then
- apt install -q -y coccinelle
- fi
- ;;
-pedantic)
- dnf -yq update >/dev/null &&
- dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
- ;;
-esac
-
-end_group "Install dependencies"
diff --git a/ci/lib.sh b/ci/lib.sh
index ff66ad3..814578f 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -18,7 +18,7 @@
then
begin_group () {
need_to_end_group=t
- printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K$1\n"
+ printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)[collapsed=true]\r\e[0K$1\n"
trap "end_group '$1'" EXIT
set -x
}
@@ -279,7 +279,7 @@
cache_dir="$HOME/none"
- runs_on_pool=$(echo "$CI_JOB_IMAGE" | tr : -)
+ distro=$(echo "$CI_JOB_IMAGE" | tr : -)
JOBS=$(nproc)
else
echo "Could not identify CI type" >&2
@@ -318,7 +318,7 @@
export GIT_TEST_CLONE_2GB=true
export SKIP_DASHED_BUILT_INS=YesPlease
-case "$runs_on_pool" in
+case "$distro" in
ubuntu-*)
if test "$jobname" = "linux-gcc-default"
then
@@ -344,10 +344,6 @@
# image.
# Keep that in mind when you encounter a broken OS X build!
export LINUX_GIT_LFS_VERSION="1.5.2"
-
- P4_PATH="$HOME/custom/p4"
- GIT_LFS_PATH="$HOME/custom/git-lfs"
- export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
;;
macos-*)
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
@@ -355,12 +351,12 @@
then
MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
fi
-
- P4_PATH="$HOME/custom/p4"
- export PATH="$P4_PATH:$PATH"
;;
esac
+CUSTOM_PATH="${CUSTOM_PATH:-$HOME/path}"
+export PATH="$CUSTOM_PATH:$PATH"
+
case "$jobname" in
linux32)
CC=gcc
diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh
index a51076d..af8065f 100755
--- a/ci/run-build-and-minimal-fuzzers.sh
+++ b/ci/run-build-and-minimal-fuzzers.sh
@@ -6,8 +6,9 @@
. ${0%/*}/lib.sh
group "Build fuzzers" make \
+ NO_CURL=NoThanks \
CC=clang \
- CXX=clang++ \
+ FUZZ_CXX=clang++ \
CFLAGS="-fsanitize=fuzzer-no-link,address" \
LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
fuzz-all
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index c192bd6..98dda42 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -53,8 +53,6 @@
then
group "Run tests" make test ||
handle_failed_tests
- group "Run unit tests" \
- make DEFAULT_UNIT_TEST_TARGET=unit-tests-prove unit-tests
fi
check_unignored_build_artifacts
diff --git a/ci/run-test-slice.sh b/ci/run-test-slice.sh
index ae80943..e167e64 100755
--- a/ci/run-test-slice.sh
+++ b/ci/run-test-slice.sh
@@ -17,7 +17,7 @@
# We only have one unit test at the moment, so run it in the first slice
if [ "$1" == "0" ] ; then
- group "Run unit tests" make --quiet -C t unit-tests-prove
+ group "Run unit tests" make --quiet -C t unit-tests-test-tool
fi
check_unignored_build_artifacts
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
index de41888..02b3af3 100755
--- a/ci/test-documentation.sh
+++ b/ci/test-documentation.sh
@@ -11,6 +11,7 @@
-e '/^ \* new asciidoc flags$/d' \
-e '/stripped namespace before processing/d' \
-e '/Attributed.*IDs for element/d' \
+ -e '/SyntaxWarning: invalid escape sequence/d' \
"$1"
}
diff --git a/color.c b/color.c
index f663c06..227a5ab 100644
--- a/color.c
+++ b/color.c
@@ -64,12 +64,16 @@ static int match_word(const char *word, int len, const char *match)
return !strncasecmp(word, match, len) && !match[len];
}
-static int get_hex_color(const char *in, unsigned char *out)
+static int get_hex_color(const char **inp, int width, unsigned char *out)
{
+ const char *in = *inp;
unsigned int val;
- val = (hexval(in[0]) << 4) | hexval(in[1]);
+
+ assert(width == 1 || width == 2);
+ val = (hexval(in[0]) << 4) | hexval(in[width - 1]);
if (val & ~0xff)
return -1;
+ *inp += width;
*out = val;
return 0;
}
@@ -135,11 +139,14 @@ static int parse_color(struct color *out, const char *name, int len)
return 0;
}
- /* Try a 24-bit RGB value */
- if (len == 7 && name[0] == '#') {
- if (!get_hex_color(name + 1, &out->red) &&
- !get_hex_color(name + 3, &out->green) &&
- !get_hex_color(name + 5, &out->blue)) {
+ /* Try a 24- or 12-bit RGB value prefixed with '#' */
+ if ((len == 7 || len == 4) && name[0] == '#') {
+ int width_per_color = (len == 7) ? 2 : 1;
+ const char *color = name + 1;
+
+ if (!get_hex_color(&color, width_per_color, &out->red) &&
+ !get_hex_color(&color, width_per_color, &out->green) &&
+ !get_hex_color(&color, width_per_color, &out->blue)) {
out->type = COLOR_RGB;
return 0;
}
diff --git a/color.h b/color.h
index bb28343..7ed259a 100644
--- a/color.h
+++ b/color.h
@@ -112,7 +112,8 @@ int want_color_fd(int fd, int var);
* Translate a Git color from 'value' into a string that the terminal can
* interpret and store it into 'dst'. The Git color values are of the form
* "foreground [background] [attr]" where fore- and background can be a color
- * name ("red"), a RGB code (#0xFF0000) or a 256-color-mode from the terminal.
+ * name ("red"), a RGB code (#FF0000 or #F00) or a 256-color-mode from the
+ * terminal.
*/
int color_parse(const char *value, char *dst);
int color_parse_mem(const char *value, int len, char *dst);
diff --git a/combine-diff.c b/combine-diff.c
index d6d6fa1..829a44e 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object-store-ll.h"
#include "commit.h"
@@ -1066,7 +1068,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
elem->mode = canon_mode(st.st_mode);
} else if (S_ISDIR(st.st_mode)) {
struct object_id oid;
- if (resolve_gitlink_ref(elem->path, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, elem->path,
+ "HEAD", &oid) < 0)
result = grab_blob(opt->repo, &elem->oid,
elem->mode, &result_size,
NULL, NULL);
diff --git a/command-list.txt b/command-list.txt
index c4cd0f3..e0bb87b 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -157,6 +157,7 @@
git-rebase mainporcelain history
git-receive-pack synchelpers
git-reflog ancillarymanipulators complete
+git-refs ancillarymanipulators complete
git-remote ancillarymanipulators complete
git-repack ancillarymanipulators complete
git-replace ancillarymanipulators complete
diff --git a/commit-graph.c b/commit-graph.c
index 7223672..93c0755 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "csum-file.h"
@@ -475,7 +477,8 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
FREE_AND_NULL(graph->bloom_filter_settings);
}
- oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len);
+ oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len,
+ the_repository->hash_algo);
free_chunkfile(cf);
return graph;
@@ -565,7 +568,8 @@ static int add_graph_to_chain(struct commit_graph *g,
if (!cur_g ||
!oideq(&oids[n], &cur_g->oid) ||
- !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_len, n))) {
+ !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_len, n),
+ the_repository->hash_algo)) {
warning(_("commit-graph chain does not match"));
return 0;
}
@@ -837,7 +841,8 @@ static void load_oid_from_graph(struct commit_graph *g,
lex_index = pos - g->num_commits_in_base;
- oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_len, lex_index));
+ oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_len, lex_index),
+ the_repository->hash_algo);
}
static struct commit_list **insert_parent_or_die(struct repository *r,
@@ -1079,7 +1084,7 @@ static struct tree *load_tree_for_commit(struct repository *r,
commit_data = g->chunk_commit_data +
st_mult(GRAPH_DATA_WIDTH, graph_pos - g->num_commits_in_base);
- oidread(&oid, commit_data);
+ oidread(&oid, commit_data, the_repository->hash_algo);
set_commit_tree(c, lookup_tree(r, &oid));
return c->maybe_tree;
@@ -1821,7 +1826,7 @@ static int add_ref_to_set(const char *refname UNUSED,
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (oid_object_info(the_repository, oid, NULL) == OBJ_COMMIT)
oidset_insert(data->commits, oid);
@@ -1845,7 +1850,8 @@ int write_commit_graph_reachable(struct object_directory *odb,
data.progress = start_delayed_progress(
_("Collecting referenced commits"), 0);
- for_each_ref(add_ref_to_set, &data);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_ref_to_set,
+ &data);
stop_progress(&data.progress);
@@ -2001,8 +2007,8 @@ static int write_graph_chunk_base(struct hashfile *f,
static int write_commit_graph_file(struct write_commit_graph_context *ctx)
{
uint32_t i;
- int fd;
struct hashfile *f;
+ struct tempfile *graph_layer; /* when ctx->split is non-zero */
struct lock_file lk = LOCK_INIT;
const unsigned hashsz = the_hash_algo->rawsz;
struct strbuf progress_title = STRBUF_INIT;
@@ -2034,24 +2040,23 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
LOCK_DIE_ON_ERROR, 0444);
free(lock_name);
- fd = git_mkstemp_mode(ctx->graph_name, 0444);
- if (fd < 0) {
+ graph_layer = mks_tempfile_m(ctx->graph_name, 0444);
+ if (!graph_layer) {
error(_("unable to create temporary graph layer"));
return -1;
}
- if (adjust_shared_perm(ctx->graph_name)) {
+ if (adjust_shared_perm(get_tempfile_path(graph_layer))) {
error(_("unable to adjust shared permissions for '%s'"),
- ctx->graph_name);
+ get_tempfile_path(graph_layer));
return -1;
}
- f = hashfd(fd, ctx->graph_name);
+ f = hashfd(get_tempfile_fd(graph_layer), get_tempfile_path(graph_layer));
} else {
hold_lock_file_for_update_mode(&lk, ctx->graph_name,
LOCK_DIE_ON_ERROR, 0444);
- fd = get_lock_file_fd(&lk);
- f = hashfd(fd, get_lock_file_path(&lk));
+ f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
}
cf = init_chunkfile(f);
@@ -2132,8 +2137,6 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
char *final_graph_name;
int result;
- close(fd);
-
if (!chainf) {
error(_("unable to open commit-graph chain file"));
return -1;
@@ -2168,7 +2171,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
free(ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1]);
ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1] = final_graph_name;
- result = rename(ctx->graph_name, final_graph_name);
+ result = rename_tempfile(&graph_layer, final_graph_name);
for (i = 0; i < ctx->num_commit_graphs_after; i++)
fprintf(get_lock_file_fp(&lk), "%s\n", ctx->commit_graph_hash_after[i]);
@@ -2554,7 +2557,8 @@ int write_commit_graph(struct object_directory *odb,
struct commit_graph *g = ctx->r->objects->commit_graph;
for (i = 0; i < g->num_commits; i++) {
struct object_id oid;
- oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
+ oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_len, i),
+ the_repository->hash_algo);
oid_array_append(&ctx->oids, &oid);
}
}
@@ -2673,7 +2677,8 @@ static int verify_one_commit_graph(struct repository *r,
for (i = 0; i < g->num_commits; i++) {
struct commit *graph_commit;
- oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
+ oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i),
+ the_repository->hash_algo);
if (i && oidcmp(&prev_oid, &cur_oid) >= 0)
graph_report(_("commit-graph has incorrect OID order: %s then %s"),
@@ -2717,7 +2722,8 @@ static int verify_one_commit_graph(struct repository *r,
timestamp_t generation;
display_progress(progress, ++(*seen));
- oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
+ oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i),
+ the_repository->hash_algo);
graph_commit = lookup_commit(r, &cur_oid);
odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r));
diff --git a/commit-graph.h b/commit-graph.h
index e519cb8..6781940 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -122,6 +122,8 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
struct commit_graph *read_commit_graph_one(struct repository *r,
struct object_directory *odb);
+struct repo_settings;
+
/*
* Callers should initialize the repo_settings with prepare_repo_settings()
* prior to calling parse_commit_graph().
diff --git a/commit-reach.c b/commit-reach.c
index 8f9b008..dabc297 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "commit-graph.h"
@@ -1106,6 +1108,10 @@ void ahead_behind(struct repository *r,
/* STALE is used here, PARENT2 is used by insert_no_dup(). */
repo_clear_commit_marks(r, PARENT2 | STALE);
+ while (prio_queue_peek(&queue)) {
+ struct commit *c = prio_queue_get(&queue);
+ free_bit_array(c);
+ }
clear_bit_arrays(&bit_arrays);
clear_prio_queue(&queue);
}
diff --git a/commit.c b/commit.c
index ead9b64..45ec3f1 100644
--- a/commit.c
+++ b/commit.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tag.h"
#include "commit.h"
@@ -1070,7 +1072,8 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
memset(&revs, 0, sizeof(revs));
revs.initial = 1;
- for_each_reflog_ent(full_refname, collect_one_reflog_ent, &revs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ full_refname, collect_one_reflog_ent, &revs);
if (!revs.nr)
add_one_commit(&oid, &revs);
diff --git a/common-main.c b/common-main.c
index 033778b..8e68ac9 100644
--- a/common-main.c
+++ b/common-main.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "exec-cmd.h"
#include "gettext.h"
@@ -48,7 +50,7 @@ int main(int argc, const char **argv)
setlocale(LC_CTYPE, "");
git_setup_gettext();
- initialize_the_repository();
+ initialize_repository(the_repository);
attr_start();
diff --git a/compat/basename.c b/compat/basename.c
index 96bd953..c33579e 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -10,7 +10,13 @@ char *gitbasename (char *path)
skip_dos_drive_prefix(&path);
if (!path || !*path)
- return ".";
+ /*
+ * basename(3P) is mis-specified because it returns a
+ * non-constant pointer even though it is specified to return a
+ * pointer to internal memory at times. The cast is a result of
+ * that.
+ */
+ return (char *) ".";
for (base = path; *path; path++) {
if (!is_dir_sep(*path))
@@ -34,7 +40,13 @@ char *gitdirname(char *path)
int dos_drive_prefix;
if (!p)
- return ".";
+ /*
+ * dirname(3P) is mis-specified because it returns a
+ * non-constant pointer even though it is specified to return a
+ * pointer to internal memory at times. The cast is a result of
+ * that.
+ */
+ return (char *) ".";
if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
goto dot;
diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c
index 6f3a954..52f4f29 100644
--- a/compat/fsmonitor/fsm-ipc-darwin.c
+++ b/compat/fsmonitor/fsm-ipc-darwin.c
@@ -17,7 +17,7 @@ const char *fsmonitor_ipc__get_path(struct repository *r)
git_SHA_CTX sha1ctx;
char *sock_dir = NULL;
struct strbuf ipc_file = STRBUF_INIT;
- unsigned char hash[GIT_MAX_RAWSZ];
+ unsigned char hash[GIT_SHA1_RAWSZ];
if (!r)
BUG("No repository passed into fsmonitor_ipc__get_path");
@@ -41,9 +41,10 @@ const char *fsmonitor_ipc__get_path(struct repository *r)
/* Create the socket file in either socketDir or $HOME */
if (sock_dir && *sock_dir) {
strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
- sock_dir, hash_to_hex(hash));
+ sock_dir, hash_to_hex_algop(hash, &hash_algos[GIT_HASH_SHA1]));
} else {
- strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
+ strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s",
+ hash_to_hex_algop(hash, &hash_algos[GIT_HASH_SHA1]));
}
free(sock_dir);
diff --git a/compat/mingw.c b/compat/mingw.c
index 8aa3ed5..6097b8f 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2278,7 +2278,11 @@ struct passwd *getpwuid(int uid)
p->pw_name = user_name;
p->pw_gecos = get_extended_user_info(NameDisplay);
if (!p->pw_gecos)
- p->pw_gecos = "unknown";
+ /*
+ * Data returned by getpwuid(3P) is treated as internal and
+ * must never be written to or freed.
+ */
+ p->pw_gecos = (char *) "unknown";
p->pw_dir = NULL;
initialized = 1;
@@ -2799,16 +2803,16 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
strbuf_addf(report, "'%s' is on a file system that does "
"not record ownership\n", path);
} else if (report) {
- LPSTR str1, str2, str3, str4, to_free1 = NULL,
- to_free3 = NULL, to_local_free2 = NULL,
- to_local_free4 = NULL;
+ PCSTR str1, str2, str3, str4;
+ LPSTR to_free1 = NULL, to_free3 = NULL,
+ to_local_free2 = NULL, to_local_free4 = NULL;
- if (user_sid_to_user_name(sid, &str1))
- to_free1 = str1;
+ if (user_sid_to_user_name(sid, &to_free1))
+ str1 = to_free1;
else
str1 = "(inconvertible)";
- if (ConvertSidToStringSidA(sid, &str2))
- to_local_free2 = str2;
+ if (ConvertSidToStringSidA(sid, &to_local_free2))
+ str2 = to_local_free2;
else
str2 = "(inconvertible)";
@@ -2821,13 +2825,13 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
str4 = "(invalid)";
} else {
if (user_sid_to_user_name(current_user_sid,
- &str3))
- to_free3 = str3;
+ &to_free3))
+ str3 = to_free3;
else
str3 = "(inconvertible)";
if (ConvertSidToStringSidA(current_user_sid,
- &str4))
- to_local_free4 = str4;
+ &to_local_free4))
+ str4 = to_local_free4;
else
str4 = "(inconvertible)";
}
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 2bc0f11..6c5d455 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -848,7 +848,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
{
unsigned int table_size;
#ifndef _LIBC
- char *codeset_name;
+ const char *codeset_name;
#endif
memset (dfa, '\0', sizeof (re_dfa_t));
diff --git a/compat/sha1-chunked.c b/compat/sha1-chunked.c
index a4a6f93..0446f99 100644
--- a/compat/sha1-chunked.c
+++ b/compat/sha1-chunked.c
@@ -1,5 +1,5 @@
#include "git-compat-util.h"
-#include "hash-ll.h"
+#include "hash.h"
int git_SHA1_Update_Chunked(platform_SHA_CTX *c, const void *data, size_t len)
{
diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
index 3ef0936..f147da7 100644
--- a/compat/win32/trace2_win32_process_info.c
+++ b/compat/win32/trace2_win32_process_info.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "../../git-compat-util.h"
#include "../../json-writer.h"
#include "../../repository.h"
diff --git a/compat/winansi.c b/compat/winansi.c
index f83610f..575813b 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -139,7 +139,7 @@ static void write_console(unsigned char *str, size_t len)
/* convert utf-8 to utf-16 */
int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
if (wlen < 0) {
- wchar_t *err = L"[invalid]";
+ const wchar_t *err = L"[invalid]";
WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
return;
}
diff --git a/config.c b/config.c
index 2766b14..83bef0e 100644
--- a/config.c
+++ b/config.c
@@ -5,6 +5,9 @@
* Copyright (C) Johannes Schindelin, 2005
*
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
@@ -125,7 +128,7 @@ struct config_include_data {
config_fn_t fn;
void *data;
const struct config_options *opts;
- struct git_config_source *config_source;
+ const struct git_config_source *config_source;
struct repository *repo;
/*
@@ -303,7 +306,8 @@ static int include_by_branch(const char *cond, size_t cond_len)
int ret;
struct strbuf pattern = STRBUF_INIT;
const char *refname = !the_repository->gitdir ?
- NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+ NULL : refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &flags);
const char *shortname;
if (!refname || !(flags & REF_ISSYMREF) ||
@@ -1243,6 +1247,15 @@ ssize_t git_config_ssize_t(const char *name, const char *value,
return ret;
}
+double git_config_double(const char *name, const char *value,
+ const struct key_value_info *kvi)
+{
+ double ret;
+ if (!git_parse_double(value, &ret))
+ die_bad_number(name, value, kvi);
+ return ret;
+}
+
static const struct fsync_component_name {
const char *name;
enum fsync_component component_bits;
@@ -1337,7 +1350,7 @@ int git_config_bool(const char *name, const char *value)
return v;
}
-int git_config_string(const char **dest, const char *var, const char *value)
+int git_config_string(char **dest, const char *var, const char *value)
{
if (!value)
return config_error_nonbool(var);
@@ -1345,7 +1358,7 @@ int git_config_string(const char **dest, const char *var, const char *value)
return 0;
}
-int git_config_pathname(const char **dest, const char *var, const char *value)
+int git_config_pathname(char **dest, const char *var, const char *value)
{
if (!value)
return config_error_nonbool(var);
@@ -1413,11 +1426,15 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.attributesfile"))
+ if (!strcmp(var, "core.attributesfile")) {
+ FREE_AND_NULL(git_attributes_file);
return git_config_pathname(&git_attributes_file, var, value);
+ }
- if (!strcmp(var, "core.hookspath"))
+ if (!strcmp(var, "core.hookspath")) {
+ FREE_AND_NULL(git_hooks_path);
return git_config_pathname(&git_hooks_path, var, value);
+ }
if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value);
@@ -1455,10 +1472,10 @@ static int git_default_core_config(const char *var, const char *value,
if (!strcasecmp(value, "auto"))
default_abbrev = -1;
else if (!git_parse_maybe_bool_text(value))
- default_abbrev = the_hash_algo->hexsz;
+ default_abbrev = GIT_MAX_HEXSZ;
else {
int abbrev = git_config_int(var, value, ctx->kvi);
- if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
+ if (abbrev < minimum_abbrev)
return error(_("abbrev length out of range: %d"), abbrev);
default_abbrev = abbrev;
}
@@ -1552,8 +1569,10 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.checkroundtripencoding"))
+ if (!strcmp(var, "core.checkroundtripencoding")) {
+ FREE_AND_NULL(check_roundtrip_encoding);
return git_config_string(&check_roundtrip_encoding, var, value);
+ }
if (!strcmp(var, "core.notesref")) {
if (!value)
@@ -1562,8 +1581,10 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.editor"))
+ if (!strcmp(var, "core.editor")) {
+ FREE_AND_NULL(editor_program);
return git_config_string(&editor_program, var, value);
+ }
if (!strcmp(var, "core.commentchar") ||
!strcmp(var, "core.commentstring")) {
@@ -1581,11 +1602,13 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.askpass"))
+ if (!strcmp(var, "core.askpass")) {
+ FREE_AND_NULL(askpass_program);
return git_config_string(&askpass_program, var, value);
+ }
if (!strcmp(var, "core.excludesfile")) {
- free((char *)excludes_file);
+ FREE_AND_NULL(excludes_file);
return git_config_pathname(&excludes_file, var, value);
}
@@ -1688,11 +1711,15 @@ static int git_default_sparse_config(const char *var, const char *value)
static int git_default_i18n_config(const char *var, const char *value)
{
- if (!strcmp(var, "i18n.commitencoding"))
+ if (!strcmp(var, "i18n.commitencoding")) {
+ FREE_AND_NULL(git_commit_encoding);
return git_config_string(&git_commit_encoding, var, value);
+ }
- if (!strcmp(var, "i18n.logoutputencoding"))
+ if (!strcmp(var, "i18n.logoutputencoding")) {
+ FREE_AND_NULL(git_log_output_encoding);
return git_config_string(&git_log_output_encoding, var, value);
+ }
/* Add other config variables here and to Documentation/config.txt. */
return 0;
@@ -1765,10 +1792,15 @@ static int git_default_push_config(const char *var, const char *value)
static int git_default_mailmap_config(const char *var, const char *value)
{
- if (!strcmp(var, "mailmap.file"))
+ if (!strcmp(var, "mailmap.file")) {
+ FREE_AND_NULL(git_mailmap_file);
return git_config_pathname(&git_mailmap_file, var, value);
- if (!strcmp(var, "mailmap.blob"))
+ }
+
+ if (!strcmp(var, "mailmap.blob")) {
+ FREE_AND_NULL(git_mailmap_blob);
return git_config_string(&git_mailmap_blob, var, value);
+ }
/* Add other config variables here and to Documentation/config.txt. */
return 0;
@@ -1776,8 +1808,10 @@ static int git_default_mailmap_config(const char *var, const char *value)
static int git_default_attr_config(const char *var, const char *value)
{
- if (!strcmp(var, "attr.tree"))
+ if (!strcmp(var, "attr.tree")) {
+ FREE_AND_NULL(git_attr_tree);
return git_config_string(&git_attr_tree, var, value);
+ }
/*
* Add other attribute related config variables here and to
@@ -2105,7 +2139,7 @@ static int do_git_config_sequence(const struct config_options *opts,
}
int config_with_options(config_fn_t fn, void *data,
- struct git_config_source *config_source,
+ const struct git_config_source *config_source,
struct repository *repo,
const struct config_options *opts)
{
@@ -2404,7 +2438,7 @@ int git_configset_get_string(struct config_set *set, const char *key, char **des
{
const char *value;
if (!git_configset_get_value(set, key, &value, NULL))
- return git_config_string((const char **)dest, key, value);
+ return git_config_string(dest, key, value);
else
return 1;
}
@@ -2482,7 +2516,7 @@ int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *d
return 1;
}
-int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, char **dest)
{
const char *value;
if (!git_configset_get_value(set, key, &value, NULL))
@@ -2627,7 +2661,7 @@ int repo_config_get_maybe_bool(struct repository *repo,
}
int repo_config_get_pathname(struct repository *repo,
- const char *key, const char **dest)
+ const char *key, char **dest)
{
int ret;
git_config_check_init(repo);
@@ -2726,7 +2760,7 @@ int git_config_get_maybe_bool(const char *key, int *dest)
return repo_config_get_maybe_bool(the_repository, key, dest);
}
-int git_config_get_pathname(const char *key, const char **dest)
+int git_config_get_pathname(const char *key, char **dest)
{
return repo_config_get_pathname(the_repository, key, dest);
}
@@ -3181,14 +3215,10 @@ void git_config_set(const char *key, const char *value)
trace2_cmd_set_config(key, value);
}
-/*
- * The ownership rule is that the caller will own the string
- * if it receives a piece of memory different from what it passed
- * as the parameter.
- */
-const char *git_config_prepare_comment_string(const char *comment)
+char *git_config_prepare_comment_string(const char *comment)
{
size_t leading_blanks;
+ char *prepared;
if (!comment)
return NULL;
@@ -3209,13 +3239,13 @@ const char *git_config_prepare_comment_string(const char *comment)
leading_blanks = strspn(comment, " \t");
if (leading_blanks && comment[leading_blanks] == '#')
- ; /* use it as-is */
+ prepared = xstrdup(comment); /* use it as-is */
else if (comment[0] == '#')
- comment = xstrfmt(" %s", comment);
+ prepared = xstrfmt(" %s", comment);
else
- comment = xstrfmt(" # %s", comment);
+ prepared = xstrfmt(" # %s", comment);
- return comment;
+ return prepared;
}
static void validate_comment_string(const char *comment)
diff --git a/config.h b/config.h
index f4966e3..54b47de 100644
--- a/config.h
+++ b/config.h
@@ -232,7 +232,7 @@ void git_config(config_fn_t fn, void *);
* sets `opts.respect_includes` to `1` by default.
*/
int config_with_options(config_fn_t fn, void *,
- struct git_config_source *config_source,
+ const struct git_config_source *config_source,
struct repository *repo,
const struct config_options *opts);
@@ -262,6 +262,13 @@ ssize_t git_config_ssize_t(const char *, const char *,
const struct key_value_info *);
/**
+ * Identically to `git_config_double`, but for double-precision floating point
+ * values.
+ */
+double git_config_double(const char *, const char *,
+ const struct key_value_info *);
+
+/**
* Same as `git_config_bool`, except that integers are returned as-is, and
* an `is_bool` flag is unset.
*/
@@ -280,13 +287,13 @@ int git_config_bool(const char *, const char *);
* Allocates and copies the value string into the `dest` parameter; if no
* string is given, prints an error message and returns -1.
*/
-int git_config_string(const char **, const char *, const char *);
+int git_config_string(char **, const char *, const char *);
/**
* Similar to `git_config_string`, but expands `~` or `~user` into the
* user's home directory when found at the beginning of the path.
*/
-int git_config_pathname(const char **, const char *, const char *);
+int git_config_pathname(char **, const char *, const char *);
int git_config_expiry_date(timestamp_t *, const char *, const char *);
int git_config_color(char *, const char *, const char *);
@@ -338,7 +345,7 @@ void git_config_set_multivar(const char *, const char *, const char *, unsigned)
int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
-const char *git_config_prepare_comment_string(const char *);
+char *git_config_prepare_comment_string(const char *);
/**
* takes four parameters:
@@ -541,7 +548,7 @@ int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned lon
int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_pathname(struct config_set *cs, const char *key, char **dest);
/* Functions for reading a repository's config */
struct repository;
@@ -577,7 +584,7 @@ int repo_config_get_bool_or_int(struct repository *repo,
int repo_config_get_maybe_bool(struct repository *repo,
const char *key, int *dest);
int repo_config_get_pathname(struct repository *repo,
- const char *key, const char **dest);
+ const char *key, char **dest);
/*
* Functions for reading protected config. By definition, protected
@@ -687,7 +694,7 @@ int git_config_get_maybe_bool(const char *key, int *dest);
* Similar to `git_config_get_string`, but expands `~` or `~user` into
* the user's home directory when found at the beginning of the path.
*/
-int git_config_get_pathname(const char *key, const char **dest);
+int git_config_get_pathname(const char *key, char **dest);
int git_config_get_index_threads(int *dest);
int git_config_get_split_index(void);
diff --git a/config.mak.dev b/config.mak.dev
index 9813047..1ce4c70 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -37,6 +37,7 @@
DEVELOPER_CFLAGS += -Wstrict-prototypes
DEVELOPER_CFLAGS += -Wunused
DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -Wwrite-strings
DEVELOPER_CFLAGS += -fno-common
ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
diff --git a/config.mak.uname b/config.mak.uname
index a7607a5..85d6382 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -68,6 +68,7 @@
ifneq ($(findstring .el7.,$(uname_R)),)
BASIC_CFLAGS += -std=c99
endif
+ LINK_FUZZ_PROGRAMS = YesPlease
endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
diff --git a/connect.c b/connect.c
index 0d77737..cf84e63 100644
--- a/connect.c
+++ b/connect.c
@@ -38,8 +38,8 @@ static int check_ref(const char *name, unsigned int flags)
REFNAME_ALLOW_ONELEVEL))
return 0;
- /* REF_HEADS means that we want regular branch heads */
- if ((flags & REF_HEADS) && starts_with(name, "heads/"))
+ /* REF_BRANCHES means that we want regular branch heads */
+ if ((flags & REF_BRANCHES) && starts_with(name, "heads/"))
return 1;
/* REF_TAGS means that we want tags */
diff --git a/connected.c b/connected.c
index 8f89376..87cc4b5 100644
--- a/connected.c
+++ b/connected.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 804629c..2f9c335 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -1005,10 +1005,11 @@
#test-tool
parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
+add_library(test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/")
add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES} ${test-reftable_SOURCES})
-target_link_libraries(test-tool common-main)
+target_link_libraries(test-tool test-lib common-main)
set_target_properties(test-fake-ssh test-tool
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/helper)
diff --git a/contrib/coccinelle/refs.cocci b/contrib/coccinelle/refs.cocci
new file mode 100644
index 0000000..31d9cad
--- /dev/null
+++ b/contrib/coccinelle/refs.cocci
@@ -0,0 +1,103 @@
+// Migrate "refs.h" to not rely on `the_repository` implicitly anymore.
+@@
+@@
+(
+- resolve_ref_unsafe
++ refs_resolve_ref_unsafe
+|
+- resolve_refdup
++ refs_resolve_refdup
+|
+- read_ref_full
++ refs_read_ref_full
+|
+- read_ref
++ refs_read_ref
+|
+- ref_exists
++ refs_ref_exists
+|
+- head_ref
++ refs_head_ref
+|
+- for_each_ref
++ refs_for_each_ref
+|
+- for_each_ref_in
++ refs_for_each_ref_in
+|
+- for_each_fullref_in
++ refs_for_each_fullref_in
+|
+- for_each_tag_ref
++ refs_for_each_tag_ref
+|
+- for_each_branch_ref
++ refs_for_each_branch_ref
+|
+- for_each_remote_ref
++ refs_for_each_remote_ref
+|
+- for_each_glob_ref
++ refs_for_each_glob_ref
+|
+- for_each_glob_ref_in
++ refs_for_each_glob_ref_in
+|
+- head_ref_namespaced
++ refs_head_ref_namespaced
+|
+- for_each_namespaced_ref
++ refs_for_each_namespaced_ref
+|
+- for_each_rawref
++ refs_for_each_rawref
+|
+- safe_create_reflog
++ refs_create_reflog
+|
+- reflog_exists
++ refs_reflog_exists
+|
+- delete_ref
++ refs_delete_ref
+|
+- delete_refs
++ refs_delete_refs
+|
+- delete_reflog
++ refs_delete_reflog
+|
+- for_each_reflog_ent
++ refs_for_each_reflog_ent
+|
+- for_each_reflog_ent_reverse
++ refs_for_each_reflog_ent_reverse
+|
+- for_each_reflog
++ refs_for_each_reflog
+|
+- shorten_unambiguous_ref
++ refs_shorten_unambiguous_ref
+|
+- rename_ref
++ refs_rename_ref
+|
+- copy_existing_ref
++ refs_copy_existing_ref
+|
+- create_symref
++ refs_create_symref
+|
+- ref_transaction_begin
++ ref_store_transaction_begin
+|
+- update_ref
++ refs_update_ref
+|
+- reflog_expire
++ refs_reflog_expire
+)
+ (
++ get_main_ref_store(the_repository),
+ ...)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 75193de..60a22d6 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -31,15 +31,29 @@
# Note that "git" is optional --- '!f() { : commit; ...}; f' would complete
# just like the 'git commit' command.
#
-# If you have a command that is not part of git, but you would still
-# like completion, you can use __git_complete:
+# To add completion for git subcommands that are implemented in external
+# scripts, define a function of the form '_git_${subcommand}' while replacing
+# all dashes with underscores, and the main git completion will make use of it.
+# For example, to add completion for 'git do-stuff' (which could e.g. live
+# in /usr/bin/git-do-stuff), name the completion function '_git_do_stuff'.
+# See _git_show, _git_bisect etc. below for more examples.
+#
+# If you have a shell command that is not part of git (and is not called as a
+# git subcommand), but you would still like git-style completion for it, use
+# __git_complete. For example, to use the same completion as for 'git log' also
+# for the 'gl' command:
#
# __git_complete gl git_log
#
-# Or if it's a main command (i.e. git or gitk):
+# Or if the 'gk' command should be completed the same as 'gitk':
#
# __git_complete gk gitk
#
+# The second parameter of __git_complete gives the completion function; it is
+# resolved as a function named "$2", or "__$2_main", or "_$2" in that order.
+# In the examples above, the actual functions used for completion will be
+# _git_log and __gitk_main.
+#
# Compatible with bash 3.2.57.
#
# You can set the following environment variables to influence the behavior of
@@ -2975,22 +2989,42 @@
_git_config ()
{
- case "$prev" in
- --get|--get-all|--unset|--unset-all)
- __gitcomp_nl "$(__git_config_get_set_variables)"
+ local subcommands subcommand
+
+ __git_resolve_builtins "config"
+
+ subcommands="$___git_resolved_builtins"
+ subcommand="$(__git_find_subcommand "$subcommands")"
+
+ if [ -z "$subcommand" ]
+ then
+ __gitcomp "$subcommands"
return
- ;;
- *.*)
- __git_complete_config_variable_value
+ fi
+
+ case "$cur" in
+ --*)
+ __gitcomp_builtin "config_$subcommand"
return
;;
esac
- case "$cur" in
- --*)
- __gitcomp_builtin config
+
+ case "$subcommand" in
+ get)
+ __gitcomp_nl "$(__git_config_get_set_variables)"
;;
- *)
- __git_complete_config_variable_name
+ set)
+ case "$prev" in
+ *.*)
+ __git_complete_config_variable_value
+ ;;
+ *)
+ __git_complete_config_variable_name
+ ;;
+ esac
+ ;;
+ unset)
+ __gitcomp_nl "$(__git_config_get_set_variables)"
;;
esac
}
@@ -3581,6 +3615,17 @@
fi
}
+_git_symbolic_ref () {
+ case "$cur" in
+ --*)
+ __gitcomp_builtin symbolic-ref
+ return
+ ;;
+ esac
+
+ __git_complete_refs
+}
+
_git_tag ()
{
local i c="$__git_cmd_idx" f=0
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index 6a40917..6ce22a2 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -12,6 +12,7 @@ static CFStringRef username;
static CFDataRef password;
static CFDataRef password_expiry_utc;
static CFDataRef oauth_refresh_token;
+static int state_seen;
static void clear_credential(void)
{
@@ -171,6 +172,9 @@ static OSStatus find_internet_password(void)
CFRelease(item);
+ write_item("capability[]", "state", strlen("state"));
+ write_item("state[]", "osxkeychain:seen=1", strlen("osxkeychain:seen=1"));
+
out:
CFRelease(attrs);
@@ -284,6 +288,9 @@ static OSStatus add_internet_password(void)
CFDictionaryRef attrs;
OSStatus result;
+ if (state_seen)
+ return errSecSuccess;
+
/* Only store complete credentials */
if (!protocol || !host || !username || !password)
return -1;
@@ -395,6 +402,10 @@ static void read_credential(void)
oauth_refresh_token = CFDataCreate(kCFAllocatorDefault,
(UInt8 *)v,
strlen(v));
+ else if (!strcmp(buf, "state[]")) {
+ if (!strcmp(v, "osxkeychain:seen=1"))
+ state_seen = 1;
+ }
/*
* Ignore other lines; we don't know what they mean, but
* this future-proofs us when later versions of git do
@@ -414,6 +425,9 @@ int main(int argc, const char **argv)
if (!argv[1])
die("%s", usage);
+ if (open(argv[0], O_RDONLY | O_EXLOCK) == -1)
+ die("failed to lock %s", argv[0]);
+
read_credential();
if (!strcmp(argv[1], "get"))
diff --git a/convert.c b/convert.c
index 35b25eb..d8737fe 100644
--- a/convert.c
+++ b/convert.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
@@ -345,30 +347,32 @@ static int check_roundtrip(const char *enc_name)
* space separated encodings (eg. "UTF-16, ASCII, CP1125").
* Search for the given encoding in that string.
*/
- const char *found = strcasestr(check_roundtrip_encoding, enc_name);
+ const char *encoding = check_roundtrip_encoding ?
+ check_roundtrip_encoding : "SHIFT-JIS";
+ const char *found = strcasestr(encoding, enc_name);
const char *next;
int len;
if (!found)
return 0;
next = found + strlen(enc_name);
- len = strlen(check_roundtrip_encoding);
+ len = strlen(encoding);
return (found && (
/*
- * check that the found encoding is at the
- * beginning of check_roundtrip_encoding or
- * that it is prefixed with a space or comma
+ * Check that the found encoding is at the beginning of
+ * encoding or that it is prefixed with a space or
+ * comma.
*/
- found == check_roundtrip_encoding || (
+ found == encoding || (
(isspace(found[-1]) || found[-1] == ',')
)
) && (
/*
- * check that the found encoding is at the
- * end of check_roundtrip_encoding or
- * that it is suffixed with a space or comma
+ * Check that the found encoding is at the end of
+ * encoding or that it is suffixed with a space
+ * or comma.
*/
- next == check_roundtrip_encoding + len || (
- next < check_roundtrip_encoding + len &&
+ next == encoding + len || (
+ next < encoding + len &&
(isspace(next[0]) || next[0] == ',')
)
));
@@ -979,9 +983,9 @@ int async_query_available_blobs(const char *cmd, struct string_list *available_p
static struct convert_driver {
const char *name;
struct convert_driver *next;
- const char *smudge;
- const char *clean;
- const char *process;
+ char *smudge;
+ char *clean;
+ char *process;
int required;
} *user_convert, **user_convert_tail;
diff --git a/convert.h b/convert.h
index ab8b4fa..0a6e408 100644
--- a/convert.h
+++ b/convert.h
@@ -4,7 +4,7 @@
#ifndef CONVERT_H
#define CONVERT_H
-#include "hash-ll.h"
+#include "hash.h"
#include "string-list.h"
struct index_state;
@@ -92,7 +92,7 @@ void convert_attrs(struct index_state *istate,
struct conv_attrs *ca, const char *path);
extern enum eol core_eol;
-extern const char *check_roundtrip_encoding;
+extern char *check_roundtrip_encoding;
const char *get_cached_convert_stats_ascii(struct index_state *istate,
const char *path);
const char *get_wt_convert_stats_ascii(const char *path);
diff --git a/credential.c b/credential.c
index 18098bd..4b1a2b9 100644
--- a/credential.c
+++ b/credential.c
@@ -20,18 +20,68 @@ void credential_init(struct credential *c)
void credential_clear(struct credential *c)
{
+ credential_clear_secrets(c);
free(c->protocol);
free(c->host);
free(c->path);
free(c->username);
- free(c->password);
free(c->oauth_refresh_token);
+ free(c->authtype);
string_list_clear(&c->helpers, 0);
strvec_clear(&c->wwwauth_headers);
+ strvec_clear(&c->state_headers);
+ strvec_clear(&c->state_headers_to_send);
credential_init(c);
}
+void credential_next_state(struct credential *c)
+{
+ strvec_clear(&c->state_headers_to_send);
+ SWAP(c->state_headers, c->state_headers_to_send);
+}
+
+void credential_clear_secrets(struct credential *c)
+{
+ FREE_AND_NULL(c->password);
+ FREE_AND_NULL(c->credential);
+}
+
+static void credential_set_capability(struct credential_capability *capa,
+ enum credential_op_type op_type)
+{
+ switch (op_type) {
+ case CREDENTIAL_OP_INITIAL:
+ capa->request_initial = 1;
+ break;
+ case CREDENTIAL_OP_HELPER:
+ capa->request_helper = 1;
+ break;
+ case CREDENTIAL_OP_RESPONSE:
+ capa->response = 1;
+ break;
+ }
+}
+
+
+void credential_set_all_capabilities(struct credential *c,
+ enum credential_op_type op_type)
+{
+ credential_set_capability(&c->capa_authtype, op_type);
+ credential_set_capability(&c->capa_state, op_type);
+}
+
+static void announce_one(struct credential_capability *cc, const char *name, FILE *fp) {
+ if (cc->request_initial)
+ fprintf(fp, "capability %s\n", name);
+}
+
+void credential_announce_capabilities(struct credential *c, FILE *fp) {
+ fprintf(fp, "version 0\n");
+ announce_one(&c->capa_authtype, "authtype", fp);
+ announce_one(&c->capa_state, "state", fp);
+}
+
int credential_match(const struct credential *want,
const struct credential *have, int match_password)
{
@@ -40,7 +90,8 @@ int credential_match(const struct credential *want,
CHECK(host) &&
CHECK(path) &&
CHECK(username) &&
- (!match_password || CHECK(password));
+ (!match_password || CHECK(password)) &&
+ (!match_password || CHECK(credential));
#undef CHECK
}
@@ -208,7 +259,26 @@ static void credential_getpass(struct credential *c)
PROMPT_ASKPASS);
}
-int credential_read(struct credential *c, FILE *fp)
+int credential_has_capability(const struct credential_capability *capa,
+ enum credential_op_type op_type)
+{
+ /*
+ * We're checking here if each previous step indicated that we had the
+ * capability. If it did, then we want to pass it along; conversely, if
+ * it did not, we don't want to report that to our caller.
+ */
+ switch (op_type) {
+ case CREDENTIAL_OP_HELPER:
+ return capa->request_initial;
+ case CREDENTIAL_OP_RESPONSE:
+ return capa->request_initial && capa->request_helper;
+ default:
+ return 0;
+ }
+}
+
+int credential_read(struct credential *c, FILE *fp,
+ enum credential_op_type op_type)
{
struct strbuf line = STRBUF_INIT;
@@ -233,6 +303,9 @@ int credential_read(struct credential *c, FILE *fp)
} else if (!strcmp(key, "password")) {
free(c->password);
c->password = xstrdup(value);
+ } else if (!strcmp(key, "credential")) {
+ free(c->credential);
+ c->credential = xstrdup(value);
} else if (!strcmp(key, "protocol")) {
free(c->protocol);
c->protocol = xstrdup(value);
@@ -242,8 +315,19 @@ int credential_read(struct credential *c, FILE *fp)
} else if (!strcmp(key, "path")) {
free(c->path);
c->path = xstrdup(value);
+ } else if (!strcmp(key, "ephemeral")) {
+ c->ephemeral = !!git_config_bool("ephemeral", value);
} else if (!strcmp(key, "wwwauth[]")) {
strvec_push(&c->wwwauth_headers, value);
+ } else if (!strcmp(key, "state[]")) {
+ strvec_push(&c->state_headers, value);
+ } else if (!strcmp(key, "capability[]")) {
+ if (!strcmp(value, "authtype"))
+ credential_set_capability(&c->capa_authtype, op_type);
+ else if (!strcmp(value, "state"))
+ credential_set_capability(&c->capa_state, op_type);
+ } else if (!strcmp(key, "continue")) {
+ c->multistage = !!git_config_bool("continue", value);
} else if (!strcmp(key, "password_expiry_utc")) {
errno = 0;
c->password_expiry_utc = parse_timestamp(value, NULL, 10);
@@ -252,6 +336,9 @@ int credential_read(struct credential *c, FILE *fp)
} else if (!strcmp(key, "oauth_refresh_token")) {
free(c->oauth_refresh_token);
c->oauth_refresh_token = xstrdup(value);
+ } else if (!strcmp(key, "authtype")) {
+ free(c->authtype);
+ c->authtype = xstrdup(value);
} else if (!strcmp(key, "url")) {
credential_from_url(c, value);
} else if (!strcmp(key, "quit")) {
@@ -280,8 +367,20 @@ static void credential_write_item(FILE *fp, const char *key, const char *value,
fprintf(fp, "%s=%s\n", key, value);
}
-void credential_write(const struct credential *c, FILE *fp)
+void credential_write(const struct credential *c, FILE *fp,
+ enum credential_op_type op_type)
{
+ if (credential_has_capability(&c->capa_authtype, op_type))
+ credential_write_item(fp, "capability[]", "authtype", 0);
+ if (credential_has_capability(&c->capa_state, op_type))
+ credential_write_item(fp, "capability[]", "state", 0);
+
+ if (credential_has_capability(&c->capa_authtype, op_type)) {
+ credential_write_item(fp, "authtype", c->authtype, 0);
+ credential_write_item(fp, "credential", c->credential, 0);
+ if (c->ephemeral)
+ credential_write_item(fp, "ephemeral", "1", 0);
+ }
credential_write_item(fp, "protocol", c->protocol, 1);
credential_write_item(fp, "host", c->host, 1);
credential_write_item(fp, "path", c->path, 0);
@@ -295,6 +394,12 @@ void credential_write(const struct credential *c, FILE *fp)
}
for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
+ if (credential_has_capability(&c->capa_state, op_type)) {
+ if (c->multistage)
+ credential_write_item(fp, "continue", "1", 0);
+ for (size_t i = 0; i < c->state_headers_to_send.nr; i++)
+ credential_write_item(fp, "state[]", c->state_headers_to_send.v[i], 0);
+ }
}
static int run_credential_helper(struct credential *c,
@@ -317,14 +422,14 @@ static int run_credential_helper(struct credential *c,
fp = xfdopen(helper.in, "w");
sigchain_push(SIGPIPE, SIG_IGN);
- credential_write(c, fp);
+ credential_write(c, fp, want_output ? CREDENTIAL_OP_HELPER : CREDENTIAL_OP_RESPONSE);
fclose(fp);
sigchain_pop(SIGPIPE);
if (want_output) {
int r;
fp = xfdopen(helper.out, "r");
- r = credential_read(c, fp);
+ r = credential_read(c, fp, CREDENTIAL_OP_HELPER);
fclose(fp);
if (r < 0) {
finish_command(&helper);
@@ -357,32 +462,45 @@ static int credential_do(struct credential *c, const char *helper,
return r;
}
-void credential_fill(struct credential *c)
+void credential_fill(struct credential *c, int all_capabilities)
{
int i;
- if (c->username && c->password)
+ if ((c->username && c->password) || c->credential)
return;
+ credential_next_state(c);
+ c->multistage = 0;
+
credential_apply_config(c);
+ if (all_capabilities)
+ credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
for (i = 0; i < c->helpers.nr; i++) {
credential_do(c, c->helpers.items[i].string, "get");
+
if (c->password_expiry_utc < time(NULL)) {
- /* Discard expired password */
- FREE_AND_NULL(c->password);
+ /*
+ * Don't use credential_clear() here: callers such as
+ * cmd_credential() expect to still be able to call
+ * credential_write() on a struct credential whose
+ * secrets have expired.
+ */
+ credential_clear_secrets(c);
/* Reset expiry to maintain consistency */
c->password_expiry_utc = TIME_MAX;
}
- if (c->username && c->password)
+ if ((c->username && c->password) || c->credential) {
+ strvec_clear(&c->wwwauth_headers);
return;
+ }
if (c->quit)
die("credential helper '%s' told us to quit",
c->helpers.items[i].string);
}
credential_getpass(c);
- if (!c->username && !c->password)
+ if (!c->username && !c->password && !c->credential)
die("unable to get password from user");
}
@@ -392,9 +510,11 @@ void credential_approve(struct credential *c)
if (c->approved)
return;
- if (!c->username || !c->password || c->password_expiry_utc < time(NULL))
+ if (((!c->username || !c->password) && !c->credential) || c->password_expiry_utc < time(NULL))
return;
+ credential_next_state(c);
+
credential_apply_config(c);
for (i = 0; i < c->helpers.nr; i++)
@@ -406,13 +526,15 @@ void credential_reject(struct credential *c)
{
int i;
+ credential_next_state(c);
+
credential_apply_config(c);
for (i = 0; i < c->helpers.nr; i++)
credential_do(c, c->helpers.items[i].string, "erase");
+ credential_clear_secrets(c);
FREE_AND_NULL(c->username);
- FREE_AND_NULL(c->password);
FREE_AND_NULL(c->oauth_refresh_token);
c->password_expiry_utc = TIME_MAX;
c->approved = 0;
diff --git a/credential.h b/credential.h
index acc41ad..5f9e6ff 100644
--- a/credential.h
+++ b/credential.h
@@ -5,8 +5,8 @@
#include "strvec.h"
/**
- * The credentials API provides an abstracted way of gathering username and
- * password credentials from the user.
+ * The credentials API provides an abstracted way of gathering
+ * authentication credentials from the user.
*
* Typical setup
* -------------
@@ -93,13 +93,35 @@
* -----------------------------------------------------------------------
*/
+/*
+ * These values define the kind of operation we're performing and the
+ * capabilities at each stage. The first is either an external request (via git
+ * credential fill) or an internal request (e.g., via the HTTP) code. The
+ * second is the call to the credential helper, and the third is the response
+ * we're providing.
+ *
+ * At each stage, we will emit the capability only if the previous stage
+ * supported it.
+ */
+enum credential_op_type {
+ CREDENTIAL_OP_INITIAL = 1,
+ CREDENTIAL_OP_HELPER = 2,
+ CREDENTIAL_OP_RESPONSE = 3,
+};
+
+struct credential_capability {
+ unsigned request_initial:1,
+ request_helper:1,
+ response:1;
+};
/**
- * This struct represents a single username/password combination
- * along with any associated context. All string fields should be
- * heap-allocated (or NULL if they are not known or not applicable).
- * The meaning of the individual context fields is the same as
- * their counterparts in the helper protocol.
+ * This struct represents a single login credential (typically a
+ * username/password combination) along with any associated
+ * context. All string fields should be heap-allocated (or NULL if
+ * they are not known or not applicable). The meaning of the
+ * individual context fields is the same as their counterparts in
+ * the helper protocol.
*
* This struct should always be initialized with `CREDENTIAL_INIT` or
* `credential_init`.
@@ -124,6 +146,16 @@ struct credential {
struct strvec wwwauth_headers;
/**
+ * A `strvec` of state headers received from credential helpers.
+ */
+ struct strvec state_headers;
+
+ /**
+ * A `strvec` of state headers to send to credential helpers.
+ */
+ struct strvec state_headers_to_send;
+
+ /**
* Internal use only. Keeps track of if we previously matched against a
* WWW-Authenticate header line in order to re-fold future continuation
* lines into one value.
@@ -131,24 +163,38 @@ struct credential {
unsigned header_is_last_match:1;
unsigned approved:1,
+ ephemeral:1,
configured:1,
+ multistage: 1,
quit:1,
use_http_path:1,
username_from_proto:1;
+ struct credential_capability capa_authtype;
+ struct credential_capability capa_state;
+
char *username;
char *password;
+ char *credential;
char *protocol;
char *host;
char *path;
char *oauth_refresh_token;
timestamp_t password_expiry_utc;
+
+ /**
+ * The authorization scheme to use. If this is NULL, libcurl is free to
+ * negotiate any scheme it likes.
+ */
+ char *authtype;
};
#define CREDENTIAL_INIT { \
.helpers = STRING_LIST_INIT_DUP, \
.password_expiry_utc = TIME_MAX, \
.wwwauth_headers = STRVEC_INIT, \
+ .state_headers = STRVEC_INIT, \
+ .state_headers_to_send = STRVEC_INIT, \
}
/* Initialize a credential structure, setting all fields to empty. */
@@ -162,13 +208,17 @@ void credential_clear(struct credential *);
/**
* Instruct the credential subsystem to fill the username and
- * password fields of the passed credential struct by first
- * consulting helpers, then asking the user. After this function
- * returns, the username and password fields of the credential are
- * guaranteed to be non-NULL. If an error occurs, the function will
- * die().
+ * password (or authtype and credential) fields of the passed
+ * credential struct by first consulting helpers, then asking the
+ * user. After this function returns, either the username and
+ * password fields or the credential field of the credential are
+ * guaranteed to be non-NULL. If an error occurs, the function
+ * will die().
+ *
+ * If all_capabilities is set, this is an internal user that is prepared
+ * to deal with all known capabilities, and we should advertise that fact.
*/
-void credential_fill(struct credential *);
+void credential_fill(struct credential *, int all_capabilities);
/**
* Inform the credential subsystem that the provided credentials
@@ -184,15 +234,53 @@ void credential_approve(struct credential *);
* have been rejected. This will cause the credential subsystem to
* notify any helpers of the rejection (which allows them, for
* example, to purge the invalid credentials from storage). It
- * will also free() the username and password fields of the
- * credential and set them to NULL (readying the credential for
- * another call to `credential_fill`). Any errors from helpers are
- * ignored.
+ * will also free() the username, password, and credential fields
+ * of the credential and set them to NULL (readying the credential
+ * for another call to `credential_fill`). Any errors from helpers
+ * are ignored.
*/
void credential_reject(struct credential *);
-int credential_read(struct credential *, FILE *);
-void credential_write(const struct credential *, FILE *);
+/**
+ * Enable all of the supported credential flags in this credential.
+ */
+void credential_set_all_capabilities(struct credential *c,
+ enum credential_op_type op_type);
+
+/**
+ * Clear the secrets in this credential, but leave other data intact.
+ *
+ * This is useful for resetting credentials in preparation for a subsequent
+ * stage of filling.
+ */
+void credential_clear_secrets(struct credential *c);
+
+/**
+ * Print a list of supported capabilities and version numbers to standard
+ * output.
+ */
+void credential_announce_capabilities(struct credential *c, FILE *fp);
+
+/**
+ * Prepares the credential for the next iteration of the helper protocol by
+ * updating the state headers to send with the ones read by the last iteration
+ * of the protocol.
+ *
+ * Except for internal callers, this should be called exactly once between
+ * reading credentials with `credential_fill` and writing them.
+ */
+void credential_next_state(struct credential *c);
+
+/**
+ * Return true if the capability is enabled for an operation of op_type.
+ */
+int credential_has_capability(const struct credential_capability *capa,
+ enum credential_op_type op_type);
+
+int credential_read(struct credential *, FILE *,
+ enum credential_op_type);
+void credential_write(const struct credential *, FILE *,
+ enum credential_op_type);
/*
* Parse a url into a credential struct, replacing any existing contents.
diff --git a/csum-file.c b/csum-file.c
index 870748e..8abbf01 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -7,6 +7,9 @@
* files. Useful when you write a file that you want to be
* able to verify hasn't been messed with afterwards.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "progress.h"
#include "csum-file.h"
@@ -68,12 +71,12 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
hashflush(f);
if (f->skip_hash)
- hashclr(f->buffer);
+ hashclr(f->buffer, the_repository->hash_algo);
else
the_hash_algo->final_fn(f->buffer, &f->ctx);
if (result)
- hashcpy(result, f->buffer);
+ hashcpy(result, f->buffer, the_repository->hash_algo);
if (flags & CSUM_HASH_IN_STREAM)
flush(f, f->buffer, the_hash_algo->rawsz);
if (flags & CSUM_FSYNC)
@@ -237,5 +240,5 @@ int hashfile_checksum_valid(const unsigned char *data, size_t total_len)
the_hash_algo->update_fn(&ctx, data, data_len);
the_hash_algo->final_fn(got, &ctx);
- return hasheq(got, data + data_len);
+ return hasheq(got, data + data_len, the_repository->hash_algo);
}
diff --git a/csum-file.h b/csum-file.h
index bc5bec2..566e05c 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -1,7 +1,7 @@
#ifndef CSUM_FILE_H
#define CSUM_FILE_H
-#include "hash-ll.h"
+#include "hash.h"
#include "write-or-die.h"
struct progress;
diff --git a/decorate.h b/decorate.h
index cdeb17c..08af658 100644
--- a/decorate.h
+++ b/decorate.h
@@ -3,7 +3,7 @@
/*
* A data structure that associates Git objects to void pointers. See
- * t/helper/test-example-decorate.c for a demonstration of how to use these
+ * t/unit-tests/t-example-decorate.c for a demonstration of how to use these
* functions.
*/
diff --git a/delta-islands.c b/delta-islands.c
index f7e0794..ffe1ca2 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object.h"
#include "commit.h"
@@ -313,7 +315,7 @@ struct island_load_data {
size_t nr;
size_t alloc;
};
-static const char *core_island_name;
+static char *core_island_name;
static void free_config_regexes(struct island_load_data *ild)
{
@@ -488,7 +490,8 @@ void load_delta_islands(struct repository *r, int progress)
git_config(island_config_callback, &ild);
ild.remote_islands = kh_init_str();
- for_each_ref(find_island_for_ref, &ild);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ find_island_for_ref, &ild);
free_config_regexes(&ild);
deduplicate_islands(ild.remote_islands, r);
free_remote_islands(ild.remote_islands);
diff --git a/diagnose.c b/diagnose.c
index 4d096c8..cc2d535 100644
--- a/diagnose.c
+++ b/diagnose.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "diagnose.h"
#include "compat/disk.h"
diff --git a/diff-lib.c b/diff-lib.c
index 12b1541..b0d0f71 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -1,6 +1,9 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "diff.h"
@@ -66,7 +69,8 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
* a directory --- the blob was removed!
*/
if (!S_ISGITLINK(ce->ce_mode) &&
- resolve_gitlink_ref(ce->name, "HEAD", &sub))
+ repo_resolve_gitlink_ref(the_repository, ce->name,
+ "HEAD", &sub))
return 1;
}
return 0;
@@ -159,7 +163,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
dpath->next = NULL;
memcpy(dpath->path, ce->name, path_len);
dpath->path[path_len] = '\0';
- oidclr(&dpath->oid);
+ oidclr(&dpath->oid, the_repository->hash_algo);
memset(&(dpath->parent[0]), 0,
sizeof(struct combine_diff_parent)*5);
@@ -411,7 +415,7 @@ static int show_modified(struct rev_info *revs,
memcpy(p->path, new_entry->name, pathlen);
p->path[pathlen] = 0;
p->mode = mode;
- oidclr(&p->oid);
+ oidclr(&p->oid, the_repository->hash_algo);
memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
p->parent[0].status = DIFF_STATUS_MODIFIED;
p->parent[0].mode = new_entry->ce_mode;
diff --git a/diff.c b/diff.c
index 108c187..7e9041a 100644
--- a/diff.c
+++ b/diff.c
@@ -1,6 +1,9 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "base85.h"
@@ -56,14 +59,14 @@ static int diff_color_moved_default;
static int diff_color_moved_ws_default;
static int diff_context_default = 3;
static int diff_interhunk_context_default;
-static const char *diff_word_regex_cfg;
-static const char *external_diff_cmd_cfg;
-static const char *diff_order_file_cfg;
+static char *diff_word_regex_cfg;
+static struct external_diff external_diff_cfg;
+static char *diff_order_file_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
-static const char *diff_src_prefix = "a/";
-static const char *diff_dst_prefix = "b/";
+static char *diff_src_prefix;
+static char *diff_dst_prefix;
static int diff_relative;
static int diff_stat_name_width;
static int diff_stat_graph_width;
@@ -411,9 +414,11 @@ int git_diff_ui_config(const char *var, const char *value,
return 0;
}
if (!strcmp(var, "diff.srcprefix")) {
+ FREE_AND_NULL(diff_src_prefix);
return git_config_string(&diff_src_prefix, var, value);
}
if (!strcmp(var, "diff.dstprefix")) {
+ FREE_AND_NULL(diff_dst_prefix);
return git_config_string(&diff_dst_prefix, var, value);
}
if (!strcmp(var, "diff.relative")) {
@@ -429,7 +434,11 @@ int git_diff_ui_config(const char *var, const char *value,
return 0;
}
if (!strcmp(var, "diff.external"))
- return git_config_string(&external_diff_cmd_cfg, var, value);
+ return git_config_string(&external_diff_cfg.cmd, var, value);
+ if (!strcmp(var, "diff.trustexitcode")) {
+ external_diff_cfg.trust_exit_code = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "diff.wordregex"))
return git_config_string(&diff_word_regex_cfg, var, value);
if (!strcmp(var, "diff.orderfile"))
@@ -546,18 +555,22 @@ static char *quote_two(const char *one, const char *two)
return strbuf_detach(&res, NULL);
}
-static const char *external_diff(void)
+static const struct external_diff *external_diff(void)
{
- static const char *external_diff_cmd = NULL;
+ static struct external_diff external_diff_env, *external_diff_ptr;
static int done_preparing = 0;
if (done_preparing)
- return external_diff_cmd;
- external_diff_cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
- if (!external_diff_cmd)
- external_diff_cmd = external_diff_cmd_cfg;
+ return external_diff_ptr;
+ external_diff_env.cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
+ if (git_env_bool("GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE", 0))
+ external_diff_env.trust_exit_code = 1;
+ if (external_diff_env.cmd)
+ external_diff_ptr = &external_diff_env;
+ else if (external_diff_cfg.cmd)
+ external_diff_ptr = &external_diff_cfg;
done_preparing = 1;
- return external_diff_cmd;
+ return external_diff_ptr;
}
/*
@@ -3433,8 +3446,8 @@ void diff_set_noprefix(struct diff_options *options)
void diff_set_default_prefix(struct diff_options *options)
{
- options->a_prefix = diff_src_prefix;
- options->b_prefix = diff_dst_prefix;
+ options->a_prefix = diff_src_prefix ? diff_src_prefix : "a/";
+ options->b_prefix = diff_dst_prefix ? diff_dst_prefix : "b/";
}
struct userdiff_driver *get_textconv(struct repository *r,
@@ -3762,7 +3775,7 @@ static void builtin_diff(const char *name_a,
return;
}
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
{
if (!is_renamed) {
if (p->status == DIFF_STATUS_ADDED) {
@@ -4074,7 +4087,7 @@ static int reuse_worktree_file(struct index_state *istate,
static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
{
struct strbuf buf = STRBUF_INIT;
- char *dirty = "";
+ const char *dirty = "";
/* Are we looking at the work tree? */
if (s->dirty_submodule)
@@ -4373,7 +4386,7 @@ static void add_external_diff_name(struct repository *r,
* infile2 infile2-sha1 infile2-mode [ rename-to ]
*
*/
-static void run_external_diff(const char *pgm,
+static void run_external_diff(const struct external_diff *pgm,
const char *name,
const char *other,
struct diff_filespec *one,
@@ -4383,8 +4396,21 @@ static void run_external_diff(const char *pgm,
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct diff_queue_struct *q = &diff_queued_diff;
+ int quiet = !(o->output_format & DIFF_FORMAT_PATCH);
+ int rc;
- strvec_push(&cmd.args, pgm);
+ /*
+ * Trivial equality is handled by diff_unmodified_pair() before
+ * we get here. If we don't need to show the diff and the
+ * external diff program lacks the ability to tell us whether
+ * it's empty then we consider it non-empty without even asking.
+ */
+ if (!pgm->trust_exit_code && quiet) {
+ o->found_changes = 1;
+ return;
+ }
+
+ strvec_push(&cmd.args, pgm->cmd);
strvec_push(&cmd.args, name);
if (one && two) {
@@ -4404,7 +4430,15 @@ static void run_external_diff(const char *pgm,
diff_free_filespec_data(one);
diff_free_filespec_data(two);
cmd.use_shell = 1;
- if (run_command(&cmd))
+ cmd.no_stdout = quiet;
+ rc = run_command(&cmd);
+ if (!pgm->trust_exit_code && rc == 0)
+ o->found_changes = 1;
+ else if (pgm->trust_exit_code && rc == 0)
+ ; /* nothing */
+ else if (pgm->trust_exit_code && rc == 1)
+ o->found_changes = 1;
+ else
die(_("external diff died, stopping at %s"), name);
remove_tempfile();
@@ -4510,7 +4544,7 @@ static void fill_metainfo(struct strbuf *msg,
}
}
-static void run_diff_cmd(const char *pgm,
+static void run_diff_cmd(const struct external_diff *pgm,
const char *name,
const char *other,
const char *attr_path,
@@ -4528,8 +4562,8 @@ static void run_diff_cmd(const char *pgm,
if (o->flags.allow_external || !o->ignore_driver_algorithm)
drv = userdiff_find_by_path(o->repo->index, attr_path);
- if (o->flags.allow_external && drv && drv->external)
- pgm = drv->external;
+ if (o->flags.allow_external && drv && drv->external.cmd)
+ pgm = &drv->external;
if (msg) {
/*
@@ -4555,6 +4589,7 @@ static void run_diff_cmd(const char *pgm,
o, complete_rewrite);
} else {
fprintf(o->file, "* Unmerged path %s\n", name);
+ o->found_changes = 1;
}
}
@@ -4564,7 +4599,7 @@ static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *is
if (!one->oid_valid) {
struct stat st;
if (one->is_stdin) {
- oidclr(&one->oid);
+ oidclr(&one->oid, the_repository->hash_algo);
return;
}
if (lstat(one->path, &st) < 0)
@@ -4574,7 +4609,7 @@ static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *is
}
}
else
- oidclr(&one->oid);
+ oidclr(&one->oid, the_repository->hash_algo);
}
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
@@ -4594,7 +4629,7 @@ static void strip_prefix(int prefix_length, const char **namep, const char **oth
static void run_diff(struct diff_filepair *p, struct diff_options *o)
{
- const char *pgm = external_diff();
+ const struct external_diff *pgm = external_diff();
struct strbuf msg;
struct diff_filespec *one = p->one;
struct diff_filespec *two = p->two;
@@ -4921,12 +4956,20 @@ void diff_setup_done(struct diff_options *options)
options->flags.exit_with_status = 1;
}
+ /*
+ * External diffs could declare non-identical contents equal
+ * (think diff --ignore-space-change).
+ */
+ if (options->flags.allow_external && options->flags.exit_with_status)
+ options->flags.diff_from_contents = 1;
+
options->diff_path_counter = 0;
if (options->flags.follow_renames)
diff_check_follow_pathspec(&options->pathspec, 1);
- if (!options->use_color || external_diff())
+ if (!options->use_color ||
+ (options->flags.allow_external && external_diff()))
options->color_moved = 0;
if (options->filter_not) {
@@ -5370,8 +5413,8 @@ static int diff_opt_default_prefix(const struct option *opt,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(optarg);
- diff_src_prefix = "a/";
- diff_dst_prefix = "b/";
+ FREE_AND_NULL(diff_src_prefix);
+ FREE_AND_NULL(diff_dst_prefix);
diff_set_default_prefix(options);
return 0;
}
@@ -6401,7 +6444,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
the_hash_algo->init_fn(&ctx);
memset(&data, 0, sizeof(struct patch_id_t));
data.ctx = &ctx;
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
for (i = 0; i < q->nr; i++) {
xpparam_t xpp;
@@ -7232,7 +7275,7 @@ size_t fill_textconv(struct repository *r,
if (!driver) {
if (!DIFF_FILE_VALID(df)) {
- *outbuf = "";
+ *outbuf = (char *) "";
return 0;
}
if (diff_populate_filespec(r, df, NULL))
diff --git a/diff.h b/diff.h
index 66bd8ae..9901c8c 100644
--- a/diff.h
+++ b/diff.h
@@ -4,7 +4,7 @@
#ifndef DIFF_H
#define DIFF_H
-#include "hash-ll.h"
+#include "hash.h"
#include "pathspec.h"
#include "strbuf.h"
diff --git a/diffcore-break.c b/diffcore-break.c
index 49ba38a..831b66b 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -1,6 +1,9 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "diffcore.h"
#include "hash.h"
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5a6e2bc..fab45b1 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -2,6 +2,9 @@
*
* Copyright (C) 2005 Junio C Hamano
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "diff.h"
#include "diffcore.h"
@@ -406,7 +409,7 @@ static const char *get_highest_rename_path(struct strintmap *counts)
return highest_destination_dir;
}
-static char *UNKNOWN_DIR = "/"; /* placeholder -- short, illegal directory */
+static const char *UNKNOWN_DIR = "/"; /* placeholder -- short, illegal directory */
static int dir_rename_already_determinable(struct strintmap *counts)
{
@@ -429,8 +432,8 @@ static int dir_rename_already_determinable(struct strintmap *counts)
}
static void increment_count(struct dir_rename_info *info,
- char *old_dir,
- char *new_dir)
+ const char *old_dir,
+ const char *new_dir)
{
struct strintmap *counts;
struct strmap_entry *e;
@@ -1422,7 +1425,7 @@ void diffcore_rename_extended(struct diff_options *options,
strcmp(options->single_follow, p->two->path))
continue; /* not interested */
else if (!options->flags.rename_empty &&
- is_empty_blob_oid(&p->two->oid))
+ is_empty_blob_oid(&p->two->oid, the_repository->hash_algo))
continue;
else if (add_rename_dst(p) < 0) {
warning("skipping rename detection, detected"
@@ -1432,7 +1435,7 @@ void diffcore_rename_extended(struct diff_options *options,
}
}
else if (!options->flags.rename_empty &&
- is_empty_blob_oid(&p->one->oid))
+ is_empty_blob_oid(&p->one->oid, the_repository->hash_algo))
continue;
else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
/*
diff --git a/diffcore.h b/diffcore.h
index 5ffe4ec..1701ed5 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -4,7 +4,7 @@
#ifndef DIFFCORE_H
#define DIFFCORE_H
-#include "hash-ll.h"
+#include "hash.h"
struct diff_options;
struct mem_pool;
diff --git a/dir.c b/dir.c
index f0e6570..b7a6625 100644
--- a/dir.c
+++ b/dir.c
@@ -5,6 +5,9 @@
* Copyright (C) Linus Torvalds, 2005-2006
* Junio Hamano, 2005-2006
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -733,6 +736,17 @@ static char *dup_and_filter_pattern(const char *pattern)
return result;
}
+static void clear_pattern_entry_hashmap(struct hashmap *map)
+{
+ struct hashmap_iter iter;
+ struct pattern_entry *entry;
+
+ hashmap_for_each_entry(map, &iter, entry, ent) {
+ free(entry->pattern);
+ }
+ hashmap_clear_and_free(map, struct pattern_entry, ent);
+}
+
static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
{
struct pattern_entry *translated;
@@ -806,6 +820,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
if (given->patternlen > 2 &&
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
+ struct pattern_entry *old;
+
if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
/* Not a cone pattern. */
warning(_("unrecognized pattern: '%s'"), given->pattern);
@@ -831,7 +847,11 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
}
hashmap_add(&pl->parent_hashmap, &translated->ent);
- hashmap_remove(&pl->recursive_hashmap, &translated->ent, &data);
+ old = hashmap_remove_entry(&pl->recursive_hashmap, translated, ent, &data);
+ if (old) {
+ free(old->pattern);
+ free(old);
+ }
free(data);
return;
}
@@ -862,8 +882,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
clear_hashmaps:
warning(_("disabling cone pattern matching"));
- hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
- hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
+ clear_pattern_entry_hashmap(&pl->recursive_hashmap);
+ clear_pattern_entry_hashmap(&pl->parent_hashmap);
pl->use_cone_patterns = 0;
}
@@ -915,12 +935,7 @@ void add_pattern(const char *string, const char *base,
int nowildcardlen;
parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen);
- if (flags & PATTERN_FLAG_MUSTBEDIR) {
- FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen);
- } else {
- pattern = xmalloc(sizeof(*pattern));
- pattern->pattern = string;
- }
+ FLEX_ALLOC_MEM(pattern, pattern, string, patternlen);
pattern->patternlen = patternlen;
pattern->nowildcardlen = nowildcardlen;
pattern->base = base;
@@ -962,9 +977,8 @@ void clear_pattern_list(struct pattern_list *pl)
for (i = 0; i < pl->nr; i++)
free(pl->patterns[i]);
free(pl->patterns);
- free(pl->filebuf);
- hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
- hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
+ clear_pattern_entry_hashmap(&pl->recursive_hashmap);
+ clear_pattern_entry_hashmap(&pl->parent_hashmap);
memset(pl, 0, sizeof(*pl));
}
@@ -1162,6 +1176,7 @@ static int add_patterns(const char *fname, const char *base, int baselen,
}
add_patterns_from_buffer(buf, size, base, baselen, pl);
+ free(buf);
return 0;
}
@@ -1169,16 +1184,15 @@ static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl)
{
+ char *orig = buf;
int i, lineno = 1;
char *entry;
hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
- pl->filebuf = buf;
-
if (skip_utf8_bom(&buf, size))
- size -= buf - pl->filebuf;
+ size -= buf - orig;
entry = buf;
@@ -1225,6 +1239,7 @@ int add_patterns_from_blob_to_list(
}
add_patterns_from_buffer(buf, size, base, baselen, pl);
+ free(buf);
return 0;
}
@@ -1675,7 +1690,7 @@ static void prep_exclude(struct dir_struct *dir,
}
/* Try to read per-directory file */
- oidclr(&oid_stat.oid);
+ oidclr(&oid_stat.oid, the_repository->hash_algo);
oid_stat.valid = 0;
if (dir->exclude_per_dir &&
/*
@@ -3338,7 +3353,8 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
struct object_id submodule_head;
if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
- !resolve_gitlink_ref(path->buf, "HEAD", &submodule_head)) {
+ !repo_resolve_gitlink_ref(the_repository, path->buf,
+ "HEAD", &submodule_head)) {
/* Do not descend and nuke a nested git work tree. */
if (kept_up)
*kept_up = 1;
@@ -3781,7 +3797,7 @@ static void read_oid(size_t pos, void *cb)
rd->data = rd->end + 1;
return;
}
- oidread(&ud->exclude_oid, rd->data);
+ oidread(&ud->exclude_oid, rd->data, the_repository->hash_algo);
rd->data += the_hash_algo->rawsz;
}
@@ -3789,7 +3805,7 @@ static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
const unsigned char *sha1)
{
stat_data_from_disk(&oid_stat->stat, data);
- oidread(&oid_stat->oid, sha1);
+ oidread(&oid_stat->oid, sha1, the_repository->hash_algo);
oid_stat->valid = 1;
}
diff --git a/dir.h b/dir.h
index b9e8e96..69a76d8 100644
--- a/dir.h
+++ b/dir.h
@@ -1,7 +1,7 @@
#ifndef DIR_H
#define DIR_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hashmap.h"
#include "pathspec.h"
#include "statinfo.h"
@@ -62,7 +62,6 @@ struct path_pattern {
*/
struct pattern_list *pl;
- const char *pattern;
int patternlen;
int nowildcardlen;
const char *base;
@@ -74,6 +73,8 @@ struct path_pattern {
* and from -1 decrementing for patterns from CLI args.
*/
int srcpos;
+
+ char pattern[FLEX_ARRAY];
};
/* used for hashmaps for cone patterns */
@@ -94,9 +95,6 @@ struct pattern_list {
int nr;
int alloc;
- /* remember pointer to exclude file contents so we can free() */
- char *filebuf;
-
/* origin of list, e.g. path to filename, or descriptive string */
const char *src;
diff --git a/entry.c b/entry.c
index b8c257f..e7ed440 100644
--- a/entry.c
+++ b/entry.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object-store-ll.h"
#include "dir.h"
@@ -167,6 +169,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
return !available;
}
+static int string_is_not_null(struct string_list_item *item, void *data UNUSED)
+{
+ return !!item->string;
+}
+
int finish_delayed_checkout(struct checkout *state, int show_progress)
{
int errs = 0;
@@ -189,7 +196,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
if (!async_query_available_blobs(filter->string, &available_paths)) {
/* Filter reported an error */
errs = 1;
- filter->string = "";
+ filter->string = NULL;
continue;
}
if (available_paths.nr <= 0) {
@@ -199,7 +206,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
* filter from the list (see
* "string_list_remove_empty_items" call below).
*/
- filter->string = "";
+ filter->string = NULL;
continue;
}
@@ -225,7 +232,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
* Do not ask the filter for available blobs,
* again, as the filter is likely buggy.
*/
- filter->string = "";
+ filter->string = NULL;
continue;
}
ce = index_file_exists(state->istate, path->string,
@@ -239,7 +246,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
errs = 1;
}
}
- string_list_remove_empty_items(&dco->filters, 0);
+
+ filter_string_list(&dco->filters, 0, string_is_not_null, NULL);
}
stop_progress(&progress);
string_list_clear(&dco->filters, 0);
diff --git a/environment.c b/environment.c
index a73ba9c..5cea2c9 100644
--- a/environment.c
+++ b/environment.c
@@ -7,6 +7,9 @@
* even if you might want to know where the git directory etc
* are.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "branch.h"
@@ -42,12 +45,12 @@ int is_bare_repository_cfg = -1; /* unspecified */
int warn_ambiguous_refs = 1;
int warn_on_object_refname_ambiguity = 1;
int repository_format_precious_objects;
-const char *git_commit_encoding;
-const char *git_log_output_encoding;
+char *git_commit_encoding;
+char *git_log_output_encoding;
char *apply_default_whitespace;
char *apply_default_ignorewhitespace;
-const char *git_attributes_file;
-const char *git_hooks_path;
+char *git_attributes_file;
+char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files = -1;
@@ -58,13 +61,13 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
size_t delta_base_cache_limit = 96 * 1024 * 1024;
unsigned long big_file_threshold = 512 * 1024 * 1024;
-const char *editor_program;
-const char *askpass_program;
-const char *excludes_file;
+char *editor_program;
+char *askpass_program;
+char *excludes_file;
enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
enum eol core_eol = EOL_UNSET;
int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
-const char *check_roundtrip_encoding = "SHIFT-JIS";
+char *check_roundtrip_encoding;
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
diff --git a/environment.h b/environment.h
index 05fd94d..e9f01d4 100644
--- a/environment.h
+++ b/environment.h
@@ -58,6 +58,13 @@ const char *getenv_safe(struct strvec *argv, const char *name);
#define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
/*
+ * Environment variable used to propagate the --no-advice global option to the
+ * advice_enabled() helper, even when run in a subprocess.
+ * This is an internal variable that should not be set by the user.
+ */
+#define GIT_ADVICE_ENVIRONMENT "GIT_ADVICE"
+
+/*
* Environment variable used in handshaking the wire protocol.
* Contains a colon ':' separated list of keys with optional values
* 'key[=value]'. Presence of unknown keys and values must be
@@ -124,8 +131,8 @@ extern int warn_ambiguous_refs;
extern int warn_on_object_refname_ambiguity;
extern char *apply_default_whitespace;
extern char *apply_default_ignorewhitespace;
-extern const char *git_attributes_file;
-extern const char *git_hooks_path;
+extern char *git_attributes_file;
+extern char *git_hooks_path;
extern int zlib_compression_level;
extern int pack_compression_level;
extern size_t packed_git_window_size;
@@ -217,12 +224,12 @@ int odb_pack_keep(const char *name);
const char *get_log_output_encoding(void);
const char *get_commit_output_encoding(void);
-extern const char *git_commit_encoding;
-extern const char *git_log_output_encoding;
+extern char *git_commit_encoding;
+extern char *git_log_output_encoding;
-extern const char *editor_program;
-extern const char *askpass_program;
-extern const char *excludes_file;
+extern char *editor_program;
+extern char *askpass_program;
+extern char *excludes_file;
/*
* Should we print an ellipsis after an abbreviated SHA-1 value
diff --git a/ewah/bitmap.c b/ewah/bitmap.c
index ac7e0af..55928da 100644
--- a/ewah/bitmap.c
+++ b/ewah/bitmap.c
@@ -138,6 +138,49 @@ void bitmap_or(struct bitmap *self, const struct bitmap *other)
self->words[i] |= other->words[i];
}
+int ewah_bitmap_is_subset(struct ewah_bitmap *self, struct bitmap *other)
+{
+ struct ewah_iterator it;
+ eword_t word;
+ size_t i;
+
+ ewah_iterator_init(&it, self);
+
+ for (i = 0; i < other->word_alloc; i++) {
+ if (!ewah_iterator_next(&word, &it)) {
+ /*
+ * If we reached the end of `self`, and haven't
+ * rejected `self` as a possible subset of
+ * `other` yet, then we are done and `self` is
+ * indeed a subset of `other`.
+ */
+ return 1;
+ }
+ if (word & ~other->words[i]) {
+ /*
+ * Otherwise, compare the next two pairs of
+ * words. If the word from `self` has bit(s) not
+ * in the word from `other`, `self` is not a
+ * subset of `other`.
+ */
+ return 0;
+ }
+ }
+
+ /*
+ * If we got to this point, there may be zero or more words
+ * remaining in `self`, with no remaining words left in `other`.
+ * If there are any bits set in the remaining word(s) in `self`,
+ * then `self` is not a subset of `other`.
+ */
+ while (ewah_iterator_next(&word, &it))
+ if (word)
+ return 0;
+
+ /* `self` is definitely a subset of `other` */
+ return 1;
+}
+
void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)
{
size_t original_size = self->word_alloc;
@@ -169,6 +212,20 @@ size_t bitmap_popcount(struct bitmap *self)
return count;
}
+size_t ewah_bitmap_popcount(struct ewah_bitmap *self)
+{
+ struct ewah_iterator it;
+ eword_t word;
+ size_t count = 0;
+
+ ewah_iterator_init(&it, self);
+
+ while (ewah_iterator_next(&word, &it))
+ count += ewah_bit_popcount64(word);
+
+ return count;
+}
+
int bitmap_is_empty(struct bitmap *self)
{
size_t i;
@@ -204,6 +261,25 @@ int bitmap_equals(struct bitmap *self, struct bitmap *other)
return 1;
}
+int bitmap_equals_ewah(struct bitmap *self, struct ewah_bitmap *other)
+{
+ struct ewah_iterator it;
+ eword_t word;
+ size_t i = 0;
+
+ ewah_iterator_init(&it, other);
+
+ while (ewah_iterator_next(&word, &it))
+ if (word != (i < self->word_alloc ? self->words[i++] : 0))
+ return 0;
+
+ for (; i < self->word_alloc; i++)
+ if (self->words[i])
+ return 0;
+
+ return 1;
+}
+
int bitmap_is_subset(struct bitmap *self, struct bitmap *other)
{
size_t common_size, i;
diff --git a/ewah/ewok.h b/ewah/ewok.h
index c11d76c..5e357e2 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -179,7 +179,14 @@ void bitmap_unset(struct bitmap *self, size_t pos);
int bitmap_get(struct bitmap *self, size_t pos);
void bitmap_free(struct bitmap *self);
int bitmap_equals(struct bitmap *self, struct bitmap *other);
+int bitmap_equals_ewah(struct bitmap *self, struct ewah_bitmap *other);
+
+/*
+ * Both `bitmap_is_subset()` and `ewah_bitmap_is_subset()` return 1 if the set
+ * of bits in 'self' are a subset of the bits in 'other'. Returns 0 otherwise.
+ */
int bitmap_is_subset(struct bitmap *self, struct bitmap *other);
+int ewah_bitmap_is_subset(struct ewah_bitmap *self, struct bitmap *other);
struct ewah_bitmap * bitmap_to_ewah(struct bitmap *bitmap);
struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah);
@@ -189,6 +196,7 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);
void bitmap_or(struct bitmap *self, const struct bitmap *other);
size_t bitmap_popcount(struct bitmap *self);
+size_t ewah_bitmap_popcount(struct ewah_bitmap *self);
int bitmap_is_empty(struct bitmap *self);
#endif
diff --git a/fetch-pack.c b/fetch-pack.c
index 8fb5648..ea8655d 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "repository.h"
#include "config.h"
@@ -290,7 +292,8 @@ static void mark_tips(struct fetch_negotiator *negotiator,
int i;
if (!negotiation_tips) {
- for_each_rawref(rev_list_insert_ref_oid, negotiator);
+ refs_for_each_rawref(get_main_ref_store(the_repository),
+ rev_list_insert_ref_oid, negotiator);
return;
}
@@ -788,7 +791,8 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
*/
trace2_region_enter("fetch-pack", "mark_complete_local_refs", NULL);
if (!args->deepen) {
- for_each_rawref(mark_complete_oid, NULL);
+ refs_for_each_rawref(get_main_ref_store(the_repository),
+ mark_complete_oid, NULL);
for_each_cached_alternate(NULL, mark_alternate_complete);
commit_list_sort_by_date(&complete);
if (cutoff)
@@ -1860,13 +1864,13 @@ static int fetch_pack_config_cb(const char *var, const char *value,
const char *msg_id;
if (strcmp(var, "fetch.fsck.skiplist") == 0) {
- const char *path;
+ char *path ;
if (git_config_pathname(&path, var, value))
return 1;
strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
fsck_msg_types.len ? ',' : '=', path);
- free((char *)path);
+ free(path);
return 0;
}
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index ae201e2..6acb37b 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -447,7 +449,7 @@ static void fmt_merge_msg_title(struct strbuf *out,
const char *current_branch)
{
int i = 0;
- char *sep = "";
+ const char *sep = "";
strbuf_addstr(out, "Merge ");
for (i = 0; i < srcs.nr; i++) {
@@ -661,7 +663,9 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
/* learn the commit that we merge into and the current branch name */
current_branch = current_branch_to_free =
- resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
+ refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING, &head_oid,
+ NULL);
if (!current_branch)
die("No current branch");
diff --git a/fsck.c b/fsck.c
index 78af29d..eea7145 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "date.h"
#include "dir.h"
@@ -203,7 +205,8 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
if (!strcmp(buf, "skiplist")) {
if (equal == len)
die("skiplist requires a path");
- oidset_parse_file(&options->skiplist, buf + equal + 1);
+ oidset_parse_file(&options->skiplist, buf + equal + 1,
+ the_repository->hash_algo);
buf += len + 1;
continue;
}
@@ -1179,7 +1182,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
}
int fsck_buffer(const struct object_id *oid, enum object_type type,
- void *data, unsigned long size,
+ const void *data, unsigned long size,
struct fsck_options *options)
{
if (type == OBJ_BLOB)
@@ -1274,13 +1277,13 @@ int git_fsck_config(const char *var, const char *value,
const char *msg_id;
if (strcmp(var, "fsck.skiplist") == 0) {
- const char *path;
+ char *path;
struct strbuf sb = STRBUF_INIT;
if (git_config_pathname(&path, var, value))
return 1;
strbuf_addf(&sb, "skiplist=%s", path);
- free((char *)path);
+ free(path);
fsck_set_msg_types(options, sb.buf);
strbuf_release(&sb);
return 0;
diff --git a/fsck.h b/fsck.h
index e3adf9d..6085a38 100644
--- a/fsck.h
+++ b/fsck.h
@@ -190,7 +190,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
* struct.
*/
int fsck_buffer(const struct object_id *oid, enum object_type,
- void *data, unsigned long size,
+ const void *data, unsigned long size,
struct fsck_options *options);
/*
diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c
index 45471b5..f1b1631 100644
--- a/fsmonitor-ipc.c
+++ b/fsmonitor-ipc.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "simple-ipc.h"
diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c
index a6a9e6b..e818583 100644
--- a/fsmonitor-settings.c
+++ b/fsmonitor-settings.c
@@ -103,6 +103,7 @@ static struct fsmonitor_settings *alloc_settings(void)
static void lookup_fsmonitor_settings(struct repository *r)
{
const char *const_str;
+ char *to_free = NULL;
int bool_value;
if (r->settings.fsmonitor)
@@ -129,8 +130,9 @@ static void lookup_fsmonitor_settings(struct repository *r)
break;
case -1: /* config value set to an arbitrary string */
- if (repo_config_get_pathname(r, "core.fsmonitor", &const_str))
+ if (repo_config_get_pathname(r, "core.fsmonitor", &to_free))
return; /* should not happen */
+ const_str = to_free;
break;
default: /* should not happen */
@@ -141,6 +143,7 @@ static void lookup_fsmonitor_settings(struct repository *r)
fsm_settings__set_hook(r, const_str);
else
fsm_settings__set_disabled(r);
+ free(to_free);
}
enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
diff --git a/git-send-email.perl b/git-send-email.perl
index 821b2b3..f0be4b4 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1664,9 +1664,11 @@
$smtp->code =~ /250|200/ or die sprintf(__("Failed to send %s\n"), $subject).$smtp->message;
}
if ($quiet) {
- printf($dry_run ? __("Dry-Sent %s\n") : __("Sent %s\n"), $subject);
+ printf($dry_run ? __("Dry-Sent %s") : __("Sent %s"), $subject);
+ print "\n";
} else {
- print($dry_run ? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));
+ print($dry_run ? __("Dry-OK. Log says:") : __("OK. Log says:"));
+ print "\n";
if (!defined $sendmail_cmd && !file_name_is_absolute($smtp_server)) {
print "Server: $smtp_server\n";
print "MAIL FROM:<$raw_from>\n";
@@ -1686,10 +1688,11 @@
print $header, "\n";
if ($smtp) {
print __("Result: "), $smtp->code, ' ',
- ($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
+ ($smtp->message =~ /\n([^\n]+\n)$/s);
} else {
- print __("Result: OK\n");
+ print __("Result: OK");
}
+ print "\n";
}
return 1;
diff --git a/git.c b/git.c
index 654d615..e35af9b 100644
--- a/git.c
+++ b/git.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "builtin.h"
#include "config.h"
#include "environment.h"
@@ -36,9 +38,10 @@ struct cmd_struct {
const char git_usage_string[] =
N_("git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
" [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
- " [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
- " [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
- " [--config-env=<name>=<envvar>] <command> [<args>]");
+ " [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n"
+ " [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]\n"
+ " [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]\n"
+ " <command> [<args>]");
const char git_more_info_string[] =
N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -337,6 +340,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
setenv(GIT_ATTR_SOURCE_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--no-advice")) {
+ setenv(GIT_ADVICE_ENVIRONMENT, "0", 1);
+ if (envchanged)
+ *envchanged = 1;
} else {
fprintf(stderr, _("unknown option: %s\n"), cmd);
usage(git_usage_string);
@@ -589,6 +596,7 @@ static struct cmd_struct commands[] = {
{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
+ { "refs", cmd_refs, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
{ "remote-ext", cmd_remote_ext, NO_PARSEOPT },
{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
diff --git a/gpg-interface.c b/gpg-interface.c
index 1ff9426..5c824ae 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -27,7 +27,9 @@ static void gpg_interface_lazy_init(void)
}
static char *configured_signing_key;
-static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file;
+static char *ssh_default_key_command;
+static char *ssh_allowed_signers;
+static char *ssh_revocation_file;
static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
struct gpg_format {
@@ -725,7 +727,7 @@ static int git_gpg_config(const char *var, const char *value,
void *cb UNUSED)
{
struct gpg_format *fmt = NULL;
- char *fmtname = NULL;
+ const char *fmtname = NULL;
char *trust;
int ret;
@@ -781,7 +783,7 @@ static int git_gpg_config(const char *var, const char *value,
if (fmtname) {
fmt = get_format_by_name(fmtname);
- return git_config_string(&fmt->program, var, value);
+ return git_config_string((char **) &fmt->program, var, value);
}
return 0;
diff --git a/hash-ll.h b/hash-ll.h
deleted file mode 100644
index 2cfde63..0000000
--- a/hash-ll.h
+++ /dev/null
@@ -1,310 +0,0 @@
-#ifndef HASH_LL_H
-#define HASH_LL_H
-
-#if defined(SHA1_APPLE)
-#include <CommonCrypto/CommonDigest.h>
-#elif defined(SHA1_OPENSSL)
-# include <openssl/sha.h>
-# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
-# define SHA1_NEEDS_CLONE_HELPER
-# include "sha1/openssl.h"
-# endif
-#elif defined(SHA1_DC)
-#include "sha1dc_git.h"
-#else /* SHA1_BLK */
-#include "block-sha1/sha1.h"
-#endif
-
-#if defined(SHA256_NETTLE)
-#include "sha256/nettle.h"
-#elif defined(SHA256_GCRYPT)
-#define SHA256_NEEDS_CLONE_HELPER
-#include "sha256/gcrypt.h"
-#elif defined(SHA256_OPENSSL)
-# include <openssl/sha.h>
-# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
-# define SHA256_NEEDS_CLONE_HELPER
-# include "sha256/openssl.h"
-# endif
-#else
-#include "sha256/block/sha256.h"
-#endif
-
-#ifndef platform_SHA_CTX
-/*
- * platform's underlying implementation of SHA-1; could be OpenSSL,
- * blk_SHA, Apple CommonCrypto, etc... Note that the relevant
- * SHA-1 header may have already defined platform_SHA_CTX for our
- * own implementations like block-sha1, so we list
- * the default for OpenSSL compatible SHA-1 implementations here.
- */
-#define platform_SHA_CTX SHA_CTX
-#define platform_SHA1_Init SHA1_Init
-#define platform_SHA1_Update SHA1_Update
-#define platform_SHA1_Final SHA1_Final
-#endif
-
-#define git_SHA_CTX platform_SHA_CTX
-#define git_SHA1_Init platform_SHA1_Init
-#define git_SHA1_Update platform_SHA1_Update
-#define git_SHA1_Final platform_SHA1_Final
-
-#ifdef platform_SHA1_Clone
-#define git_SHA1_Clone platform_SHA1_Clone
-#endif
-
-#ifndef platform_SHA256_CTX
-#define platform_SHA256_CTX SHA256_CTX
-#define platform_SHA256_Init SHA256_Init
-#define platform_SHA256_Update SHA256_Update
-#define platform_SHA256_Final SHA256_Final
-#endif
-
-#define git_SHA256_CTX platform_SHA256_CTX
-#define git_SHA256_Init platform_SHA256_Init
-#define git_SHA256_Update platform_SHA256_Update
-#define git_SHA256_Final platform_SHA256_Final
-
-#ifdef platform_SHA256_Clone
-#define git_SHA256_Clone platform_SHA256_Clone
-#endif
-
-#ifdef SHA1_MAX_BLOCK_SIZE
-#include "compat/sha1-chunked.h"
-#undef git_SHA1_Update
-#define git_SHA1_Update git_SHA1_Update_Chunked
-#endif
-
-#ifndef SHA1_NEEDS_CLONE_HELPER
-static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
-{
- memcpy(dst, src, sizeof(*dst));
-}
-#endif
-
-#ifndef SHA256_NEEDS_CLONE_HELPER
-static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
-{
- memcpy(dst, src, sizeof(*dst));
-}
-#endif
-
-/*
- * Note that these constants are suitable for indexing the hash_algos array and
- * comparing against each other, but are otherwise arbitrary, so they should not
- * be exposed to the user or serialized to disk. To know whether a
- * git_hash_algo struct points to some usable hash function, test the format_id
- * field for being non-zero. Use the name field for user-visible situations and
- * the format_id field for fixed-length fields on disk.
- */
-/* An unknown hash function. */
-#define GIT_HASH_UNKNOWN 0
-/* SHA-1 */
-#define GIT_HASH_SHA1 1
-/* SHA-256 */
-#define GIT_HASH_SHA256 2
-/* Number of algorithms supported (including unknown). */
-#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
-
-/* "sha1", big-endian */
-#define GIT_SHA1_FORMAT_ID 0x73686131
-
-/* The length in bytes and in hex digits of an object name (SHA-1 value). */
-#define GIT_SHA1_RAWSZ 20
-#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
-/* The block size of SHA-1. */
-#define GIT_SHA1_BLKSZ 64
-
-/* "s256", big-endian */
-#define GIT_SHA256_FORMAT_ID 0x73323536
-
-/* The length in bytes and in hex digits of an object name (SHA-256 value). */
-#define GIT_SHA256_RAWSZ 32
-#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
-/* The block size of SHA-256. */
-#define GIT_SHA256_BLKSZ 64
-
-/* The length in byte and in hex digits of the largest possible hash value. */
-#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
-#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
-/* The largest possible block size for any supported hash. */
-#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
-
-struct object_id {
- unsigned char hash[GIT_MAX_RAWSZ];
- int algo; /* XXX requires 4-byte alignment */
-};
-
-#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_REQUIRE_PATH 010000
-#define GET_OID_HASH_ANY 020000
-
-#define GET_OID_DISAMBIGUATORS \
- (GET_OID_COMMIT | GET_OID_COMMITTISH | \
- GET_OID_TREE | GET_OID_TREEISH | \
- GET_OID_BLOB)
-
-enum get_oid_result {
- FOUND = 0,
- MISSING_OBJECT = -1, /* The requested object is missing */
- SHORT_NAME_AMBIGUOUS = -2,
- /* The following only apply when symlinks are followed */
- DANGLING_SYMLINK = -4, /*
- * The initial symlink is there, but
- * (transitively) points to a missing
- * in-tree file
- */
- SYMLINK_LOOP = -5,
- NOT_DIR = -6, /*
- * Somewhere along the symlink chain, a path is
- * requested which contains a file as a
- * non-final element.
- */
-};
-
-/* A suitably aligned type for stack allocations of hash contexts. */
-union git_hash_ctx {
- git_SHA_CTX sha1;
- git_SHA256_CTX sha256;
-};
-typedef union git_hash_ctx git_hash_ctx;
-
-typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
-typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
-typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
-typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
-typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);
-
-struct git_hash_algo {
- /*
- * The name of the algorithm, as appears in the config file and in
- * messages.
- */
- const char *name;
-
- /* A four-byte version identifier, used in pack indices. */
- uint32_t format_id;
-
- /* The length of the hash in binary. */
- size_t rawsz;
-
- /* The length of the hash in hex characters. */
- size_t hexsz;
-
- /* The block size of the hash. */
- size_t blksz;
-
- /* The hash initialization function. */
- git_hash_init_fn init_fn;
-
- /* The hash context cloning function. */
- git_hash_clone_fn clone_fn;
-
- /* The hash update function. */
- git_hash_update_fn update_fn;
-
- /* The hash finalization function. */
- git_hash_final_fn final_fn;
-
- /* The hash finalization function for object IDs. */
- git_hash_final_oid_fn final_oid_fn;
-
- /* The OID of the empty tree. */
- const struct object_id *empty_tree;
-
- /* The OID of the empty blob. */
- const struct object_id *empty_blob;
-
- /* The all-zeros OID. */
- const struct object_id *null_oid;
-};
-extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
-
-/*
- * Return a GIT_HASH_* constant based on the name. Returns GIT_HASH_UNKNOWN if
- * the name doesn't match a known algorithm.
- */
-int hash_algo_by_name(const char *name);
-/* Identical, except based on the format ID. */
-int hash_algo_by_id(uint32_t format_id);
-/* Identical, except based on the length. */
-int hash_algo_by_length(int len);
-/* Identical, except for a pointer to struct git_hash_algo. */
-static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
-{
- return p - hash_algos;
-}
-
-const struct object_id *null_oid(void);
-
-static inline int hashcmp_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
-{
- /*
- * Teach the compiler that there are only two possibilities of hash size
- * here, so that it can optimize for this case as much as possible.
- */
- if (algop->rawsz == GIT_MAX_RAWSZ)
- return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
- return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
-}
-
-static inline int hasheq_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
-{
- /*
- * We write this here instead of deferring to hashcmp so that the
- * compiler can properly inline it and avoid calling memcmp.
- */
- if (algop->rawsz == GIT_MAX_RAWSZ)
- return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
- return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
-}
-
-static inline void oidcpy(struct object_id *dst, const struct object_id *src)
-{
- memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
- dst->algo = src->algo;
-}
-
-static inline struct object_id *oiddup(const struct object_id *src)
-{
- struct object_id *dst = xmalloc(sizeof(struct object_id));
- oidcpy(dst, src);
- return dst;
-}
-
-static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
-{
- oid->algo = hash_algo_by_ptr(algop);
-}
-
-/*
- * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
- * for use in hash tables. Cryptographic hashes are supposed to have
- * uniform distribution, so in contrast to `memhash()`, this just copies
- * the first `sizeof(int)` bytes without shuffling any bits. Note that
- * the results will be different on big-endian and little-endian
- * platforms, so they should not be stored or transferred over the net.
- */
-static inline unsigned int oidhash(const struct object_id *oid)
-{
- /*
- * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
- * platforms that don't support unaligned reads.
- */
- unsigned int hash;
- memcpy(&hash, oid->hash, sizeof(hash));
- return hash;
-}
-
-const char *empty_tree_oid_hex(void);
-const char *empty_blob_oid_hex(void);
-
-#endif
diff --git a/hash-lookup.c b/hash-lookup.c
index 9f0f95e..5f808ca 100644
--- a/hash-lookup.c
+++ b/hash-lookup.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hash.h"
#include "hash-lookup.h"
@@ -112,7 +114,8 @@ int bsearch_hash(const unsigned char *hash, const uint32_t *fanout_nbo,
while (lo < hi) {
unsigned mi = lo + (hi - lo) / 2;
- int cmp = hashcmp(table + mi * stride, hash);
+ int cmp = hashcmp(table + mi * stride, hash,
+ the_repository->hash_algo);
if (!cmp) {
if (result)
diff --git a/hash.h b/hash.h
index e064807..72ffbc8 100644
--- a/hash.h
+++ b/hash.h
@@ -1,107 +1,369 @@
#ifndef HASH_H
#define HASH_H
-#include "hash-ll.h"
-#include "repository.h"
+#if defined(SHA1_APPLE)
+#include <CommonCrypto/CommonDigest.h>
+#elif defined(SHA1_OPENSSL)
+# include <openssl/sha.h>
+# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+# define SHA1_NEEDS_CLONE_HELPER
+# include "sha1/openssl.h"
+# endif
+#elif defined(SHA1_DC)
+#include "sha1dc_git.h"
+#else /* SHA1_BLK */
+#include "block-sha1/sha1.h"
+#endif
-#define the_hash_algo the_repository->hash_algo
+#if defined(SHA256_NETTLE)
+#include "sha256/nettle.h"
+#elif defined(SHA256_GCRYPT)
+#define SHA256_NEEDS_CLONE_HELPER
+#include "sha256/gcrypt.h"
+#elif defined(SHA256_OPENSSL)
+# include <openssl/sha.h>
+# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+# define SHA256_NEEDS_CLONE_HELPER
+# include "sha256/openssl.h"
+# endif
+#else
+#include "sha256/block/sha256.h"
+#endif
-static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
+#ifndef platform_SHA_CTX
+/*
+ * platform's underlying implementation of SHA-1; could be OpenSSL,
+ * blk_SHA, Apple CommonCrypto, etc... Note that the relevant
+ * SHA-1 header may have already defined platform_SHA_CTX for our
+ * own implementations like block-sha1, so we list
+ * the default for OpenSSL compatible SHA-1 implementations here.
+ */
+#define platform_SHA_CTX SHA_CTX
+#define platform_SHA1_Init SHA1_Init
+#define platform_SHA1_Update SHA1_Update
+#define platform_SHA1_Final SHA1_Final
+#endif
+
+#define git_SHA_CTX platform_SHA_CTX
+#define git_SHA1_Init platform_SHA1_Init
+#define git_SHA1_Update platform_SHA1_Update
+#define git_SHA1_Final platform_SHA1_Final
+
+#ifdef platform_SHA1_Clone
+#define git_SHA1_Clone platform_SHA1_Clone
+#endif
+
+#ifndef platform_SHA256_CTX
+#define platform_SHA256_CTX SHA256_CTX
+#define platform_SHA256_Init SHA256_Init
+#define platform_SHA256_Update SHA256_Update
+#define platform_SHA256_Final SHA256_Final
+#endif
+
+#define git_SHA256_CTX platform_SHA256_CTX
+#define git_SHA256_Init platform_SHA256_Init
+#define git_SHA256_Update platform_SHA256_Update
+#define git_SHA256_Final platform_SHA256_Final
+
+#ifdef platform_SHA256_Clone
+#define git_SHA256_Clone platform_SHA256_Clone
+#endif
+
+#ifdef SHA1_MAX_BLOCK_SIZE
+#include "compat/sha1-chunked.h"
+#undef git_SHA1_Update
+#define git_SHA1_Update git_SHA1_Update_Chunked
+#endif
+
+#ifndef SHA1_NEEDS_CLONE_HELPER
+static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
{
- return hashcmp_algop(sha1, sha2, the_hash_algo);
+ memcpy(dst, src, sizeof(*dst));
+}
+#endif
+
+#ifndef SHA256_NEEDS_CLONE_HELPER
+static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
+{
+ memcpy(dst, src, sizeof(*dst));
+}
+#endif
+
+/*
+ * Note that these constants are suitable for indexing the hash_algos array and
+ * comparing against each other, but are otherwise arbitrary, so they should not
+ * be exposed to the user or serialized to disk. To know whether a
+ * git_hash_algo struct points to some usable hash function, test the format_id
+ * field for being non-zero. Use the name field for user-visible situations and
+ * the format_id field for fixed-length fields on disk.
+ */
+/* An unknown hash function. */
+#define GIT_HASH_UNKNOWN 0
+/* SHA-1 */
+#define GIT_HASH_SHA1 1
+/* SHA-256 */
+#define GIT_HASH_SHA256 2
+/* Number of algorithms supported (including unknown). */
+#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
+
+/* "sha1", big-endian */
+#define GIT_SHA1_FORMAT_ID 0x73686131
+
+/* The length in bytes and in hex digits of an object name (SHA-1 value). */
+#define GIT_SHA1_RAWSZ 20
+#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The block size of SHA-1. */
+#define GIT_SHA1_BLKSZ 64
+
+/* "s256", big-endian */
+#define GIT_SHA256_FORMAT_ID 0x73323536
+
+/* The length in bytes and in hex digits of an object name (SHA-256 value). */
+#define GIT_SHA256_RAWSZ 32
+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
+/* The block size of SHA-256. */
+#define GIT_SHA256_BLKSZ 64
+
+/* The length in byte and in hex digits of the largest possible hash value. */
+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
+/* The largest possible block size for any supported hash. */
+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
+
+struct object_id {
+ unsigned char hash[GIT_MAX_RAWSZ];
+ int algo; /* XXX requires 4-byte alignment */
+};
+
+#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_REQUIRE_PATH 010000
+#define GET_OID_HASH_ANY 020000
+
+#define GET_OID_DISAMBIGUATORS \
+ (GET_OID_COMMIT | GET_OID_COMMITTISH | \
+ GET_OID_TREE | GET_OID_TREEISH | \
+ GET_OID_BLOB)
+
+enum get_oid_result {
+ FOUND = 0,
+ MISSING_OBJECT = -1, /* The requested object is missing */
+ SHORT_NAME_AMBIGUOUS = -2,
+ /* The following only apply when symlinks are followed */
+ DANGLING_SYMLINK = -4, /*
+ * The initial symlink is there, but
+ * (transitively) points to a missing
+ * in-tree file
+ */
+ SYMLINK_LOOP = -5,
+ NOT_DIR = -6, /*
+ * Somewhere along the symlink chain, a path is
+ * requested which contains a file as a
+ * non-final element.
+ */
+};
+
+#ifdef USE_THE_REPOSITORY_VARIABLE
+# include "repository.h"
+# define the_hash_algo the_repository->hash_algo
+#endif
+
+/* A suitably aligned type for stack allocations of hash contexts. */
+union git_hash_ctx {
+ git_SHA_CTX sha1;
+ git_SHA256_CTX sha256;
+};
+typedef union git_hash_ctx git_hash_ctx;
+
+typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
+typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
+typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
+typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
+typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);
+
+struct git_hash_algo {
+ /*
+ * The name of the algorithm, as appears in the config file and in
+ * messages.
+ */
+ const char *name;
+
+ /* A four-byte version identifier, used in pack indices. */
+ uint32_t format_id;
+
+ /* The length of the hash in binary. */
+ size_t rawsz;
+
+ /* The length of the hash in hex characters. */
+ size_t hexsz;
+
+ /* The block size of the hash. */
+ size_t blksz;
+
+ /* The hash initialization function. */
+ git_hash_init_fn init_fn;
+
+ /* The hash context cloning function. */
+ git_hash_clone_fn clone_fn;
+
+ /* The hash update function. */
+ git_hash_update_fn update_fn;
+
+ /* The hash finalization function. */
+ git_hash_final_fn final_fn;
+
+ /* The hash finalization function for object IDs. */
+ git_hash_final_oid_fn final_oid_fn;
+
+ /* The OID of the empty tree. */
+ const struct object_id *empty_tree;
+
+ /* The OID of the empty blob. */
+ const struct object_id *empty_blob;
+
+ /* The all-zeros OID. */
+ const struct object_id *null_oid;
+};
+extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
+
+/*
+ * Return a GIT_HASH_* constant based on the name. Returns GIT_HASH_UNKNOWN if
+ * the name doesn't match a known algorithm.
+ */
+int hash_algo_by_name(const char *name);
+/* Identical, except based on the format ID. */
+int hash_algo_by_id(uint32_t format_id);
+/* Identical, except based on the length. */
+int hash_algo_by_length(int len);
+/* Identical, except for a pointer to struct git_hash_algo. */
+static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
+{
+ return p - hash_algos;
+}
+
+const struct object_id *null_oid(void);
+
+static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
+{
+ /*
+ * Teach the compiler that there are only two possibilities of hash size
+ * here, so that it can optimize for this case as much as possible.
+ */
+ if (algop->rawsz == GIT_MAX_RAWSZ)
+ return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+ return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+}
+
+static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
+{
+ /*
+ * We write this here instead of deferring to hashcmp so that the
+ * compiler can properly inline it and avoid calling memcmp.
+ */
+ if (algop->rawsz == GIT_MAX_RAWSZ)
+ return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+ return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+}
+
+static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src,
+ const struct git_hash_algo *algop)
+{
+ memcpy(sha_dst, sha_src, algop->rawsz);
+}
+
+static inline void hashclr(unsigned char *hash, const struct git_hash_algo *algop)
+{
+ memset(hash, 0, algop->rawsz);
}
static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
{
- const struct git_hash_algo *algop;
- if (!oid1->algo)
- algop = the_hash_algo;
- else
- algop = &hash_algos[oid1->algo];
- return hashcmp_algop(oid1->hash, oid2->hash, algop);
-}
-
-static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
-{
- return hasheq_algop(sha1, sha2, the_hash_algo);
+ return memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);
}
static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
{
- const struct git_hash_algo *algop;
- if (!oid1->algo)
- algop = the_hash_algo;
- else
- algop = &hash_algos[oid1->algo];
- return hasheq_algop(oid1->hash, oid2->hash, algop);
+ return !memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);
+}
+
+static inline void oidcpy(struct object_id *dst, const struct object_id *src)
+{
+ memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
+ dst->algo = src->algo;
+}
+
+static inline void oidread(struct object_id *oid, const unsigned char *hash,
+ const struct git_hash_algo *algop)
+{
+ memcpy(oid->hash, hash, algop->rawsz);
+ if (algop->rawsz < GIT_MAX_RAWSZ)
+ memset(oid->hash + algop->rawsz, 0, GIT_MAX_RAWSZ - algop->rawsz);
+ oid->algo = hash_algo_by_ptr(algop);
+}
+
+static inline void oidclr(struct object_id *oid,
+ const struct git_hash_algo *algop)
+{
+ memset(oid->hash, 0, GIT_MAX_RAWSZ);
+ oid->algo = hash_algo_by_ptr(algop);
+}
+
+static inline struct object_id *oiddup(const struct object_id *src)
+{
+ struct object_id *dst = xmalloc(sizeof(struct object_id));
+ oidcpy(dst, src);
+ return dst;
+}
+
+static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
+{
+ oid->algo = hash_algo_by_ptr(algop);
+}
+
+/*
+ * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
+ * for use in hash tables. Cryptographic hashes are supposed to have
+ * uniform distribution, so in contrast to `memhash()`, this just copies
+ * the first `sizeof(int)` bytes without shuffling any bits. Note that
+ * the results will be different on big-endian and little-endian
+ * platforms, so they should not be stored or transferred over the net.
+ */
+static inline unsigned int oidhash(const struct object_id *oid)
+{
+ /*
+ * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
+ * platforms that don't support unaligned reads.
+ */
+ unsigned int hash;
+ memcpy(&hash, oid->hash, sizeof(hash));
+ return hash;
}
static inline int is_null_oid(const struct object_id *oid)
{
- return oideq(oid, null_oid());
+ static const unsigned char null_hash[GIT_MAX_RAWSZ];
+ return !memcmp(oid->hash, null_hash, GIT_MAX_RAWSZ);
}
-static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
+const char *empty_tree_oid_hex(const struct git_hash_algo *algop);
+
+static inline int is_empty_blob_oid(const struct object_id *oid,
+ const struct git_hash_algo *algop)
{
- memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
+ return oideq(oid, algop->empty_blob);
}
-/* Like oidcpy() but zero-pads the unused bytes in dst's hash array. */
-static inline void oidcpy_with_padding(struct object_id *dst,
- const struct object_id *src)
+static inline int is_empty_tree_oid(const struct object_id *oid,
+ const struct git_hash_algo *algop)
{
- size_t hashsz;
-
- if (!src->algo)
- hashsz = the_hash_algo->rawsz;
- else
- hashsz = hash_algos[src->algo].rawsz;
-
- memcpy(dst->hash, src->hash, hashsz);
- memset(dst->hash + hashsz, 0, GIT_MAX_RAWSZ - hashsz);
- dst->algo = src->algo;
-}
-
-static inline void hashclr(unsigned char *hash)
-{
- memset(hash, 0, the_hash_algo->rawsz);
-}
-
-static inline void oidclr(struct object_id *oid)
-{
- memset(oid->hash, 0, GIT_MAX_RAWSZ);
- oid->algo = hash_algo_by_ptr(the_hash_algo);
-}
-
-static inline void oidread_algop(struct object_id *oid, const unsigned char *hash, const struct git_hash_algo *algop)
-{
- memcpy(oid->hash, hash, algop->rawsz);
- oid->algo = hash_algo_by_ptr(algop);
-}
-
-static inline void oidread(struct object_id *oid, const unsigned char *hash)
-{
- oidread_algop(oid, hash, the_hash_algo);
-}
-
-static inline int is_empty_blob_sha1(const unsigned char *sha1)
-{
- return hasheq(sha1, the_hash_algo->empty_blob->hash);
-}
-
-static inline int is_empty_blob_oid(const struct object_id *oid)
-{
- return oideq(oid, the_hash_algo->empty_blob);
-}
-
-static inline int is_empty_tree_sha1(const unsigned char *sha1)
-{
- return hasheq(sha1, the_hash_algo->empty_tree->hash);
-}
-
-static inline int is_empty_tree_oid(const struct object_id *oid)
-{
- return oideq(oid, the_hash_algo->empty_tree);
+ return oideq(oid, algop->empty_tree);
}
#endif
diff --git a/help.c b/help.c
index 2dbe57b..de0bdd3 100644
--- a/help.c
+++ b/help.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "builtin.h"
@@ -15,6 +17,10 @@
#include "prompt.h"
#include "fsmonitor-ipc.h"
+#ifndef NO_CURL
+#include "git-curl-compat.h" /* For LIBCURL_VERSION only */
+#endif
+
struct category_description {
uint32_t category;
const char *desc;
@@ -757,6 +763,15 @@ void get_version_info(struct strbuf *buf, int show_build_options)
if (fsmonitor_ipc__is_supported())
strbuf_addstr(buf, "feature: fsmonitor--daemon\n");
+#if defined LIBCURL_VERSION
+ strbuf_addf(buf, "libcurl: %s\n", LIBCURL_VERSION);
+#endif
+#if defined OPENSSL_VERSION_TEXT
+ strbuf_addf(buf, "OpenSSL: %s\n", OPENSSL_VERSION_TEXT);
+#endif
+#if defined ZLIB_VERSION
+ strbuf_addf(buf, "zlib: %s\n", ZLIB_VERSION);
+#endif
}
}
@@ -800,7 +815,7 @@ static int append_similar_ref(const char *refname,
if (starts_with(refname, "refs/remotes/") &&
!strcmp(branch, cb->base_ref))
string_list_append_nodup(cb->similar_refs,
- shorten_unambiguous_ref(refname, 1));
+ refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), refname, 1));
return 0;
}
@@ -811,7 +826,8 @@ static struct string_list guess_refs(const char *ref)
ref_cb.base_ref = ref;
ref_cb.similar_refs = &similar_refs;
- for_each_ref(append_similar_ref, &ref_cb);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_similar_ref, &ref_cb);
return similar_refs;
}
diff --git a/hex.c b/hex.c
index d42262b..5ca78a7 100644
--- a/hex.c
+++ b/hex.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hash.h"
#include "hex.h"
@@ -25,8 +27,12 @@ int get_oid_hex_algop(const char *hex, struct object_id *oid,
const struct git_hash_algo *algop)
{
int ret = get_hash_hex_algop(hex, oid->hash, algop);
- if (!ret)
+ if (!ret) {
oid_set_algo(oid, algop);
+ if (algop->rawsz != GIT_MAX_RAWSZ)
+ memset(oid->hash + algop->rawsz, 0,
+ GIT_MAX_RAWSZ - algop->rawsz);
+ }
return ret;
}
diff --git a/hex.h b/hex.h
index e0b83f7..e9ccb54 100644
--- a/hex.h
+++ b/hex.h
@@ -1,7 +1,7 @@
#ifndef HEX_H
#define HEX_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hex-ll.h"
/*
@@ -13,10 +13,6 @@
* input, so it is safe to pass this function an arbitrary
* null-terminated string.
*/
-int get_hash_hex(const char *hex, unsigned char *hash);
-int get_oid_hex(const char *hex, struct object_id *oid);
-
-/* Like get_oid_hex, but for an arbitrary hash algorithm. */
int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
/*
@@ -35,7 +31,6 @@ int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_h
char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
char *oid_to_hex_r(char *out, const struct object_id *oid);
char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *); /* static buffer result! */
-char *hash_to_hex(const unsigned char *hash); /* same static buffer */
char *oid_to_hex(const struct object_id *oid); /* same static buffer */
/*
@@ -45,13 +40,9 @@ char *oid_to_hex(const struct object_id *oid); /* same static buffer */
* other invalid character. end is only updated on success; otherwise, it is
* unmodified.
*/
-int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
-
-/* Like parse_oid_hex, but for an arbitrary hash algorithm. */
int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end,
const struct git_hash_algo *algo);
-
/*
* These functions work like get_oid_hex and parse_oid_hex, but they will parse
* a hex value for any algorithm. The algorithm is detected based on the length
@@ -61,4 +52,19 @@ int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end
int get_oid_hex_any(const char *hex, struct object_id *oid);
int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
-#endif
+#ifdef USE_THE_REPOSITORY_VARIABLE
+
+/* Like get_oid_hex_algop, but for `the_hash_algo`. */
+int get_hash_hex(const char *hex, unsigned char *hash);
+int get_oid_hex(const char *hex, struct object_id *oid);
+
+/* Like parse_oid_hex_algop, but uses `the_hash_algo`. */
+int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+
+/*
+ * Same as `hash_to_hex_algop()`, but uses `the_hash_algo`.
+ */
+char *hash_to_hex(const unsigned char *hash);
+
+#endif /* USE_THE_REPOSITORY_VARIABLE */
+#endif /* HEX_H */
diff --git a/http-backend.c b/http-backend.c
index 1ed1e29..4614249 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -559,7 +561,8 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
} else {
select_getanyfile(hdr);
- for_each_namespaced_ref(NULL, show_text_ref, &buf);
+ refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
+ NULL, show_text_ref, &buf);
send_strbuf(hdr, "text/plain", &buf);
}
strbuf_release(&buf);
@@ -571,9 +574,10 @@ static int show_head_ref(const char *refname, const struct object_id *oid,
struct strbuf *buf = cb_data;
if (flag & REF_ISSYMREF) {
- const char *target = resolve_ref_unsafe(refname,
- RESOLVE_REF_READING,
- NULL, NULL);
+ const char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname,
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (target)
strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
@@ -589,7 +593,8 @@ static void get_head(struct strbuf *hdr, char *arg UNUSED)
struct strbuf buf = STRBUF_INIT;
select_getanyfile(hdr);
- head_ref_namespaced(show_head_ref, &buf);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ show_head_ref, &buf);
send_strbuf(hdr, "text/plain", &buf);
strbuf_release(&buf);
}
@@ -750,7 +755,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
- char *method = getenv("REQUEST_METHOD");
+ const char *method = getenv("REQUEST_METHOD");
const char *proto_header;
char *dir;
struct service_cmd *cmd = NULL;
diff --git a/http-fetch.c b/http-fetch.c
index bec9498..d460bb1 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "gettext.h"
@@ -127,8 +129,12 @@ int cmd_main(int argc, const char **argv)
} else if (skip_prefix(argv[arg], "--packfile=", &p)) {
const char *end;
+ if (nongit)
+ die(_("not a git repository"));
+
packfile = 1;
- if (parse_oid_hex(p, &packfile_hash, &end) || *end)
+ if (parse_oid_hex_algop(p, &packfile_hash, &end,
+ the_repository->hash_algo) || *end)
die(_("argument to --packfile must be a valid hash (got '%s')"), p);
} else if (skip_prefix(argv[arg], "--index-pack-arg=", &p)) {
strvec_push(&index_pack_args, p);
diff --git a/http-push.c b/http-push.c
index 1fe5122..7315a69 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "hex.h"
@@ -1016,6 +1018,7 @@ static void remote_ls(const char *path, int flags,
/* extract hex from sharded "xx/x{38}" filename */
static int get_oid_hex_from_objpath(const char *path, struct object_id *oid)
{
+ memset(oid->hash, 0, GIT_MAX_RAWSZ);
oid->algo = hash_algo_by_ptr(the_hash_algo);
if (strlen(path) != the_hash_algo->hexsz + 1)
@@ -1552,7 +1555,7 @@ static void fetch_symref(const char *path, char **symref, struct object_id *oid)
free(url);
FREE_AND_NULL(*symref);
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
if (buffer.len == 0)
return;
diff --git a/http-walker.c b/http-walker.c
index b395ef1..e417a7f 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "repository.h"
#include "hex.h"
@@ -152,7 +154,7 @@ static void prefetch(struct walker *walker, unsigned char *sha1)
newreq = xmalloc(sizeof(*newreq));
newreq->walker = walker;
- oidread(&newreq->oid, sha1);
+ oidread(&newreq->oid, sha1, the_repository->hash_algo);
newreq->repo = data->alt;
newreq->state = WAITING;
newreq->req = NULL;
@@ -485,7 +487,7 @@ static int fetch_object(struct walker *walker, unsigned char *hash)
list_for_each(pos, head) {
obj_req = list_entry(pos, struct object_request, node);
- if (hasheq(obj_req->oid.hash, hash))
+ if (hasheq(obj_req->oid.hash, hash, the_repository->hash_algo))
break;
}
if (!obj_req)
diff --git a/http.c b/http.c
index 3d80bd6..13fa94b 100644
--- a/http.c
+++ b/http.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "git-curl-compat.h"
#include "hex.h"
@@ -38,11 +40,11 @@ char curl_errorstr[CURL_ERROR_SIZE];
static int curl_ssl_verify = -1;
static int curl_ssl_try;
-static const char *curl_http_version = NULL;
-static const char *ssl_cert;
-static const char *ssl_cert_type;
-static const char *ssl_cipherlist;
-static const char *ssl_version;
+static char *curl_http_version;
+static char *ssl_cert;
+static char *ssl_cert_type;
+static char *ssl_cipherlist;
+static char *ssl_version;
static struct {
const char *name;
long ssl_version;
@@ -59,23 +61,23 @@ static struct {
{ "tlsv1.3", CURL_SSLVERSION_TLSv1_3 },
#endif
};
-static const char *ssl_key;
-static const char *ssl_key_type;
-static const char *ssl_capath;
-static const char *curl_no_proxy;
+static char *ssl_key;
+static char *ssl_key_type;
+static char *ssl_capath;
+static char *curl_no_proxy;
#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
-static const char *ssl_pinnedkey;
+static char *ssl_pinnedkey;
#endif
-static const char *ssl_cainfo;
+static char *ssl_cainfo;
static long curl_low_speed_limit = -1;
static long curl_low_speed_time = -1;
static int curl_ftp_no_epsv;
-static const char *curl_http_proxy;
-static const char *http_proxy_authmethod;
+static char *curl_http_proxy;
+static char *http_proxy_authmethod;
-static const char *http_proxy_ssl_cert;
-static const char *http_proxy_ssl_key;
-static const char *http_proxy_ssl_ca_info;
+static char *http_proxy_ssl_cert;
+static char *http_proxy_ssl_key;
+static char *http_proxy_ssl_ca_info;
static struct credential proxy_cert_auth = CREDENTIAL_INIT;
static int proxy_ssl_cert_password_required;
@@ -95,7 +97,7 @@ static struct {
*/
};
#ifdef CURLGSSAPI_DELEGATION_FLAG
-static const char *curl_deleg;
+static char *curl_deleg;
static struct {
const char *name;
long curl_deleg_param;
@@ -108,11 +110,11 @@ static struct {
static struct credential proxy_auth = CREDENTIAL_INIT;
static const char *curl_proxyuserpwd;
-static const char *curl_cookie_file;
+static char *curl_cookie_file;
static int curl_save_cookies;
struct credential http_auth = CREDENTIAL_INIT;
static int http_proactive_auth;
-static const char *user_agent;
+static char *user_agent;
static int curl_empty_auth = -1;
enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
@@ -128,7 +130,6 @@ static unsigned long empty_auth_useless =
| CURLAUTH_DIGEST;
static struct curl_slist *pragma_header;
-static struct curl_slist *no_pragma_header;
static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
static struct curl_slist *host_resolutions;
@@ -299,6 +300,11 @@ size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,
return nmemb;
}
+static struct curl_slist *object_request_headers(void)
+{
+ return curl_slist_append(http_copy_default_headers(), "Pragma:");
+}
+
static void closedown_active_slot(struct active_request_slot *slot)
{
active_requests--;
@@ -557,42 +563,63 @@ static int curl_empty_auth_enabled(void)
return 0;
}
+struct curl_slist *http_append_auth_header(const struct credential *c,
+ struct curl_slist *headers)
+{
+ if (c->authtype && c->credential) {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addf(&auth, "Authorization: %s %s",
+ c->authtype, c->credential);
+ headers = curl_slist_append(headers, auth.buf);
+ strbuf_release(&auth);
+ }
+ return headers;
+}
+
static void init_curl_http_auth(CURL *result)
{
- if (!http_auth.username || !*http_auth.username) {
+ if ((!http_auth.username || !*http_auth.username) &&
+ (!http_auth.credential || !*http_auth.credential)) {
if (curl_empty_auth_enabled())
curl_easy_setopt(result, CURLOPT_USERPWD, ":");
return;
}
- credential_fill(&http_auth);
+ credential_fill(&http_auth, 1);
- curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
- curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+ if (http_auth.password) {
+ curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
+ curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+ }
}
/* *var must be free-able */
-static void var_override(const char **var, char *value)
+static void var_override(char **var, char *value)
{
if (value) {
- free((void *)*var);
+ free(*var);
*var = xstrdup(value);
}
}
static void set_proxyauth_name_password(CURL *result)
{
+ if (proxy_auth.password) {
curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
proxy_auth.username);
curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
proxy_auth.password);
+ } else if (proxy_auth.authtype && proxy_auth.credential) {
+ curl_easy_setopt(result, CURLOPT_PROXYHEADER,
+ http_append_auth_header(&proxy_auth, NULL));
+ }
}
static void init_curl_proxy_auth(CURL *result)
{
if (proxy_auth.username) {
- if (!proxy_auth.password)
- credential_fill(&proxy_auth);
+ if (!proxy_auth.password && !proxy_auth.credential)
+ credential_fill(&proxy_auth, 1);
set_proxyauth_name_password(result);
}
@@ -626,7 +653,7 @@ static int has_cert_password(void)
cert_auth.host = xstrdup("");
cert_auth.username = xstrdup("");
cert_auth.path = xstrdup(ssl_cert);
- credential_fill(&cert_auth);
+ credential_fill(&cert_auth, 0);
}
return 1;
}
@@ -641,7 +668,7 @@ static int has_proxy_cert_password(void)
proxy_cert_auth.host = xstrdup("");
proxy_cert_auth.username = xstrdup("");
proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
- credential_fill(&proxy_cert_auth);
+ credential_fill(&proxy_cert_auth, 0);
}
return 1;
}
@@ -1208,11 +1235,13 @@ static CURL *get_curl_handle(void)
return result;
}
-static void set_from_env(const char **var, const char *envname)
+static void set_from_env(char **var, const char *envname)
{
const char *val = getenv(envname);
- if (val)
- *var = val;
+ if (val) {
+ FREE_AND_NULL(*var);
+ *var = xstrdup(val);
+ }
}
void http_init(struct remote *remote, const char *url, int proactive_auth)
@@ -1275,8 +1304,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
pragma_header = curl_slist_append(http_copy_default_headers(),
"Pragma: no-cache");
- no_pragma_header = curl_slist_append(http_copy_default_headers(),
- "Pragma:");
{
char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
@@ -1360,9 +1387,6 @@ void http_cleanup(void)
curl_slist_free_all(pragma_header);
pragma_header = NULL;
- curl_slist_free_all(no_pragma_header);
- no_pragma_header = NULL;
-
curl_slist_free_all(host_resolutions);
host_resolutions = NULL;
@@ -1470,7 +1494,7 @@ struct active_request_slot *get_active_slot(void)
curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
- if (http_auth.password || curl_empty_auth_enabled())
+ if (http_auth.password || http_auth.credential || curl_empty_auth_enabled())
init_curl_http_auth(slot->curl);
return slot;
@@ -1759,7 +1783,12 @@ static int handle_curl_result(struct slot_results *results)
} else if (missing_target(results))
return HTTP_MISSING_TARGET;
else if (results->http_code == 401) {
- if (http_auth.username && http_auth.password) {
+ if ((http_auth.username && http_auth.password) ||\
+ (http_auth.authtype && http_auth.credential)) {
+ if (http_auth.multistage) {
+ credential_clear_secrets(&http_auth);
+ return HTTP_REAUTH;
+ }
credential_reject(&http_auth);
return HTTP_NOAUTH;
} else {
@@ -1947,7 +1976,7 @@ static void write_accept_language(struct strbuf *buf)
/* add '*' */
REALLOC_ARRAY(language_tags, num_langs + 1);
- language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+ language_tags[num_langs++] = xstrdup("*");
/* compute decimal_places */
for (max_q = 1, decimal_places = 0;
@@ -1977,8 +2006,7 @@ static void write_accept_language(struct strbuf *buf)
}
}
- /* free language tags -- last one is a static '*' */
- for (i = 0; i < num_langs - 1; i++)
+ for (i = 0; i < num_langs; i++)
free(language_tags[i]);
free(language_tags);
}
@@ -2067,11 +2095,15 @@ static int http_request(const char *url,
/* Add additional headers here */
if (options && options->extra_headers) {
const struct string_list_item *item;
- for_each_string_list_item(item, options->extra_headers) {
- headers = curl_slist_append(headers, item->string);
+ if (options && options->extra_headers) {
+ for_each_string_list_item(item, options->extra_headers) {
+ headers = curl_slist_append(headers, item->string);
+ }
}
}
+ headers = http_append_auth_header(&http_auth, headers);
+
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
@@ -2153,6 +2185,7 @@ static int http_request_reauth(const char *url,
void *result, int target,
struct http_get_options *options)
{
+ int i = 3;
int ret = http_request(url, result, target, options);
if (ret != HTTP_OK && ret != HTTP_REAUTH)
@@ -2166,35 +2199,35 @@ static int http_request_reauth(const char *url,
}
}
- if (ret != HTTP_REAUTH)
- return ret;
+ while (ret == HTTP_REAUTH && --i) {
+ /*
+ * The previous request may have put cruft into our output stream; we
+ * should clear it out before making our next request.
+ */
+ switch (target) {
+ case HTTP_REQUEST_STRBUF:
+ strbuf_reset(result);
+ break;
+ case HTTP_REQUEST_FILE:
+ if (fflush(result)) {
+ error_errno("unable to flush a file");
+ return HTTP_START_FAILED;
+ }
+ rewind(result);
+ if (ftruncate(fileno(result), 0) < 0) {
+ error_errno("unable to truncate a file");
+ return HTTP_START_FAILED;
+ }
+ break;
+ default:
+ BUG("Unknown http_request target");
+ }
- /*
- * The previous request may have put cruft into our output stream; we
- * should clear it out before making our next request.
- */
- switch (target) {
- case HTTP_REQUEST_STRBUF:
- strbuf_reset(result);
- break;
- case HTTP_REQUEST_FILE:
- if (fflush(result)) {
- error_errno("unable to flush a file");
- return HTTP_START_FAILED;
- }
- rewind(result);
- if (ftruncate(fileno(result), 0) < 0) {
- error_errno("unable to truncate a file");
- return HTTP_START_FAILED;
- }
- break;
- default:
- BUG("Unknown http_request target");
+ credential_fill(&http_auth, 1);
+
+ ret = http_request(url, result, target, options);
}
-
- credential_fill(&http_auth);
-
- return http_request(url, result, target, options);
+ return ret;
}
int http_get_strbuf(const char *url,
@@ -2371,6 +2404,7 @@ void release_http_pack_request(struct http_pack_request *preq)
}
preq->slot = NULL;
strbuf_release(&preq->tmpfile);
+ curl_slist_free_all(preq->headers);
free(preq->url);
free(preq);
}
@@ -2455,11 +2489,11 @@ struct http_pack_request *new_direct_http_pack_request(
}
preq->slot = get_active_slot();
+ preq->headers = object_request_headers();
curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEDATA, preq->packfile);
curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
- curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
- no_pragma_header);
+ curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, preq->headers);
/*
* If there is data present from a previous transfer attempt,
@@ -2625,13 +2659,14 @@ struct http_object_request *new_http_object_request(const char *base_url,
}
freq->slot = get_active_slot();
+ freq->headers = object_request_headers();
curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEDATA, freq);
curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
- curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+ curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, freq->headers);
/*
* If we have successfully processed data from a previous fetch
@@ -2719,5 +2754,6 @@ void release_http_object_request(struct http_object_request *freq)
release_active_slot(freq->slot);
freq->slot = NULL;
}
+ curl_slist_free_all(freq->headers);
strbuf_release(&freq->tmpfile);
}
diff --git a/http.h b/http.h
index 3af19a8..a516ca4 100644
--- a/http.h
+++ b/http.h
@@ -175,6 +175,9 @@ int http_get_file(const char *url, const char *filename,
int http_fetch_ref(const char *base, struct ref *ref);
+struct curl_slist *http_append_auth_header(const struct credential *c,
+ struct curl_slist *headers);
+
/* Helpers for fetching packs */
int http_get_info_packs(const char *base_url,
struct packed_git **packs_head);
@@ -196,6 +199,7 @@ struct http_pack_request {
FILE *packfile;
struct strbuf tmpfile;
struct active_request_slot *slot;
+ struct curl_slist *headers;
};
struct http_pack_request *new_http_pack_request(
@@ -229,6 +233,7 @@ struct http_object_request {
int zret;
int rename;
struct active_request_slot *slot;
+ struct curl_slist *headers;
};
struct http_object_request *new_http_object_request(
diff --git a/ident.c b/ident.c
index cc7afdb..caf41fb 100644
--- a/ident.c
+++ b/ident.c
@@ -46,9 +46,9 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
pw = getpwuid(getuid());
if (!pw) {
static struct passwd fallback;
- fallback.pw_name = "unknown";
+ fallback.pw_name = (char *) "unknown";
#ifndef NO_GECOS_IN_PWENT
- fallback.pw_gecos = "Unknown";
+ fallback.pw_gecos = (char *) "Unknown";
#endif
pw = &fallback;
if (is_bogus)
diff --git a/imap-send.c b/imap-send.c
index 0afd088..01404e5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,21 +69,16 @@ static void imap_warn(const char *, ...);
static char *next_arg(char **);
struct imap_server_conf {
- const char *name;
- const char *tunnel;
- const char *host;
+ char *tunnel;
+ char *host;
int port;
- const char *folder;
- const char *user;
- const char *pass;
+ char *folder;
+ char *user;
+ char *pass;
int use_ssl;
int ssl_verify;
int use_html;
- const char *auth_method;
-};
-
-static struct imap_server_conf server = {
- .ssl_verify = 1,
+ char *auth_method;
};
struct imap_socket {
@@ -110,6 +105,7 @@ struct imap {
};
struct imap_store {
+ const struct imap_server_conf *cfg;
/* currently open mailbox */
const char *name; /* foreign! maybe preset? */
int uidvalidity;
@@ -194,8 +190,8 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
#ifdef NO_OPENSSL
static int ssl_socket_connect(struct imap_socket *sock UNUSED,
- int use_tls_only UNUSED,
- int verify UNUSED)
+ const struct imap_server_conf *cfg,
+ int use_tls_only UNUSED)
{
fprintf(stderr, "SSL requested but SSL support not compiled in\n");
return -1;
@@ -250,7 +246,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
cname, hostname);
}
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock,
+ const struct imap_server_conf *cfg,
+ int use_tls_only)
{
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
const SSL_METHOD *meth;
@@ -279,7 +277,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
if (use_tls_only)
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
- if (verify)
+ if (cfg->ssl_verify)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
if (!SSL_CTX_set_default_verify_paths(ctx)) {
@@ -306,9 +304,9 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
* OpenSSL does not document this function, but the implementation
* returns 1 on success, 0 on failure after calling SSLerr().
*/
- ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+ ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
if (ret != 1)
- warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+ warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
#endif
ret = SSL_connect(sock->ssl);
@@ -317,12 +315,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
return -1;
}
- if (verify) {
+ if (cfg->ssl_verify) {
/* make sure the hostname matches that of the certificate */
cert = SSL_get_peer_certificate(sock->ssl);
if (!cert)
return error("unable to get peer certificate.");
- if (verify_hostname(cert, server.host) < 0)
+ if (verify_hostname(cert, cfg->host) < 0)
return -1;
}
@@ -895,7 +893,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
int ret;
char *response;
- response = cram(prompt, server.user, server.pass);
+ response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response))
@@ -917,7 +915,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
- credential_fill(cred);
+ credential_fill(cred, 1);
if (!srvc->user)
srvc->user = xstrdup(cred->username);
@@ -935,6 +933,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
CALLOC_ARRAY(ctx, 1);
+ ctx->cfg = srvc;
ctx->imap = CALLOC_ARRAY(imap, 1);
imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
imap->in_progress_append = &imap->in_progress;
@@ -1035,7 +1034,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[1] = dup(s);
if (srvc->use_ssl &&
- ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+ ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
close(s);
goto bail;
}
@@ -1068,8 +1067,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (!srvc->use_ssl && CAP(STARTTLS)) {
if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
goto bail;
- if (ssl_socket_connect(&imap->buf.sock, 1,
- srvc->ssl_verify))
+ if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
goto bail;
/* capabilities may have changed, so get the new capabilities */
if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
@@ -1215,9 +1213,9 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
static void wrap_in_html(struct strbuf *msg)
{
struct strbuf buf = STRBUF_INIT;
- static char *content_type = "Content-Type: text/html;\n";
- static char *pre_open = "<pre>\n";
- static char *pre_close = "</pre>\n";
+ static const char *content_type = "Content-Type: text/html;\n";
+ static const char *pre_open = "<pre>\n";
+ static const char *pre_close = "</pre>\n";
const char *body = strstr(msg->buf, "\n\n");
if (!body)
@@ -1299,24 +1297,30 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
static int git_imap_config(const char *var, const char *val,
const struct config_context *ctx, void *cb)
{
+ struct imap_server_conf *cfg = cb;
- if (!strcmp("imap.sslverify", var))
- server.ssl_verify = git_config_bool(var, val);
- else if (!strcmp("imap.preformattedhtml", var))
- server.use_html = git_config_bool(var, val);
- else if (!strcmp("imap.folder", var))
- return git_config_string(&server.folder, var, val);
- else if (!strcmp("imap.user", var))
- return git_config_string(&server.user, var, val);
- else if (!strcmp("imap.pass", var))
- return git_config_string(&server.pass, var, val);
- else if (!strcmp("imap.tunnel", var))
- return git_config_string(&server.tunnel, var, val);
- else if (!strcmp("imap.authmethod", var))
- return git_config_string(&server.auth_method, var, val);
- else if (!strcmp("imap.port", var))
- server.port = git_config_int(var, val, ctx->kvi);
- else if (!strcmp("imap.host", var)) {
+ if (!strcmp("imap.sslverify", var)) {
+ cfg->ssl_verify = git_config_bool(var, val);
+ } else if (!strcmp("imap.preformattedhtml", var)) {
+ cfg->use_html = git_config_bool(var, val);
+ } else if (!strcmp("imap.folder", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->folder, var, val);
+ } else if (!strcmp("imap.user", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->user, var, val);
+ } else if (!strcmp("imap.pass", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->pass, var, val);
+ } else if (!strcmp("imap.tunnel", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->tunnel, var, val);
+ } else if (!strcmp("imap.authmethod", var)) {
+ FREE_AND_NULL(cfg->folder);
+ return git_config_string(&cfg->auth_method, var, val);
+ } else if (!strcmp("imap.port", var)) {
+ cfg->port = git_config_int(var, val, ctx->kvi);
+ } else if (!strcmp("imap.host", var)) {
if (!val) {
return config_error_nonbool(var);
} else {
@@ -1324,14 +1328,15 @@ static int git_imap_config(const char *var, const char *val,
val += 5;
else if (starts_with(val, "imaps:")) {
val += 6;
- server.use_ssl = 1;
+ cfg->use_ssl = 1;
}
if (starts_with(val, "//"))
val += 2;
- server.host = xstrdup(val);
+ cfg->host = xstrdup(val);
}
- } else
+ } else {
return git_default_config(var, val, ctx, cb);
+ }
return 0;
}
@@ -1497,12 +1502,16 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
int cmd_main(int argc, const char **argv)
{
+ struct imap_server_conf server = {
+ .ssl_verify = 1,
+ };
struct strbuf all_msgs = STRBUF_INIT;
int total;
int nongit_ok;
+ int ret;
setup_git_directory_gently(&nongit_ok);
- git_config(git_imap_config, NULL);
+ git_config(git_imap_config, &server);
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
@@ -1526,42 +1535,56 @@ int cmd_main(int argc, const char **argv)
if (!server.folder) {
fprintf(stderr, "no imap store specified\n");
- return 1;
+ ret = 1;
+ goto out;
}
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no imap host specified\n");
- return 1;
+ ret = 1;
+ goto out;
}
- server.host = "tunnel";
+ server.host = xstrdup("tunnel");
}
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
- return 1;
+ ret = 1;
+ goto out;
}
if (all_msgs.len == 0) {
fprintf(stderr, "nothing to send\n");
- return 1;
+ ret = 1;
+ goto out;
}
total = count_messages(&all_msgs);
if (!total) {
fprintf(stderr, "no messages to send\n");
- return 1;
+ ret = 1;
+ goto out;
}
/* write it to the imap server */
if (server.tunnel)
- return append_msgs_to_imap(&server, &all_msgs, total);
-
+ ret = append_msgs_to_imap(&server, &all_msgs, total);
#ifdef USE_CURL_FOR_IMAP_SEND
- if (use_curl)
- return curl_append_msgs_to_imap(&server, &all_msgs, total);
+ else if (use_curl)
+ ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
#endif
+ else
+ ret = append_msgs_to_imap(&server, &all_msgs, total);
- return append_msgs_to_imap(&server, &all_msgs, total);
+out:
+ free(server.tunnel);
+ free(server.host);
+ free(server.folder);
+ free(server.user);
+ free(server.pass);
+ free(server.auth_method);
+ strbuf_release(&all_msgs);
+ return ret;
}
diff --git a/json-writer.c b/json-writer.c
index 005c820..25b9201 100644
--- a/json-writer.c
+++ b/json-writer.c
@@ -46,10 +46,7 @@ static void append_quoted_string(struct strbuf *out, const char *in)
static void indent_pretty(struct json_writer *jw)
{
- int k;
-
- for (k = 0; k < jw->open_stack.len; k++)
- strbuf_addstr(&jw->json, " ");
+ strbuf_addstrings(&jw->json, " ", jw->open_stack.len);
}
/*
diff --git a/line-log.c b/line-log.c
index 8ff6ccb..67c80b3 100644
--- a/line-log.c
+++ b/line-log.c
@@ -899,14 +899,12 @@ static void print_line(const char *prefix, char first,
static char *output_prefix(struct diff_options *opt)
{
- char *prefix = "";
-
if (opt->output_prefix) {
struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
- prefix = sb->buf;
+ return sb->buf;
+ } else {
+ return xstrdup("");
}
-
- return prefix;
}
static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
@@ -927,7 +925,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
if (!pair || !diff)
- return;
+ goto out;
if (pair->one->oid_valid)
fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
@@ -1002,8 +1000,10 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
c_context, c_reset, opt->file);
}
+out:
free(p_ends);
free(t_ends);
+ free(prefix);
}
/*
@@ -1012,7 +1012,11 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
*/
static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
{
- fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
+ char *prefix = output_prefix(&rev->diffopt);
+
+ fprintf(rev->diffopt.file, "%s\n", prefix);
+ free(prefix);
+
while (range) {
dump_diff_hacky_one(rev, range);
range = range->next;
@@ -1032,6 +1036,7 @@ static int process_diff_filepair(struct rev_info *rev,
struct range_set tmp;
struct diff_ranges diff;
mmfile_t file_parent, file_target;
+ char *parent_data_to_free = NULL;
assert(pair->two->path);
while (rg) {
@@ -1056,7 +1061,7 @@ static int process_diff_filepair(struct rev_info *rev,
file_parent.ptr = pair->one->data;
file_parent.size = pair->one->size;
} else {
- file_parent.ptr = "";
+ file_parent.ptr = parent_data_to_free = xstrdup("");
file_parent.size = 0;
}
@@ -1075,6 +1080,7 @@ static int process_diff_filepair(struct rev_info *rev,
diff_ranges_release(&diff);
+ free(parent_data_to_free);
return ((*diff_out)->parent.nr > 0);
}
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index c5f363c..0061110 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "gettext.h"
diff --git a/list-objects-filter.c b/list-objects-filter.c
index 4346f8d..49e2fa6 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "dir.h"
#include "gettext.h"
diff --git a/list-objects.c b/list-objects.c
index 11ad8be..985d008 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tag.h"
#include "commit.h"
diff --git a/log-tree.c b/log-tree.c
index 16031b4..101079e 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit-reach.h"
#include "config.h"
@@ -232,8 +234,10 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
}
decoration_loaded = 1;
decoration_flags = flags;
- for_each_ref(add_ref_decoration, filter);
- head_ref(add_ref_decoration, filter);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_ref_decoration, filter);
+ refs_head_ref(get_main_ref_store(the_repository),
+ add_ref_decoration, filter);
for_each_commit_graft(add_graft_decoration, filter);
}
}
@@ -277,7 +281,8 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
return NULL;
/* Now resolve and find the matching current branch */
- branch_name = resolve_ref_unsafe("HEAD", 0, NULL, &rru_flags);
+ branch_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, &rru_flags);
if (!branch_name || !(rru_flags & REF_ISSYMREF))
return NULL;
@@ -670,6 +675,51 @@ static void next_commentary_block(struct rev_info *opt, struct strbuf *sb)
opt->shown_dashes = 1;
}
+static void show_diff_of_diff(struct rev_info *opt)
+{
+ if (!cmit_fmt_is_mail(opt->commit_format))
+ return;
+
+ if (opt->idiff_oid1) {
+ struct diff_queue_struct dq;
+
+ memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
+ DIFF_QUEUE_CLEAR(&diff_queued_diff);
+
+ fprintf_ln(opt->diffopt.file, "\n%s", opt->idiff_title);
+ show_interdiff(opt->idiff_oid1, opt->idiff_oid2, 2,
+ &opt->diffopt);
+
+ memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
+ }
+
+ if (opt->rdiff1) {
+ struct diff_queue_struct dq;
+ struct diff_options opts;
+ struct range_diff_options range_diff_opts = {
+ .creation_factor = opt->creation_factor,
+ .dual_color = 1,
+ .diffopt = &opts
+ };
+
+ memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
+ DIFF_QUEUE_CLEAR(&diff_queued_diff);
+
+ fprintf_ln(opt->diffopt.file, "\n%s", opt->rdiff_title);
+ /*
+ * Pass minimum required diff-options to range-diff; others
+ * can be added later if deemed desirable.
+ */
+ repo_diff_setup(the_repository, &opts);
+ opts.file = opt->diffopt.file;
+ opts.use_color = opt->diffopt.use_color;
+ diff_setup_done(&opts);
+ show_range_diff(opt->rdiff1, opt->rdiff2, &range_diff_opts);
+
+ memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
+ }
+}
+
void show_log(struct rev_info *opt)
{
struct strbuf msgbuf = STRBUF_INIT;
@@ -853,47 +903,6 @@ void show_log(struct rev_info *opt)
strbuf_release(&msgbuf);
free(ctx.notes_message);
free(ctx.after_subject);
-
- if (cmit_fmt_is_mail(ctx.fmt) && opt->idiff_oid1) {
- struct diff_queue_struct dq;
-
- memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
- DIFF_QUEUE_CLEAR(&diff_queued_diff);
-
- next_commentary_block(opt, NULL);
- fprintf_ln(opt->diffopt.file, "%s", opt->idiff_title);
- show_interdiff(opt->idiff_oid1, opt->idiff_oid2, 2,
- &opt->diffopt);
-
- memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
- }
-
- if (cmit_fmt_is_mail(ctx.fmt) && opt->rdiff1) {
- struct diff_queue_struct dq;
- struct diff_options opts;
- struct range_diff_options range_diff_opts = {
- .creation_factor = opt->creation_factor,
- .dual_color = 1,
- .diffopt = &opts
- };
-
- memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
- DIFF_QUEUE_CLEAR(&diff_queued_diff);
-
- next_commentary_block(opt, NULL);
- fprintf_ln(opt->diffopt.file, "%s", opt->rdiff_title);
- /*
- * Pass minimum required diff-options to range-diff; others
- * can be added later if deemed desirable.
- */
- repo_diff_setup(the_repository, &opts);
- opts.file = opt->diffopt.file;
- opts.use_color = opt->diffopt.use_color;
- diff_setup_done(&opts);
- show_range_diff(opt->rdiff1, opt->rdiff2, &range_diff_opts);
-
- memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
- }
}
int log_tree_diff_flush(struct rev_info *opt)
@@ -1162,9 +1171,12 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
}
if (opt->track_linear && !opt->linear && opt->reverse_output_stage)
fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
+ if (shown)
+ show_diff_of_diff(opt);
opt->loginfo = NULL;
maybe_flush_or_die(opt->diffopt.file, "stdout");
opt->diffopt.no_free = no_free;
+
diff_free(&opt->diffopt);
return shown;
}
diff --git a/loose.c b/loose.c
index f6faa62..a8bf772 100644
--- a/loose.c
+++ b/loose.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hash.h"
#include "path.h"
diff --git a/loose.h b/loose.h
index 2c29570..2851230 100644
--- a/loose.h
+++ b/loose.h
@@ -3,6 +3,8 @@
#include "khash.h"
+struct repository;
+
struct loose_object_map {
kh_oid_map_t *to_compat;
kh_oid_map_t *to_storage;
diff --git a/ls-refs.c b/ls-refs.c
index 819cbef..2dd925b 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -95,9 +97,11 @@ static int send_ref(const char *refname, const struct object_id *oid,
strbuf_addf(&data->buf, "unborn %s", refname_nons);
if (data->symrefs && flag & REF_ISSYMREF) {
struct object_id unused;
- const char *symref_target = resolve_ref_unsafe(refname, 0,
- &unused,
- &flag);
+ const char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname,
+ 0,
+ &unused,
+ &flag);
if (!symref_target)
die("'%s' is a symref but it is not?", refname);
@@ -108,7 +112,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
if (data->peel && oid) {
struct object_id peeled;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
@@ -126,7 +130,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
int oid_is_null;
strbuf_addf(&namespaced, "%sHEAD", get_git_namespace());
- if (!resolve_ref_unsafe(namespaced.buf, 0, &oid, &flag))
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))
return; /* bad ref */
oid_is_null = is_null_oid(&oid);
if (!oid_is_null ||
diff --git a/mailmap.c b/mailmap.c
index 3d6a5e9..2d0212f 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "string-list.h"
@@ -6,8 +8,8 @@
#include "object-store-ll.h"
#include "setup.h"
-const char *git_mailmap_file;
-const char *git_mailmap_blob;
+char *git_mailmap_file;
+char *git_mailmap_blob;
struct mailmap_info {
char *name;
@@ -216,7 +218,7 @@ int read_mailmap(struct string_list *map)
map->cmp = namemap_cmp;
if (!git_mailmap_blob && is_bare_repository())
- git_mailmap_blob = "HEAD:.mailmap";
+ git_mailmap_blob = xstrdup("HEAD:.mailmap");
if (!startup_info->have_repository || !is_bare_repository())
err |= read_mailmap_file(map, ".mailmap",
diff --git a/mailmap.h b/mailmap.h
index 0f8fd2c..cbda9bc 100644
--- a/mailmap.h
+++ b/mailmap.h
@@ -3,8 +3,8 @@
struct string_list;
-extern const char *git_mailmap_file;
-extern const char *git_mailmap_blob;
+extern char *git_mailmap_file;
+extern char *git_mailmap_blob;
int read_mailmap(struct string_list *map);
void clear_mailmap(struct string_list *map);
diff --git a/match-trees.c b/match-trees.c
index 3412b6a..f17c74d 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hex.h"
#include "match-trees.h"
@@ -229,7 +231,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
oid_to_hex(oid1));
if (*subpath) {
struct object_id tree_oid;
- oidread(&tree_oid, rewrite_here);
+ oidread(&tree_oid, rewrite_here, the_repository->hash_algo);
status = splice_tree(&tree_oid, subpath, oid2, &subtree);
if (status)
return status;
@@ -237,7 +239,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
} else {
rewrite_with = oid2;
}
- hashcpy(rewrite_here, rewrite_with->hash);
+ hashcpy(rewrite_here, rewrite_with->hash, the_repository->hash_algo);
status = write_object_file(buf, sz, OBJ_TREE, result);
free(buf);
return status;
diff --git a/merge-blobs.c b/merge-blobs.c
index 2f659fd..0ad0390 100644
--- a/merge-blobs.c
+++ b/merge-blobs.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "merge-ll.h"
#include "blob.h"
diff --git a/merge-ll.c b/merge-ll.c
index bf1077a..180c19d 100644
--- a/merge-ll.c
+++ b/merge-ll.c
@@ -29,7 +29,7 @@ struct ll_merge_driver {
const char *name;
const char *description;
ll_merge_fn fn;
- const char *recursive;
+ char *recursive;
struct ll_merge_driver *next;
char *cmdline;
};
@@ -268,7 +268,7 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
* merge.default and merge.driver configuration items
*/
static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
-static const char *default_ll_merge;
+static char *default_ll_merge;
static int read_merge_config(const char *var, const char *value,
const struct config_context *ctx UNUSED,
@@ -304,8 +304,13 @@ static int read_merge_config(const char *var, const char *value,
ll_user_merge_tail = &(fn->next);
}
- if (!strcmp("name", key))
- return git_config_string(&fn->description, var, value);
+ if (!strcmp("name", key)) {
+ /*
+ * The description is leaking, but that's okay as we want to
+ * keep around the merge drivers anyway.
+ */
+ return git_config_string((char **) &fn->description, var, value);
+ }
if (!strcmp("driver", key)) {
if (!value)
diff --git a/merge-ort.c b/merge-ort.c
index eaede6c..691db90 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -14,6 +14,8 @@
* "cale", "peedy", or "ins" instead of "ort"?)
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "merge-ort.h"
diff --git a/merge-ort.h b/merge-ort.h
index ce56ec1..a994c9a 100644
--- a/merge-ort.h
+++ b/merge-ort.h
@@ -2,7 +2,7 @@
#define MERGE_ORT_H
#include "merge-recursive.h"
-#include "hash-ll.h"
+#include "hash.h"
struct commit;
struct tree;
diff --git a/merge-recursive.c b/merge-recursive.c
index 8ff29ed..46ee364 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,6 +3,9 @@
* Fredrik Kuivinen.
* The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "merge-recursive.h"
diff --git a/merge.c b/merge.c
index 752a937..fe3efa4 100644
--- a/merge.c
+++ b/merge.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hash.h"
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index 734d15a..f8ad6b3 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -325,7 +325,7 @@
fi
# If this is a single window diff with all the buffers
- if ! echo "$tab" | grep ",\|/" >/dev/null
+ if ! echo "$tab" | grep -E ",|/" >/dev/null
then
CMD="$CMD | silent execute 'bufdo diffthis'"
fi
diff --git a/midx-write.c b/midx-write.c
index 65e69d2..478b42e 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -100,6 +102,32 @@ struct write_midx_context {
struct string_list *to_include;
};
+static int should_include_pack(const struct write_midx_context *ctx,
+ const char *file_name)
+{
+ /*
+ * Note that at most one of ctx->m and ctx->to_include are set,
+ * so we are testing midx_contains_pack() and
+ * string_list_has_string() independently (guarded by the
+ * appropriate NULL checks).
+ *
+ * We could support passing to_include while reusing an existing
+ * MIDX, but don't currently since the reuse process drags
+ * forward all packs from an existing MIDX (without checking
+ * whether or not they appear in the to_include list).
+ *
+ * If we added support for that, these next two conditional
+ * should be performed independently (likely checking
+ * to_include before the existing MIDX).
+ */
+ if (ctx->m && midx_contains_pack(ctx->m, file_name))
+ return 0;
+ else if (ctx->to_include &&
+ !string_list_has_string(ctx->to_include, file_name))
+ return 0;
+ return 1;
+}
+
static void add_pack_to_midx(const char *full_path, size_t full_path_len,
const char *file_name, void *data)
{
@@ -108,29 +136,11 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
if (ends_with(file_name, ".idx")) {
display_progress(ctx->progress, ++ctx->pack_paths_checked);
- /*
- * Note that at most one of ctx->m and ctx->to_include are set,
- * so we are testing midx_contains_pack() and
- * string_list_has_string() independently (guarded by the
- * appropriate NULL checks).
- *
- * We could support passing to_include while reusing an existing
- * MIDX, but don't currently since the reuse process drags
- * forward all packs from an existing MIDX (without checking
- * whether or not they appear in the to_include list).
- *
- * If we added support for that, these next two conditional
- * should be performed independently (likely checking
- * to_include before the existing MIDX).
- */
- if (ctx->m && midx_contains_pack(ctx->m, file_name))
- return;
- else if (ctx->to_include &&
- !string_list_has_string(ctx->to_include, file_name))
+
+ if (!should_include_pack(ctx, file_name))
return;
ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
-
p = add_packed_git(full_path, full_path_len, 0);
if (!p) {
warning(_("failed to add packfile '%s'"),
@@ -299,21 +309,16 @@ static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
* Copy only the de-duplicated entries (selected by most-recent modified time
* of a packfile containing the object).
*/
-static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
- struct pack_info *info,
- uint32_t nr_packs,
- size_t *nr_objects,
- int preferred_pack)
+static void compute_sorted_entries(struct write_midx_context *ctx,
+ uint32_t start_pack)
{
uint32_t cur_fanout, cur_pack, cur_object;
size_t alloc_objects, total_objects = 0;
struct midx_fanout fanout = { 0 };
- struct pack_midx_entry *deduplicated_entries = NULL;
- uint32_t start_pack = m ? m->num_packs : 0;
- for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
+ for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++)
total_objects = st_add(total_objects,
- info[cur_pack].p->num_objects);
+ ctx->info[cur_pack].p->num_objects);
/*
* As we de-duplicate by fanout value, we expect the fanout
@@ -323,26 +328,26 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
ALLOC_ARRAY(fanout.entries, fanout.alloc);
- ALLOC_ARRAY(deduplicated_entries, alloc_objects);
- *nr_objects = 0;
+ ALLOC_ARRAY(ctx->entries, alloc_objects);
+ ctx->entries_nr = 0;
for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
fanout.nr = 0;
- if (m)
- midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
- preferred_pack);
+ if (ctx->m)
+ midx_fanout_add_midx_fanout(&fanout, ctx->m, cur_fanout,
+ ctx->preferred_pack_idx);
- for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
- int preferred = cur_pack == preferred_pack;
+ for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++) {
+ int preferred = cur_pack == ctx->preferred_pack_idx;
midx_fanout_add_pack_fanout(&fanout,
- info, cur_pack,
+ ctx->info, cur_pack,
preferred, cur_fanout);
}
- if (-1 < preferred_pack && preferred_pack < start_pack)
- midx_fanout_add_pack_fanout(&fanout, info,
- preferred_pack, 1,
+ if (-1 < ctx->preferred_pack_idx && ctx->preferred_pack_idx < start_pack)
+ midx_fanout_add_pack_fanout(&fanout, ctx->info,
+ ctx->preferred_pack_idx, 1,
cur_fanout);
midx_fanout_sort(&fanout);
@@ -356,17 +361,16 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
&fanout.entries[cur_object].oid))
continue;
- ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
+ ALLOC_GROW(ctx->entries, st_add(ctx->entries_nr, 1),
alloc_objects);
- memcpy(&deduplicated_entries[*nr_objects],
+ memcpy(&ctx->entries[ctx->entries_nr],
&fanout.entries[cur_object],
sizeof(struct pack_midx_entry));
- (*nr_objects)++;
+ ctx->entries_nr++;
}
}
free(fanout.entries);
- return deduplicated_entries;
}
static int write_midx_pack_names(struct hashfile *f, void *data)
@@ -664,7 +668,7 @@ static int add_ref_to_pending(const char *refname,
return 0;
}
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
object = parse_object_or_die(oid, refname);
@@ -755,7 +759,8 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
read_refs_snapshot(refs_snapshot, &revs);
} else {
setup_revisions(0, NULL, &revs, NULL);
- for_each_ref(add_ref_to_pending, &revs);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_ref_to_pending, &revs);
}
/*
@@ -798,6 +803,7 @@ static int write_midx_bitmap(const char *midx_name,
{
int ret, i;
uint16_t options = 0;
+ struct bitmap_writer writer;
struct pack_idx_entry **index;
char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
hash_to_hex(midx_hash));
@@ -819,8 +825,10 @@ static int write_midx_bitmap(const char *midx_name,
for (i = 0; i < pdata->nr_objects; i++)
index[i] = &pdata->objects[i].idx;
- bitmap_writer_show_progress(flags & MIDX_PROGRESS);
- bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
+ bitmap_writer_init(&writer, the_repository);
+ bitmap_writer_show_progress(&writer, flags & MIDX_PROGRESS);
+ bitmap_writer_build_type_index(&writer, pdata, index,
+ pdata->nr_objects);
/*
* bitmap_writer_finish expects objects in lex order, but pack_order
@@ -838,17 +846,19 @@ static int write_midx_bitmap(const char *midx_name,
for (i = 0; i < pdata->nr_objects; i++)
index[pack_order[i]] = &pdata->objects[i].idx;
- bitmap_writer_select_commits(commits, commits_nr, -1);
- ret = bitmap_writer_build(pdata);
+ bitmap_writer_select_commits(&writer, commits, commits_nr);
+ ret = bitmap_writer_build(&writer, pdata);
if (ret < 0)
goto cleanup;
- bitmap_writer_set_checksum(midx_hash);
- bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
+ bitmap_writer_set_checksum(&writer, midx_hash);
+ bitmap_writer_finish(&writer, index, pdata->nr_objects, bitmap_name,
+ options);
cleanup:
free(index);
free(bitmap_name);
+ bitmap_writer_free(&writer);
trace2_region_leave("midx", "write_midx_bitmap", the_repository);
@@ -880,6 +890,40 @@ static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
return result;
}
+static int fill_packs_from_midx(struct write_midx_context *ctx,
+ const char *preferred_pack_name, uint32_t flags)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->m->num_packs; i++) {
+ ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
+
+ if (flags & MIDX_WRITE_REV_INDEX || preferred_pack_name) {
+ /*
+ * If generating a reverse index, need to have
+ * packed_git's loaded to compare their
+ * mtimes and object count.
+ *
+ *
+ * If a preferred pack is specified, need to
+ * have packed_git's loaded to ensure the chosen
+ * preferred pack has a non-zero object count.
+ */
+ if (prepare_midx_pack(the_repository, ctx->m, i))
+ return error(_("could not load pack"));
+
+ if (open_pack_index(ctx->m->packs[i]))
+ die(_("could not open index for %s"),
+ ctx->m->packs[i]->pack_name);
+ }
+
+ fill_pack_info(&ctx->info[ctx->nr++], ctx->m->packs[i],
+ ctx->m->pack_names[i], i);
+ }
+
+ return 0;
+}
+
static int write_midx_internal(const char *object_dir,
struct string_list *packs_to_include,
struct string_list *packs_to_drop,
@@ -889,7 +933,7 @@ static int write_midx_internal(const char *object_dir,
{
struct strbuf midx_name = STRBUF_INIT;
unsigned char midx_hash[GIT_MAX_RAWSZ];
- uint32_t i;
+ uint32_t i, start_pack;
struct hashfile *f = NULL;
struct lock_file lk;
struct write_midx_context ctx = { 0 };
@@ -925,32 +969,14 @@ static int write_midx_internal(const char *object_dir,
ctx.info = NULL;
ALLOC_ARRAY(ctx.info, ctx.alloc);
- if (ctx.m) {
- for (i = 0; i < ctx.m->num_packs; i++) {
- ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
-
- if (flags & MIDX_WRITE_REV_INDEX) {
- /*
- * If generating a reverse index, need to have
- * packed_git's loaded to compare their
- * mtimes and object count.
- */
- if (prepare_midx_pack(the_repository, ctx.m, i)) {
- error(_("could not load pack"));
- result = 1;
- goto cleanup;
- }
-
- if (open_pack_index(ctx.m->packs[i]))
- die(_("could not open index for %s"),
- ctx.m->packs[i]->pack_name);
- }
-
- fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
- ctx.m->pack_names[i], i);
- }
+ if (ctx.m && fill_packs_from_midx(&ctx, preferred_pack_name,
+ flags) < 0) {
+ result = 1;
+ goto cleanup;
}
+ start_pack = ctx.nr;
+
ctx.pack_paths_checked = 0;
if (flags & MIDX_PROGRESS)
ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
@@ -1048,8 +1074,7 @@ static int write_midx_internal(const char *object_dir,
}
}
- ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
- ctx.preferred_pack_idx);
+ compute_sorted_entries(&ctx, start_pack);
ctx.large_offsets_needed = 0;
for (i = 0; i < ctx.entries_nr; i++) {
diff --git a/midx.c b/midx.c
index 6f07de3..3992b05 100644
--- a/midx.c
+++ b/midx.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "dir.h"
@@ -25,13 +27,15 @@ const unsigned char *get_midx_checksum(struct multi_pack_index *m)
void get_midx_filename(struct strbuf *out, const char *object_dir)
{
- strbuf_addf(out, "%s/pack/multi-pack-index", object_dir);
+ get_midx_filename_ext(out, object_dir, NULL, NULL);
}
-void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m)
+void get_midx_filename_ext(struct strbuf *out, const char *object_dir,
+ const unsigned char *hash, const char *ext)
{
- get_midx_filename(out, m->object_dir);
- strbuf_addf(out, "-%s.rev", hash_to_hex(get_midx_checksum(m)));
+ strbuf_addf(out, "%s/pack/multi-pack-index", object_dir);
+ if (ext)
+ strbuf_addf(out, "-%s.%s", hash_to_hex(hash), ext);
}
static int midx_read_oid_fanout(const unsigned char *chunk_start,
@@ -302,7 +306,8 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid,
if (n >= m->num_objects)
return NULL;
- oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n));
+ oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n),
+ the_repository->hash_algo);
return oid;
}
diff --git a/midx.h b/midx.h
index dc477df..8554f2d 100644
--- a/midx.h
+++ b/midx.h
@@ -74,9 +74,13 @@ struct multi_pack_index {
#define MIDX_WRITE_BITMAP_HASH_CACHE (1 << 3)
#define MIDX_WRITE_BITMAP_LOOKUP_TABLE (1 << 4)
+#define MIDX_EXT_REV "rev"
+#define MIDX_EXT_BITMAP "bitmap"
+
const unsigned char *get_midx_checksum(struct multi_pack_index *m);
void get_midx_filename(struct strbuf *out, const char *object_dir);
-void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m);
+void get_midx_filename_ext(struct strbuf *out, const char *object_dir,
+ const unsigned char *hash, const char *ext);
struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local);
int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t pack_int_id);
diff --git a/negotiator/default.c b/negotiator/default.c
index 9a5b696..e3fa5c3 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "default.h"
#include "../commit.h"
@@ -192,6 +194,7 @@ void default_negotiator_init(struct fetch_negotiator *negotiator)
ns->rev_list.compare = compare_commits_by_commit_date;
if (marked)
- for_each_ref(clear_marks, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ clear_marks, NULL);
marked = 1;
}
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 5b91520..f109928 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "skipping.h"
#include "../commit.h"
@@ -261,6 +263,7 @@ void skipping_negotiator_init(struct fetch_negotiator *negotiator)
data->rev_list.compare = compare;
if (marked)
- for_each_ref(clear_marks, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ clear_marks, NULL);
marked = 1;
}
diff --git a/notes-cache.c b/notes-cache.c
index 0e1d5b1..ecfdf6e 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "notes-cache.h"
#include "object-store-ll.h"
@@ -17,7 +19,7 @@ static int notes_cache_match_validity(struct repository *r,
struct strbuf msg = STRBUF_INIT;
int ret;
- if (read_ref(ref, &oid) < 0)
+ if (refs_read_ref(get_main_ref_store(the_repository), ref, &oid) < 0)
return 0;
commit = lookup_commit_reference_gently(r, &oid, 1);
@@ -66,8 +68,8 @@ int notes_cache_write(struct notes_cache *c)
if (commit_tree(c->validity, strlen(c->validity), &tree_oid, NULL,
&commit_oid, NULL, NULL) < 0)
return -1;
- if (update_ref("update notes cache", c->tree.update_ref, &commit_oid,
- NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
+ if (refs_update_ref(get_main_ref_store(the_repository), "update notes cache", c->tree.update_ref, &commit_oid,
+ NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
return -1;
return 0;
diff --git a/notes-merge.c b/notes-merge.c
index 5128293..d95e683 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "commit.h"
@@ -240,7 +242,7 @@ static void diff_tree_local(struct notes_merge_options *o,
* (will be overwritten by following addition)
*/
if (oideq(&mp->local, &uninitialized))
- oidclr(&mp->local);
+ oidclr(&mp->local, the_repository->hash_algo);
} else if (is_null_oid(&p->one->oid)) { /* addition */
/*
* Either this is a true addition (1), or it is part
@@ -556,13 +558,13 @@ int notes_merge(struct notes_merge_options *o,
assert(o->local_ref && o->remote_ref);
assert(!strcmp(o->local_ref, local_tree->ref));
- oidclr(result_oid);
+ oidclr(result_oid, the_repository->hash_algo);
trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n",
o->local_ref, o->remote_ref);
/* Dereference o->local_ref into local_sha1 */
- if (read_ref_full(o->local_ref, 0, &local_oid, NULL))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), o->local_ref, 0, &local_oid, NULL))
die("Failed to resolve local notes ref '%s'", o->local_ref);
else if (!check_refname_format(o->local_ref, 0) &&
is_null_oid(&local_oid))
@@ -579,7 +581,7 @@ int notes_merge(struct notes_merge_options *o,
* unborn ref, perform the merge using an empty notes tree.
*/
if (!check_refname_format(o->remote_ref, 0)) {
- oidclr(&remote_oid);
+ oidclr(&remote_oid, the_repository->hash_algo);
remote = NULL;
} else {
die("Failed to resolve remote notes ref '%s'",
diff --git a/notes-utils.c b/notes-utils.c
index 6197a5a..bca7127 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
@@ -23,7 +25,7 @@ void create_notes_commit(struct repository *r,
if (!parents) {
/* Deduce parent commit from t->ref */
struct object_id parent_oid;
- if (!read_ref(t->ref, &parent_oid)) {
+ if (!refs_read_ref(get_main_ref_store(the_repository), t->ref, &parent_oid)) {
struct commit *parent = lookup_commit(r, &parent_oid);
if (repo_parse_commit(r, parent))
die("Failed to find/parse commit %s", t->ref);
@@ -55,8 +57,9 @@ void commit_notes(struct repository *r, struct notes_tree *t, const char *msg)
create_notes_commit(r, t, NULL, buf.buf, buf.len, &commit_oid);
strbuf_insertstr(&buf, 0, "notes: ");
- update_ref(buf.buf, t->update_ref, &commit_oid, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository), buf.buf,
+ t->update_ref, &commit_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
strbuf_release(&buf);
}
diff --git a/notes.c b/notes.c
index fed1eda..b6a13d0 100644
--- a/notes.c
+++ b/notes.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -149,7 +151,7 @@ static struct leaf_node *note_tree_find(struct notes_tree *t,
void **p = note_tree_search(t, &tree, &n, key_sha1);
if (GET_PTR_TYPE(*p) == PTR_TYPE_NOTE) {
struct leaf_node *l = (struct leaf_node *) CLR_PTR_TYPE(*p);
- if (hasheq(key_sha1, l->key_oid.hash))
+ if (hasheq(key_sha1, l->key_oid.hash, the_repository->hash_algo))
return l;
}
return NULL;
@@ -353,7 +355,7 @@ static void add_non_note(struct notes_tree *t, char *path,
n->next = NULL;
n->path = path;
n->mode = mode;
- oidread(&n->oid, sha1);
+ oidread(&n->oid, sha1, the_repository->hash_algo);
t->prev_non_note = n;
if (!t->first_non_note) {
@@ -427,6 +429,8 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
hashsz - prefix_len))
goto handle_non_note; /* entry.path is not a SHA1 */
+ memset(object_oid.hash + hashsz, 0, GIT_MAX_RAWSZ - hashsz);
+
type = PTR_TYPE_NOTE;
} else if (path_len == 2) {
/* This is potentially an internal node */
@@ -945,7 +949,8 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
{
assert(list->strdup_strings);
if (has_glob_specials(glob)) {
- for_each_glob_ref(string_list_add_one_ref, glob, list);
+ refs_for_each_glob_ref(get_main_ref_store(the_repository),
+ string_list_add_one_ref, glob, list);
} else {
struct object_id oid;
if (repo_get_oid(the_repository, glob, &oid))
@@ -1029,13 +1034,13 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
if (flags & NOTES_INIT_EMPTY ||
repo_get_oid_treeish(the_repository, notes_ref, &object_oid))
return;
- if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))
+ if (flags & NOTES_INIT_WRITABLE && refs_read_ref(get_main_ref_store(the_repository), notes_ref, &object_oid))
die("Cannot use notes ref %s", notes_ref);
if (get_tree_entry(the_repository, &object_oid, "", &oid, &mode))
die("Failed to read notes tree referenced by %s (%s)",
notes_ref, oid_to_hex(&object_oid));
- oidclr(&root_tree.key_oid);
+ oidclr(&root_tree.key_oid, the_repository->hash_algo);
oidcpy(&root_tree.val_oid, &oid);
load_subtree(t, &root_tree, t->root, 0);
}
@@ -1145,8 +1150,8 @@ int remove_note(struct notes_tree *t, const unsigned char *object_sha1)
if (!t)
t = &default_notes_tree;
assert(t->initialized);
- oidread(&l.key_oid, object_sha1);
- oidclr(&l.val_oid);
+ oidread(&l.key_oid, object_sha1, the_repository->hash_algo);
+ oidclr(&l.val_oid, the_repository->hash_algo);
note_tree_remove(t, t->root, 0, &l);
if (is_null_oid(&l.val_oid)) /* no note was removed */
return 1;
diff --git a/object-file-convert.c b/object-file-convert.c
index 4f61890..3887d6d 100644
--- a/object-file-convert.c
+++ b/object-file-convert.c
@@ -1,9 +1,11 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "strbuf.h"
#include "hex.h"
#include "repository.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "hash.h"
#include "object.h"
#include "loose.h"
@@ -56,7 +58,7 @@ static int decode_tree_entry_raw(struct object_id *oid, const char **path,
return -1;
*len = strlen(*path) + 1;
- oidread_algop(oid, (const unsigned char *)*path + *len, algo);
+ oidread(oid, (const unsigned char *)*path + *len, algo);
return 0;
}
diff --git a/object-file.c b/object-file.c
index 610b1f4..065103b 100644
--- a/object-file.c
+++ b/object-file.c
@@ -6,6 +6,9 @@
* This handles basic git object files - packing, unpacking,
* creation etc.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -227,16 +230,10 @@ const struct object_id *null_oid(void)
return the_hash_algo->null_oid;
}
-const char *empty_tree_oid_hex(void)
+const char *empty_tree_oid_hex(const struct git_hash_algo *algop)
{
static char buf[GIT_MAX_HEXSZ + 1];
- return oid_to_hex_r(buf, the_hash_algo->empty_tree);
-}
-
-const char *empty_blob_oid_hex(void)
-{
- static char buf[GIT_MAX_HEXSZ + 1];
- return oid_to_hex_r(buf, the_hash_algo->empty_blob);
+ return oid_to_hex_r(buf, algop->empty_tree);
}
int hash_algo_by_name(const char *name)
@@ -277,7 +274,7 @@ int hash_algo_by_length(int len)
static struct cached_object {
struct object_id oid;
enum object_type type;
- void *buf;
+ const void *buf;
unsigned long size;
} *cached_objects;
static int cached_object_nr, cached_object_alloc;
@@ -1446,7 +1443,7 @@ static int loose_object_info(struct repository *r,
int allow_unknown = flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
if (oi->delta_base_oid)
- oidclr(oi->delta_base_oid);
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
/*
* If we don't care about type or size, then we don't
@@ -1580,7 +1577,7 @@ static int do_oid_object_info_extended(struct repository *r,
if (oi->disk_sizep)
*(oi->disk_sizep) = 0;
if (oi->delta_base_oid)
- oidclr(oi->delta_base_oid);
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
if (oi->type_name)
strbuf_addstr(oi->type_name, type_name(co->type));
if (oi->contentp)
@@ -1711,9 +1708,9 @@ static int oid_object_info_convert(struct repository *r,
ret = convert_object_file(&outbuf,
the_hash_algo, input_algo,
content, size, type, !do_die);
+ free(content);
if (ret == -1)
return -1;
- free(content);
size = outbuf.len;
content = strbuf_detach(&outbuf, NULL);
}
@@ -1778,6 +1775,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
struct object_id *oid)
{
struct cached_object *co;
+ char *co_buf;
hash_object_file(the_hash_algo, buf, len, type, oid);
if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
@@ -1787,8 +1785,9 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
co = &cached_objects[cached_object_nr++];
co->size = len;
co->type = type;
- co->buf = xmalloc(len);
- memcpy(co->buf, buf, len);
+ co_buf = xmalloc(len);
+ memcpy(co_buf, buf, len);
+ co->buf = co_buf;
oidcpy(&co->oid, oid);
return 0;
}
@@ -2482,12 +2481,13 @@ static int hash_format_check_report(struct fsck_options *opts UNUSED,
}
static int index_mem(struct index_state *istate,
- struct object_id *oid, void *buf, size_t size,
+ struct object_id *oid,
+ const void *buf, size_t size,
enum object_type type,
const char *path, unsigned flags)
{
+ struct strbuf nbuf = STRBUF_INIT;
int ret = 0;
- int re_allocated = 0;
int write_object = flags & HASH_WRITE_OBJECT;
if (!type)
@@ -2497,11 +2497,10 @@ static int index_mem(struct index_state *istate,
* Convert blobs to git internal format
*/
if ((type == OBJ_BLOB) && path) {
- struct strbuf nbuf = STRBUF_INIT;
if (convert_to_git(istate, path, buf, size, &nbuf,
get_conv_flags(flags))) {
- buf = strbuf_detach(&nbuf, &size);
- re_allocated = 1;
+ buf = nbuf.buf;
+ size = nbuf.len;
}
}
if (flags & HASH_FORMAT_CHECK) {
@@ -2518,8 +2517,8 @@ static int index_mem(struct index_state *istate,
ret = write_object_file(buf, size, type, oid);
else
hash_object_file(the_hash_algo, buf, size, type, oid);
- if (re_allocated)
- free(buf);
+
+ strbuf_release(&nbuf);
return ret;
}
@@ -2669,7 +2668,7 @@ int index_path(struct index_state *istate, struct object_id *oid,
strbuf_release(&sb);
break;
case S_IFDIR:
- return resolve_gitlink_ref(path, "HEAD", oid);
+ return repo_resolve_gitlink_ref(the_repository, path, "HEAD", oid);
default:
return error(_("%s: unsupported file type"), path);
}
@@ -2743,6 +2742,8 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,
!hex_to_bytes(oid.hash + 1, de->d_name,
the_hash_algo->rawsz - 1)) {
oid_set_algo(&oid, the_hash_algo);
+ memset(oid.hash + the_hash_algo->rawsz, 0,
+ GIT_MAX_RAWSZ - the_hash_algo->rawsz);
if (obj_cb) {
r = obj_cb(&oid, path->buf, data);
if (r)
diff --git a/object-name.c b/object-name.c
index 523af6f..f362d54 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "object-name.h"
#include "advice.h"
@@ -837,7 +839,7 @@ int repo_find_unique_abbrev_r(struct repository *r, char *hex,
}
oid_to_hex_r(hex, oid);
- if (len == hexsz || !len)
+ if (len >= hexsz || !len)
return hexsz;
mad.repo = r;
diff --git a/object.c b/object.c
index 51e3848..0c0fcb7 100644
--- a/object.c
+++ b/object.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
@@ -207,6 +209,29 @@ struct object *lookup_object_by_type(struct repository *r,
}
}
+enum peel_status peel_object(struct repository *r,
+ const struct object_id *name,
+ struct object_id *oid)
+{
+ struct object *o = lookup_unknown_object(r, name);
+
+ if (o->type == OBJ_NONE) {
+ int type = oid_object_info(r, name, NULL);
+ if (type < 0 || !object_as_type(o, type, 0))
+ return PEEL_INVALID;
+ }
+
+ if (o->type != OBJ_TAG)
+ return PEEL_NON_TAG;
+
+ o = deref_tag_noverify(r, o);
+ if (!o)
+ return PEEL_INVALID;
+
+ oidcpy(oid, &o->oid);
+ return PEEL_PEELED;
+}
+
struct object *parse_object_buffer(struct repository *r, const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
{
struct object *obj;
diff --git a/object.h b/object.h
index 9293e70..3ea7125 100644
--- a/object.h
+++ b/object.h
@@ -1,7 +1,7 @@
#ifndef OBJECT_H
#define OBJECT_H
-#include "hash-ll.h"
+#include "hash.h"
struct buffer_slab;
struct repository;
@@ -81,6 +81,7 @@ void object_array_init(struct object_array *array);
* reflog.c: 10--12
* builtin/show-branch.c: 0-------------------------------------------26
* builtin/unpack-objects.c: 2021
+ * pack-bitmap.h: 2122
*/
#define FLAG_BITS 28
@@ -256,6 +257,41 @@ struct object *lookup_unknown_object(struct repository *r, const struct object_i
struct object *lookup_object_by_type(struct repository *r, const struct object_id *oid,
enum object_type type);
+enum peel_status {
+ /* object was peeled successfully: */
+ PEEL_PEELED = 0,
+
+ /*
+ * object cannot be peeled because the named object (or an
+ * object referred to by a tag in the peel chain), does not
+ * exist.
+ */
+ PEEL_INVALID = -1,
+
+ /* object cannot be peeled because it is not a tag: */
+ PEEL_NON_TAG = -2,
+
+ /* ref_entry contains no peeled value because it is a symref: */
+ PEEL_IS_SYMREF = -3,
+
+ /*
+ * ref_entry cannot be peeled because it is broken (i.e., the
+ * symbolic reference cannot even be resolved to an object
+ * name):
+ */
+ PEEL_BROKEN = -4
+};
+
+/*
+ * Peel the named object; i.e., if the object is a tag, resolve the
+ * tag recursively until a non-tag is found. If successful, store the
+ * result to oid and return PEEL_PEELED. If the object is not a tag
+ * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
+ * and leave oid unchanged.
+ */
+enum peel_status peel_object(struct repository *r,
+ const struct object_id *name, struct object_id *oid);
+
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
diff --git a/oid-array.c b/oid-array.c
index 1f36651..9cac974 100644
--- a/oid-array.c
+++ b/oid-array.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "oid-array.h"
#include "hash-lookup.h"
diff --git a/oidmap.h b/oidmap.h
index 05c673e..fad4128 100644
--- a/oidmap.h
+++ b/oidmap.h
@@ -1,7 +1,7 @@
#ifndef OIDMAP_H
#define OIDMAP_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hashmap.h"
/*
diff --git a/oidset.c b/oidset.c
index 91d1385..8d36aef8 100644
--- a/oidset.c
+++ b/oidset.c
@@ -48,12 +48,14 @@ void oidset_clear(struct oidset *set)
oidset_init(set, 0);
}
-void oidset_parse_file(struct oidset *set, const char *path)
+void oidset_parse_file(struct oidset *set, const char *path,
+ const struct git_hash_algo *algop)
{
- oidset_parse_file_carefully(set, path, NULL, NULL);
+ oidset_parse_file_carefully(set, path, algop, NULL, NULL);
}
void oidset_parse_file_carefully(struct oidset *set, const char *path,
+ const struct git_hash_algo *algop,
oidset_parse_tweak_fn fn, void *cbdata)
{
FILE *fp;
@@ -79,7 +81,7 @@ void oidset_parse_file_carefully(struct oidset *set, const char *path,
if (!sb.len)
continue;
- if (parse_oid_hex(sb.buf, &oid, &p) || *p != '\0')
+ if (parse_oid_hex_algop(sb.buf, &oid, &p, algop) || *p != '\0')
die("invalid object name: %s", sb.buf);
if (fn && fn(&oid, cbdata))
continue;
diff --git a/oidset.h b/oidset.h
index 262f425..0106b6f 100644
--- a/oidset.h
+++ b/oidset.h
@@ -80,7 +80,8 @@ void oidset_clear(struct oidset *set);
* are allowed. Leading whitespace and empty or white-space only lines are
* ignored.
*/
-void oidset_parse_file(struct oidset *set, const char *path);
+void oidset_parse_file(struct oidset *set, const char *path,
+ const struct git_hash_algo *algop);
/*
* Similar to the above, but with a callback which can (1) return non-zero to
@@ -89,6 +90,7 @@ void oidset_parse_file(struct oidset *set, const char *path);
*/
typedef int (*oidset_parse_tweak_fn)(struct object_id *, void *);
void oidset_parse_file_carefully(struct oidset *set, const char *path,
+ const struct git_hash_algo *algop,
oidset_parse_tweak_fn fn, void *cbdata);
struct oidset_iter {
diff --git a/oidtree.c b/oidtree.c
index daef175..92d03b5 100644
--- a/oidtree.c
+++ b/oidtree.c
@@ -42,7 +42,7 @@ void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
* Clear the padding and copy the result in separate steps to
* respect the 4-byte alignment needed by struct object_id.
*/
- oidcpy_with_padding(&k, oid);
+ oidcpy(&k, oid);
memcpy(on->k, &k, sizeof(k));
/*
@@ -60,7 +60,7 @@ int oidtree_contains(struct oidtree *ot, const struct object_id *oid)
struct object_id k;
size_t klen = sizeof(k);
- oidcpy_with_padding(&k, oid);
+ oidcpy(&k, oid);
if (oid->algo == GIT_HASH_UNKNOWN)
klen -= sizeof(oid->algo);
diff --git a/oidtree.h b/oidtree.h
index 55c8351..77898f5 100644
--- a/oidtree.h
+++ b/oidtree.h
@@ -2,7 +2,7 @@
#define OIDTREE_H
#include "cbtree.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "mem-pool.h"
struct oidtree {
diff --git a/oss-fuzz/fuzz-commit-graph.c b/oss-fuzz/fuzz-commit-graph.c
index 2992079..951c9c0 100644
--- a/oss-fuzz/fuzz-commit-graph.c
+++ b/oss-fuzz/fuzz-commit-graph.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit-graph.h"
#include "repository.h"
@@ -11,13 +13,15 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
struct commit_graph *g;
- initialize_the_repository();
+ initialize_repository(the_repository);
+
/*
* Initialize the_repository with commit-graph settings that would
* normally be read from the repository's gitdir. We want to avoid
* touching the disk to keep the individual fuzz-test cases as fast as
* possible.
*/
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
the_repository->settings.commit_graph_generation_version = 2;
the_repository->settings.commit_graph_read_changed_paths = 1;
g = parse_commit_graph(&the_repository->settings, (void *)data, size);
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index c6c8f94..bf96c80 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -17,6 +19,12 @@
#include "trace2.h"
#include "tree.h"
#include "tree-walk.h"
+#include "pseudo-merge.h"
+#include "oid-array.h"
+#include "config.h"
+#include "alloc.h"
+#include "refs.h"
+#include "strmap.h"
struct bitmapped_commit {
struct commit *commit;
@@ -25,45 +33,82 @@ struct bitmapped_commit {
int flags;
int xor_offset;
uint32_t commit_pos;
+ unsigned pseudo_merge : 1;
};
-struct bitmap_writer {
- struct ewah_bitmap *commits;
- struct ewah_bitmap *trees;
- struct ewah_bitmap *blobs;
- struct ewah_bitmap *tags;
-
- kh_oid_map_t *bitmaps;
- struct packing_data *to_pack;
-
- struct bitmapped_commit *selected;
- unsigned int selected_nr, selected_alloc;
-
- struct progress *progress;
- int show_progress;
- unsigned char pack_checksum[GIT_MAX_RAWSZ];
-};
-
-static struct bitmap_writer writer;
-
-void bitmap_writer_show_progress(int show)
+static inline int bitmap_writer_nr_selected_commits(struct bitmap_writer *writer)
{
- writer.show_progress = show;
+ return writer->selected_nr - writer->pseudo_merges_nr;
+}
+
+void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r)
+{
+ memset(writer, 0, sizeof(struct bitmap_writer));
+ if (writer->bitmaps)
+ BUG("bitmap writer already initialized");
+ writer->bitmaps = kh_init_oid_map();
+ writer->pseudo_merge_commits = kh_init_oid_map();
+
+ string_list_init_dup(&writer->pseudo_merge_groups);
+
+ load_pseudo_merges_from_config(&writer->pseudo_merge_groups);
+}
+
+static void free_pseudo_merge_commit_idx(struct pseudo_merge_commit_idx *idx)
+{
+ if (!idx)
+ return;
+ free(idx->pseudo_merge);
+ free(idx);
+}
+
+void bitmap_writer_free(struct bitmap_writer *writer)
+{
+ uint32_t i;
+ struct pseudo_merge_commit_idx *idx;
+
+ if (!writer)
+ return;
+
+ ewah_free(writer->commits);
+ ewah_free(writer->trees);
+ ewah_free(writer->blobs);
+ ewah_free(writer->tags);
+
+ kh_destroy_oid_map(writer->bitmaps);
+
+ kh_foreach_value(writer->pseudo_merge_commits, idx,
+ free_pseudo_merge_commit_idx(idx));
+ kh_destroy_oid_map(writer->pseudo_merge_commits);
+
+ for (i = 0; i < writer->selected_nr; i++) {
+ struct bitmapped_commit *bc = &writer->selected[i];
+ if (bc->write_as != bc->bitmap)
+ ewah_free(bc->write_as);
+ ewah_free(bc->bitmap);
+ }
+ free(writer->selected);
+}
+
+void bitmap_writer_show_progress(struct bitmap_writer *writer, int show)
+{
+ writer->show_progress = show;
}
/**
* Build the initial type index for the packfile or multi-pack-index
*/
-void bitmap_writer_build_type_index(struct packing_data *to_pack,
+void bitmap_writer_build_type_index(struct bitmap_writer *writer,
+ struct packing_data *to_pack,
struct pack_idx_entry **index,
uint32_t index_nr)
{
uint32_t i;
- writer.commits = ewah_new();
- writer.trees = ewah_new();
- writer.blobs = ewah_new();
- writer.tags = ewah_new();
+ writer->commits = ewah_new();
+ writer->trees = ewah_new();
+ writer->blobs = ewah_new();
+ writer->tags = ewah_new();
ALLOC_ARRAY(to_pack->in_pack_pos, to_pack->nr_objects);
for (i = 0; i < index_nr; ++i) {
@@ -88,19 +133,19 @@ void bitmap_writer_build_type_index(struct packing_data *to_pack,
switch (real_type) {
case OBJ_COMMIT:
- ewah_set(writer.commits, i);
+ ewah_set(writer->commits, i);
break;
case OBJ_TREE:
- ewah_set(writer.trees, i);
+ ewah_set(writer->trees, i);
break;
case OBJ_BLOB:
- ewah_set(writer.blobs, i);
+ ewah_set(writer->blobs, i);
break;
case OBJ_TAG:
- ewah_set(writer.tags, i);
+ ewah_set(writer->tags, i);
break;
default:
@@ -111,27 +156,49 @@ void bitmap_writer_build_type_index(struct packing_data *to_pack,
}
}
+int bitmap_writer_has_bitmapped_object_id(struct bitmap_writer *writer,
+ const struct object_id *oid)
+{
+ return kh_get_oid_map(writer->bitmaps, *oid) != kh_end(writer->bitmaps);
+}
+
/**
* Compute the actual bitmaps
*/
-static inline void push_bitmapped_commit(struct commit *commit)
+void bitmap_writer_push_commit(struct bitmap_writer *writer,
+ struct commit *commit, unsigned pseudo_merge)
{
- if (writer.selected_nr >= writer.selected_alloc) {
- writer.selected_alloc = (writer.selected_alloc + 32) * 2;
- REALLOC_ARRAY(writer.selected, writer.selected_alloc);
+ if (writer->selected_nr >= writer->selected_alloc) {
+ writer->selected_alloc = (writer->selected_alloc + 32) * 2;
+ REALLOC_ARRAY(writer->selected, writer->selected_alloc);
}
- writer.selected[writer.selected_nr].commit = commit;
- writer.selected[writer.selected_nr].bitmap = NULL;
- writer.selected[writer.selected_nr].flags = 0;
+ if (!pseudo_merge) {
+ int hash_ret;
+ khiter_t hash_pos = kh_put_oid_map(writer->bitmaps,
+ commit->object.oid,
+ &hash_ret);
- writer.selected_nr++;
+ if (!hash_ret)
+ die(_("duplicate entry when writing bitmap index: %s"),
+ oid_to_hex(&commit->object.oid));
+ kh_value(writer->bitmaps, hash_pos) = NULL;
+ }
+
+ writer->selected[writer->selected_nr].commit = commit;
+ writer->selected[writer->selected_nr].bitmap = NULL;
+ writer->selected[writer->selected_nr].write_as = NULL;
+ writer->selected[writer->selected_nr].flags = 0;
+ writer->selected[writer->selected_nr].pseudo_merge = pseudo_merge;
+
+ writer->selected_nr++;
}
-static uint32_t find_object_pos(const struct object_id *oid, int *found)
+static uint32_t find_object_pos(struct bitmap_writer *writer,
+ const struct object_id *oid, int *found)
{
- struct object_entry *entry = packlist_find(writer.to_pack, oid);
+ struct object_entry *entry = packlist_find(writer->to_pack, oid);
if (!entry) {
if (found)
@@ -143,30 +210,34 @@ static uint32_t find_object_pos(const struct object_id *oid, int *found)
if (found)
*found = 1;
- return oe_in_pack_pos(writer.to_pack, entry);
+ return oe_in_pack_pos(writer->to_pack, entry);
}
-static void compute_xor_offsets(void)
+static void compute_xor_offsets(struct bitmap_writer *writer)
{
static const int MAX_XOR_OFFSET_SEARCH = 10;
int i, next = 0;
- while (next < writer.selected_nr) {
- struct bitmapped_commit *stored = &writer.selected[next];
-
+ while (next < writer->selected_nr) {
+ struct bitmapped_commit *stored = &writer->selected[next];
int best_offset = 0;
struct ewah_bitmap *best_bitmap = stored->bitmap;
struct ewah_bitmap *test_xor;
+ if (stored->pseudo_merge)
+ goto next;
+
for (i = 1; i <= MAX_XOR_OFFSET_SEARCH; ++i) {
int curr = next - i;
if (curr < 0)
break;
+ if (writer->selected[curr].pseudo_merge)
+ continue;
test_xor = ewah_pool_new();
- ewah_xor(writer.selected[curr].bitmap, stored->bitmap, test_xor);
+ ewah_xor(writer->selected[curr].bitmap, stored->bitmap, test_xor);
if (test_xor->buffer_size < best_bitmap->buffer_size) {
if (best_bitmap != stored->bitmap)
@@ -179,6 +250,7 @@ static void compute_xor_offsets(void)
}
}
+next:
stored->xor_offset = best_offset;
stored->write_as = best_bitmap;
@@ -191,7 +263,8 @@ struct bb_commit {
struct bitmap *commit_mask;
struct bitmap *bitmap;
unsigned selected:1,
- maximal:1;
+ maximal:1,
+ pseudo_merge:1;
unsigned idx; /* within selected array */
};
@@ -229,17 +302,18 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
revs.first_parent_only = 1;
for (i = 0; i < writer->selected_nr; i++) {
- struct commit *c = writer->selected[i].commit;
- struct bb_commit *ent = bb_data_at(&bb->data, c);
+ struct bitmapped_commit *bc = &writer->selected[i];
+ struct bb_commit *ent = bb_data_at(&bb->data, bc->commit);
ent->selected = 1;
ent->maximal = 1;
+ ent->pseudo_merge = bc->pseudo_merge;
ent->idx = i;
ent->commit_mask = bitmap_new();
bitmap_set(ent->commit_mask, i);
- add_pending_object(&revs, &c->object, "");
+ add_pending_object(&revs, &bc->commit->object, "");
}
if (prepare_revision_walk(&revs))
@@ -348,7 +422,8 @@ static void bitmap_builder_clear(struct bitmap_builder *bb)
bb->commits_nr = bb->commits_alloc = 0;
}
-static int fill_bitmap_tree(struct bitmap *bitmap,
+static int fill_bitmap_tree(struct bitmap_writer *writer,
+ struct bitmap *bitmap,
struct tree *tree)
{
int found;
@@ -360,7 +435,7 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
* If our bit is already set, then there is nothing to do. Both this
* tree and all of its children will be set.
*/
- pos = find_object_pos(&tree->object.oid, &found);
+ pos = find_object_pos(writer, &tree->object.oid, &found);
if (!found)
return -1;
if (bitmap_get(bitmap, pos))
@@ -375,12 +450,12 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
- if (fill_bitmap_tree(bitmap,
+ if (fill_bitmap_tree(writer, bitmap,
lookup_tree(the_repository, &entry.oid)) < 0)
return -1;
break;
case OBJ_BLOB:
- pos = find_object_pos(&entry.oid, &found);
+ pos = find_object_pos(writer, &entry.oid, &found);
if (!found)
return -1;
bitmap_set(bitmap, pos);
@@ -396,8 +471,10 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
}
static int reused_bitmaps_nr;
+static int reused_pseudo_merge_bitmaps_nr;
-static int fill_bitmap_commit(struct bb_commit *ent,
+static int fill_bitmap_commit(struct bitmap_writer *writer,
+ struct bb_commit *ent,
struct commit *commit,
struct prio_queue *queue,
struct prio_queue *tree_queue,
@@ -416,8 +493,13 @@ static int fill_bitmap_commit(struct bb_commit *ent,
struct commit *c = prio_queue_get(queue);
if (old_bitmap && mapping) {
- struct ewah_bitmap *old = bitmap_for_commit(old_bitmap, c);
+ struct ewah_bitmap *old;
struct bitmap *remapped = bitmap_new();
+
+ if (commit->object.flags & BITMAP_PSEUDO_MERGE)
+ old = pseudo_merge_bitmap_for_commit(old_bitmap, c);
+ else
+ old = bitmap_for_commit(old_bitmap, c);
/*
* If this commit has an old bitmap, then translate that
* bitmap and add its bits to this one. No need to walk
@@ -426,7 +508,10 @@ static int fill_bitmap_commit(struct bb_commit *ent,
if (old && !rebuild_bitmap(mapping, old, remapped)) {
bitmap_or(ent->bitmap, remapped);
bitmap_free(remapped);
- reused_bitmaps_nr++;
+ if (commit->object.flags & BITMAP_PSEUDO_MERGE)
+ reused_pseudo_merge_bitmaps_nr++;
+ else
+ reused_bitmaps_nr++;
continue;
}
bitmap_free(remapped);
@@ -436,15 +521,18 @@ static int fill_bitmap_commit(struct bb_commit *ent,
* Mark ourselves and queue our tree. The commit
* walk ensures we cover all parents.
*/
- pos = find_object_pos(&c->object.oid, &found);
- if (!found)
- return -1;
- bitmap_set(ent->bitmap, pos);
- prio_queue_put(tree_queue,
- repo_get_commit_tree(the_repository, c));
+ if (!(c->object.flags & BITMAP_PSEUDO_MERGE)) {
+ pos = find_object_pos(writer, &c->object.oid, &found);
+ if (!found)
+ return -1;
+ bitmap_set(ent->bitmap, pos);
+ prio_queue_put(tree_queue,
+ repo_get_commit_tree(the_repository, c));
+ }
for (p = c->parents; p; p = p->next) {
- pos = find_object_pos(&p->item->object.oid, &found);
+ pos = find_object_pos(writer, &p->item->object.oid,
+ &found);
if (!found)
return -1;
if (!bitmap_get(ent->bitmap, pos)) {
@@ -455,29 +543,34 @@ static int fill_bitmap_commit(struct bb_commit *ent,
}
while (tree_queue->nr) {
- if (fill_bitmap_tree(ent->bitmap,
+ if (fill_bitmap_tree(writer, ent->bitmap,
prio_queue_get(tree_queue)) < 0)
return -1;
}
return 0;
}
-static void store_selected(struct bb_commit *ent, struct commit *commit)
+static void store_selected(struct bitmap_writer *writer,
+ struct bb_commit *ent, struct commit *commit)
{
- struct bitmapped_commit *stored = &writer.selected[ent->idx];
+ struct bitmapped_commit *stored = &writer->selected[ent->idx];
khiter_t hash_pos;
- int hash_ret;
stored->bitmap = bitmap_to_ewah(ent->bitmap);
- hash_pos = kh_put_oid_map(writer.bitmaps, commit->object.oid, &hash_ret);
- if (hash_ret == 0)
- die("Duplicate entry when writing index: %s",
+ if (ent->pseudo_merge)
+ return;
+
+ hash_pos = kh_get_oid_map(writer->bitmaps, commit->object.oid);
+ if (hash_pos == kh_end(writer->bitmaps))
+ die(_("attempted to store non-selected commit: '%s'"),
oid_to_hex(&commit->object.oid));
- kh_value(writer.bitmaps, hash_pos) = stored;
+
+ kh_value(writer->bitmaps, hash_pos) = stored;
}
-int bitmap_writer_build(struct packing_data *to_pack)
+int bitmap_writer_build(struct bitmap_writer *writer,
+ struct packing_data *to_pack)
{
struct bitmap_builder bb;
size_t i;
@@ -488,11 +581,11 @@ int bitmap_writer_build(struct packing_data *to_pack)
uint32_t *mapping;
int closed = 1; /* until proven otherwise */
- writer.bitmaps = kh_init_oid_map();
- writer.to_pack = to_pack;
+ writer->to_pack = to_pack;
- if (writer.show_progress)
- writer.progress = start_progress("Building bitmaps", writer.selected_nr);
+ if (writer->show_progress)
+ writer->progress = start_progress("Building bitmaps",
+ writer->selected_nr);
trace2_region_enter("pack-bitmap-write", "building_bitmaps_total",
the_repository);
@@ -502,23 +595,23 @@ int bitmap_writer_build(struct packing_data *to_pack)
else
mapping = NULL;
- bitmap_builder_init(&bb, &writer, old_bitmap);
+ bitmap_builder_init(&bb, writer, old_bitmap);
for (i = bb.commits_nr; i > 0; i--) {
struct commit *commit = bb.commits[i-1];
struct bb_commit *ent = bb_data_at(&bb.data, commit);
struct commit *child;
int reused = 0;
- if (fill_bitmap_commit(ent, commit, &queue, &tree_queue,
+ if (fill_bitmap_commit(writer, ent, commit, &queue, &tree_queue,
old_bitmap, mapping) < 0) {
closed = 0;
break;
}
if (ent->selected) {
- store_selected(ent, commit);
+ store_selected(writer, ent, commit);
nr_stored++;
- display_progress(writer.progress, nr_stored);
+ display_progress(writer->progress, nr_stored);
}
while ((child = pop_commit(&ent->reverse_edges))) {
@@ -548,11 +641,14 @@ int bitmap_writer_build(struct packing_data *to_pack)
the_repository);
trace2_data_intmax("pack-bitmap-write", the_repository,
"building_bitmaps_reused", reused_bitmaps_nr);
+ trace2_data_intmax("pack-bitmap-write", the_repository,
+ "building_bitmaps_pseudo_merge_reused",
+ reused_pseudo_merge_bitmaps_nr);
- stop_progress(&writer.progress);
+ stop_progress(&writer->progress);
if (closed)
- compute_xor_offsets();
+ compute_xor_offsets(writer);
return closed ? 0 : -1;
}
@@ -590,9 +686,9 @@ static int date_compare(const void *_a, const void *_b)
return (long)b->date - (long)a->date;
}
-void bitmap_writer_select_commits(struct commit **indexed_commits,
- unsigned int indexed_commits_nr,
- int max_bitmaps)
+void bitmap_writer_select_commits(struct bitmap_writer *writer,
+ struct commit **indexed_commits,
+ unsigned int indexed_commits_nr)
{
unsigned int i = 0, j, next;
@@ -600,12 +696,12 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
if (indexed_commits_nr < 100) {
for (i = 0; i < indexed_commits_nr; ++i)
- push_bitmapped_commit(indexed_commits[i]);
+ bitmap_writer_push_commit(writer, indexed_commits[i], 0);
return;
}
- if (writer.show_progress)
- writer.progress = start_progress("Selecting bitmap commits", 0);
+ if (writer->show_progress)
+ writer->progress = start_progress("Selecting bitmap commits", 0);
for (;;) {
struct commit *chosen = NULL;
@@ -615,11 +711,6 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
if (i + next >= indexed_commits_nr)
break;
- if (max_bitmaps > 0 && writer.selected_nr >= max_bitmaps) {
- writer.selected_nr = max_bitmaps;
- break;
- }
-
if (next == 0) {
chosen = indexed_commits[i];
} else {
@@ -638,13 +729,15 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
}
}
- push_bitmapped_commit(chosen);
+ bitmap_writer_push_commit(writer, chosen, 0);
i += next + 1;
- display_progress(writer.progress, i);
+ display_progress(writer->progress, i);
}
- stop_progress(&writer.progress);
+ stop_progress(&writer->progress);
+
+ select_pseudo_merges(writer, indexed_commits, indexed_commits_nr);
}
@@ -670,19 +763,21 @@ static const struct object_id *oid_access(size_t pos, const void *table)
return &index[pos]->oid;
}
-static void write_selected_commits_v1(struct hashfile *f,
- uint32_t *commit_positions,
- off_t *offsets)
+static void write_selected_commits_v1(struct bitmap_writer *writer,
+ struct hashfile *f, off_t *offsets)
{
int i;
- for (i = 0; i < writer.selected_nr; ++i) {
- struct bitmapped_commit *stored = &writer.selected[i];
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); ++i) {
+ struct bitmapped_commit *stored = &writer->selected[i];
+ if (stored->pseudo_merge)
+ BUG("unexpected pseudo-merge among selected: %s",
+ oid_to_hex(&stored->commit->object.oid));
if (offsets)
offsets[i] = hashfile_total(f);
- hashwrite_be32(f, commit_positions[i]);
+ hashwrite_be32(f, stored->commit_pos);
hashwrite_u8(f, stored->xor_offset);
hashwrite_u8(f, stored->flags);
@@ -690,31 +785,154 @@ static void write_selected_commits_v1(struct hashfile *f,
}
}
+static void write_pseudo_merges(struct bitmap_writer *writer,
+ struct hashfile *f)
+{
+ struct oid_array commits = OID_ARRAY_INIT;
+ struct bitmap **commits_bitmap = NULL;
+ off_t *pseudo_merge_ofs = NULL;
+ off_t start, table_start, next_ext;
+
+ uint32_t base = bitmap_writer_nr_selected_commits(writer);
+ size_t i, j = 0;
+
+ CALLOC_ARRAY(commits_bitmap, writer->pseudo_merges_nr);
+ CALLOC_ARRAY(pseudo_merge_ofs, writer->pseudo_merges_nr);
+
+ for (i = 0; i < writer->pseudo_merges_nr; i++) {
+ struct bitmapped_commit *merge = &writer->selected[base + i];
+ struct commit_list *p;
+
+ if (!merge->pseudo_merge)
+ BUG("found non-pseudo merge commit at %"PRIuMAX, (uintmax_t)i);
+
+ commits_bitmap[i] = bitmap_new();
+
+ for (p = merge->commit->parents; p; p = p->next)
+ bitmap_set(commits_bitmap[i],
+ find_object_pos(writer, &p->item->object.oid,
+ NULL));
+ }
+
+ start = hashfile_total(f);
+
+ for (i = 0; i < writer->pseudo_merges_nr; i++) {
+ struct ewah_bitmap *commits_ewah = bitmap_to_ewah(commits_bitmap[i]);
+
+ pseudo_merge_ofs[i] = hashfile_total(f);
+
+ dump_bitmap(f, commits_ewah);
+ dump_bitmap(f, writer->selected[base+i].write_as);
+
+ ewah_free(commits_ewah);
+ }
+
+ next_ext = st_add(hashfile_total(f),
+ st_mult(kh_size(writer->pseudo_merge_commits),
+ sizeof(uint64_t)));
+
+ table_start = hashfile_total(f);
+
+ commits.alloc = kh_size(writer->pseudo_merge_commits);
+ CALLOC_ARRAY(commits.oid, commits.alloc);
+
+ for (i = kh_begin(writer->pseudo_merge_commits); i != kh_end(writer->pseudo_merge_commits); i++) {
+ if (!kh_exist(writer->pseudo_merge_commits, i))
+ continue;
+ oid_array_append(&commits, &kh_key(writer->pseudo_merge_commits, i));
+ }
+
+ oid_array_sort(&commits);
+
+ /* write lookup table (non-extended) */
+ for (i = 0; i < commits.nr; i++) {
+ int hash_pos;
+ struct pseudo_merge_commit_idx *c;
+
+ hash_pos = kh_get_oid_map(writer->pseudo_merge_commits,
+ commits.oid[i]);
+ if (hash_pos == kh_end(writer->pseudo_merge_commits))
+ BUG("could not find pseudo-merge commit %s",
+ oid_to_hex(&commits.oid[i]));
+
+ c = kh_value(writer->pseudo_merge_commits, hash_pos);
+
+ hashwrite_be32(f, find_object_pos(writer, &commits.oid[i],
+ NULL));
+ if (c->nr == 1)
+ hashwrite_be64(f, pseudo_merge_ofs[c->pseudo_merge[0]]);
+ else if (c->nr > 1) {
+ if (next_ext & ((uint64_t)1<<63))
+ die(_("too many pseudo-merges"));
+ hashwrite_be64(f, next_ext | ((uint64_t)1<<63));
+ next_ext = st_add3(next_ext,
+ sizeof(uint32_t),
+ st_mult(c->nr, sizeof(uint64_t)));
+ } else
+ BUG("expected commit '%s' to have at least one "
+ "pseudo-merge", oid_to_hex(&commits.oid[i]));
+ }
+
+ /* write lookup table (extended) */
+ for (i = 0; i < commits.nr; i++) {
+ int hash_pos;
+ struct pseudo_merge_commit_idx *c;
+
+ hash_pos = kh_get_oid_map(writer->pseudo_merge_commits,
+ commits.oid[i]);
+ if (hash_pos == kh_end(writer->pseudo_merge_commits))
+ BUG("could not find pseudo-merge commit %s",
+ oid_to_hex(&commits.oid[i]));
+
+ c = kh_value(writer->pseudo_merge_commits, hash_pos);
+ if (c->nr == 1)
+ continue;
+
+ hashwrite_be32(f, c->nr);
+ for (j = 0; j < c->nr; j++)
+ hashwrite_be64(f, pseudo_merge_ofs[c->pseudo_merge[j]]);
+ }
+
+ /* write positions for all pseudo merges */
+ for (i = 0; i < writer->pseudo_merges_nr; i++)
+ hashwrite_be64(f, pseudo_merge_ofs[i]);
+
+ hashwrite_be32(f, writer->pseudo_merges_nr);
+ hashwrite_be32(f, kh_size(writer->pseudo_merge_commits));
+ hashwrite_be64(f, table_start - start);
+ hashwrite_be64(f, hashfile_total(f) - start + sizeof(uint64_t));
+
+ for (i = 0; i < writer->pseudo_merges_nr; i++)
+ bitmap_free(commits_bitmap[i]);
+
+ free(pseudo_merge_ofs);
+ free(commits_bitmap);
+}
+
static int table_cmp(const void *_va, const void *_vb, void *_data)
{
- uint32_t *commit_positions = _data;
- uint32_t a = commit_positions[*(uint32_t *)_va];
- uint32_t b = commit_positions[*(uint32_t *)_vb];
+ struct bitmap_writer *writer = _data;
+ struct bitmapped_commit *a = &writer->selected[*(uint32_t *)_va];
+ struct bitmapped_commit *b = &writer->selected[*(uint32_t *)_vb];
- if (a > b)
- return 1;
- else if (a < b)
+ if (a->commit_pos < b->commit_pos)
return -1;
+ else if (a->commit_pos > b->commit_pos)
+ return 1;
return 0;
}
-static void write_lookup_table(struct hashfile *f,
- uint32_t *commit_positions,
+static void write_lookup_table(struct bitmap_writer *writer, struct hashfile *f,
off_t *offsets)
{
uint32_t i;
uint32_t *table, *table_inv;
- ALLOC_ARRAY(table, writer.selected_nr);
- ALLOC_ARRAY(table_inv, writer.selected_nr);
+ ALLOC_ARRAY(table, bitmap_writer_nr_selected_commits(writer));
+ ALLOC_ARRAY(table_inv, bitmap_writer_nr_selected_commits(writer));
- for (i = 0; i < writer.selected_nr; i++)
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++)
table[i] = i;
/*
@@ -722,17 +940,17 @@ static void write_lookup_table(struct hashfile *f,
* bitmap corresponds to j'th bitmapped commit (among the selected
* commits) in lex order of OIDs.
*/
- QSORT_S(table, writer.selected_nr, table_cmp, commit_positions);
+ QSORT_S(table, bitmap_writer_nr_selected_commits(writer), table_cmp, writer);
/* table_inv helps us discover that relationship (i'th bitmap
* to j'th commit by j = table_inv[i])
*/
- for (i = 0; i < writer.selected_nr; i++)
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++)
table_inv[table[i]] = i;
trace2_region_enter("pack-bitmap-write", "writing_lookup_table", the_repository);
- for (i = 0; i < writer.selected_nr; i++) {
- struct bitmapped_commit *selected = &writer.selected[table[i]];
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
+ struct bitmapped_commit *selected = &writer->selected[table[i]];
uint32_t xor_offset = selected->xor_offset;
uint32_t xor_row;
@@ -753,7 +971,7 @@ static void write_lookup_table(struct hashfile *f,
xor_row = 0xffffffff;
}
- hashwrite_be32(f, commit_positions[table[i]]);
+ hashwrite_be32(f, writer->selected[table[i]].commit_pos);
hashwrite_be64(f, (uint64_t)offsets[table[i]]);
hashwrite_be32(f, xor_row);
}
@@ -775,12 +993,14 @@ static void write_hash_cache(struct hashfile *f,
}
}
-void bitmap_writer_set_checksum(const unsigned char *sha1)
+void bitmap_writer_set_checksum(struct bitmap_writer *writer,
+ const unsigned char *sha1)
{
- hashcpy(writer.pack_checksum, sha1);
+ hashcpy(writer->pack_checksum, sha1, the_repository->hash_algo);
}
-void bitmap_writer_finish(struct pack_idx_entry **index,
+void bitmap_writer_finish(struct bitmap_writer *writer,
+ struct pack_idx_entry **index,
uint32_t index_nr,
const char *filename,
uint16_t options)
@@ -789,7 +1009,6 @@ void bitmap_writer_finish(struct pack_idx_entry **index,
static uint16_t flags = BITMAP_OPT_FULL_DAG;
struct strbuf tmp_file = STRBUF_INIT;
struct hashfile *f;
- uint32_t *commit_positions = NULL;
off_t *offsets = NULL;
uint32_t i;
@@ -797,39 +1016,43 @@ void bitmap_writer_finish(struct pack_idx_entry **index,
int fd = odb_mkstemp(&tmp_file, "pack/tmp_bitmap_XXXXXX");
+ if (writer->pseudo_merges_nr)
+ options |= BITMAP_OPT_PSEUDO_MERGES;
+
f = hashfd(fd, tmp_file.buf);
memcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));
header.version = htons(default_version);
header.options = htons(flags | options);
- header.entry_count = htonl(writer.selected_nr);
- hashcpy(header.checksum, writer.pack_checksum);
+ header.entry_count = htonl(bitmap_writer_nr_selected_commits(writer));
+ hashcpy(header.checksum, writer->pack_checksum, the_repository->hash_algo);
hashwrite(f, &header, sizeof(header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz);
- dump_bitmap(f, writer.commits);
- dump_bitmap(f, writer.trees);
- dump_bitmap(f, writer.blobs);
- dump_bitmap(f, writer.tags);
+ dump_bitmap(f, writer->commits);
+ dump_bitmap(f, writer->trees);
+ dump_bitmap(f, writer->blobs);
+ dump_bitmap(f, writer->tags);
if (options & BITMAP_OPT_LOOKUP_TABLE)
CALLOC_ARRAY(offsets, index_nr);
- ALLOC_ARRAY(commit_positions, writer.selected_nr);
-
- for (i = 0; i < writer.selected_nr; i++) {
- struct bitmapped_commit *stored = &writer.selected[i];
- int commit_pos = oid_pos(&stored->commit->object.oid, index, index_nr, oid_access);
+ for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
+ struct bitmapped_commit *stored = &writer->selected[i];
+ int commit_pos = oid_pos(&stored->commit->object.oid, index,
+ index_nr, oid_access);
if (commit_pos < 0)
BUG(_("trying to write commit not in index"));
-
- commit_positions[i] = commit_pos;
+ stored->commit_pos = commit_pos;
}
- write_selected_commits_v1(f, commit_positions, offsets);
+ write_selected_commits_v1(writer, f, offsets);
+
+ if (options & BITMAP_OPT_PSEUDO_MERGES)
+ write_pseudo_merges(writer, f);
if (options & BITMAP_OPT_LOOKUP_TABLE)
- write_lookup_table(f, commit_positions, offsets);
+ write_lookup_table(writer, f, offsets);
if (options & BITMAP_OPT_HASH_CACHE)
write_hash_cache(f, index, index_nr);
@@ -844,6 +1067,5 @@ void bitmap_writer_finish(struct pack_idx_entry **index,
die_errno("unable to rename temporary bitmap file to '%s'", filename);
strbuf_release(&tmp_file);
- free(commit_positions);
free(offsets);
}
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 35c5ef9..2e657a2 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "gettext.h"
@@ -20,6 +22,7 @@
#include "list-objects-filter-options.h"
#include "midx.h"
#include "config.h"
+#include "pseudo-merge.h"
/*
* An entry on the bitmap index, representing the bitmap for a given
@@ -86,6 +89,9 @@ struct bitmap_index {
*/
unsigned char *table_lookup;
+ /* This contains the pseudo-merge cache within 'map' (if found). */
+ struct pseudo_merge_map pseudo_merges;
+
/*
* Extended index.
*
@@ -110,6 +116,13 @@ struct bitmap_index {
unsigned int version;
};
+static int pseudo_merges_satisfied_nr;
+static int pseudo_merges_cascades_nr;
+static int existing_bitmaps_hits_nr;
+static int existing_bitmaps_misses_nr;
+static int roots_with_bitmaps_nr;
+static int roots_without_bitmaps_nr;
+
static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)
{
struct ewah_bitmap *parent;
@@ -129,17 +142,13 @@ static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)
return composed;
}
-/*
- * Read a bitmap from the current read position on the mmaped
- * index, and increase the read position accordingly
- */
-static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
+struct ewah_bitmap *read_bitmap(const unsigned char *map,
+ size_t map_size, size_t *map_pos)
{
struct ewah_bitmap *b = ewah_pool_new();
- ssize_t bitmap_size = ewah_read_mmap(b,
- index->map + index->map_pos,
- index->map_size - index->map_pos);
+ ssize_t bitmap_size = ewah_read_mmap(b, map + *map_pos,
+ map_size - *map_pos);
if (bitmap_size < 0) {
error(_("failed to load bitmap index (corrupted?)"));
@@ -147,10 +156,20 @@ static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
return NULL;
}
- index->map_pos += bitmap_size;
+ *map_pos += bitmap_size;
+
return b;
}
+/*
+ * Read a bitmap from the current read position on the mmaped
+ * index, and increase the read position accordingly
+ */
+static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
+{
+ return read_bitmap(index->map, index->map_size, &index->map_pos);
+}
+
static uint32_t bitmap_num_objects(struct bitmap_index *index)
{
if (index->midx)
@@ -199,6 +218,46 @@ static int load_bitmap_header(struct bitmap_index *index)
index->table_lookup = (void *)(index_end - table_size);
index_end -= table_size;
}
+
+ if (flags & BITMAP_OPT_PSEUDO_MERGES) {
+ unsigned char *pseudo_merge_ofs;
+ size_t table_size;
+ uint32_t i;
+
+ if (sizeof(table_size) > index_end - index->map - header_size)
+ return error(_("corrupted bitmap index file (too short to fit pseudo-merge table header)"));
+
+ table_size = get_be64(index_end - 8);
+ if (table_size > index_end - index->map - header_size)
+ return error(_("corrupted bitmap index file (too short to fit pseudo-merge table)"));
+
+ if (git_env_bool("GIT_TEST_USE_PSEUDO_MERGES", 1)) {
+ const unsigned char *ext = (index_end - table_size);
+
+ index->pseudo_merges.map = index->map;
+ index->pseudo_merges.map_size = index->map_size;
+ index->pseudo_merges.commits = ext + get_be64(index_end - 16);
+ index->pseudo_merges.commits_nr = get_be32(index_end - 20);
+ index->pseudo_merges.nr = get_be32(index_end - 24);
+
+ if (st_add(st_mult(index->pseudo_merges.nr,
+ sizeof(uint64_t)),
+ 24) > table_size)
+ return error(_("corrupted bitmap index file, pseudo-merge table too short"));
+
+ CALLOC_ARRAY(index->pseudo_merges.v,
+ index->pseudo_merges.nr);
+
+ pseudo_merge_ofs = index_end - 24 -
+ (index->pseudo_merges.nr * sizeof(uint64_t));
+ for (i = 0; i < index->pseudo_merges.nr; i++) {
+ index->pseudo_merges.v[i].at = get_be64(pseudo_merge_ofs);
+ pseudo_merge_ofs += sizeof(uint64_t);
+ }
+ }
+
+ index_end -= table_size;
+ }
}
index->entry_count = ntohl(header->entry_count);
@@ -309,9 +368,8 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)
char *midx_bitmap_filename(struct multi_pack_index *midx)
{
struct strbuf buf = STRBUF_INIT;
-
- get_midx_filename(&buf, midx->object_dir);
- strbuf_addf(&buf, "-%s.bitmap", hash_to_hex(get_midx_checksum(midx)));
+ get_midx_filename_ext(&buf, midx->object_dir, get_midx_checksum(midx),
+ MIDX_EXT_BITMAP);
return strbuf_detach(&buf, NULL);
}
@@ -368,7 +426,8 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
if (load_bitmap_header(bitmap_git) < 0)
goto cleanup;
- if (!hasheq(get_midx_checksum(bitmap_git->midx), bitmap_git->checksum)) {
+ if (!hasheq(get_midx_checksum(bitmap_git->midx), bitmap_git->checksum,
+ the_repository->hash_algo)) {
error(_("checksum doesn't match in MIDX and bitmap"));
goto cleanup;
}
@@ -961,6 +1020,22 @@ static void show_commit(struct commit *commit UNUSED,
{
}
+static unsigned apply_pseudo_merges_for_commit_1(struct bitmap_index *bitmap_git,
+ struct bitmap *result,
+ struct commit *commit,
+ uint32_t commit_pos)
+{
+ int ret;
+
+ ret = apply_pseudo_merges_for_commit(&bitmap_git->pseudo_merges,
+ result, commit, commit_pos);
+
+ if (ret)
+ pseudo_merges_satisfied_nr += ret;
+
+ return ret;
+}
+
static int add_to_include_set(struct bitmap_index *bitmap_git,
struct include_data *data,
struct commit *commit,
@@ -976,11 +1051,19 @@ static int add_to_include_set(struct bitmap_index *bitmap_git,
partial = bitmap_for_commit(bitmap_git, commit);
if (partial) {
+ existing_bitmaps_hits_nr++;
+
bitmap_or_ewah(data->base, partial);
return 0;
}
+ existing_bitmaps_misses_nr++;
+
bitmap_set(data->base, bitmap_pos);
+ if (apply_pseudo_merges_for_commit_1(bitmap_git, data->base, commit,
+ bitmap_pos))
+ return 0;
+
return 1;
}
@@ -1031,8 +1114,12 @@ static int add_commit_to_bitmap(struct bitmap_index *bitmap_git,
{
struct ewah_bitmap *or_with = bitmap_for_commit(bitmap_git, commit);
- if (!or_with)
+ if (!or_with) {
+ existing_bitmaps_misses_nr++;
return 0;
+ }
+
+ existing_bitmaps_hits_nr++;
if (!*base)
*base = ewah_to_bitmap(or_with);
@@ -1106,6 +1193,20 @@ static void show_boundary_object(struct object *object UNUSED,
BUG("should not be called");
}
+static unsigned cascade_pseudo_merges_1(struct bitmap_index *bitmap_git,
+ struct bitmap *result,
+ struct bitmap *roots)
+{
+ int ret = cascade_pseudo_merges(&bitmap_git->pseudo_merges,
+ result, roots);
+ if (ret) {
+ pseudo_merges_cascades_nr++;
+ pseudo_merges_satisfied_nr += ret;
+ }
+
+ return ret;
+}
+
static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
struct rev_info *revs,
struct object_list *roots)
@@ -1115,6 +1216,7 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
unsigned int i;
unsigned int tmp_blobs, tmp_trees, tmp_tags;
int any_missing = 0;
+ int existing_bitmaps = 0;
cb.bitmap_git = bitmap_git;
cb.base = bitmap_new();
@@ -1122,6 +1224,25 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
revs->ignore_missing_links = 1;
+ if (bitmap_git->pseudo_merges.nr) {
+ struct bitmap *roots_bitmap = bitmap_new();
+ struct object_list *objects = NULL;
+
+ for (objects = roots; objects; objects = objects->next) {
+ struct object *object = objects->item;
+ int pos;
+
+ pos = bitmap_position(bitmap_git, &object->oid);
+ if (pos < 0)
+ continue;
+
+ bitmap_set(roots_bitmap, pos);
+ }
+
+ if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap))
+ bitmap_free(roots_bitmap);
+ }
+
/*
* OR in any existing reachability bitmaps among `roots` into
* `cb.base`.
@@ -1133,8 +1254,10 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
continue;
if (add_commit_to_bitmap(bitmap_git, &cb.base,
- (struct commit *)object))
+ (struct commit *)object)) {
+ existing_bitmaps = 1;
continue;
+ }
any_missing = 1;
}
@@ -1142,6 +1265,9 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
if (!any_missing)
goto cleanup;
+ if (existing_bitmaps)
+ cascade_pseudo_merges_1(bitmap_git, cb.base, NULL);
+
tmp_blobs = revs->blob_objects;
tmp_trees = revs->tree_objects;
tmp_tags = revs->blob_objects;
@@ -1197,6 +1323,44 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
return cb.base;
}
+struct ewah_bitmap *pseudo_merge_bitmap_for_commit(struct bitmap_index *bitmap_git,
+ struct commit *commit)
+{
+ struct commit_list *p;
+ struct bitmap *parents;
+ struct pseudo_merge *match = NULL;
+
+ if (!bitmap_git->pseudo_merges.nr)
+ return NULL;
+
+ parents = bitmap_new();
+
+ for (p = commit->parents; p; p = p->next) {
+ int pos = bitmap_position(bitmap_git, &p->item->object.oid);
+ if (pos < 0 || pos >= bitmap_num_objects(bitmap_git))
+ goto done;
+
+ bitmap_set(parents, pos);
+ }
+
+ match = pseudo_merge_for_parents(&bitmap_git->pseudo_merges,
+ parents);
+
+done:
+ bitmap_free(parents);
+ if (match)
+ return pseudo_merge_bitmap(&bitmap_git->pseudo_merges, match);
+
+ return NULL;
+}
+
+static void unsatisfy_all_pseudo_merges(struct bitmap_index *bitmap_git)
+{
+ uint32_t i;
+ for (i = 0; i < bitmap_git->pseudo_merges.nr; i++)
+ bitmap_git->pseudo_merges.v[i].satisfied = 0;
+}
+
static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
struct rev_info *revs,
struct object_list *roots,
@@ -1204,9 +1368,32 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
{
struct bitmap *base = NULL;
int needs_walk = 0;
+ unsigned existing_bitmaps = 0;
struct object_list *not_mapped = NULL;
+ unsatisfy_all_pseudo_merges(bitmap_git);
+
+ if (bitmap_git->pseudo_merges.nr) {
+ struct bitmap *roots_bitmap = bitmap_new();
+ struct object_list *objects = NULL;
+
+ for (objects = roots; objects; objects = objects->next) {
+ struct object *object = objects->item;
+ int pos;
+
+ pos = bitmap_position(bitmap_git, &object->oid);
+ if (pos < 0)
+ continue;
+
+ bitmap_set(roots_bitmap, pos);
+ }
+
+ base = bitmap_new();
+ if (!cascade_pseudo_merges_1(bitmap_git, base, roots_bitmap))
+ bitmap_free(roots_bitmap);
+ }
+
/*
* Go through all the roots for the walk. The ones that have bitmaps
* on the bitmap index will be `or`ed together to form an initial
@@ -1217,11 +1404,21 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
*/
while (roots) {
struct object *object = roots->item;
+
roots = roots->next;
+ if (base) {
+ int pos = bitmap_position(bitmap_git, &object->oid);
+ if (pos > 0 && bitmap_get(base, pos)) {
+ object->flags |= SEEN;
+ continue;
+ }
+ }
+
if (object->type == OBJ_COMMIT &&
add_commit_to_bitmap(bitmap_git, &base, (struct commit *)object)) {
object->flags |= SEEN;
+ existing_bitmaps = 1;
continue;
}
@@ -1237,6 +1434,9 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
roots = not_mapped;
+ if (existing_bitmaps)
+ cascade_pseudo_merges_1(bitmap_git, base, NULL);
+
/*
* Let's iterate through all the roots that don't have bitmaps to
* check if we can determine them to be reachable from the existing
@@ -1257,8 +1457,12 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
object->flags &= ~UNINTERESTING;
add_pending_object(revs, object, "");
needs_walk = 1;
+
+ roots_without_bitmaps_nr++;
} else {
object->flags |= SEEN;
+
+ roots_with_bitmaps_nr++;
}
}
@@ -1821,6 +2025,19 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
object_list_free(&wants);
object_list_free(&haves);
+ trace2_data_intmax("bitmap", the_repository, "pseudo_merges_satisfied",
+ pseudo_merges_satisfied_nr);
+ trace2_data_intmax("bitmap", the_repository, "pseudo_merges_cascades",
+ pseudo_merges_cascades_nr);
+ trace2_data_intmax("bitmap", the_repository, "bitmap/hits",
+ existing_bitmaps_hits_nr);
+ trace2_data_intmax("bitmap", the_repository, "bitmap/misses",
+ existing_bitmaps_misses_nr);
+ trace2_data_intmax("bitmap", the_repository, "bitmap/roots_with_bitmap",
+ roots_with_bitmaps_nr);
+ trace2_data_intmax("bitmap", the_repository, "bitmap/roots_without_bitmap",
+ roots_without_bitmaps_nr);
+
return bitmap_git;
cleanup:
@@ -2074,6 +2291,7 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
QSORT(packs, packs_nr, bitmapped_pack_cmp);
} else {
struct packed_git *pack;
+ uint32_t pack_int_id;
if (bitmap_is_midx(bitmap_git)) {
uint32_t preferred_pack_pos;
@@ -2084,12 +2302,24 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
}
pack = bitmap_git->midx->packs[preferred_pack_pos];
+ pack_int_id = preferred_pack_pos;
} else {
pack = bitmap_git->pack;
+ /*
+ * Any value for 'pack_int_id' will do here. When we
+ * process the pack via try_partial_reuse(), we won't
+ * use the `pack_int_id` field since we have a non-MIDX
+ * bitmap.
+ *
+ * Use '-1' as a sentinel value to make it clear
+ * that we do not expect to read this field.
+ */
+ pack_int_id = -1;
}
ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
packs[packs_nr].p = pack;
+ packs[packs_nr].pack_int_id = pack_int_id;
packs[packs_nr].bitmap_nr = pack->num_objects;
packs[packs_nr].bitmap_pos = 0;
@@ -2398,6 +2628,132 @@ int test_bitmap_hashes(struct repository *r)
return 0;
}
+static void bit_pos_to_object_id(struct bitmap_index *bitmap_git,
+ uint32_t bit_pos,
+ struct object_id *oid)
+{
+ uint32_t index_pos;
+
+ if (bitmap_is_midx(bitmap_git))
+ index_pos = pack_pos_to_midx(bitmap_git->midx, bit_pos);
+ else
+ index_pos = pack_pos_to_index(bitmap_git->pack, bit_pos);
+
+ nth_bitmap_object_oid(bitmap_git, oid, index_pos);
+}
+
+int test_bitmap_pseudo_merges(struct repository *r)
+{
+ struct bitmap_index *bitmap_git;
+ uint32_t i;
+
+ bitmap_git = prepare_bitmap_git(r);
+ if (!bitmap_git || !bitmap_git->pseudo_merges.nr)
+ goto cleanup;
+
+ for (i = 0; i < bitmap_git->pseudo_merges.nr; i++) {
+ struct pseudo_merge *merge;
+ struct ewah_bitmap *commits_bitmap, *merge_bitmap;
+
+ merge = use_pseudo_merge(&bitmap_git->pseudo_merges,
+ &bitmap_git->pseudo_merges.v[i]);
+ commits_bitmap = merge->commits;
+ merge_bitmap = pseudo_merge_bitmap(&bitmap_git->pseudo_merges,
+ merge);
+
+ printf("at=%"PRIuMAX", commits=%"PRIuMAX", objects=%"PRIuMAX"\n",
+ (uintmax_t)merge->at,
+ (uintmax_t)ewah_bitmap_popcount(commits_bitmap),
+ (uintmax_t)ewah_bitmap_popcount(merge_bitmap));
+ }
+
+cleanup:
+ free_bitmap_index(bitmap_git);
+ return 0;
+}
+
+static void dump_ewah_object_ids(struct bitmap_index *bitmap_git,
+ struct ewah_bitmap *bitmap)
+
+{
+ struct ewah_iterator it;
+ eword_t word;
+ uint32_t pos = 0;
+
+ ewah_iterator_init(&it, bitmap);
+
+ while (ewah_iterator_next(&word, &it)) {
+ struct object_id oid;
+ uint32_t offset;
+
+ for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+ if (!(word >> offset))
+ break;
+
+ offset += ewah_bit_ctz64(word >> offset);
+
+ bit_pos_to_object_id(bitmap_git, pos + offset, &oid);
+ printf("%s\n", oid_to_hex(&oid));
+ }
+ pos += BITS_IN_EWORD;
+ }
+}
+
+int test_bitmap_pseudo_merge_commits(struct repository *r, uint32_t n)
+{
+ struct bitmap_index *bitmap_git;
+ struct pseudo_merge *merge;
+ int ret = 0;
+
+ bitmap_git = prepare_bitmap_git(r);
+ if (!bitmap_git || !bitmap_git->pseudo_merges.nr)
+ goto cleanup;
+
+ if (n >= bitmap_git->pseudo_merges.nr) {
+ ret = error(_("pseudo-merge index out of range "
+ "(%"PRIu32" >= %"PRIuMAX")"),
+ n, (uintmax_t)bitmap_git->pseudo_merges.nr);
+ goto cleanup;
+ }
+
+ merge = use_pseudo_merge(&bitmap_git->pseudo_merges,
+ &bitmap_git->pseudo_merges.v[n]);
+ dump_ewah_object_ids(bitmap_git, merge->commits);
+
+cleanup:
+ free_bitmap_index(bitmap_git);
+ return ret;
+}
+
+int test_bitmap_pseudo_merge_objects(struct repository *r, uint32_t n)
+{
+ struct bitmap_index *bitmap_git;
+ struct pseudo_merge *merge;
+ int ret = 0;
+
+ bitmap_git = prepare_bitmap_git(r);
+ if (!bitmap_git || !bitmap_git->pseudo_merges.nr)
+ goto cleanup;
+
+ if (n >= bitmap_git->pseudo_merges.nr) {
+ ret = error(_("pseudo-merge index out of range "
+ "(%"PRIu32" >= %"PRIuMAX")"),
+ n, (uintmax_t)bitmap_git->pseudo_merges.nr);
+ goto cleanup;
+ }
+
+ merge = use_pseudo_merge(&bitmap_git->pseudo_merges,
+ &bitmap_git->pseudo_merges.v[n]);
+
+ dump_ewah_object_ids(bitmap_git,
+ pseudo_merge_bitmap(&bitmap_git->pseudo_merges,
+ merge));
+
+cleanup:
+ free_bitmap_index(bitmap_git);
+ return ret;
+}
+
int rebuild_bitmap(const uint32_t *reposition,
struct ewah_bitmap *source,
struct bitmap *dest)
@@ -2504,6 +2860,7 @@ void free_bitmap_index(struct bitmap_index *b)
*/
close_midx_revindex(b->midx);
}
+ free_pseudo_merge_map(&b->pseudo_merges);
free(b);
}
diff --git a/pack-bitmap.h b/pack-bitmap.h
index c7dea13..1171e6d 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -21,6 +21,7 @@ struct bitmap_disk_header {
unsigned char checksum[GIT_MAX_RAWSZ];
};
+#define BITMAP_PSEUDO_MERGE (1u<<21)
#define NEEDS_BITMAP (1u<<22)
/*
@@ -36,6 +37,7 @@ enum pack_bitmap_opts {
BITMAP_OPT_FULL_DAG = 0x1,
BITMAP_OPT_HASH_CACHE = 0x4,
BITMAP_OPT_LOOKUP_TABLE = 0x10,
+ BITMAP_OPT_PSEUDO_MERGES = 0x20,
};
enum pack_bitmap_flags {
@@ -71,6 +73,9 @@ void traverse_bitmap_commit_list(struct bitmap_index *,
void test_bitmap_walk(struct rev_info *revs);
int test_bitmap_commits(struct repository *r);
int test_bitmap_hashes(struct repository *r);
+int test_bitmap_pseudo_merges(struct repository *r);
+int test_bitmap_pseudo_merge_commits(struct repository *r, uint32_t n);
+int test_bitmap_pseudo_merge_objects(struct repository *r, uint32_t n);
#define GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL \
"GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL"
@@ -97,11 +102,39 @@ int bitmap_has_oid_in_uninteresting(struct bitmap_index *, const struct object_i
off_t get_disk_usage_from_bitmap(struct bitmap_index *, struct rev_info *);
-void bitmap_writer_show_progress(int show);
-void bitmap_writer_set_checksum(const unsigned char *sha1);
-void bitmap_writer_build_type_index(struct packing_data *to_pack,
+struct bitmap_writer {
+ struct ewah_bitmap *commits;
+ struct ewah_bitmap *trees;
+ struct ewah_bitmap *blobs;
+ struct ewah_bitmap *tags;
+
+ kh_oid_map_t *bitmaps;
+ struct packing_data *to_pack;
+
+ struct bitmapped_commit *selected;
+ unsigned int selected_nr, selected_alloc;
+
+ struct string_list pseudo_merge_groups;
+ kh_oid_map_t *pseudo_merge_commits; /* oid -> pseudo merge(s) */
+ uint32_t pseudo_merges_nr;
+
+ struct progress *progress;
+ int show_progress;
+ unsigned char pack_checksum[GIT_MAX_RAWSZ];
+};
+
+void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r);
+void bitmap_writer_show_progress(struct bitmap_writer *writer, int show);
+void bitmap_writer_set_checksum(struct bitmap_writer *writer,
+ const unsigned char *sha1);
+void bitmap_writer_build_type_index(struct bitmap_writer *writer,
+ struct packing_data *to_pack,
struct pack_idx_entry **index,
uint32_t index_nr);
+int bitmap_writer_has_bitmapped_object_id(struct bitmap_writer *writer,
+ const struct object_id *oid);
+void bitmap_writer_push_commit(struct bitmap_writer *writer,
+ struct commit *commit, unsigned pseudo_merge);
uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
struct packing_data *mapping);
int rebuild_bitmap(const uint32_t *reposition,
@@ -109,13 +142,19 @@ int rebuild_bitmap(const uint32_t *reposition,
struct bitmap *dest);
struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
struct commit *commit);
-void bitmap_writer_select_commits(struct commit **indexed_commits,
- unsigned int indexed_commits_nr, int max_bitmaps);
-int bitmap_writer_build(struct packing_data *to_pack);
-void bitmap_writer_finish(struct pack_idx_entry **index,
+struct ewah_bitmap *pseudo_merge_bitmap_for_commit(struct bitmap_index *bitmap_git,
+ struct commit *commit);
+void bitmap_writer_select_commits(struct bitmap_writer *writer,
+ struct commit **indexed_commits,
+ unsigned int indexed_commits_nr);
+int bitmap_writer_build(struct bitmap_writer *writer,
+ struct packing_data *to_pack);
+void bitmap_writer_finish(struct bitmap_writer *writer,
+ struct pack_idx_entry **index,
uint32_t index_nr,
const char *filename,
uint16_t options);
+void bitmap_writer_free(struct bitmap_writer *writer);
char *midx_bitmap_filename(struct multi_pack_index *midx);
char *pack_bitmap_filename(struct packed_git *p);
@@ -126,4 +165,6 @@ int bitmap_is_preferred_refname(struct repository *r, const char *refname);
int verify_bitmap_files(struct repository *r);
+struct ewah_bitmap *read_bitmap(const unsigned char *map,
+ size_t map_size, size_t *map_pos);
#endif
diff --git a/pack-check.c b/pack-check.c
index 25104d5..e883dae 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "hex.h"
@@ -78,10 +80,11 @@ static int verify_packfile(struct repository *r,
} while (offset < pack_sig_ofs);
r->hash_algo->final_fn(hash, &ctx);
pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL);
- if (!hasheq(hash, pack_sig))
+ if (!hasheq(hash, pack_sig, the_repository->hash_algo))
err = error("%s pack checksum mismatch",
p->pack_name);
- if (!hasheq(index_base + index_size - r->hash_algo->hexsz, pack_sig))
+ if (!hasheq(index_base + index_size - r->hash_algo->hexsz, pack_sig,
+ the_repository->hash_algo))
err = error("%s pack checksum does not match its index",
p->pack_name);
unuse_pack(w_curs);
diff --git a/pack-revindex.c b/pack-revindex.c
index a7624d8..22d3c23 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "pack-revindex.h"
@@ -381,7 +383,8 @@ int load_midx_revindex(struct multi_pack_index *m)
trace2_data_string("load_midx_revindex", the_repository,
"source", "rev");
- get_midx_rev_filename(&revindex_name, m);
+ get_midx_filename_ext(&revindex_name, m->object_dir,
+ get_midx_checksum(m), MIDX_EXT_REV);
ret = load_revindex_from_disk(revindex_name.buf,
m->num_objects,
@@ -526,6 +529,9 @@ static int midx_key_to_pack_pos(struct multi_pack_index *m,
{
uint32_t *found;
+ if (key->pack >= m->num_packs)
+ BUG("MIDX pack lookup out of bounds (%"PRIu32" >= %"PRIu32")",
+ key->pack, m->num_packs);
/*
* The preferred pack sorts first, so determine its identifier by
* looking at the first object in pseudo-pack order.
diff --git a/pack-write.c b/pack-write.c
index 80ecfa5..d07f03d 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -428,7 +430,8 @@ void fixup_pack_header_footer(int pack_fd,
if (partial_pack_offset == 0) {
unsigned char hash[GIT_MAX_RAWSZ];
the_hash_algo->final_fn(hash, &old_hash_ctx);
- if (!hasheq(hash, partial_pack_hash))
+ if (!hasheq(hash, partial_pack_hash,
+ the_repository->hash_algo))
die("Unexpected checksum for %s "
"(disk corruption?)", pack_name);
/*
diff --git a/packfile.c b/packfile.c
index d4df7fd..8135846 100644
--- a/packfile.c
+++ b/packfile.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -242,7 +244,7 @@ struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
struct packed_git *p = alloc_packed_git(alloc);
memcpy(p->pack_name, path, alloc); /* includes NUL */
- hashcpy(p->hash, sha1);
+ hashcpy(p->hash, sha1, the_repository->hash_algo);
if (check_packed_git_idx(idx_path, p)) {
free(p);
return NULL;
@@ -596,7 +598,7 @@ static int open_packed_git_1(struct packed_git *p)
if (read_result != hashsz)
return error("packfile %s signature is unavailable", p->pack_name);
idx_hash = ((unsigned char *)p->index_data) + p->index_size - hashsz * 2;
- if (!hasheq(hash, idx_hash))
+ if (!hasheq(hash, idx_hash, the_repository->hash_algo))
return error("packfile %s does not match index", p->pack_name);
return 0;
}
@@ -751,7 +753,7 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
p->mtime = st.st_mtime;
if (path_len < the_hash_algo->hexsz ||
get_hash_hex(path + path_len - the_hash_algo->hexsz, p->hash))
- hashclr(p->hash);
+ hashclr(p->hash, the_repository->hash_algo);
return p;
}
@@ -1251,7 +1253,7 @@ static int get_delta_base_oid(struct packed_git *p,
{
if (type == OBJ_REF_DELTA) {
unsigned char *base = use_pack(p, w_curs, curpos, NULL);
- oidread(oid, base);
+ oidread(oid, base, the_repository->hash_algo);
return 0;
} else if (type == OBJ_OFS_DELTA) {
uint32_t base_pos;
@@ -1593,7 +1595,7 @@ int packed_object_info(struct repository *r, struct packed_git *p,
goto out;
}
} else
- oidclr(oi->delta_base_oid);
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
}
oi->whence = in_delta_base_cache(p, obj_offset) ? OI_DBCACHED :
@@ -1917,10 +1919,12 @@ int nth_packed_object_id(struct object_id *oid,
return -1;
index += 4 * 256;
if (p->index_version == 1) {
- oidread(oid, index + st_add(st_mult(hashsz + 4, n), 4));
+ oidread(oid, index + st_add(st_mult(hashsz + 4, n), 4),
+ the_repository->hash_algo);
} else {
index += 8;
- oidread(oid, index + st_mult(hashsz, n));
+ oidread(oid, index + st_mult(hashsz, n),
+ the_repository->hash_algo);
}
return 0;
}
@@ -1971,7 +1975,7 @@ off_t find_pack_entry_one(const unsigned char *sha1,
return 0;
}
- hashcpy(oid.hash, sha1);
+ hashcpy(oid.hash, sha1, the_repository->hash_algo);
if (bsearch_pack(&oid, p, &result))
return nth_packed_object_offset(p, result);
return 0;
diff --git a/packfile.h b/packfile.h
index 28c8fd3..eb18ec1 100644
--- a/packfile.h
+++ b/packfile.h
@@ -101,6 +101,8 @@ int close_pack_fd(struct packed_git *p);
uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
+struct raw_object_store;
+
unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
void close_pack_windows(struct packed_git *);
void close_pack(struct packed_git *);
diff --git a/pager.c b/pager.c
index b8822a9..e9e121d 100644
--- a/pager.c
+++ b/pager.c
@@ -13,7 +13,7 @@ int pager_use_color = 1;
#endif
static struct child_process pager_process;
-static const char *pager_program;
+static char *pager_program;
/* Is the value coming back from term_columns() just a guess? */
static int term_columns_guessed;
diff --git a/parallel-checkout.c b/parallel-checkout.c
index b5a714c..08b960a 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -429,13 +429,7 @@ static void send_one_item(int fd, struct parallel_checkout_item *pc_item)
fixed_portion->ident = pc_item->ca.ident;
fixed_portion->name_len = name_len;
fixed_portion->working_tree_encoding_len = working_tree_encoding_len;
- /*
- * We pad the unused bytes in the hash array because, otherwise,
- * Valgrind would complain about passing uninitialized bytes to a
- * write() syscall. The warning doesn't represent any real risk here,
- * but it could hinder the detection of actual errors.
- */
- oidcpy_with_padding(&fixed_portion->oid, &pc_item->ce->oid);
+ oidcpy(&fixed_portion->oid, &pc_item->ce->oid);
variant = data + sizeof(*fixed_portion);
if (working_tree_encoding_len) {
diff --git a/parse-options-cb.c b/parse-options-cb.c
index bdc7fae..166d35e 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "parse-options.h"
#include "branch.h"
@@ -7,6 +9,7 @@
#include "environment.h"
#include "gettext.h"
#include "object-name.h"
+#include "setup.h"
#include "string-list.h"
#include "strvec.h"
#include "oid-array.h"
@@ -29,8 +32,6 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
opt->long_name);
if (v && v < MINIMUM_ABBREV)
v = MINIMUM_ABBREV;
- else if (v > the_hash_algo->hexsz)
- v = the_hash_algo->hexsz;
}
*(int *)(opt->value) = v;
return 0;
diff --git a/parse-options.h b/parse-options.h
index bd62e20..ae15342 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -355,7 +355,7 @@ struct option {
.type = OPTION_ALIAS, \
.short_name = (s), \
.long_name = (l), \
- .value = (source_long_name), \
+ .value = (char *)(source_long_name), \
}
#define OPT_SUBCOMMAND_F(l, v, fn, f) { \
diff --git a/parse.c b/parse.c
index 42d691a..7a60a4f 100644
--- a/parse.c
+++ b/parse.c
@@ -125,6 +125,35 @@ int git_parse_ssize_t(const char *value, ssize_t *ret)
return 1;
}
+int git_parse_double(const char *value, double *ret)
+{
+ char *end;
+ double val;
+ uintmax_t factor;
+
+ if (!value || !*value) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ errno = 0;
+ val = strtod(value, &end);
+ if (errno == ERANGE)
+ return 0;
+ if (end == value) {
+ errno = EINVAL;
+ return 0;
+ }
+ factor = get_unit_factor(end);
+ if (!factor) {
+ errno = EINVAL;
+ return 0;
+ }
+ val *= factor;
+ *ret = val;
+ return 1;
+}
+
int git_parse_maybe_bool_text(const char *value)
{
if (!value)
diff --git a/parse.h b/parse.h
index 07d2193..6bb9a54 100644
--- a/parse.h
+++ b/parse.h
@@ -6,6 +6,7 @@ int git_parse_ssize_t(const char *, ssize_t *);
int git_parse_ulong(const char *, unsigned long *);
int git_parse_int(const char *value, int *ret);
int git_parse_int64(const char *value, int64_t *ret);
+int git_parse_double(const char *value, double *ret);
/**
* Same as `git_config_bool`, except that it returns -1 on error rather
diff --git a/path.c b/path.c
index d838d4b..19f7684 100644
--- a/path.c
+++ b/path.c
@@ -1,11 +1,13 @@
/*
* Utilities for paths and pathnames
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "environment.h"
#include "gettext.h"
-#include "hex.h"
#include "repository.h"
#include "strbuf.h"
#include "string-list.h"
@@ -647,58 +649,6 @@ void strbuf_git_common_path(struct strbuf *sb,
va_end(args);
}
-int validate_headref(const char *path)
-{
- struct stat st;
- char buffer[256];
- const char *refname;
- struct object_id oid;
- int fd;
- ssize_t len;
-
- if (lstat(path, &st) < 0)
- return -1;
-
- /* Make sure it is a "refs/.." symlink */
- if (S_ISLNK(st.st_mode)) {
- len = readlink(path, buffer, sizeof(buffer)-1);
- if (len >= 5 && !memcmp("refs/", buffer, 5))
- return 0;
- return -1;
- }
-
- /*
- * Anything else, just open it and try to see if it is a symbolic ref.
- */
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -1;
- len = read_in_full(fd, buffer, sizeof(buffer)-1);
- close(fd);
-
- if (len < 0)
- return -1;
- buffer[len] = '\0';
-
- /*
- * Is it a symbolic ref?
- */
- if (skip_prefix(buffer, "ref:", &refname)) {
- while (isspace(*refname))
- refname++;
- if (starts_with(refname, "refs/"))
- return 0;
- }
-
- /*
- * Is this a detached HEAD?
- */
- if (!get_oid_hex(buffer, &oid))
- return 0;
-
- return -1;
-}
-
static struct passwd *getpw_str(const char *username, size_t len)
{
struct passwd *pw;
diff --git a/path.h b/path.h
index 0c7596d..a6f0b70 100644
--- a/path.h
+++ b/path.h
@@ -182,7 +182,6 @@ const char *git_path_fetch_head(struct repository *r);
const char *git_path_shallow(struct repository *r);
int ends_with_path_components(const char *path, const char *components);
-int validate_headref(const char *ref);
int calc_shared_perm(int mode);
int adjust_shared_perm(const char *path);
diff --git a/pathspec.c b/pathspec.c
index 2133b9f..fe1f0f4 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "parse.h"
diff --git a/perl/Git.pm b/perl/Git.pm
index 03bf570..aebfe0c 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -418,7 +418,7 @@
and it is the fourth value returned by C<command_bidi_pipe()>. The call idiom
is:
- my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check');
+ my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe(qw(cat-file --batch-check));
print $out "000000000\n";
while (<$in>) { ... }
$r->command_close_bidi_pipe($pid, $in, $out, $ctx);
@@ -431,7 +431,7 @@
calling this function. This may be useful in a query-response type of
commands where caller first writes a query and later reads response, eg:
- my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check');
+ my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe(qw(cat-file --batch-check));
print $out "000000000\n";
close $out;
while (<$in>) { ... }
diff --git a/pretty.c b/pretty.c
index 7ead078..44222fb 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
@@ -62,7 +64,7 @@ static int git_pretty_formats_config(const char *var, const char *value,
{
struct cmt_fmt_map *commit_format = NULL;
const char *name;
- const char *fmt;
+ char *fmt;
int i;
if (!skip_prefix(var, "pretty.", &name))
@@ -93,13 +95,17 @@ static int git_pretty_formats_config(const char *var, const char *value,
if (git_config_string(&fmt, var, value))
return -1;
- if (skip_prefix(fmt, "format:", &fmt))
+ if (skip_prefix(fmt, "format:", &commit_format->user_format)) {
commit_format->is_tformat = 0;
- else if (skip_prefix(fmt, "tformat:", &fmt) || strchr(fmt, '%'))
+ } else if (skip_prefix(fmt, "tformat:", &commit_format->user_format)) {
commit_format->is_tformat = 1;
- else
+ } else if (strchr(fmt, '%')) {
+ commit_format->is_tformat = 1;
+ commit_format->user_format = fmt;
+ } else {
commit_format->is_alias = 1;
- commit_format->user_format = fmt;
+ commit_format->user_format = fmt;
+ }
return 0;
}
@@ -1321,7 +1327,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
static size_t parse_describe_args(const char *start, struct strvec *args)
{
struct {
- char *name;
+ const char *name;
enum {
DESCRIBE_ARG_BOOL,
DESCRIBE_ARG_INTEGER,
@@ -1580,8 +1586,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
case 'D':
{
const struct decoration_options opts = {
- .prefix = "",
- .suffix = ""
+ .prefix = (char *) "",
+ .suffix = (char *) "",
};
format_decorations(sb, commit, c->auto_color, &opts);
diff --git a/progress.c b/progress.c
index c83cb60..0d44c18 100644
--- a/progress.c
+++ b/progress.c
@@ -9,6 +9,8 @@
*/
#define GIT_TEST_PROGRESS_ONLY
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "pager.h"
#include "progress.h"
diff --git a/promisor-remote.c b/promisor-remote.c
index b414922..317e1b1 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
@@ -23,6 +25,7 @@ static int fetch_objects(struct repository *repo,
struct child_process child = CHILD_PROCESS_INIT;
int i;
FILE *child_in;
+ int quiet;
if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0)) {
static int warning_shown;
@@ -41,6 +44,8 @@ static int fetch_objects(struct repository *repo,
"fetch", remote_name, "--no-tags",
"--no-write-fetch-head", "--recurse-submodules=no",
"--filter=blob:none", "--stdin", NULL);
+ if (!git_config_get_bool("promisor.quiet", &quiet) && quiet)
+ strvec_push(&child.args, "--quiet");
if (start_command(&child))
die(_("promisor-remote: unable to fork off fetch subprocess"));
child_in = xfdopen(child.in, "w");
diff --git a/promisor-remote.h b/promisor-remote.h
index 2cb9eda..88cb599 100644
--- a/promisor-remote.h
+++ b/promisor-remote.h
@@ -13,7 +13,7 @@ struct object_id;
*/
struct promisor_remote {
struct promisor_remote *next;
- const char *partial_clone_filter;
+ char *partial_clone_filter;
const char name[FLEX_ARRAY];
};
diff --git a/protocol-caps.c b/protocol-caps.c
index 75f4cbb..855f279 100644
--- a/protocol-caps.c
+++ b/protocol-caps.c
@@ -3,10 +3,11 @@
#include "gettext.h"
#include "hex.h"
#include "pkt-line.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "hex.h"
#include "object.h"
#include "object-store-ll.h"
+#include "repository.h"
#include "string-list.h"
#include "strbuf.h"
@@ -52,7 +53,7 @@ static void send_info(struct repository *r, struct packet_writer *writer,
struct object_id oid;
unsigned long object_size;
- if (get_oid_hex(oid_str, &oid) < 0) {
+ if (get_oid_hex_algop(oid_str, &oid, r->hash_algo) < 0) {
packet_writer_error(
writer,
"object-info: protocol error, expected to get oid, not '%s'",
diff --git a/pseudo-merge.c b/pseudo-merge.c
new file mode 100644
index 0000000..f0fde13
--- /dev/null
+++ b/pseudo-merge.c
@@ -0,0 +1,759 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "git-compat-util.h"
+#include "pseudo-merge.h"
+#include "date.h"
+#include "oid-array.h"
+#include "strbuf.h"
+#include "config.h"
+#include "string-list.h"
+#include "refs.h"
+#include "pack-bitmap.h"
+#include "commit.h"
+#include "alloc.h"
+#include "progress.h"
+#include "hex.h"
+
+#define DEFAULT_PSEUDO_MERGE_DECAY 1.0
+#define DEFAULT_PSEUDO_MERGE_MAX_MERGES 64
+#define DEFAULT_PSEUDO_MERGE_SAMPLE_RATE 1
+#define DEFAULT_PSEUDO_MERGE_THRESHOLD approxidate("1.week.ago")
+#define DEFAULT_PSEUDO_MERGE_STABLE_THRESHOLD approxidate("1.month.ago")
+#define DEFAULT_PSEUDO_MERGE_STABLE_SIZE 512
+
+static double gitexp(double base, int exp)
+{
+ double result = 1;
+ while (1) {
+ if (exp % 2)
+ result *= base;
+ exp >>= 1;
+ if (!exp)
+ break;
+ base *= base;
+ }
+ return result;
+}
+
+static uint32_t pseudo_merge_group_size(const struct pseudo_merge_group *group,
+ const struct pseudo_merge_matches *matches,
+ uint32_t i)
+{
+ double C = 0.0f;
+ uint32_t n;
+
+ /*
+ * The size of pseudo-merge groups decays according to a power series,
+ * which looks like:
+ *
+ * f(n) = C * n^-k
+ *
+ * , where 'n' is the n-th pseudo-merge group, 'f(n)' is its size, 'k'
+ * is the decay rate, and 'C' is a scaling value.
+ *
+ * The value of C depends on the number of groups, decay rate, and total
+ * number of commits. It is computed such that if there are M and N
+ * total groups and commits, respectively, that:
+ *
+ * N = f(0) + f(1) + ... f(M-1)
+ *
+ * Rearranging to isolate C, we get:
+ *
+ * N = \sum_{n=1}^M C / n^k
+ *
+ * N / C = \sum_{n=1}^M n^-k
+ *
+ * C = N / \sum_{n=1}^M n^-k
+ *
+ * For example, if we have a decay rate of 'k' being equal to 1.5, 'N'
+ * total commits equal to 10,000, and 'M' being equal to 6 groups, then
+ * the (rounded) group sizes are:
+ *
+ * { 5469, 1934, 1053, 684, 489, 372 }
+ *
+ * increasing the number of total groups, say to 10, scales the group
+ * sizes appropriately:
+ *
+ * { 5012, 1772, 964, 626, 448, 341, 271, 221, 186, 158 }
+ */
+ for (n = 0; n < group->max_merges; n++)
+ C += 1.0 / gitexp(n + 1, group->decay);
+ C = matches->unstable_nr / C;
+
+ return (uint32_t)((C / gitexp(i + 1, group->decay)) + 0.5);
+}
+
+static void pseudo_merge_group_init(struct pseudo_merge_group *group)
+{
+ memset(group, 0, sizeof(struct pseudo_merge_group));
+
+ strmap_init_with_options(&group->matches, NULL, 0);
+
+ group->decay = DEFAULT_PSEUDO_MERGE_DECAY;
+ group->max_merges = DEFAULT_PSEUDO_MERGE_MAX_MERGES;
+ group->sample_rate = DEFAULT_PSEUDO_MERGE_SAMPLE_RATE;
+ group->threshold = DEFAULT_PSEUDO_MERGE_THRESHOLD;
+ group->stable_threshold = DEFAULT_PSEUDO_MERGE_STABLE_THRESHOLD;
+ group->stable_size = DEFAULT_PSEUDO_MERGE_STABLE_SIZE;
+}
+
+static int pseudo_merge_config(const char *var, const char *value,
+ const struct config_context *ctx,
+ void *cb_data)
+{
+ struct string_list *list = cb_data;
+ struct string_list_item *item;
+ struct pseudo_merge_group *group;
+ struct strbuf buf = STRBUF_INIT;
+ const char *sub, *key;
+ size_t sub_len;
+ int ret = 0;
+
+ if (parse_config_key(var, "bitmappseudomerge", &sub, &sub_len, &key))
+ goto done;
+
+ if (!sub_len)
+ goto done;
+
+ strbuf_add(&buf, sub, sub_len);
+
+ item = string_list_lookup(list, buf.buf);
+ if (!item) {
+ item = string_list_insert(list, buf.buf);
+
+ item->util = xmalloc(sizeof(struct pseudo_merge_group));
+ pseudo_merge_group_init(item->util);
+ }
+
+ group = item->util;
+
+ if (!strcmp(key, "pattern")) {
+ struct strbuf re = STRBUF_INIT;
+
+ free(group->pattern);
+ if (*value != '^')
+ strbuf_addch(&re, '^');
+ strbuf_addstr(&re, value);
+
+ group->pattern = xcalloc(1, sizeof(regex_t));
+ if (regcomp(group->pattern, re.buf, REG_EXTENDED))
+ die(_("failed to load pseudo-merge regex for %s: '%s'"),
+ sub, re.buf);
+
+ strbuf_release(&re);
+ } else if (!strcmp(key, "decay")) {
+ group->decay = git_config_double(var, value, ctx->kvi);
+ if (group->decay < 0) {
+ warning(_("%s must be non-negative, using default"), var);
+ group->decay = DEFAULT_PSEUDO_MERGE_DECAY;
+ }
+ } else if (!strcmp(key, "samplerate")) {
+ group->sample_rate = git_config_double(var, value, ctx->kvi);
+ if (!(0 <= group->sample_rate && group->sample_rate <= 1)) {
+ warning(_("%s must be between 0 and 1, using default"), var);
+ group->sample_rate = DEFAULT_PSEUDO_MERGE_SAMPLE_RATE;
+ }
+ } else if (!strcmp(key, "threshold")) {
+ if (git_config_expiry_date(&group->threshold, var, value)) {
+ ret = -1;
+ goto done;
+ }
+ } else if (!strcmp(key, "maxmerges")) {
+ group->max_merges = git_config_int(var, value, ctx->kvi);
+ if (group->max_merges < 0) {
+ warning(_("%s must be non-negative, using default"), var);
+ group->max_merges = DEFAULT_PSEUDO_MERGE_MAX_MERGES;
+ }
+ } else if (!strcmp(key, "stablethreshold")) {
+ if (git_config_expiry_date(&group->stable_threshold, var, value)) {
+ ret = -1;
+ goto done;
+ }
+ } else if (!strcmp(key, "stablesize")) {
+ group->stable_size = git_config_int(var, value, ctx->kvi);
+ if (group->stable_size <= 0) {
+ warning(_("%s must be positive, using default"), var);
+ group->stable_size = DEFAULT_PSEUDO_MERGE_STABLE_SIZE;
+ }
+ }
+
+done:
+ strbuf_release(&buf);
+
+ return ret;
+}
+
+void load_pseudo_merges_from_config(struct string_list *list)
+{
+ struct string_list_item *item;
+
+ git_config(pseudo_merge_config, list);
+
+ for_each_string_list_item(item, list) {
+ struct pseudo_merge_group *group = item->util;
+ if (!group->pattern)
+ die(_("pseudo-merge group '%s' missing required pattern"),
+ item->string);
+ if (group->threshold < group->stable_threshold)
+ die(_("pseudo-merge group '%s' has unstable threshold "
+ "before stable one"), item->string);
+ }
+}
+
+static int find_pseudo_merge_group_for_ref(const char *refname,
+ const struct object_id *oid,
+ int flags UNUSED,
+ void *_data)
+{
+ struct bitmap_writer *writer = _data;
+ struct object_id peeled;
+ struct commit *c;
+ uint32_t i;
+ int has_bitmap;
+
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
+ oid = &peeled;
+
+ c = lookup_commit(the_repository, oid);
+ if (!c)
+ return 0;
+
+ has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, oid);
+
+ for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
+ struct pseudo_merge_group *group;
+ struct pseudo_merge_matches *matches;
+ struct strbuf group_name = STRBUF_INIT;
+ regmatch_t captures[16];
+ size_t j;
+
+ group = writer->pseudo_merge_groups.items[i].util;
+ if (regexec(group->pattern, refname, ARRAY_SIZE(captures),
+ captures, 0))
+ continue;
+
+ if (captures[ARRAY_SIZE(captures) - 1].rm_so != -1)
+ warning(_("pseudo-merge regex from config has too many capture "
+ "groups (max=%"PRIuMAX")"),
+ (uintmax_t)ARRAY_SIZE(captures) - 2);
+
+ for (j = !!group->pattern->re_nsub; j < ARRAY_SIZE(captures); j++) {
+ regmatch_t *match = &captures[j];
+ if (match->rm_so == -1)
+ continue;
+
+ if (group_name.len)
+ strbuf_addch(&group_name, '-');
+
+ strbuf_add(&group_name, refname + match->rm_so,
+ match->rm_eo - match->rm_so);
+ }
+
+ matches = strmap_get(&group->matches, group_name.buf);
+ if (!matches) {
+ matches = xcalloc(1, sizeof(*matches));
+ strmap_put(&group->matches, strbuf_detach(&group_name, NULL),
+ matches);
+ }
+
+ if (c->date <= group->stable_threshold) {
+ ALLOC_GROW(matches->stable, matches->stable_nr + 1,
+ matches->stable_alloc);
+ matches->stable[matches->stable_nr++] = c;
+ } else if (c->date <= group->threshold && !has_bitmap) {
+ ALLOC_GROW(matches->unstable, matches->unstable_nr + 1,
+ matches->unstable_alloc);
+ matches->unstable[matches->unstable_nr++] = c;
+ }
+
+ strbuf_release(&group_name);
+ }
+
+ return 0;
+}
+
+static struct commit *push_pseudo_merge(struct pseudo_merge_group *group)
+{
+ struct commit *merge;
+
+ ALLOC_GROW(group->merges, group->merges_nr + 1, group->merges_alloc);
+
+ merge = alloc_commit_node(the_repository);
+ merge->object.parsed = 1;
+ merge->object.flags |= BITMAP_PSEUDO_MERGE;
+
+ group->merges[group->merges_nr++] = merge;
+
+ return merge;
+}
+
+static struct pseudo_merge_commit_idx *pseudo_merge_idx(kh_oid_map_t *pseudo_merge_commits,
+ const struct object_id *oid)
+
+{
+ struct pseudo_merge_commit_idx *pmc;
+ int hash_ret;
+ khiter_t hash_pos = kh_put_oid_map(pseudo_merge_commits, *oid,
+ &hash_ret);
+
+ if (hash_ret) {
+ CALLOC_ARRAY(pmc, 1);
+ kh_value(pseudo_merge_commits, hash_pos) = pmc;
+ } else {
+ pmc = kh_value(pseudo_merge_commits, hash_pos);
+ }
+
+ return pmc;
+}
+
+#define MIN_PSEUDO_MERGE_SIZE 8
+
+static void select_pseudo_merges_1(struct bitmap_writer *writer,
+ struct pseudo_merge_group *group,
+ struct pseudo_merge_matches *matches)
+{
+ uint32_t i, j;
+ uint32_t stable_merges_nr;
+
+ if (!matches->stable_nr && !matches->unstable_nr)
+ return; /* all tips in this group already have bitmaps */
+
+ stable_merges_nr = matches->stable_nr / group->stable_size;
+ if (matches->stable_nr % group->stable_size)
+ stable_merges_nr++;
+
+ /* make stable_merges_nr pseudo merges for stable commits */
+ for (i = 0, j = 0; i < stable_merges_nr; i++) {
+ struct commit *merge;
+ struct commit_list **p;
+
+ merge = push_pseudo_merge(group);
+ p = &merge->parents;
+
+ /*
+ * For each pseudo-merge created above, add parents to the
+ * allocated commit node from the stable set of commits
+ * (un-bitmapped, newer than the stable threshold).
+ */
+ do {
+ struct commit *c;
+ struct pseudo_merge_commit_idx *pmc;
+
+ if (j >= matches->stable_nr)
+ break;
+
+ c = matches->stable[j++];
+ /*
+ * Here and below, make sure that we keep our mapping of
+ * commits -> pseudo-merge(s) which include the key'd
+ * commit up-to-date.
+ */
+ pmc = pseudo_merge_idx(writer->pseudo_merge_commits,
+ &c->object.oid);
+
+ ALLOC_GROW(pmc->pseudo_merge, pmc->nr + 1, pmc->alloc);
+
+ pmc->pseudo_merge[pmc->nr++] = writer->pseudo_merges_nr;
+ p = commit_list_append(c, p);
+ } while (j % group->stable_size);
+
+ bitmap_writer_push_commit(writer, merge, 1);
+ writer->pseudo_merges_nr++;
+ }
+
+ /* make up to group->max_merges pseudo merges for unstable commits */
+ for (i = 0, j = 0; i < group->max_merges; i++) {
+ struct commit *merge;
+ struct commit_list **p;
+ uint32_t size, end;
+
+ merge = push_pseudo_merge(group);
+ p = &merge->parents;
+
+ size = pseudo_merge_group_size(group, matches, i);
+ end = size < MIN_PSEUDO_MERGE_SIZE ? matches->unstable_nr : j + size;
+
+ /*
+ * For each pseudo-merge commit created above, add parents to
+ * the allocated commit node from the unstable set of commits
+ * (newer than the stable threshold).
+ *
+ * Account for the sample rate, since not every candidate from
+ * the set of stable commits will be included as a pseudo-merge
+ * parent.
+ */
+ for (; j < end && j < matches->unstable_nr; j++) {
+ struct commit *c = matches->unstable[j];
+ struct pseudo_merge_commit_idx *pmc;
+
+ if (j % (uint32_t)(1.0 / group->sample_rate))
+ continue;
+
+ pmc = pseudo_merge_idx(writer->pseudo_merge_commits,
+ &c->object.oid);
+
+ ALLOC_GROW(pmc->pseudo_merge, pmc->nr + 1, pmc->alloc);
+
+ pmc->pseudo_merge[pmc->nr++] = writer->pseudo_merges_nr;
+ p = commit_list_append(c, p);
+ }
+
+ bitmap_writer_push_commit(writer, merge, 1);
+ writer->pseudo_merges_nr++;
+ if (end >= matches->unstable_nr)
+ break;
+ }
+}
+
+static int commit_date_cmp(const void *va, const void *vb)
+{
+ timestamp_t a = (*(const struct commit **)va)->date;
+ timestamp_t b = (*(const struct commit **)vb)->date;
+
+ if (a < b)
+ return -1;
+ else if (a > b)
+ return 1;
+ return 0;
+}
+
+static void sort_pseudo_merge_matches(struct pseudo_merge_matches *matches)
+{
+ QSORT(matches->stable, matches->stable_nr, commit_date_cmp);
+ QSORT(matches->unstable, matches->unstable_nr, commit_date_cmp);
+}
+
+void select_pseudo_merges(struct bitmap_writer *writer,
+ struct commit **commits, size_t commits_nr)
+{
+ struct progress *progress = NULL;
+ uint32_t i;
+
+ if (!writer->pseudo_merge_groups.nr)
+ return;
+
+ if (writer->show_progress)
+ progress = start_progress("Selecting pseudo-merge commits",
+ writer->pseudo_merge_groups.nr);
+
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ find_pseudo_merge_group_for_ref, writer);
+
+ for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
+ struct pseudo_merge_group *group;
+ struct hashmap_iter iter;
+ struct strmap_entry *e;
+
+ group = writer->pseudo_merge_groups.items[i].util;
+ strmap_for_each_entry(&group->matches, &iter, e) {
+ struct pseudo_merge_matches *matches = e->value;
+
+ sort_pseudo_merge_matches(matches);
+
+ select_pseudo_merges_1(writer, group, matches);
+ }
+
+ display_progress(progress, i + 1);
+ }
+
+ stop_progress(&progress);
+}
+
+void free_pseudo_merge_map(struct pseudo_merge_map *pm)
+{
+ uint32_t i;
+ for (i = 0; i < pm->nr; i++) {
+ ewah_pool_free(pm->v[i].commits);
+ ewah_pool_free(pm->v[i].bitmap);
+ }
+ free(pm->v);
+}
+
+struct pseudo_merge_commit_ext {
+ uint32_t nr;
+ const unsigned char *ptr;
+};
+
+static int pseudo_merge_ext_at(const struct pseudo_merge_map *pm,
+ struct pseudo_merge_commit_ext *ext, size_t at)
+{
+ if (at >= pm->map_size)
+ return error(_("extended pseudo-merge read out-of-bounds "
+ "(%"PRIuMAX" >= %"PRIuMAX")"),
+ (uintmax_t)at, (uintmax_t)pm->map_size);
+ if (at + 4 >= pm->map_size)
+ return error(_("extended pseudo-merge entry is too short "
+ "(%"PRIuMAX" >= %"PRIuMAX")"),
+ (uintmax_t)(at + 4), (uintmax_t)pm->map_size);
+
+ ext->nr = get_be32(pm->map + at);
+ ext->ptr = pm->map + at + sizeof(uint32_t);
+
+ return 0;
+}
+
+struct ewah_bitmap *pseudo_merge_bitmap(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge)
+{
+ if (!merge->loaded_commits)
+ BUG("cannot use unloaded pseudo-merge bitmap");
+
+ if (!merge->loaded_bitmap) {
+ size_t at = merge->bitmap_at;
+
+ merge->bitmap = read_bitmap(pm->map, pm->map_size, &at);
+ merge->loaded_bitmap = 1;
+ }
+
+ return merge->bitmap;
+}
+
+struct pseudo_merge *use_pseudo_merge(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge)
+{
+ if (!merge->loaded_commits) {
+ size_t pos = merge->at;
+
+ merge->commits = read_bitmap(pm->map, pm->map_size, &pos);
+ merge->bitmap_at = pos;
+ merge->loaded_commits = 1;
+ }
+ return merge;
+}
+
+static struct pseudo_merge *pseudo_merge_at(const struct pseudo_merge_map *pm,
+ struct object_id *oid,
+ size_t want)
+{
+ size_t lo = 0;
+ size_t hi = pm->nr;
+
+ while (lo < hi) {
+ size_t mi = lo + (hi - lo) / 2;
+ size_t got = pm->v[mi].at;
+
+ if (got == want)
+ return use_pseudo_merge(pm, &pm->v[mi]);
+ else if (got < want)
+ hi = mi;
+ else
+ lo = mi + 1;
+ }
+
+ warning(_("could not find pseudo-merge for commit %s at offset %"PRIuMAX),
+ oid_to_hex(oid), (uintmax_t)want);
+
+ return NULL;
+}
+
+struct pseudo_merge_commit {
+ uint32_t commit_pos;
+ uint64_t pseudo_merge_ofs;
+};
+
+#define PSEUDO_MERGE_COMMIT_RAWSZ (sizeof(uint32_t)+sizeof(uint64_t))
+
+static void read_pseudo_merge_commit_at(struct pseudo_merge_commit *merge,
+ const unsigned char *at)
+{
+ merge->commit_pos = get_be32(at);
+ merge->pseudo_merge_ofs = get_be64(at + sizeof(uint32_t));
+}
+
+static int nth_pseudo_merge_ext(const struct pseudo_merge_map *pm,
+ struct pseudo_merge_commit_ext *ext,
+ struct pseudo_merge_commit *merge,
+ uint32_t n)
+{
+ size_t ofs;
+
+ if (n >= ext->nr)
+ return error(_("extended pseudo-merge lookup out-of-bounds "
+ "(%"PRIu32" >= %"PRIu32")"), n, ext->nr);
+
+ ofs = get_be64(ext->ptr + st_mult(n, sizeof(uint64_t)));
+ if (ofs >= pm->map_size)
+ return error(_("out-of-bounds read: (%"PRIuMAX" >= %"PRIuMAX")"),
+ (uintmax_t)ofs, (uintmax_t)pm->map_size);
+
+ read_pseudo_merge_commit_at(merge, pm->map + ofs);
+
+ return 0;
+}
+
+static unsigned apply_pseudo_merge(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge,
+ struct bitmap *result,
+ struct bitmap *roots)
+{
+ if (merge->satisfied)
+ return 0;
+
+ if (!ewah_bitmap_is_subset(merge->commits, roots ? roots : result))
+ return 0;
+
+ bitmap_or_ewah(result, pseudo_merge_bitmap(pm, merge));
+ if (roots)
+ bitmap_or_ewah(roots, pseudo_merge_bitmap(pm, merge));
+ merge->satisfied = 1;
+
+ return 1;
+}
+
+static int pseudo_merge_commit_cmp(const void *va, const void *vb)
+{
+ struct pseudo_merge_commit merge;
+ uint32_t key = *(uint32_t*)va;
+
+ read_pseudo_merge_commit_at(&merge, vb);
+
+ if (key < merge.commit_pos)
+ return -1;
+ if (key > merge.commit_pos)
+ return 1;
+ return 0;
+}
+
+static struct pseudo_merge_commit *find_pseudo_merge(const struct pseudo_merge_map *pm,
+ uint32_t pos)
+{
+ if (!pm->commits_nr)
+ return NULL;
+
+ return bsearch(&pos, pm->commits, pm->commits_nr,
+ PSEUDO_MERGE_COMMIT_RAWSZ, pseudo_merge_commit_cmp);
+}
+
+int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
+ struct bitmap *result,
+ struct commit *commit, uint32_t commit_pos)
+{
+ struct pseudo_merge *merge;
+ struct pseudo_merge_commit *merge_commit;
+ int ret = 0;
+
+ merge_commit = find_pseudo_merge(pm, commit_pos);
+ if (!merge_commit)
+ return 0;
+
+ if (merge_commit->pseudo_merge_ofs & ((uint64_t)1<<63)) {
+ struct pseudo_merge_commit_ext ext = { 0 };
+ off_t ofs = merge_commit->pseudo_merge_ofs & ~((uint64_t)1<<63);
+ uint32_t i;
+
+ if (pseudo_merge_ext_at(pm, &ext, ofs) < -1) {
+ warning(_("could not read extended pseudo-merge table "
+ "for commit %s"),
+ oid_to_hex(&commit->object.oid));
+ return ret;
+ }
+
+ for (i = 0; i < ext.nr; i++) {
+ if (nth_pseudo_merge_ext(pm, &ext, merge_commit, i) < 0)
+ return ret;
+
+ merge = pseudo_merge_at(pm, &commit->object.oid,
+ merge_commit->pseudo_merge_ofs);
+
+ if (!merge)
+ return ret;
+
+ if (apply_pseudo_merge(pm, merge, result, NULL))
+ ret++;
+ }
+ } else {
+ merge = pseudo_merge_at(pm, &commit->object.oid,
+ merge_commit->pseudo_merge_ofs);
+
+ if (!merge)
+ return ret;
+
+ if (apply_pseudo_merge(pm, merge, result, NULL))
+ ret++;
+ }
+
+ if (ret)
+ cascade_pseudo_merges(pm, result, NULL);
+
+ return ret;
+}
+
+int cascade_pseudo_merges(const struct pseudo_merge_map *pm,
+ struct bitmap *result,
+ struct bitmap *roots)
+{
+ unsigned any_satisfied;
+ int ret = 0;
+
+ do {
+ struct pseudo_merge *merge;
+ uint32_t i;
+
+ any_satisfied = 0;
+
+ for (i = 0; i < pm->nr; i++) {
+ merge = use_pseudo_merge(pm, &pm->v[i]);
+ if (apply_pseudo_merge(pm, merge, result, roots)) {
+ any_satisfied |= 1;
+ ret++;
+ }
+ }
+ } while (any_satisfied);
+
+ return ret;
+}
+
+struct pseudo_merge *pseudo_merge_for_parents(const struct pseudo_merge_map *pm,
+ struct bitmap *parents)
+{
+ struct pseudo_merge *match = NULL;
+ size_t i;
+
+ if (!pm->nr)
+ return NULL;
+
+ /*
+ * NOTE: this loop is quadratic in the worst-case (where no
+ * matching pseudo-merge bitmaps are found), but in practice
+ * this is OK for a few reasons:
+ *
+ * - Rejecting pseudo-merge bitmaps that do not match the
+ * given commit is done quickly (i.e. `bitmap_equals_ewah()`
+ * returns early when we know the two bitmaps aren't equal.
+ *
+ * - Already matched pseudo-merge bitmaps (which we track with
+ * the `->satisfied` bit here) are skipped as potential
+ * candidates.
+ *
+ * - The number of pseudo-merges should be small (in the
+ * hundreds for most repositories).
+ *
+ * If in the future this semi-quadratic behavior does become a
+ * problem, another approach would be to keep track of which
+ * pseudo-merges are still "viable" after enumerating the
+ * pseudo-merge commit's parents:
+ *
+ * - A pseudo-merge bitmap becomes non-viable when the bit(s)
+ * corresponding to one or more parent(s) of the given
+ * commit are not set in a candidate pseudo-merge's commits
+ * bitmap.
+ *
+ * - After processing all bits, enumerate the remaining set of
+ * viable pseudo-merge bitmaps, and check that their
+ * popcount() matches the number of parents in the given
+ * commit.
+ */
+ for (i = 0; i < pm->nr; i++) {
+ struct pseudo_merge *candidate = use_pseudo_merge(pm, &pm->v[i]);
+ if (!candidate || candidate->satisfied)
+ continue;
+ if (!bitmap_equals_ewah(parents, candidate->commits))
+ continue;
+
+ match = candidate;
+ match->satisfied = 1;
+ break;
+ }
+
+ return match;
+}
diff --git a/pseudo-merge.h b/pseudo-merge.h
new file mode 100644
index 0000000..2aca01d
--- /dev/null
+++ b/pseudo-merge.h
@@ -0,0 +1,216 @@
+#ifndef PSEUDO_MERGE_H
+#define PSEUDO_MERGE_H
+
+#include "git-compat-util.h"
+#include "strmap.h"
+#include "khash.h"
+#include "ewah/ewok.h"
+
+struct commit;
+struct string_list;
+struct bitmap_index;
+struct bitmap_writer;
+
+/*
+ * A pseudo-merge group tracks the set of non-bitmapped reference tips
+ * that match the given pattern.
+ *
+ * Within those matches, they are further segmented by separating
+ * consecutive capture groups with '-' dash character capture groups
+ * with '-' dash characters.
+ *
+ * Those groups are then ordered by committer date and partitioned
+ * into individual pseudo-merge(s) according to the decay, max_merges,
+ * sample_rate, and threshold parameters.
+ */
+struct pseudo_merge_group {
+ regex_t *pattern;
+
+ /* capture group(s) -> struct pseudo_merge_matches */
+ struct strmap matches;
+
+ /*
+ * The individual pseudo-merge(s) that are generated from the
+ * above array of matches, partitioned according to the below
+ * parameters.
+ */
+ struct commit **merges;
+ size_t merges_nr;
+ size_t merges_alloc;
+
+ /*
+ * Pseudo-merge grouping parameters. See git-config(1) for
+ * more information.
+ */
+ double decay;
+ int max_merges;
+ double sample_rate;
+ int stable_size;
+ timestamp_t threshold;
+ timestamp_t stable_threshold;
+};
+
+struct pseudo_merge_matches {
+ struct commit **stable;
+ struct commit **unstable;
+ size_t stable_nr, stable_alloc;
+ size_t unstable_nr, unstable_alloc;
+};
+
+/*
+ * Read the repository's configuration:
+ *
+ * - bitmapPseudoMerge.<name>.pattern
+ * - bitmapPseudoMerge.<name>.decay
+ * - bitmapPseudoMerge.<name>.sampleRate
+ * - bitmapPseudoMerge.<name>.threshold
+ * - bitmapPseudoMerge.<name>.maxMerges
+ * - bitmapPseudoMerge.<name>.stableThreshold
+ * - bitmapPseudoMerge.<name>.stableSize
+ *
+ * and populates the given `list` with pseudo-merge groups. String
+ * entry keys are the pseudo-merge group names, and the values are
+ * pointers to the pseudo_merge_group structure itself.
+ */
+void load_pseudo_merges_from_config(struct string_list *list);
+
+/*
+ * A pseudo-merge commit index (pseudo_merge_commit_idx) maps a
+ * particular (non-pseudo-merge) commit to the list of pseudo-merge(s)
+ * it appears in.
+ */
+struct pseudo_merge_commit_idx {
+ uint32_t *pseudo_merge;
+ size_t nr, alloc;
+};
+
+/*
+ * Selects pseudo-merges from a list of commits, populating the given
+ * string_list of pseudo-merge groups.
+ *
+ * Populates the pseudo_merge_commits map with a commit_idx
+ * corresponding to each commit in the list. Counts the total number
+ * of pseudo-merges generated.
+ *
+ * Optionally shows a progress meter.
+ */
+void select_pseudo_merges(struct bitmap_writer *writer,
+ struct commit **commits, size_t commits_nr);
+
+/*
+ * Represents a serialized view of a file containing pseudo-merge(s)
+ * (see Documentation/technical/bitmap-format.txt for a specification
+ * of the format).
+ */
+struct pseudo_merge_map {
+ /*
+ * An array of pseudo-merge(s), lazily loaded from the .bitmap
+ * file.
+ */
+ struct pseudo_merge *v;
+ size_t nr;
+ size_t commits_nr;
+
+ /*
+ * Pointers into a memory-mapped view of the .bitmap file:
+ *
+ * - map: the beginning of the .bitmap file
+ * - commits: the beginning of the pseudo-merge commit index
+ * - map_size: the size of the .bitmap file
+ */
+ const unsigned char *map;
+ const unsigned char *commits;
+
+ size_t map_size;
+};
+
+/*
+ * An individual pseudo-merge, storing a pair of lazily-loaded
+ * bitmaps:
+ *
+ * - commits: the set of commit(s) that are part of the pseudo-merge
+ * - bitmap: the set of object(s) reachable from the above set of
+ * commits.
+ *
+ * The `at` and `bitmap_at` fields are used to store the locations of
+ * each of the above bitmaps in the .bitmap file.
+ */
+struct pseudo_merge {
+ struct ewah_bitmap *commits;
+ struct ewah_bitmap *bitmap;
+
+ off_t at;
+ off_t bitmap_at;
+
+ /*
+ * `satisfied` indicates whether the given pseudo-merge has been
+ * used.
+ *
+ * `loaded_commits` and `loaded_bitmap` indicate whether the
+ * respective bitmaps have been loaded and read from the
+ * .bitmap file.
+ */
+ unsigned satisfied : 1,
+ loaded_commits : 1,
+ loaded_bitmap : 1;
+};
+
+/*
+ * Frees the given pseudo-merge map, releasing any memory held by (a)
+ * parsed EWAH bitmaps, or (b) the array of pseudo-merges itself. Does
+ * not free the memory-mapped view of the .bitmap file.
+ */
+void free_pseudo_merge_map(struct pseudo_merge_map *pm);
+
+/*
+ * Loads the bitmap corresponding to the given pseudo-merge from the
+ * map, if it has not already been loaded.
+ */
+struct ewah_bitmap *pseudo_merge_bitmap(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge);
+
+/*
+ * Loads the pseudo-merge and its commits bitmap from the given
+ * pseudo-merge map, if it has not already been loaded.
+ */
+struct pseudo_merge *use_pseudo_merge(const struct pseudo_merge_map *pm,
+ struct pseudo_merge *merge);
+
+/*
+ * Applies pseudo-merge(s) containing the given commit to the bitmap
+ * "result".
+ *
+ * If any pseudo-merge(s) were satisfied, returns the number
+ * satisfied, otherwise returns 0. If any were satisfied, the
+ * remaining unsatisfied pseudo-merges are cascaded (see below).
+ */
+int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
+ struct bitmap *result,
+ struct commit *commit, uint32_t commit_pos);
+
+/*
+ * Applies pseudo-merge(s) which are satisfied according to the
+ * current bitmap in result (or roots, see below). If any
+ * pseudo-merges were satisfied, repeat the process over unsatisfied
+ * pseudo-merge commits until no more pseudo-merges are satisfied.
+ *
+ * Result is the bitmap to which the pseudo-merge(s) are applied.
+ * Roots (if given) is a bitmap of the traversal tip(s) for either
+ * side of a reachability traversal.
+ *
+ * Roots may given instead of a populated results bitmap at the
+ * beginning of a traversal on either side where the reachability
+ * closure over tips is not yet known.
+ */
+int cascade_pseudo_merges(const struct pseudo_merge_map *pm,
+ struct bitmap *result,
+ struct bitmap *roots);
+
+/*
+ * Returns a pseudo-merge which contains the exact set of commits
+ * listed in the "parents" bitamp, or NULL if none could be found.
+ */
+struct pseudo_merge *pseudo_merge_for_parents(const struct pseudo_merge_map *pm,
+ struct bitmap *parents);
+
+#endif
diff --git a/range-diff.c b/range-diff.c
index c45b6d8..5f01605 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
diff --git a/range-diff.h b/range-diff.h
index 04ffe21..2f69f6a 100644
--- a/range-diff.h
+++ b/range-diff.h
@@ -6,6 +6,12 @@
#define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60
+/*
+ * A much higher value than the default, when we KNOW we are comparing
+ * the same series (e.g., used when format-patch calls range-diff).
+ */
+#define CREATION_FACTOR_FOR_THE_SAME_SERIES 999
+
struct range_diff_options {
int creation_factor;
unsigned dual_color:1;
diff --git a/reachable.c b/reachable.c
index 3b85add..46613a6 100644
--- a/reachable.c
+++ b/reachable.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
@@ -363,10 +365,11 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
add_index_objects_to_pending(revs, 0);
/* Add all external refs */
- for_each_ref(add_one_ref, revs);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_one_ref,
+ revs);
/* detached HEAD is not included in the list above */
- head_ref(add_one_ref, revs);
+ refs_head_ref(get_main_ref_store(the_repository), add_one_ref, revs);
other_head_refs(add_one_ref, revs);
/* rebase autostash and orig-head */
diff --git a/read-cache-ll.h b/read-cache-ll.h
index 09414af..e0e3960 100644
--- a/read-cache-ll.h
+++ b/read-cache-ll.h
@@ -1,7 +1,7 @@
#ifndef READ_CACHE_LL_H
#define READ_CACHE_LL_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hashmap.h"
#include "statinfo.h"
diff --git a/read-cache.c b/read-cache.c
index a6db25a..48bf24f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3,6 +3,9 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "bulk-checkin.h"
#include "config.h"
@@ -271,7 +274,8 @@ static int ce_compare_gitlink(const struct cache_entry *ce)
*
* If so, we consider it always to match.
*/
- if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, ce->name,
+ "HEAD", &oid) < 0)
return 0;
return !oideq(&oid, &ce->oid);
}
@@ -336,7 +340,7 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
/* Racily smudged entry? */
if (!ce->ce_stat_data.sd_size) {
- if (!is_empty_blob_sha1(ce->oid.hash))
+ if (!is_empty_blob_oid(&ce->oid, the_repository->hash_algo))
changed |= DATA_CHANGED;
}
@@ -711,7 +715,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
namelen = strlen(path);
if (S_ISDIR(st_mode)) {
- if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+ if (repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid) < 0)
return error(_("'%s' does not have a commit checked out"), path);
while (namelen && path[namelen-1] == '/')
namelen--;
@@ -1727,14 +1731,14 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
end = (unsigned char *)hdr + size;
start = end - the_hash_algo->rawsz;
- oidread(&oid, start);
+ oidread(&oid, start, the_repository->hash_algo);
if (oideq(&oid, null_oid()))
return 0;
the_hash_algo->init_fn(&c);
the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
the_hash_algo->final_fn(hash, &c);
- if (!hasheq(hash, start))
+ if (!hasheq(hash, start, the_repository->hash_algo))
return error(_("bad index file sha1 signature"));
return 0;
}
@@ -1875,7 +1879,8 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
ce->ce_flags = flags & ~CE_NAMEMASK;
ce->ce_namelen = len;
ce->index = 0;
- oidread(&ce->oid, (const unsigned char *)ondisk + offsetof(struct ondisk_cache_entry, data));
+ oidread(&ce->oid, (const unsigned char *)ondisk + offsetof(struct ondisk_cache_entry, data),
+ the_repository->hash_algo);
if (expand_name_field) {
if (copy_len)
@@ -2248,7 +2253,8 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
if (verify_hdr(hdr, mmap_size) < 0)
goto unmap;
- oidread(&istate->oid, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz);
+ oidread(&istate->oid, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz,
+ the_repository->hash_algo);
istate->version = ntohl(hdr->hdr_version);
istate->cache_nr = ntohl(hdr->hdr_entries);
istate->cache_alloc = alloc_nr(istate->cache_nr);
@@ -2640,7 +2646,7 @@ static void copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
ondisk->uid = htonl(ce->ce_stat_data.sd_uid);
ondisk->gid = htonl(ce->ce_stat_data.sd_gid);
ondisk->size = htonl(ce->ce_stat_data.sd_size);
- hashcpy(ondisk->data, ce->oid.hash);
+ hashcpy(ondisk->data, ce->oid.hash, the_repository->hash_algo);
flags = ce->ce_flags & ~CE_NAMEMASK;
flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
@@ -2729,7 +2735,7 @@ static int verify_index_from(const struct index_state *istate, const char *path)
if (n != the_hash_algo->rawsz)
goto out;
- if (!hasheq(istate->oid.hash, hash))
+ if (!hasheq(istate->oid.hash, hash, the_repository->hash_algo))
goto out;
close(fd);
@@ -3602,7 +3608,7 @@ static size_t read_eoie_extension(const char *mmap, size_t mmap_size)
src_offset += extsize;
}
the_hash_algo->final_fn(hash, &c);
- if (!hasheq(hash, (const unsigned char *)index))
+ if (!hasheq(hash, (const unsigned char *)index, the_repository->hash_algo))
return 0;
/* Validate that the extension offsets returned us back to the eoie extension. */
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 56fd720..cbeb864 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "editor.h"
diff --git a/ref-filter.c b/ref-filter.c
index 59ad6f5..8c5e673 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -895,7 +897,9 @@ static int head_atom_parser(struct ref_format *format UNUSED,
{
if (arg)
return err_no_arg(err, "HEAD");
- atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
+ atom->u.head = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", RESOLVE_REF_READING, NULL,
+ NULL);
return 0;
}
@@ -2135,7 +2139,9 @@ static const char *rstrip_ref_components(const char *refname, int len)
static const char *show_ref(struct refname_atom *atom, const char *refname)
{
if (atom->option == R_SHORT)
- return shorten_unambiguous_ref(refname, warn_ambiguous_refs);
+ return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname,
+ warn_ambiguous_refs);
else if (atom->option == R_LSTRIP)
return lstrip_ref_components(refname, atom->lstrip);
else if (atom->option == R_RSTRIP)
@@ -2338,8 +2344,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
CALLOC_ARRAY(ref->value, used_atom_cnt);
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
- ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
- NULL, NULL);
+ ref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),
+ ref->refname,
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (!ref->symref)
ref->symref = xstrdup("");
}
@@ -2514,7 +2522,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
* If it is a tag object, see if we use the peeled value. If we do,
* grab the peeled OID.
*/
- if (need_tagged && peel_iterated_oid(&obj->oid, &oi_deref.oid))
+ if (need_tagged && peel_iterated_oid(the_repository, &obj->oid, &oi_deref.oid))
die("bad tag");
return get_object(ref, 1, &obj, &oi_deref, err);
@@ -2628,7 +2636,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
each_ref_fn cb,
void *cb_data)
{
- if (filter->kind == FILTER_REFS_KIND_MASK) {
+ if (filter->kind & FILTER_REFS_ROOT_REFS) {
/* In this case, we want to print all refs including root refs. */
return refs_for_each_include_root_refs(get_main_ref_store(the_repository),
cb, cb_data);
@@ -2640,7 +2648,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
* prefixes like "refs/heads/" etc. are stripped off,
* so we have to look at everything:
*/
- return for_each_fullref_in("", cb, cb_data);
+ return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "", NULL, cb, cb_data);
}
if (filter->ignore_case) {
@@ -2649,7 +2658,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
* so just return everything and let the caller
* sort it out.
*/
- return for_each_fullref_in("", cb, cb_data);
+ return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "", NULL, cb, cb_data);
}
if (!filter->name_patterns[0]) {
@@ -2756,8 +2766,10 @@ static int ref_kind_from_refname(const char *refname)
return ref_kind[i].kind;
}
- if (is_pseudoref(get_main_ref_store(the_repository), refname))
+ if (is_pseudo_ref(refname))
return FILTER_REFS_PSEUDOREFS;
+ if (is_root_ref(refname))
+ return FILTER_REFS_ROOT_REFS;
return FILTER_REFS_OTHERS;
}
@@ -2794,11 +2806,11 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct
/*
* Generally HEAD refs are printed with special description denoting a rebase,
* detached state and so forth. This is useful when only printing the HEAD ref
- * But when it is being printed along with other pseudorefs, it makes sense to
- * keep the formatting consistent. So we mask the type to act like a pseudoref.
+ * But when it is being printed along with other root refs, it makes sense to
+ * keep the formatting consistent. So we mask the type to act like a root ref.
*/
- if (filter->kind == FILTER_REFS_KIND_MASK && kind == FILTER_REFS_DETACHED_HEAD)
- kind = FILTER_REFS_PSEUDOREFS;
+ if (filter->kind & FILTER_REFS_ROOT_REFS && kind == FILTER_REFS_DETACHED_HEAD)
+ kind = FILTER_REFS_ROOT_REFS;
else if (!(kind & filter->kind))
return NULL;
@@ -3060,11 +3072,17 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
* of filter_ref_kind().
*/
if (filter->kind == FILTER_REFS_BRANCHES)
- ret = for_each_fullref_in("refs/heads/", fn, cb_data);
+ ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/heads/", NULL,
+ fn, cb_data);
else if (filter->kind == FILTER_REFS_REMOTES)
- ret = for_each_fullref_in("refs/remotes/", fn, cb_data);
+ ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/remotes/", NULL,
+ fn, cb_data);
else if (filter->kind == FILTER_REFS_TAGS)
- ret = for_each_fullref_in("refs/tags/", fn, cb_data);
+ ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "refs/tags/", NULL, fn,
+ cb_data);
else if (filter->kind & FILTER_REFS_REGULAR)
ret = for_each_fullref_in_pattern(filter, fn, cb_data);
@@ -3072,9 +3090,10 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
* When printing all ref types, HEAD is already included,
* so we don't want to print HEAD again.
*/
- if (!ret && (filter->kind != FILTER_REFS_KIND_MASK) &&
+ if (!ret && !(filter->kind & FILTER_REFS_ROOT_REFS) &&
(filter->kind & FILTER_REFS_DETACHED_HEAD))
- head_ref(fn, cb_data);
+ refs_head_ref(get_main_ref_store(the_repository), fn,
+ cb_data);
}
clear_contains_cache(&filter->internal.contains_cache);
diff --git a/ref-filter.h b/ref-filter.h
index 0ca28d2..27ae1aa 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -23,9 +23,9 @@
FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
#define FILTER_REFS_DETACHED_HEAD 0x0020
#define FILTER_REFS_PSEUDOREFS 0x0040
-#define FILTER_REFS_ROOT_REFS (FILTER_REFS_DETACHED_HEAD | FILTER_REFS_PSEUDOREFS)
+#define FILTER_REFS_ROOT_REFS 0x0080
#define FILTER_REFS_KIND_MASK (FILTER_REFS_REGULAR | FILTER_REFS_DETACHED_HEAD | \
- FILTER_REFS_PSEUDOREFS)
+ FILTER_REFS_PSEUDOREFS | FILTER_REFS_ROOT_REFS)
struct atom_value;
struct ref_sorting;
diff --git a/reflog-walk.c b/reflog-walk.c
index 66484f4..c7070b1 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "commit.h"
#include "refs.h"
@@ -67,24 +69,32 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
struct complete_reflogs *reflogs =
xcalloc(1, sizeof(struct complete_reflogs));
reflogs->ref = xstrdup(ref);
- for_each_reflog_ent(ref, read_one_reflog, reflogs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository), ref,
+ read_one_reflog, reflogs);
if (reflogs->nr == 0) {
const char *name;
void *name_to_free;
- name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
- NULL, NULL);
+ name = name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+ ref,
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (name) {
- for_each_reflog_ent(name, read_one_reflog, reflogs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ name, read_one_reflog,
+ reflogs);
free(name_to_free);
}
}
if (reflogs->nr == 0) {
char *refname = xstrfmt("refs/%s", ref);
- for_each_reflog_ent(refname, read_one_reflog, reflogs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname, read_one_reflog, reflogs);
if (reflogs->nr == 0) {
free(refname);
refname = xstrfmt("refs/heads/%s", ref);
- for_each_reflog_ent(refname, read_one_reflog, reflogs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname, read_one_reflog,
+ reflogs);
}
free(refname);
}
@@ -174,7 +184,8 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
else {
if (*branch == '\0') {
free(branch);
- branch = resolve_refdup("HEAD", 0, NULL, NULL);
+ branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, NULL);
if (!branch)
die("no current branch");
@@ -182,8 +193,8 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
reflogs = read_complete_reflog(branch);
if (!reflogs || reflogs->nr == 0) {
char *b;
- int ret = dwim_log(branch, strlen(branch),
- NULL, &b);
+ int ret = repo_dwim_log(the_repository, branch, strlen(branch),
+ NULL, &b);
if (ret > 1)
free(b);
else if (ret == 1) {
@@ -236,7 +247,9 @@ void get_reflog_selector(struct strbuf *sb,
if (shorten) {
if (!commit_reflog->reflogs->short_ref)
commit_reflog->reflogs->short_ref
- = shorten_unambiguous_ref(commit_reflog->reflogs->ref, 0);
+ = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ commit_reflog->reflogs->ref,
+ 0);
printed_ref = commit_reflog->reflogs->short_ref;
} else {
printed_ref = commit_reflog->reflogs->ref;
diff --git a/reflog.c b/reflog.c
index 647f3ca..5ca9445 100644
--- a/reflog.c
+++ b/reflog.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "object-store-ll.h"
@@ -343,7 +345,8 @@ void reflog_expiry_prepare(const char *refname,
case UE_ALWAYS:
return;
case UE_HEAD:
- for_each_ref(push_tip_to_list, &cb->tips);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ push_tip_to_list, &cb->tips);
for (elem = cb->tips; elem; elem = elem->next)
commit_list_insert(elem->item, &cb->mark_list);
break;
@@ -408,7 +411,7 @@ int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
if (!spec)
return error(_("not a reflog: %s"), rev);
- if (!dwim_log(rev, spec - rev, NULL, &ref)) {
+ if (!repo_dwim_log(the_repository, rev, spec - rev, NULL, &ref)) {
status |= error(_("no reflog for '%s'"), rev);
goto cleanup;
}
@@ -416,19 +419,22 @@ int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
recno = strtoul(spec + 2, &ep, 10);
if (*ep == '}') {
cmd.recno = -recno;
- for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ ref, count_reflog_ent, &cmd);
} else {
cmd.expire_total = approxidate(spec + 2);
- for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ ref, count_reflog_ent, &cmd);
cmd.expire_total = 0;
}
cb.cmd = cmd;
- status |= reflog_expire(ref, flags,
- reflog_expiry_prepare,
- should_prune_fn,
- reflog_expiry_cleanup,
- &cb);
+ status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
+ flags,
+ reflog_expiry_prepare,
+ should_prune_fn,
+ reflog_expiry_cleanup,
+ &cb);
cleanup:
free(ref);
diff --git a/refs.c b/refs.c
index 55d2e0b..bb90a18 100644
--- a/refs.c
+++ b/refs.c
@@ -2,11 +2,13 @@
* The backend-independent part of the reference module.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
#include "environment.h"
-#include "hashmap.h"
+#include "strmap.h"
#include "gettext.h"
#include "hex.h"
#include "lockfile.h"
@@ -19,7 +21,6 @@
#include "object-store-ll.h"
#include "object.h"
#include "path.h"
-#include "tag.h"
#include "submodule.h"
#include "worktree.h"
#include "strvec.h"
@@ -38,14 +39,15 @@ static const struct ref_storage_be *refs_backends[] = {
[REF_STORAGE_FORMAT_REFTABLE] = &refs_be_reftable,
};
-static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
+static const struct ref_storage_be *find_ref_storage_backend(
+ enum ref_storage_format ref_storage_format)
{
if (ref_storage_format < ARRAY_SIZE(refs_backends))
return refs_backends[ref_storage_format];
return NULL;
}
-unsigned int ref_storage_format_by_name(const char *name)
+enum ref_storage_format ref_storage_format_by_name(const char *name)
{
for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
@@ -53,7 +55,7 @@ unsigned int ref_storage_format_by_name(const char *name)
return REF_STORAGE_FORMAT_UNKNOWN;
}
-const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format)
{
const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
if (!be)
@@ -159,7 +161,7 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref)
{
struct ref_namespace_info *info = &ref_namespace[namespace];
if (info->ref_updated)
- free(info->ref);
+ free((char *)info->ref);
info->ref = ref;
info->ref_updated = 1;
}
@@ -384,14 +386,6 @@ char *refs_resolve_refdup(struct ref_store *refs,
return xstrdup_or_null(result);
}
-char *resolve_refdup(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags)
-{
- return refs_resolve_refdup(get_main_ref_store(the_repository),
- refname, resolve_flags,
- oid, flags);
-}
-
/* The argument to for_each_filter_refs */
struct for_each_ref_filter {
const char *pattern;
@@ -400,19 +394,18 @@ struct for_each_ref_filter {
void *cb_data;
};
-int read_ref_full(const char *refname, int resolve_flags, struct object_id *oid, int *flags)
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+ int resolve_flags, struct object_id *oid, int *flags)
{
- struct ref_store *refs = get_main_ref_store(the_repository);
-
if (refs_resolve_ref_unsafe(refs, refname, resolve_flags,
oid, flags))
return 0;
return -1;
}
-int read_ref(const char *refname, struct object_id *oid)
+int refs_read_ref(struct ref_store *refs, const char *refname, struct object_id *oid)
{
- return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
+ return refs_read_ref_full(refs, refname, RESOLVE_REF_READING, oid, NULL);
}
int refs_ref_exists(struct ref_store *refs, const char *refname)
@@ -421,11 +414,6 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
NULL, NULL);
}
-int ref_exists(const char *refname)
-{
- return refs_ref_exists(get_main_ref_store(the_repository), refname);
-}
-
static int for_each_filter_refs(const char *refname,
const struct object_id *oid,
int flags, void *data)
@@ -439,28 +427,8 @@ static int for_each_filter_refs(const char *refname,
return filter->fn(refname, oid, flags, filter->cb_data);
}
-enum peel_status peel_object(const struct object_id *name, struct object_id *oid)
-{
- struct object *o = lookup_unknown_object(the_repository, name);
-
- if (o->type == OBJ_NONE) {
- int type = oid_object_info(the_repository, name, NULL);
- if (type < 0 || !object_as_type(o, type, 0))
- return PEEL_INVALID;
- }
-
- if (o->type != OBJ_TAG)
- return PEEL_NON_TAG;
-
- o = deref_tag_noverify(o);
- if (!o)
- return PEEL_INVALID;
-
- oidcpy(oid, &o->oid);
- return PEEL_PEELED;
-}
-
struct warn_if_dangling_data {
+ struct ref_store *refs;
FILE *fp;
const char *refname;
const struct string_list *refnames;
@@ -477,7 +445,7 @@ static int warn_if_dangling_symref(const char *refname,
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref_unsafe(refname, 0, NULL, NULL);
+ resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);
if (!resolves_to
|| (d->refname
? strcmp(resolves_to, d->refname)
@@ -490,26 +458,28 @@ static int warn_if_dangling_symref(const char *refname,
return 0;
}
-void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
+void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp,
+ const char *msg_fmt, const char *refname)
{
- struct warn_if_dangling_data data;
-
- data.fp = fp;
- data.refname = refname;
- data.refnames = NULL;
- data.msg_fmt = msg_fmt;
- for_each_rawref(warn_if_dangling_symref, &data);
+ struct warn_if_dangling_data data = {
+ .refs = refs,
+ .fp = fp,
+ .refname = refname,
+ .msg_fmt = msg_fmt,
+ };
+ refs_for_each_rawref(refs, warn_if_dangling_symref, &data);
}
-void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
+void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp,
+ const char *msg_fmt, const struct string_list *refnames)
{
- struct warn_if_dangling_data data;
-
- data.fp = fp;
- data.refname = NULL;
- data.refnames = refnames;
- data.msg_fmt = msg_fmt;
- for_each_rawref(warn_if_dangling_symref, &data);
+ struct warn_if_dangling_data data = {
+ .refs = refs,
+ .fp = fp,
+ .refnames = refnames,
+ .msg_fmt = msg_fmt,
+ };
+ refs_for_each_rawref(refs, warn_if_dangling_symref, &data);
}
int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -517,32 +487,17 @@ int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data);
}
-int for_each_tag_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_tag_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
}
-int for_each_branch_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_branch_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
}
-int for_each_remote_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_remote_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
-int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
struct strbuf buf = STRBUF_INIT;
int ret = 0;
@@ -550,7 +505,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!read_ref_full(buf.buf, RESOLVE_REF_READING, &oid, &flag))
+ if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
ret = fn(buf.buf, &oid, flag, cb_data);
strbuf_release(&buf);
@@ -583,8 +538,8 @@ void normalize_glob_ref(struct string_list_item *item, const char *prefix,
strbuf_release(&normalized_pattern);
}
-int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
- const char *prefix, void *cb_data)
+int refs_for_each_glob_ref_in(struct ref_store *refs, each_ref_fn fn,
+ const char *pattern, const char *prefix, void *cb_data)
{
struct strbuf real_pattern = STRBUF_INIT;
struct for_each_ref_filter filter;
@@ -607,15 +562,16 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
filter.prefix = prefix;
filter.fn = fn;
filter.cb_data = cb_data;
- ret = for_each_ref(for_each_filter_refs, &filter);
+ ret = refs_for_each_ref(refs, for_each_filter_refs, &filter);
strbuf_release(&real_pattern);
return ret;
}
-int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+int refs_for_each_glob_ref(struct ref_store *refs, each_ref_fn fn,
+ const char *pattern, void *cb_data)
{
- return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
+ return refs_for_each_glob_ref_in(refs, fn, pattern, NULL, cb_data);
}
const char *prettify_refname(const char *name)
@@ -711,16 +667,6 @@ char *repo_default_branch_name(struct repository *r, int quiet)
return ret;
}
-const char *git_default_branch_name(int quiet)
-{
- static char *ret;
-
- if (!ret)
- ret = repo_default_branch_name(the_repository, quiet);
-
- return ret;
-}
-
/*
* *string and *len will only be substituted, and *string returned (for
* later free()ing) if the string passed in is a magic short-hand form
@@ -832,11 +778,6 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
return logs_found;
}
-int dwim_log(const char *str, int len, struct object_id *oid, char **log)
-{
- return repo_dwim_log(the_repository, str, len, oid, log);
-}
-
int is_per_worktree_ref(const char *refname)
{
return starts_with(refname, "refs/worktree/") ||
@@ -844,7 +785,22 @@ int is_per_worktree_ref(const char *refname)
starts_with(refname, "refs/rewritten/");
}
-static int is_pseudoref_syntax(const char *refname)
+int is_pseudo_ref(const char *refname)
+{
+ static const char * const pseudo_refs[] = {
+ "FETCH_HEAD",
+ "MERGE_HEAD",
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(pseudo_refs); i++)
+ if (!strcmp(refname, pseudo_refs[i]))
+ return 1;
+
+ return 0;
+}
+
+static int is_root_ref_syntax(const char *refname)
{
const char *c;
@@ -853,56 +809,37 @@ static int is_pseudoref_syntax(const char *refname)
return 0;
}
- /*
- * HEAD is not a pseudoref, but it certainly uses the
- * pseudoref syntax.
- */
return 1;
}
-int is_pseudoref(struct ref_store *refs, const char *refname)
+int is_root_ref(const char *refname)
{
- static const char *const irregular_pseudorefs[] = {
+ static const char *const irregular_root_refs[] = {
+ "HEAD",
"AUTO_MERGE",
"BISECT_EXPECTED_REV",
"NOTES_MERGE_PARTIAL",
"NOTES_MERGE_REF",
"MERGE_AUTOSTASH",
};
- struct object_id oid;
size_t i;
- if (!is_pseudoref_syntax(refname))
+ if (!is_root_ref_syntax(refname) ||
+ is_pseudo_ref(refname))
return 0;
- if (ends_with(refname, "_HEAD")) {
- refs_resolve_ref_unsafe(refs, refname,
- RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &oid, NULL);
- return !is_null_oid(&oid);
- }
+ if (ends_with(refname, "_HEAD"))
+ return 1;
- for (i = 0; i < ARRAY_SIZE(irregular_pseudorefs); i++)
- if (!strcmp(refname, irregular_pseudorefs[i])) {
- refs_resolve_ref_unsafe(refs, refname,
- RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &oid, NULL);
- return !is_null_oid(&oid);
- }
-
- return 0;
-}
-
-int is_headref(struct ref_store *refs, const char *refname)
-{
- if (!strcmp(refname, "HEAD"))
- return refs_ref_exists(refs, refname);
+ for (i = 0; i < ARRAY_SIZE(irregular_root_refs); i++)
+ if (!strcmp(refname, irregular_root_refs[i]))
+ return 1;
return 0;
}
static int is_current_worktree_ref(const char *ref) {
- return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
+ return is_root_ref_syntax(ref) || is_per_worktree_ref(ref);
}
enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
@@ -979,7 +916,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
transaction = ref_store_transaction_begin(refs, &err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_oid,
- flags, msg, &err) ||
+ NULL, flags, msg, &err) ||
ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ref_transaction_free(transaction);
@@ -991,13 +928,6 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
return 0;
}
-int delete_ref(const char *msg, const char *refname,
- const struct object_id *old_oid, unsigned int flags)
-{
- return refs_delete_ref(get_main_ref_store(the_repository), msg, refname,
- old_oid, flags);
-}
-
static void copy_reflog_msg(struct strbuf *sb, const char *msg)
{
char c;
@@ -1190,11 +1120,6 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
return tr;
}
-struct ref_transaction *ref_transaction_begin(struct strbuf *err)
-{
- return ref_store_transaction_begin(get_main_ref_store(the_repository), err);
-}
-
void ref_transaction_free(struct ref_transaction *transaction)
{
size_t i;
@@ -1217,6 +1142,8 @@ void ref_transaction_free(struct ref_transaction *transaction)
for (i = 0; i < transaction->nr; i++) {
free(transaction->updates[i]->msg);
+ free((char *)transaction->updates[i]->new_target);
+ free((char *)transaction->updates[i]->old_target);
free(transaction->updates[i]);
}
free(transaction->updates);
@@ -1228,6 +1155,7 @@ struct ref_update *ref_transaction_add_update(
const char *refname, unsigned int flags,
const struct object_id *new_oid,
const struct object_id *old_oid,
+ const char *new_target, const char *old_target,
const char *msg)
{
struct ref_update *update;
@@ -1235,16 +1163,24 @@ struct ref_update *ref_transaction_add_update(
if (transaction->state != REF_TRANSACTION_OPEN)
BUG("update called for transaction that is not open");
+ if (old_oid && old_target)
+ BUG("only one of old_oid and old_target should be non NULL");
+ if (new_oid && new_target)
+ BUG("only one of new_oid and new_target should be non NULL");
+
FLEX_ALLOC_STR(update, refname, refname);
ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
transaction->updates[transaction->nr++] = update;
update->flags = flags;
- if (flags & REF_HAVE_NEW)
+ update->new_target = xstrdup_or_null(new_target);
+ update->old_target = xstrdup_or_null(old_target);
+ if ((flags & REF_HAVE_NEW) && new_oid)
oidcpy(&update->new_oid, new_oid);
- if (flags & REF_HAVE_OLD)
+ if ((flags & REF_HAVE_OLD) && old_oid)
oidcpy(&update->old_oid, old_oid);
+
update->msg = normalize_reflog_message(msg);
return update;
}
@@ -1253,11 +1189,19 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
+ const char *new_target,
+ const char *old_target,
unsigned int flags, const char *msg,
struct strbuf *err)
{
assert(err);
+ if ((flags & REF_FORCE_CREATE_REFLOG) &&
+ (flags & REF_SKIP_CREATE_REFLOG)) {
+ strbuf_addstr(err, _("refusing to force and skip creation of reflog"));
+ return -1;
+ }
+
if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
((new_oid && !is_null_oid(new_oid)) ?
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
@@ -1267,6 +1211,13 @@ int ref_transaction_update(struct ref_transaction *transaction,
return -1;
}
+ if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
+ is_pseudo_ref(refname)) {
+ strbuf_addf(err, _("refusing to update pseudoref '%s'"),
+ refname);
+ return -1;
+ }
+
if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
@@ -1278,49 +1229,68 @@ int ref_transaction_update(struct ref_transaction *transaction,
flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
+ flags |= (new_target ? REF_HAVE_NEW : 0) | (old_target ? REF_HAVE_OLD : 0);
ref_transaction_add_update(transaction, refname, flags,
- new_oid, old_oid, msg);
+ new_oid, old_oid, new_target,
+ old_target, msg);
return 0;
}
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
+ const char *new_target,
unsigned int flags, const char *msg,
struct strbuf *err)
{
- if (!new_oid || is_null_oid(new_oid)) {
- strbuf_addf(err, "'%s' has a null OID", refname);
+ if (new_oid && new_target)
+ BUG("create called with both new_oid and new_target set");
+ if ((!new_oid || is_null_oid(new_oid)) && !new_target) {
+ strbuf_addf(err, "'%s' has neither a valid OID nor a target", refname);
return 1;
}
return ref_transaction_update(transaction, refname, new_oid,
- null_oid(), flags, msg, err);
+ null_oid(), new_target, NULL, flags,
+ msg, err);
}
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const struct object_id *old_oid,
- unsigned int flags, const char *msg,
+ const char *old_target,
+ unsigned int flags,
+ const char *msg,
struct strbuf *err)
{
if (old_oid && is_null_oid(old_oid))
BUG("delete called with old_oid set to zeros");
+ if (old_oid && old_target)
+ BUG("delete called with both old_oid and old_target set");
+ if (old_target && !(flags & REF_NO_DEREF))
+ BUG("delete cannot operate on symrefs with deref mode");
return ref_transaction_update(transaction, refname,
null_oid(), old_oid,
- flags, msg, err);
+ NULL, old_target, flags,
+ msg, err);
}
int ref_transaction_verify(struct ref_transaction *transaction,
const char *refname,
const struct object_id *old_oid,
+ const char *old_target,
unsigned int flags,
struct strbuf *err)
{
- if (!old_oid)
- BUG("verify called with old_oid set to NULL");
+ if (!old_target && !old_oid)
+ BUG("verify called with old_oid and old_target set to NULL");
+ if (old_oid && old_target)
+ BUG("verify called with both old_oid and old_target set");
+ if (old_target && !(flags & REF_NO_DEREF))
+ BUG("verify cannot operate on symrefs with deref mode");
return ref_transaction_update(transaction, refname,
NULL, old_oid,
+ NULL, old_target,
flags, NULL, err);
}
@@ -1335,8 +1305,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
t = ref_store_transaction_begin(refs, &err);
if (!t ||
- ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
- &err) ||
+ ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
+ flags, msg, &err) ||
ref_transaction_commit(t, &err)) {
ret = 1;
ref_transaction_free(t);
@@ -1363,15 +1333,6 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
return 0;
}
-int update_ref(const char *msg, const char *refname,
- const struct object_id *new_oid,
- const struct object_id *old_oid,
- unsigned int flags, enum action_on_err onerr)
-{
- return refs_update_ref(get_main_ref_store(the_repository), msg, refname, new_oid,
- old_oid, flags, onerr);
-}
-
/*
* Check that the string refname matches a rule of the form
* "{prefix}%.*s{suffix}". So "foo/bar/baz" would match the rule
@@ -1473,12 +1434,6 @@ char *refs_shorten_unambiguous_ref(struct ref_store *refs,
return xstrdup(refname);
}
-char *shorten_unambiguous_ref(const char *refname, int strict)
-{
- return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
- refname, strict);
-}
-
int parse_hide_refs_config(const char *var, const char *value, const char *section,
struct strvec *hide_refs)
{
@@ -1597,11 +1552,6 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
return 0;
}
-int head_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
const char *prefix,
@@ -1633,53 +1583,12 @@ struct ref_iterator *refs_ref_iterator_begin(
return iter;
}
-/*
- * Call fn for each reference in the specified submodule for which the
- * refname begins with prefix. If trim is non-zero, then trim that
- * many characters off the beginning of each refname before passing
- * the refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to
- * include broken references in the iteration. If fn ever returns a
- * non-zero value, stop the iteration and return that value;
- * otherwise, return 0.
- */
-static int do_for_each_repo_ref(struct repository *r, const char *prefix,
- each_repo_ref_fn fn, int trim, int flags,
- void *cb_data)
-{
- struct ref_iterator *iter;
- struct ref_store *refs = get_main_ref_store(r);
-
- if (!refs)
- return 0;
-
- iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags);
-
- return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
-}
-
-struct do_for_each_ref_help {
- each_ref_fn *fn;
- void *cb_data;
-};
-
-static int do_for_each_ref_helper(struct repository *r UNUSED,
- const char *refname,
- const struct object_id *oid,
- int flags,
- void *cb_data)
-{
- struct do_for_each_ref_help *hp = cb_data;
-
- return hp->fn(refname, oid, flags, hp->cb_data);
-}
-
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
const char **exclude_patterns,
each_ref_fn fn, int trim,
enum do_for_each_ref_flags flags, void *cb_data)
{
struct ref_iterator *iter;
- struct do_for_each_ref_help hp = { fn, cb_data };
if (!refs)
return 0;
@@ -1687,8 +1596,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim,
flags);
- return do_for_each_repo_ref_iterator(the_repository, iter,
- do_for_each_ref_helper, &hp);
+ return do_for_each_ref_iterator(iter, fn, cb_data);
}
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -1696,28 +1604,12 @@ int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
return do_for_each_ref(refs, "", NULL, fn, 0, 0, cb_data);
}
-int for_each_ref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(refs, prefix, NULL, fn, strlen(prefix), 0, cb_data);
}
-int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data);
-}
-
-int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(get_main_ref_store(the_repository),
- prefix, NULL, fn, 0, 0, cb_data);
-}
-
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
const char **exclude_patterns,
each_ref_fn fn, void *cb_data)
@@ -1725,22 +1617,22 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
return do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);
}
-int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
+int refs_for_each_replace_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
- return do_for_each_repo_ref(r, git_replace_ref_base, fn,
- strlen(git_replace_ref_base),
- DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+ return do_for_each_ref(refs, git_replace_ref_base, NULL, fn,
+ strlen(git_replace_ref_base),
+ DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
-int for_each_namespaced_ref(const char **exclude_patterns,
- each_ref_fn fn, void *cb_data)
+int refs_for_each_namespaced_ref(struct ref_store *refs,
+ const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data)
{
struct strbuf buf = STRBUF_INIT;
int ret;
strbuf_addf(&buf, "%srefs/", get_git_namespace());
- ret = do_for_each_ref(get_main_ref_store(the_repository),
- buf.buf, exclude_patterns, fn, 0, 0, cb_data);
+ ret = do_for_each_ref(refs, buf.buf, exclude_patterns, fn, 0, 0, cb_data);
strbuf_release(&buf);
return ret;
}
@@ -1751,11 +1643,6 @@ int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
-int for_each_rawref(each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
void *cb_data)
{
@@ -1876,43 +1763,12 @@ static int refs_read_special_head(struct ref_store *ref_store,
return result;
}
-static int is_special_ref(const char *refname)
-{
- /*
- * Special references are refs that have different semantics compared
- * to "normal" refs. These refs can thus not be stored in the ref
- * backend, but must always be accessed via the filesystem. The
- * following refs are special:
- *
- * - FETCH_HEAD may contain multiple object IDs, and each one of them
- * carries additional metadata like where it came from.
- *
- * - MERGE_HEAD may contain multiple object IDs when merging multiple
- * heads.
- *
- * Reading, writing or deleting references must consistently go either
- * through the filesystem (special refs) or through the reference
- * backend (normal ones).
- */
- static const char * const special_refs[] = {
- "FETCH_HEAD",
- "MERGE_HEAD",
- };
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(special_refs); i++)
- if (!strcmp(refname, special_refs[i]))
- return 1;
-
- return 0;
-}
-
int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno)
{
assert(failure_errno);
- if (is_special_ref(refname))
+ if (is_pseudo_ref(refname))
return refs_read_special_head(ref_store, refname, oid, referent,
type, failure_errno);
@@ -1982,7 +1838,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
failure_errno != ENOTDIR)
return NULL;
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
if (*flags & REF_BAD_NAME)
*flags |= REF_ISBROKEN;
return refname;
@@ -1992,7 +1848,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
if (!(read_flags & REF_ISSYMREF)) {
if (*flags & REF_BAD_NAME) {
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
*flags |= REF_ISBROKEN;
}
return refname;
@@ -2000,7 +1856,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
refname = sb_refname.buf;
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
- oidclr(oid);
+ oidclr(oid, the_repository->hash_algo);
return refname;
}
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
@@ -2016,26 +1872,24 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
}
/* backend functions */
-int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)
+int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err)
{
- return refs->be->init_db(refs, flags, err);
+ return refs->be->create_on_disk(refs, flags, err);
}
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags)
+int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err)
{
- return refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname,
- resolve_flags, oid, flags);
+ return refs->be->remove_on_disk(refs, err);
}
-int resolve_gitlink_ref(const char *submodule, const char *refname,
- struct object_id *oid)
+int repo_resolve_gitlink_ref(struct repository *r,
+ const char *submodule, const char *refname,
+ struct object_id *oid)
{
struct ref_store *refs;
int flags;
- refs = get_submodule_ref_store(submodule);
-
+ refs = repo_get_submodule_ref_store(r, submodule);
if (!refs)
return -1;
@@ -2045,80 +1899,36 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
return 0;
}
-struct ref_store_hash_entry
-{
- struct hashmap_entry ent;
-
- struct ref_store *refs;
-
- /* NUL-terminated identifier of the ref store: */
- char name[FLEX_ARRAY];
-};
-
-static int ref_store_hash_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata)
-{
- const struct ref_store_hash_entry *e1, *e2;
- const char *name;
-
- e1 = container_of(eptr, const struct ref_store_hash_entry, ent);
- e2 = container_of(entry_or_key, const struct ref_store_hash_entry, ent);
- name = keydata ? keydata : e2->name;
-
- return strcmp(e1->name, name);
-}
-
-static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
- const char *name, struct ref_store *refs)
-{
- struct ref_store_hash_entry *entry;
-
- FLEX_ALLOC_STR(entry, name, name);
- hashmap_entry_init(&entry->ent, strhash(name));
- entry->refs = refs;
- return entry;
-}
-
-/* A hashmap of ref_stores, stored by submodule name: */
-static struct hashmap submodule_ref_stores;
-
-/* A hashmap of ref_stores, stored by worktree id: */
-static struct hashmap worktree_ref_stores;
-
/*
* Look up a ref store by name. If that ref_store hasn't been
* registered yet, return NULL.
*/
-static struct ref_store *lookup_ref_store_map(struct hashmap *map,
+static struct ref_store *lookup_ref_store_map(struct strmap *map,
const char *name)
{
- struct ref_store_hash_entry *entry;
- unsigned int hash;
+ struct strmap_entry *entry;
- if (!map->tablesize)
+ if (!map->map.tablesize)
/* It's initialized on demand in register_ref_store(). */
return NULL;
- hash = strhash(name);
- entry = hashmap_get_entry_from_hash(map, hash, name,
- struct ref_store_hash_entry, ent);
- return entry ? entry->refs : NULL;
+ entry = strmap_get_entry(map, name);
+ return entry ? entry->value : NULL;
}
/*
* Create, record, and return a ref_store instance for the specified
- * gitdir.
+ * gitdir using the given ref storage format.
*/
static struct ref_store *ref_store_init(struct repository *repo,
+ enum ref_storage_format format,
const char *gitdir,
unsigned int flags)
{
const struct ref_storage_be *be;
struct ref_store *refs;
- be = find_ref_storage_backend(repo->ref_storage_format);
+ be = find_ref_storage_backend(format);
if (!be)
BUG("reference backend is unknown");
@@ -2126,6 +1936,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
return refs;
}
+void ref_store_release(struct ref_store *ref_store)
+{
+ ref_store->be->release(ref_store);
+ free(ref_store->gitdir);
+}
+
struct ref_store *get_main_ref_store(struct repository *r)
{
if (r->refs_private)
@@ -2134,7 +1950,8 @@ struct ref_store *get_main_ref_store(struct repository *r)
if (!r->gitdir)
BUG("attempting to get main_ref_store outside of repository");
- r->refs_private = ref_store_init(r, r->gitdir, REF_STORE_ALL_CAPS);
+ r->refs_private = ref_store_init(r, r->ref_storage_format,
+ r->gitdir, REF_STORE_ALL_CAPS);
r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
return r->refs_private;
}
@@ -2143,22 +1960,19 @@ struct ref_store *get_main_ref_store(struct repository *r)
* Associate a ref store with a name. It is a fatal error to call this
* function twice for the same name.
*/
-static void register_ref_store_map(struct hashmap *map,
+static void register_ref_store_map(struct strmap *map,
const char *type,
struct ref_store *refs,
const char *name)
{
- struct ref_store_hash_entry *entry;
-
- if (!map->tablesize)
- hashmap_init(map, ref_store_hash_cmp, NULL, 0);
-
- entry = alloc_ref_store_hash_entry(name, refs);
- if (hashmap_put(map, &entry->ent))
+ if (!map->map.tablesize)
+ strmap_init(map);
+ if (strmap_put(map, name, refs))
BUG("%s ref_store '%s' initialized twice", type, name);
}
-struct ref_store *get_submodule_ref_store(const char *submodule)
+struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
+ const char *submodule)
{
struct strbuf submodule_sb = STRBUF_INIT;
struct ref_store *refs;
@@ -2179,7 +1993,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
/* We need to strip off one or more trailing slashes */
submodule = to_free = xmemdupz(submodule, len);
- refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
+ refs = lookup_ref_store_map(&repo->submodule_ref_stores, submodule);
if (refs)
goto done;
@@ -2191,20 +2005,16 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
goto done;
subrepo = xmalloc(sizeof(*subrepo));
- /*
- * NEEDSWORK: Make get_submodule_ref_store() work with arbitrary
- * superprojects other than the_repository. This probably should be
- * done by making it take a struct repository * parameter instead of a
- * submodule path.
- */
- if (repo_submodule_init(subrepo, the_repository, submodule,
+
+ if (repo_submodule_init(subrepo, repo, submodule,
null_oid())) {
free(subrepo);
goto done;
}
- refs = ref_store_init(subrepo, submodule_sb.buf,
+ refs = ref_store_init(subrepo, the_repository->ref_storage_format,
+ submodule_sb.buf,
REF_STORE_READ | REF_STORE_ODB);
- register_ref_store_map(&submodule_ref_stores, "submodule",
+ register_ref_store_map(&repo->submodule_ref_stores, "submodule",
refs, submodule);
done:
@@ -2220,25 +2030,29 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
const char *id;
if (wt->is_current)
- return get_main_ref_store(the_repository);
+ return get_main_ref_store(wt->repo);
id = wt->id ? wt->id : "/";
- refs = lookup_ref_store_map(&worktree_ref_stores, id);
+ refs = lookup_ref_store_map(&wt->repo->worktree_ref_stores, id);
if (refs)
return refs;
- if (wt->id)
- refs = ref_store_init(the_repository,
- git_common_path("worktrees/%s", wt->id),
- REF_STORE_ALL_CAPS);
- else
- refs = ref_store_init(the_repository,
- get_git_common_dir(),
- REF_STORE_ALL_CAPS);
+ if (wt->id) {
+ struct strbuf common_path = STRBUF_INIT;
+ strbuf_git_common_path(&common_path, wt->repo,
+ "worktrees/%s", wt->id);
+ refs = ref_store_init(wt->repo, wt->repo->ref_storage_format,
+ common_path.buf, REF_STORE_ALL_CAPS);
+ strbuf_release(&common_path);
+ } else {
+ refs = ref_store_init(wt->repo, the_repository->ref_storage_format,
+ wt->repo->commondir, REF_STORE_ALL_CAPS);
+ }
if (refs)
- register_ref_store_map(&worktree_ref_stores, "worktree",
- refs, id);
+ register_ref_store_map(&wt->repo->worktree_ref_stores,
+ "worktree", refs, id);
+
return refs;
}
@@ -2256,36 +2070,37 @@ int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts)
return refs->be->pack_refs(refs, opts);
}
-int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
+int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
{
if (current_ref_iter &&
(current_ref_iter->oid == base ||
oideq(current_ref_iter->oid, base)))
return ref_iterator_peel(current_ref_iter, peeled);
- return peel_object(base, peeled) ? -1 : 0;
+ return peel_object(r, base, peeled) ? -1 : 0;
}
-int refs_create_symref(struct ref_store *refs,
- const char *ref_target,
- const char *refs_heads_master,
- const char *logmsg)
+int refs_update_symref(struct ref_store *refs, const char *ref,
+ const char *target, const char *logmsg)
{
- char *msg;
- int retval;
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
+ int ret = 0;
- msg = normalize_reflog_message(logmsg);
- retval = refs->be->create_symref(refs, ref_target, refs_heads_master,
- msg);
- free(msg);
- return retval;
-}
+ transaction = ref_store_transaction_begin(refs, &err);
+ if (!transaction ||
+ ref_transaction_update(transaction, ref, NULL, NULL,
+ target, NULL, REF_NO_DEREF,
+ logmsg, &err) ||
+ ref_transaction_commit(transaction, &err)) {
+ ret = error("%s", err.buf);
+ }
-int create_symref(const char *ref_target, const char *refs_heads_master,
- const char *logmsg)
-{
- return refs_create_symref(get_main_ref_store(the_repository), ref_target,
- refs_heads_master, logmsg);
+ strbuf_release(&err);
+ if (transaction)
+ ref_transaction_free(transaction);
+
+ return ret;
}
int ref_update_reject_duplicates(struct string_list *refnames,
@@ -2338,10 +2153,22 @@ static int run_transaction_hook(struct ref_transaction *transaction,
struct ref_update *update = transaction->updates[i];
strbuf_reset(&buf);
- strbuf_addf(&buf, "%s %s %s\n",
- oid_to_hex(&update->old_oid),
- oid_to_hex(&update->new_oid),
- update->refname);
+
+ if (!(update->flags & REF_HAVE_OLD))
+ strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
+ else if (update->old_target)
+ strbuf_addf(&buf, "ref:%s ", update->old_target);
+ else
+ strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
+
+ if (!(update->flags & REF_HAVE_NEW))
+ strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
+ else if (update->new_target)
+ strbuf_addf(&buf, "ref:%s ", update->new_target);
+ else
+ strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
+
+ strbuf_addf(&buf, "%s\n", update->refname);
if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
if (errno != EPIPE) {
@@ -2560,8 +2387,7 @@ struct do_for_each_reflog_help {
void *cb_data;
};
-static int do_for_each_reflog_helper(struct repository *r UNUSED,
- const char *refname,
+static int do_for_each_reflog_helper(const char *refname,
const struct object_id *oid UNUSED,
int flags,
void *cb_data)
@@ -2577,13 +2403,7 @@ int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_dat
iter = refs->be->reflog_iterator_begin(refs);
- return do_for_each_repo_ref_iterator(the_repository, iter,
- do_for_each_reflog_helper, &hp);
-}
-
-int for_each_reflog(each_reflog_fn fn, void *cb_data)
-{
- return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
+ return do_for_each_ref_iterator(iter, do_for_each_reflog_helper, &hp);
}
int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
@@ -2595,58 +2415,28 @@ int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
fn, cb_data);
}
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
- void *cb_data)
-{
- return refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
- refname, fn, cb_data);
-}
-
int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
each_reflog_ent_fn fn, void *cb_data)
{
return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
}
-int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
- void *cb_data)
-{
- return refs_for_each_reflog_ent(get_main_ref_store(the_repository), refname,
- fn, cb_data);
-}
-
int refs_reflog_exists(struct ref_store *refs, const char *refname)
{
return refs->be->reflog_exists(refs, refname);
}
-int reflog_exists(const char *refname)
-{
- return refs_reflog_exists(get_main_ref_store(the_repository), refname);
-}
-
int refs_create_reflog(struct ref_store *refs, const char *refname,
struct strbuf *err)
{
return refs->be->create_reflog(refs, refname, err);
}
-int safe_create_reflog(const char *refname, struct strbuf *err)
-{
- return refs_create_reflog(get_main_ref_store(the_repository), refname,
- err);
-}
-
int refs_delete_reflog(struct ref_store *refs, const char *refname)
{
return refs->be->delete_reflog(refs, refname);
}
-int delete_reflog(const char *refname)
-{
- return refs_delete_reflog(get_main_ref_store(the_repository), refname);
-}
-
int refs_reflog_expire(struct ref_store *refs,
const char *refname,
unsigned int flags,
@@ -2660,19 +2450,6 @@ int refs_reflog_expire(struct ref_store *refs,
cleanup_fn, policy_cb_data);
}
-int reflog_expire(const char *refname,
- 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 refs_reflog_expire(get_main_ref_store(the_repository),
- refname, flags,
- prepare_fn, should_prune_fn,
- cleanup_fn, policy_cb_data);
-}
-
int initial_ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err)
{
@@ -2724,7 +2501,7 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg,
for_each_string_list_item(item, refnames) {
ret = ref_transaction_delete(transaction, item->string,
- NULL, flags, msg, &err);
+ NULL, NULL, flags, msg, &err);
if (ret) {
warning(_("could not delete reference %s: %s"),
item->string, err.buf);
@@ -2751,12 +2528,6 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg,
return ret;
}
-int delete_refs(const char *msg, struct string_list *refnames,
- unsigned int flags)
-{
- return refs_delete_refs(get_main_ref_store(the_repository), msg, refnames, flags);
-}
-
int refs_rename_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
@@ -2769,11 +2540,6 @@ int refs_rename_ref(struct ref_store *refs, const char *oldref,
return retval;
}
-int rename_ref(const char *oldref, const char *newref, const char *logmsg)
-{
- return refs_rename_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
-}
-
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
@@ -2786,7 +2552,352 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
return retval;
}
-int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
+const char *ref_update_original_update_refname(struct ref_update *update)
{
- return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+ while (update->parent_update)
+ update = update->parent_update;
+
+ return update->refname;
}
+
+int ref_update_has_null_new_value(struct ref_update *update)
+{
+ return !update->new_target && is_null_oid(&update->new_oid);
+}
+
+int ref_update_check_old_target(const char *referent, struct ref_update *update,
+ struct strbuf *err)
+{
+ if (!update->old_target)
+ BUG("called without old_target set");
+
+ if (!strcmp(referent, update->old_target))
+ return 0;
+
+ if (!strcmp(referent, ""))
+ strbuf_addf(err, "verifying symref target: '%s': "
+ "reference is missing but expected %s",
+ ref_update_original_update_refname(update),
+ update->old_target);
+ else
+ strbuf_addf(err, "verifying symref target: '%s': "
+ "is at %s but expected %s",
+ ref_update_original_update_refname(update),
+ referent, update->old_target);
+ return -1;
+}
+
+struct migration_data {
+ struct ref_store *old_refs;
+ struct ref_transaction *transaction;
+ struct strbuf *errbuf;
+};
+
+static int migrate_one_ref(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
+{
+ struct migration_data *data = cb_data;
+ struct strbuf symref_target = STRBUF_INIT;
+ int ret;
+
+ if (flags & REF_ISSYMREF) {
+ ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
+ if (ret < 0)
+ goto done;
+
+ ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(),
+ symref_target.buf, NULL,
+ REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
+ if (ret < 0)
+ goto done;
+ } else {
+ ret = ref_transaction_create(data->transaction, refname, oid, NULL,
+ REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
+ NULL, data->errbuf);
+ if (ret < 0)
+ goto done;
+ }
+
+done:
+ strbuf_release(&symref_target);
+ return ret;
+}
+
+static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf)
+{
+ struct strbuf from_buf = STRBUF_INIT, to_buf = STRBUF_INIT;
+ size_t from_len, to_len;
+ DIR *from_dir;
+ int ret;
+
+ from_dir = opendir(from_path);
+ if (!from_dir) {
+ strbuf_addf(errbuf, "could not open source directory '%s': %s",
+ from_path, strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ strbuf_addstr(&from_buf, from_path);
+ strbuf_complete(&from_buf, '/');
+ from_len = from_buf.len;
+
+ strbuf_addstr(&to_buf, to_path);
+ strbuf_complete(&to_buf, '/');
+ to_len = to_buf.len;
+
+ while (1) {
+ struct dirent *ent;
+
+ errno = 0;
+ ent = readdir(from_dir);
+ if (!ent)
+ break;
+
+ if (!strcmp(ent->d_name, ".") ||
+ !strcmp(ent->d_name, ".."))
+ continue;
+
+ strbuf_setlen(&from_buf, from_len);
+ strbuf_addstr(&from_buf, ent->d_name);
+
+ strbuf_setlen(&to_buf, to_len);
+ strbuf_addstr(&to_buf, ent->d_name);
+
+ ret = rename(from_buf.buf, to_buf.buf);
+ if (ret < 0) {
+ strbuf_addf(errbuf, "could not link file '%s' to '%s': %s",
+ from_buf.buf, to_buf.buf, strerror(errno));
+ goto done;
+ }
+ }
+
+ if (errno) {
+ strbuf_addf(errbuf, "could not read entry from directory '%s': %s",
+ from_path, strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ strbuf_release(&from_buf);
+ strbuf_release(&to_buf);
+ if (from_dir)
+ closedir(from_dir);
+ return ret;
+}
+
+static int count_reflogs(const char *reflog UNUSED, void *payload)
+{
+ size_t *reflog_count = payload;
+ (*reflog_count)++;
+ return 0;
+}
+
+static int has_worktrees(void)
+{
+ struct worktree **worktrees = get_worktrees();
+ int ret = 0;
+ size_t i;
+
+ for (i = 0; worktrees[i]; i++) {
+ if (is_main_worktree(worktrees[i]))
+ continue;
+ ret = 1;
+ }
+
+ free_worktrees(worktrees);
+ return ret;
+}
+
+int repo_migrate_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format,
+ unsigned int flags,
+ struct strbuf *errbuf)
+{
+ struct ref_store *old_refs = NULL, *new_refs = NULL;
+ struct ref_transaction *transaction = NULL;
+ struct strbuf new_gitdir = STRBUF_INIT;
+ struct migration_data data;
+ size_t reflog_count = 0;
+ int did_migrate_refs = 0;
+ int ret;
+
+ if (repo->ref_storage_format == format) {
+ strbuf_addstr(errbuf, "current and new ref storage format are equal");
+ ret = -1;
+ goto done;
+ }
+
+ old_refs = get_main_ref_store(repo);
+
+ /*
+ * We do not have any interfaces that would allow us to write many
+ * reflog entries. Once we have them we can remove this restriction.
+ */
+ if (refs_for_each_reflog(old_refs, count_reflogs, &reflog_count) < 0) {
+ strbuf_addstr(errbuf, "cannot count reflogs");
+ ret = -1;
+ goto done;
+ }
+ if (reflog_count) {
+ strbuf_addstr(errbuf, "migrating reflogs is not supported yet");
+ ret = -1;
+ goto done;
+ }
+
+ /*
+ * Worktrees complicate the migration because every worktree has a
+ * separate ref storage. While it should be feasible to implement, this
+ * is pushed out to a future iteration.
+ *
+ * TODO: we should really be passing the caller-provided repository to
+ * `has_worktrees()`, but our worktree subsystem doesn't yet support
+ * that.
+ */
+ if (has_worktrees()) {
+ strbuf_addstr(errbuf, "migrating repositories with worktrees is not supported yet");
+ ret = -1;
+ goto done;
+ }
+
+ /*
+ * The overall logic looks like this:
+ *
+ * 1. Set up a new temporary directory and initialize it with the new
+ * format. This is where all refs will be migrated into.
+ *
+ * 2. Enumerate all refs and write them into the new ref storage.
+ * This operation is safe as we do not yet modify the main
+ * repository.
+ *
+ * 3. If we're in dry-run mode then we are done and can hand over the
+ * directory to the caller for inspection. If not, we now start
+ * with the destructive part.
+ *
+ * 4. Delete the old ref storage from disk. As we have a copy of refs
+ * in the new ref storage it's okay(ish) if we now get interrupted
+ * as there is an equivalent copy of all refs available.
+ *
+ * 5. Move the new ref storage files into place.
+ *
+ * 6. Change the repository format to the new ref format.
+ */
+ strbuf_addf(&new_gitdir, "%s/%s", old_refs->gitdir, "ref_migration.XXXXXX");
+ if (!mkdtemp(new_gitdir.buf)) {
+ strbuf_addf(errbuf, "cannot create migration directory: %s",
+ strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ new_refs = ref_store_init(repo, format, new_gitdir.buf,
+ REF_STORE_ALL_CAPS);
+ ret = ref_store_create_on_disk(new_refs, 0, errbuf);
+ if (ret < 0)
+ goto done;
+
+ transaction = ref_store_transaction_begin(new_refs, errbuf);
+ if (!transaction)
+ goto done;
+
+ data.old_refs = old_refs;
+ data.transaction = transaction;
+ data.errbuf = errbuf;
+
+ /*
+ * We need to use the internal `do_for_each_ref()` here so that we can
+ * also include broken refs and symrefs. These would otherwise be
+ * skipped silently.
+ *
+ * Ideally, we would do this call while locking the old ref storage
+ * such that there cannot be any concurrent modifications. We do not
+ * have the infra for that though, and the "files" backend does not
+ * allow for a central lock due to its design. It's thus on the user to
+ * ensure that there are no concurrent writes.
+ */
+ ret = do_for_each_ref(old_refs, "", NULL, migrate_one_ref, 0,
+ DO_FOR_EACH_INCLUDE_ROOT_REFS | DO_FOR_EACH_INCLUDE_BROKEN,
+ &data);
+ if (ret < 0)
+ goto done;
+
+ /*
+ * TODO: we might want to migrate to `initial_ref_transaction_commit()`
+ * here, which is more efficient for the files backend because it would
+ * write new refs into the packed-refs file directly. At this point,
+ * the files backend doesn't handle pseudo-refs and symrefs correctly
+ * though, so this requires some more work.
+ */
+ ret = ref_transaction_commit(transaction, errbuf);
+ if (ret < 0)
+ goto done;
+ did_migrate_refs = 1;
+
+ if (flags & REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN) {
+ printf(_("Finished dry-run migration of refs, "
+ "the result can be found at '%s'\n"), new_gitdir.buf);
+ ret = 0;
+ goto done;
+ }
+
+ /*
+ * Until now we were in the non-destructive phase, where we only
+ * populated the new ref store. From hereon though we are about
+ * to get hands by deleting the old ref store and then moving
+ * the new one into place.
+ *
+ * Assuming that there were no concurrent writes, the new ref
+ * store should have all information. So if we fail from hereon
+ * we may be in an in-between state, but it would still be able
+ * to recover by manually moving remaining files from the
+ * temporary migration directory into place.
+ */
+ ret = ref_store_remove_on_disk(old_refs, errbuf);
+ if (ret < 0)
+ goto done;
+
+ ret = move_files(new_gitdir.buf, old_refs->gitdir, errbuf);
+ if (ret < 0)
+ goto done;
+
+ if (rmdir(new_gitdir.buf) < 0)
+ warning_errno(_("could not remove temporary migration directory '%s'"),
+ new_gitdir.buf);
+
+ /*
+ * We have migrated the repository, so we now need to adjust the
+ * repository format so that clients will use the new ref store.
+ * We also need to swap out the repository's main ref store.
+ */
+ initialize_repository_version(hash_algo_by_ptr(repo->hash_algo), format, 1);
+
+ free(new_refs->gitdir);
+ new_refs->gitdir = xstrdup(old_refs->gitdir);
+ repo->refs_private = new_refs;
+ ref_store_release(old_refs);
+
+ ret = 0;
+
+done:
+ if (ret && did_migrate_refs) {
+ strbuf_complete(errbuf, '\n');
+ strbuf_addf(errbuf, _("migrated refs can be found at '%s'"),
+ new_gitdir.buf);
+ }
+
+ if (ret && new_refs)
+ ref_store_release(new_refs);
+ ref_transaction_free(transaction);
+ strbuf_release(&new_gitdir);
+ return ret;
+}
+
+int ref_update_expects_existing_old_ref(struct ref_update *update)
+{
+ return (update->flags & REF_HAVE_OLD) &&
+ (!is_null_oid(&update->old_oid) || update->old_target);
+}
+
diff --git a/refs.h b/refs.h
index d278775..0ecba21 100644
--- a/refs.h
+++ b/refs.h
@@ -2,17 +2,17 @@
#define REFS_H
#include "commit.h"
+#include "repository.h"
struct object_id;
struct ref_store;
-struct repository;
struct strbuf;
struct string_list;
struct string_list_item;
struct worktree;
-unsigned int ref_storage_format_by_name(const char *name);
-const char *ref_storage_format_to_name(unsigned int ref_storage_format);
+enum ref_storage_format ref_storage_format_by_name(const char *name);
+const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format);
/*
* Resolve a reference, recursively following symbolic refererences.
@@ -72,18 +72,14 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
struct object_id *oid,
int *flags);
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags);
-
char *refs_resolve_refdup(struct ref_store *refs,
const char *refname, int resolve_flags,
struct object_id *oid, int *flags);
-char *resolve_refdup(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags);
-int read_ref_full(const char *refname, int resolve_flags,
- struct object_id *oid, int *flags);
-int read_ref(const char *refname, struct object_id *oid);
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+ int resolve_flags, struct object_id *oid, int *flags);
+
+int refs_read_ref(struct ref_store *refs, const char *refname, struct object_id *oid);
int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
struct strbuf *referent);
@@ -114,27 +110,36 @@ int refs_verify_refname_available(struct ref_store *refs,
int refs_ref_exists(struct ref_store *refs, const char *refname);
-int ref_exists(const char *refname);
-
int should_autocreate_reflog(const char *refname);
int is_branch(const char *refname);
-#define REFS_INIT_DB_IS_WORKTREE (1 << 0)
+#define REF_STORE_CREATE_ON_DISK_IS_WORKTREE (1 << 0)
-int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err);
+int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err);
+
+/*
+ * Release all memory and resources associated with the ref store.
+ */
+void ref_store_release(struct ref_store *ref_store);
+
+/*
+ * Remove the ref store from disk. This deletes all associated data.
+ */
+int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err);
/*
* Return the peeled value of the oid currently being iterated via
* for_each_ref(), etc. This is equivalent to calling:
*
- * peel_object(oid, &peeled);
+ * peel_object(r, oid, &peeled);
*
* with the "oid" value given to the each_ref_fn callback, except
* that some ref storage may be able to answer the query without
* actually loading the object in memory.
*/
-int peel_iterated_oid(const struct object_id *base, struct object_id *peeled);
+int peel_iterated_oid(struct repository *r,
+ const struct object_id *base, struct object_id *peeled);
/**
* Resolve refname in the nested "gitlink" repository in the specified
@@ -142,8 +147,9 @@ int peel_iterated_oid(const struct object_id *base, struct object_id *peeled);
* successful, return 0 and set oid to the name of the object;
* otherwise, return a non-zero value.
*/
-int resolve_gitlink_ref(const char *submodule, const char *refname,
- struct object_id *oid);
+int repo_resolve_gitlink_ref(struct repository *r,
+ const char *submodule, const char *refname,
+ struct object_id *oid);
/*
* Return true iff abbrev_name is a possible abbreviation for
@@ -163,15 +169,12 @@ int expand_ref(struct repository *r, const char *str, int len, struct object_id
int repo_dwim_ref(struct repository *r, const char *str, int len,
struct object_id *oid, char **ref, int nonfatal_dangling_mark);
int repo_dwim_log(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
-int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
/*
* Retrieves the default branch name for newly-initialized repositories.
*
- * The return value of `repo_default_branch_name()` is an allocated string. The
- * return value of `git_default_branch_name()` is a singleton.
+ * The return value is an allocated string.
*/
-const char *git_default_branch_name(int quiet);
char *repo_default_branch_name(struct repository *r, int quiet);
/*
@@ -299,16 +302,6 @@ typedef int each_ref_fn(const char *refname,
const struct object_id *oid, int flags, void *cb_data);
/*
- * The same as each_ref_fn, but also with a repository argument that
- * contains the repository associated with the callback.
- */
-typedef int each_repo_ref_fn(struct repository *r,
- const char *refname,
- const struct object_id *oid,
- int flags,
- void *cb_data);
-
-/*
* The following functions invoke the specified callback function for
* each reference indicated. If the function ever returns a nonzero
* value, stop the iteration and return that value. Please note that
@@ -329,18 +322,8 @@ int refs_for_each_branch_ref(struct ref_store *refs,
each_ref_fn fn, void *cb_data);
int refs_for_each_remote_ref(struct ref_store *refs,
each_ref_fn fn, void *cb_data);
-
-/* just iterates the head ref. */
-int head_ref(each_ref_fn fn, void *cb_data);
-
-/* iterates all refs. */
-int for_each_ref(each_ref_fn fn, void *cb_data);
-
-/**
- * iterates all refs which have a defined prefix and strips that prefix from
- * the passed variable refname.
- */
-int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
+int refs_for_each_replace_ref(struct ref_store *refs,
+ each_ref_fn fn, void *cb_data);
/*
* references matching any pattern in "exclude_patterns" are omitted from the
@@ -349,7 +332,6 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
const char **exclude_patterns,
each_ref_fn fn, void *cb_data);
-int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
/**
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
@@ -366,31 +348,25 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
const char **exclude_patterns,
each_ref_fn fn, void *cb_data);
-/**
- * iterate refs from the respective area.
- */
-int for_each_tag_ref(each_ref_fn fn, void *cb_data);
-int for_each_branch_ref(each_ref_fn fn, void *cb_data);
-int for_each_remote_ref(each_ref_fn fn, void *cb_data);
-int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
-
/* iterates all refs that match the specified glob pattern. */
-int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
+int refs_for_each_glob_ref(struct ref_store *refs, each_ref_fn fn,
+ const char *pattern, void *cb_data);
-int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
- const char *prefix, void *cb_data);
+int refs_for_each_glob_ref_in(struct ref_store *refs, each_ref_fn fn,
+ const char *pattern, const char *prefix, void *cb_data);
-int head_ref_namespaced(each_ref_fn fn, void *cb_data);
+int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_data);
+
/*
* references matching any pattern in "exclude_patterns" are omitted from the
* result set on a best-effort basis.
*/
-int for_each_namespaced_ref(const char **exclude_patterns,
- each_ref_fn fn, void *cb_data);
+int refs_for_each_namespaced_ref(struct ref_store *refs,
+ const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data);
/* can be used to learn about broken ref and symref */
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
-int for_each_rawref(each_ref_fn fn, void *cb_data);
/*
* Iterates over all refs including root refs, i.e. pseudorefs and HEAD.
@@ -415,9 +391,10 @@ static inline const char *has_glob_specials(const char *pattern)
return strpbrk(pattern, "?*[");
}
-void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
-void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
- const struct string_list *refnames);
+void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp,
+ const char *msg_fmt, const char *refname);
+void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp,
+ const char *msg_fmt, const struct string_list *refnames);
/*
* Flags for controlling behaviour of pack_refs()
@@ -446,7 +423,6 @@ int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts);
*/
int refs_create_reflog(struct ref_store *refs, const char *refname,
struct strbuf *err);
-int safe_create_reflog(const char *refname, struct strbuf *err);
/**
* Reads log for the value of ref during at_time (in which case "cnt" should be
@@ -470,7 +446,6 @@ int read_ref_at(struct ref_store *refs,
/** Check if a particular reflog exists */
int refs_reflog_exists(struct ref_store *refs, const char *refname);
-int reflog_exists(const char *refname);
/*
* Delete the specified reference. If old_oid is non-NULL, then
@@ -484,8 +459,6 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
const char *refname,
const struct object_id *old_oid,
unsigned int flags);
-int delete_ref(const char *msg, const char *refname,
- const struct object_id *old_oid, unsigned int flags);
/*
* Delete the specified references. If there are any problems, emit
@@ -495,12 +468,9 @@ int delete_ref(const char *msg, const char *refname,
*/
int refs_delete_refs(struct ref_store *refs, const char *msg,
struct string_list *refnames, unsigned int flags);
-int delete_refs(const char *msg, struct string_list *refnames,
- unsigned int flags);
/** Delete a reflog */
int refs_delete_reflog(struct ref_store *refs, const char *refname);
-int delete_reflog(const char *refname);
/*
* Callback to process a reflog entry found by the iteration functions (see
@@ -546,17 +516,7 @@ int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
void *cb_data);
/*
- * Iterate over reflog entries in the log for `refname` in the main ref store.
- */
-
-/* oldest entry first */
-int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
-
-/* youngest entry first */
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
-
-/*
- * The signature for the callback function for the {refs_,}for_each_reflog()
+ * The signature for the callback function for the refs_for_each_reflog()
* functions below. The memory pointed to by the refname argument is only
* guaranteed to be valid for the duration of a single callback invocation.
*/
@@ -567,7 +527,6 @@ typedef int each_reflog_fn(const char *refname, void *cb_data);
* and returns the value. Reflog file order is unspecified.
*/
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data);
-int for_each_reflog(each_reflog_fn fn, void *cb_data);
#define REFNAME_ALLOW_ONELEVEL 1
#define REFNAME_REFSPEC_PATTERN 2
@@ -592,23 +551,17 @@ const char *prettify_refname(const char *refname);
char *refs_shorten_unambiguous_ref(struct ref_store *refs,
const char *refname, int strict);
-char *shorten_unambiguous_ref(const char *refname, int strict);
/** rename ref, return 0 on success **/
int refs_rename_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg);
-int rename_ref(const char *oldref, const char *newref,
- const char *logmsg);
/** copy ref, return 0 on success **/
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg);
-int copy_existing_ref(const char *oldref, const char *newref,
- const char *logmsg);
-int refs_create_symref(struct ref_store *refs, const char *refname,
+int refs_update_symref(struct ref_store *refs, const char *refname,
const char *target, const char *logmsg);
-int create_symref(const char *refname, const char *target, const char *logmsg);
enum action_on_err {
UPDATE_REFS_MSG_ON_ERR,
@@ -622,7 +575,6 @@ enum action_on_err {
*/
struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
struct strbuf *err);
-struct ref_transaction *ref_transaction_begin(struct strbuf *err);
/*
* Reference transaction updates
@@ -648,6 +600,16 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
* before the update. A copy of this value is made in the
* transaction.
*
+ * new_target -- the target reference that the reference will be
+ * updated to point to. If the reference is a regular reference,
+ * it will be converted to a symbolic reference. Cannot be set
+ * together with `new_oid`. A copy of this value is made in the
+ * transaction.
+ *
+ * old_target -- the reference that the reference must be pointing to.
+ * Canont be set together with `old_oid`. A copy of this value is
+ * made in the transaction.
+ *
* flags -- flags affecting the update, passed to
* update_ref_lock(). Possible flags: REF_NO_DEREF,
* REF_FORCE_CREATE_REFLOG. See those constants for more
@@ -697,12 +659,18 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
#define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
/*
+ * Skip creation of a reflog entry, even if it would have otherwise been
+ * created.
+ */
+#define REF_SKIP_CREATE_REFLOG (1 << 12)
+
+/*
* Bitmask of all of the flags that are allowed to be passed in to
* ref_transaction_update() and friends:
*/
#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
- REF_SKIP_REFNAME_VERIFICATION)
+ REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG)
/*
* Add a reference update to transaction. `new_oid` is the value that
@@ -713,7 +681,11 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
* beforehand. The old value is checked after the lock is taken to
* prevent races. If the old value doesn't agree with old_oid, the
* whole transaction fails. If old_oid is NULL, then the previous
- * value is not checked.
+ * value is not checked. If `old_target` is not NULL, treat the reference
+ * as a symbolic ref and validate that its target before the update is
+ * `old_target`. If the `new_target` is not NULL, then the reference
+ * will be updated to a symbolic ref which targets `new_target`.
+ * Together, these allow us to update between regular refs and symrefs.
*
* See the above comment "Reference transaction updates" for more
* information.
@@ -722,6 +694,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
+ const char *new_target,
+ const char *old_target,
unsigned int flags, const char *msg,
struct strbuf *err);
@@ -737,6 +711,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
+ const char *new_target,
unsigned int flags, const char *msg,
struct strbuf *err);
@@ -751,7 +726,9 @@ int ref_transaction_create(struct ref_transaction *transaction,
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const struct object_id *old_oid,
- unsigned int flags, const char *msg,
+ const char *old_target,
+ unsigned int flags,
+ const char *msg,
struct strbuf *err);
/*
@@ -765,6 +742,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,
int ref_transaction_verify(struct ref_transaction *transaction,
const char *refname,
const struct object_id *old_oid,
+ const char *old_target,
unsigned int flags,
struct strbuf *err);
@@ -853,9 +831,6 @@ void ref_transaction_free(struct ref_transaction *transaction);
int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,
const struct object_id *new_oid, const struct object_id *old_oid,
unsigned int flags, enum action_on_err onerr);
-int update_ref(const char *msg, const char *refname,
- const struct object_id *new_oid, const struct object_id *old_oid,
- unsigned int flags, enum action_on_err onerr);
int parse_hide_refs_config(const char *var, const char *value, const char *,
struct strvec *);
@@ -913,7 +888,7 @@ enum expire_reflog_flags {
/*
* The following interface is used for reflog expiration. The caller
- * calls reflog_expire(), supplying it with three callback functions,
+ * calls refs_reflog_expire(), supplying it with three callback functions,
* of the following types. The callback functions define the
* expiration policy that is desired.
*
@@ -950,12 +925,6 @@ int refs_reflog_expire(struct ref_store *refs,
reflog_expiry_should_prune_fn should_prune_fn,
reflog_expiry_cleanup_fn cleanup_fn,
void *policy_cb_data);
-int reflog_expire(const char *refname,
- unsigned int flags,
- reflog_expiry_prepare_fn prepare_fn,
- reflog_expiry_should_prune_fn should_prune_fn,
- reflog_expiry_cleanup_fn cleanup_fn,
- void *policy_cb_data);
struct ref_store *get_main_ref_store(struct repository *r);
@@ -1003,7 +972,8 @@ struct ref_store *get_main_ref_store(struct repository *r);
* For backwards compatibility, submodule=="" is treated the same as
* submodule==NULL.
*/
-struct ref_store *get_submodule_ref_store(const char *submodule);
+struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
+ const char *submodule);
struct ref_store *get_worktree_ref_store(const struct worktree *wt);
/*
@@ -1013,7 +983,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt);
*/
struct ref_namespace_info {
- char *ref;
+ const char *ref;
enum decoration_type decoration;
/*
@@ -1051,7 +1021,276 @@ extern struct ref_namespace_info ref_namespace[NAMESPACE__COUNT];
*/
void update_ref_namespace(enum ref_namespace namespace, char *ref);
-int is_pseudoref(struct ref_store *refs, const char *refname);
-int is_headref(struct ref_store *refs, const char *refname);
+/*
+ * Check whether the provided name names a root reference. This function only
+ * performs a syntactic check.
+ *
+ * A root ref is a reference that lives in the root of the reference hierarchy.
+ * These references must conform to special syntax:
+ *
+ * - Their name must be all-uppercase or underscores ("_").
+ *
+ * - Their name must end with "_HEAD". As a special rule, "HEAD" is a root
+ * ref, as well.
+ *
+ * - Their name may not contain a slash.
+ *
+ * There is a special set of irregular root refs that exist due to historic
+ * reasons, only. This list shall not be expanded in the future:
+ *
+ * - AUTO_MERGE
+ *
+ * - BISECT_EXPECTED_REV
+ *
+ * - NOTES_MERGE_PARTIAL
+ *
+ * - NOTES_MERGE_REF
+ *
+ * - MERGE_AUTOSTASH
+ */
+int is_root_ref(const char *refname);
+
+/*
+ * Pseudorefs are refs that have different semantics compared to
+ * "normal" refs. These refs can thus not be stored in the ref backend,
+ * but must always be accessed via the filesystem. The following refs
+ * are pseudorefs:
+ *
+ * - FETCH_HEAD may contain multiple object IDs, and each one of them
+ * carries additional metadata like where it came from.
+ *
+ * - MERGE_HEAD may contain multiple object IDs when merging multiple
+ * heads.
+ *
+ * Reading, writing or deleting references must consistently go either
+ * through the filesystem (pseudorefs) or through the reference
+ * backend (normal ones).
+ */
+int is_pseudo_ref(const char *refname);
+
+/*
+ * The following flags can be passed to `repo_migrate_ref_storage_format()`:
+ *
+ * - REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN: perform a dry-run migration
+ * without touching the main repository. The result will be written into a
+ * temporary ref storage directory.
+ */
+#define REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN (1 << 0)
+
+/*
+ * Migrate the ref storage format used by the repository to the
+ * specified one.
+ */
+int repo_migrate_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format,
+ unsigned int flags,
+ struct strbuf *err);
+
+/*
+ * The following functions have been removed in Git v2.45 in favor of functions
+ * that receive a `ref_store` as parameter. The intent of this section is
+ * merely to help patch authors of in-flight series to have a reference what
+ * they should be migrating to. The section will be removed in Git v2.46.
+ */
+#if 0
+static char *resolve_refdup(const char *refname, int resolve_flags,
+ struct object_id *oid, int *flags)
+{
+ return refs_resolve_refdup(get_main_ref_store(the_repository),
+ refname, resolve_flags,
+ oid, flags);
+}
+
+static int read_ref_full(const char *refname, int resolve_flags,
+ struct object_id *oid, int *flags)
+{
+ return refs_read_ref_full(get_main_ref_store(the_repository), refname,
+ resolve_flags, oid, flags);
+}
+
+static int read_ref(const char *refname, struct object_id *oid)
+{
+ return refs_read_ref(get_main_ref_store(the_repository), refname, oid);
+}
+
+static int ref_exists(const char *refname)
+{
+ return refs_ref_exists(get_main_ref_store(the_repository), refname);
+}
+
+static int for_each_tag_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_tag_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_branch_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_branch_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_remote_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_remote_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+{
+ return refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ fn, cb_data);
+}
+
+static int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
+ const char *prefix, void *cb_data)
+{
+ return refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ fn, pattern, prefix, cb_data);
+}
+
+static int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+{
+ return refs_for_each_glob_ref(get_main_ref_store(the_repository),
+ fn, pattern, cb_data);
+}
+
+static int delete_ref(const char *msg, const char *refname,
+ const struct object_id *old_oid, unsigned int flags)
+{
+ return refs_delete_ref(get_main_ref_store(the_repository), msg, refname,
+ old_oid, flags);
+}
+
+static struct ref_transaction *ref_transaction_begin(struct strbuf *err)
+{
+ return ref_store_transaction_begin(get_main_ref_store(the_repository), err);
+}
+
+static int update_ref(const char *msg, const char *refname,
+ const struct object_id *new_oid,
+ const struct object_id *old_oid,
+ unsigned int flags, enum action_on_err onerr)
+{
+ return refs_update_ref(get_main_ref_store(the_repository), msg, refname, new_oid,
+ old_oid, flags, onerr);
+}
+
+static char *shorten_unambiguous_ref(const char *refname, int strict)
+{
+ return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname, strict);
+}
+
+static int head_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_ref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_ref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data);
+}
+
+static int for_each_fullref_in(const char *prefix,
+ const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ prefix, exclude_patterns, fn, cb_data);
+}
+
+static int for_each_namespaced_ref(const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
+ exclude_patterns, fn, cb_data);
+}
+
+static int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+ struct object_id *oid, int *flags)
+{
+ return refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname,
+ resolve_flags, oid, flags);
+}
+
+static int create_symref(const char *ref_target, const char *refs_heads_master,
+ const char *logmsg)
+{
+ return refs_create_symref(get_main_ref_store(the_repository), ref_target,
+ refs_heads_master, logmsg);
+}
+
+static int for_each_reflog(each_reflog_fn fn, void *cb_data)
+{
+ return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
+}
+
+static int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ return refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
+ refname, fn, cb_data);
+}
+
+static int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ return refs_for_each_reflog_ent(get_main_ref_store(the_repository), refname,
+ fn, cb_data);
+}
+
+static int reflog_exists(const char *refname)
+{
+ return refs_reflog_exists(get_main_ref_store(the_repository), refname);
+}
+
+static int safe_create_reflog(const char *refname, struct strbuf *err)
+{
+ return refs_create_reflog(get_main_ref_store(the_repository), refname,
+ err);
+}
+
+static int delete_reflog(const char *refname)
+{
+ return refs_delete_reflog(get_main_ref_store(the_repository), refname);
+}
+
+static int reflog_expire(const char *refname,
+ 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 refs_reflog_expire(get_main_ref_store(the_repository),
+ refname, flags,
+ prepare_fn, should_prune_fn,
+ cleanup_fn, policy_cb_data);
+}
+
+static int delete_refs(const char *msg, struct string_list *refnames,
+ unsigned int flags)
+{
+ return refs_delete_refs(get_main_ref_store(the_repository), msg, refnames, flags);
+}
+
+static int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+ return refs_rename_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+}
+
+static int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+ return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+}
+#endif
#endif /* REFS_H */
diff --git a/refs/debug.c b/refs/debug.c
index c7531b1..547d924 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -33,11 +33,18 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor
return (struct ref_store *)res;
}
-static int debug_init_db(struct ref_store *refs, int flags, struct strbuf *err)
+static void debug_release(struct ref_store *refs)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
- int res = drefs->refs->be->init_db(drefs->refs, flags, err);
- trace_printf_key(&trace_refs, "init_db: %d\n", res);
+ drefs->refs->be->release(drefs->refs);
+ trace_printf_key(&trace_refs, "release\n");
+}
+
+static int debug_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
+ int res = drefs->refs->be->create_on_disk(drefs->refs, flags, err);
+ trace_printf_key(&trace_refs, "create_on_disk: %d\n", res);
return res;
}
@@ -131,18 +138,6 @@ static int debug_pack_refs(struct ref_store *ref_store, struct pack_refs_opts *o
return res;
}
-static int debug_create_symref(struct ref_store *ref_store,
- const char *ref_name, const char *target,
- const char *logmsg)
-{
- struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
- int res = drefs->refs->be->create_symref(drefs->refs, ref_name, target,
- logmsg);
- trace_printf_key(&trace_refs, "create_symref: %s -> %s \"%s\": %d\n", ref_name,
- target, logmsg, res);
- return res;
-}
-
static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
const char *newref, const char *logmsg)
{
@@ -427,7 +422,8 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
struct ref_storage_be refs_be_debug = {
.name = "debug",
.init = NULL,
- .init_db = debug_init_db,
+ .release = debug_release,
+ .create_on_disk = debug_create_on_disk,
/*
* None of these should be NULL. If the "files" backend (in
@@ -441,7 +437,6 @@ struct ref_storage_be refs_be_debug = {
.initial_transaction_commit = debug_initial_transaction_commit,
.pack_refs = debug_pack_refs,
- .create_symref = debug_create_symref,
.rename_ref = debug_rename_ref,
.copy_ref = debug_copy_ref,
diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14..aa52d9b 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "../git-compat-util.h"
#include "../copy.h"
#include "../environment.h"
@@ -89,9 +91,9 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
* Create a new submodule ref cache and add it to the internal
* set of caches.
*/
-static struct ref_store *files_ref_store_create(struct repository *repo,
- const char *gitdir,
- unsigned int flags)
+static struct ref_store *files_ref_store_init(struct repository *repo,
+ const char *gitdir,
+ unsigned int flags)
{
struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
@@ -102,7 +104,7 @@ static struct ref_store *files_ref_store_create(struct repository *repo,
get_common_dir_noenv(&sb, gitdir);
refs->gitcommondir = strbuf_detach(&sb, NULL);
refs->packed_ref_store =
- packed_ref_store_create(repo, refs->gitcommondir, flags);
+ packed_ref_store_init(repo, refs->gitcommondir, flags);
chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
chdir_notify_reparent("files-backend $GIT_COMMONDIR",
@@ -149,6 +151,14 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
return refs;
}
+static void files_ref_store_release(struct ref_store *ref_store)
+{
+ struct files_ref_store *refs = files_downcast(ref_store, 0, "release");
+ free_ref_cache(refs->loose);
+ free(refs->gitcommondir);
+ ref_store_release(refs->packed_ref_store);
+}
+
static void files_reflog_path(struct files_ref_store *refs,
struct strbuf *sb,
const char *refname)
@@ -238,7 +248,7 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,
if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,
&oid, &flag)) {
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
flag |= REF_ISBROKEN;
} else if (is_null_oid(&oid)) {
/*
@@ -255,7 +265,7 @@ static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(refname))
die("loose refname is dangerous: %s", refname);
- oidclr(&oid);
+ oidclr(&oid, the_repository->hash_algo);
flag |= REF_BAD_NAME | REF_ISBROKEN;
}
add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));
@@ -315,19 +325,15 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
add_per_worktree_entries_to_dir(dir, dirname);
}
-/*
- * Add pseudorefs to the ref dir by parsing the directory for any files
- * which follow the pseudoref syntax.
- */
-static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
- struct ref_dir *dir,
- const char *dirname)
+static int for_each_root_ref(struct files_ref_store *refs,
+ int (*cb)(const char *refname, void *cb_data),
+ void *cb_data)
{
- struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT;
+ const char *dirname = refs->loose->root->name;
struct dirent *de;
size_t dirnamelen;
+ int ret;
DIR *d;
files_ref_path(refs, &path, dirname);
@@ -335,7 +341,7 @@ static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
d = opendir(path.buf);
if (!d) {
strbuf_release(&path);
- return;
+ return -1;
}
strbuf_addstr(&refname, dirname);
@@ -351,15 +357,49 @@ static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
strbuf_addstr(&refname, de->d_name);
dtype = get_dtype(de, &path, 1);
- if (dtype == DT_REG && (is_pseudoref(ref_store, de->d_name) ||
- is_headref(ref_store, de->d_name)))
- loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
+ if (dtype == DT_REG && is_root_ref(de->d_name)) {
+ ret = cb(refname.buf, cb_data);
+ if (ret)
+ goto done;
+ }
strbuf_setlen(&refname, dirnamelen);
}
+
+ ret = 0;
+
+done:
strbuf_release(&refname);
strbuf_release(&path);
closedir(d);
+ return ret;
+}
+
+struct fill_root_ref_data {
+ struct files_ref_store *refs;
+ struct ref_dir *dir;
+};
+
+static int fill_root_ref(const char *refname, void *cb_data)
+{
+ struct fill_root_ref_data *data = cb_data;
+ loose_fill_ref_dir_regular_file(data->refs, refname, data->dir);
+ return 0;
+}
+
+/*
+ * Add root refs to the ref dir by parsing the directory for any files which
+ * follow the root ref syntax.
+ */
+static void add_root_refs(struct files_ref_store *refs,
+ struct ref_dir *dir)
+{
+ struct fill_root_ref_data data = {
+ .refs = refs,
+ .dir = dir,
+ };
+
+ for_each_root_ref(refs, fill_root_ref, &data);
}
static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
@@ -381,8 +421,7 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
dir = get_ref_dir(refs->loose->root);
if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS)
- add_pseudoref_and_head_entries(dir->cache->ref_store, dir,
- refs->loose->root->name);
+ add_root_refs(refs, dir);
/*
* Add an incomplete entry for "refs/" (to be filled
@@ -794,8 +833,10 @@ static int lock_raw_ref(struct files_ref_store *refs,
*/
if (refs_verify_refname_available(
refs->packed_ref_store, refname,
- extras, NULL, err))
+ extras, NULL, err)) {
+ ret = TRANSACTION_NAME_CONFLICT;
goto error_return;
+ }
}
ret = 0;
@@ -1111,7 +1152,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
if (!refs_resolve_ref_unsafe(&refs->base, lock->ref_name, 0,
&lock->old_oid, NULL))
- oidclr(&lock->old_oid);
+ oidclr(&lock->old_oid, the_repository->hash_algo);
goto out;
error_return:
@@ -1198,7 +1239,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
ref_transaction_add_update(
transaction, r->name,
REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
- null_oid(), &r->oid, NULL);
+ null_oid(), &r->oid, NULL, NULL, NULL);
if (ref_transaction_commit(transaction, &err))
goto cleanup;
@@ -1229,7 +1270,8 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
/*
* Return true if the specified reference should be packed.
*/
-static int should_pack_ref(const char *refname,
+static int should_pack_ref(struct files_ref_store *refs,
+ const char *refname,
const struct object_id *oid, unsigned int ref_flags,
struct pack_refs_opts *opts)
{
@@ -1245,7 +1287,7 @@ static int should_pack_ref(const char *refname,
return 0;
/* Do not pack broken refs: */
- if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags))
+ if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags))
return 0;
if (ref_excluded(opts->exclusions, refname))
@@ -1277,14 +1319,14 @@ static int files_pack_refs(struct ref_store *ref_store,
packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
- the_repository, 0);
+ refs->base.repo, 0);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
/*
* If the loose reference can be packed, add an entry
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
- if (!should_pack_ref(iter->refname, iter->oid, iter->flags, opts))
+ if (!should_pack_ref(refs, iter->refname, iter->oid, iter->flags, opts))
continue;
/*
@@ -1292,7 +1334,7 @@ static int files_pack_refs(struct ref_store *ref_store,
* packed-refs transaction:
*/
if (ref_transaction_update(transaction, iter->refname,
- iter->oid, NULL,
+ iter->oid, NULL, NULL, NULL,
REF_NO_DEREF, NULL, &err))
die("failure preparing to create packed reference %s: %s",
iter->refname, err.buf);
@@ -1381,7 +1423,8 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
return ret;
}
-static int write_ref_to_lockfile(struct ref_lock *lock,
+static int write_ref_to_lockfile(struct files_ref_store *refs,
+ struct ref_lock *lock,
const struct object_id *oid,
int skip_oid_verification, struct strbuf *err);
static int commit_ref_update(struct files_ref_store *refs,
@@ -1529,7 +1572,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
}
oidcpy(&lock->old_oid, &orig_oid);
- if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) ||
+ if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) ||
commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) {
error("unable to write current sha1 into %s: %s", newrefname, err.buf);
strbuf_release(&err);
@@ -1549,7 +1592,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
flag = log_all_ref_updates;
log_all_ref_updates = LOG_REFS_NONE;
- if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) ||
+ if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) ||
commit_ref_update(refs, lock, &orig_oid, NULL, &err)) {
error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
strbuf_release(&err);
@@ -1741,6 +1784,9 @@ static int files_log_ref_write(struct files_ref_store *refs,
{
int logfd, result;
+ if (flags & REF_SKIP_CREATE_REFLOG)
+ return 0;
+
if (log_all_ref_updates == LOG_REFS_UNSET)
log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
@@ -1783,7 +1829,8 @@ static int files_log_ref_write(struct files_ref_store *refs,
* Write oid into the open lockfile, then close the lockfile. On
* errors, rollback the lockfile, fill in *err and return -1.
*/
-static int write_ref_to_lockfile(struct ref_lock *lock,
+static int write_ref_to_lockfile(struct files_ref_store *refs,
+ struct ref_lock *lock,
const struct object_id *oid,
int skip_oid_verification, struct strbuf *err)
{
@@ -1792,7 +1839,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
int fd;
if (!skip_oid_verification) {
- o = parse_object(the_repository, oid);
+ o = parse_object(refs->base.repo, oid);
if (!o) {
strbuf_addf(
err,
@@ -1811,7 +1858,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
}
}
fd = get_lock_file_fd(&lock->lk);
- if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
+ if (write_in_full(fd, oid_to_hex(oid), refs->base.repo->hash_algo->hexsz) < 0 ||
write_in_full(fd, &term, 1) < 0 ||
fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&lock->lk)) < 0 ||
close_ref_gently(lock) < 0) {
@@ -1903,66 +1950,23 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
return ret;
}
-static void update_symref_reflog(struct files_ref_store *refs,
- struct ref_lock *lock, const char *refname,
- const char *target, const char *logmsg)
+static int create_symref_lock(struct files_ref_store *refs,
+ struct ref_lock *lock, const char *refname,
+ const char *target, struct strbuf *err)
{
- struct strbuf err = STRBUF_INIT;
- struct object_id new_oid;
-
- if (logmsg &&
- refs_resolve_ref_unsafe(&refs->base, target,
- RESOLVE_REF_READING, &new_oid, NULL) &&
- files_log_ref_write(refs, refname, &lock->old_oid,
- &new_oid, logmsg, 0, &err)) {
- error("%s", err.buf);
- strbuf_release(&err);
- }
-}
-
-static int create_symref_locked(struct files_ref_store *refs,
- struct ref_lock *lock, const char *refname,
- const char *target, const char *logmsg)
-{
- if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
- update_symref_reflog(refs, lock, refname, target, logmsg);
- return 0;
- }
-
- if (!fdopen_lock_file(&lock->lk, "w"))
- return error("unable to fdopen %s: %s",
+ if (!fdopen_lock_file(&lock->lk, "w")) {
+ strbuf_addf(err, "unable to fdopen %s: %s",
get_lock_file_path(&lock->lk), strerror(errno));
-
- update_symref_reflog(refs, lock, refname, target, logmsg);
-
- /* no error check; commit_ref will check ferror */
- fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target);
- if (commit_ref(lock) < 0)
- return error("unable to write symref for %s: %s", refname,
- strerror(errno));
- return 0;
-}
-
-static int files_create_symref(struct ref_store *ref_store,
- const char *refname, const char *target,
- const char *logmsg)
-{
- struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_WRITE, "create_symref");
- struct strbuf err = STRBUF_INIT;
- struct ref_lock *lock;
- int ret;
-
- lock = lock_ref_oid_basic(refs, refname, &err);
- if (!lock) {
- error("%s", err.buf);
- strbuf_release(&err);
return -1;
}
- ret = create_symref_locked(refs, lock, refname, target, logmsg);
- unlock_ref(lock);
- return ret;
+ if (fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target) < 0) {
+ strbuf_addf(err, "unable to write to %s: %s",
+ get_lock_file_path(&lock->lk), strerror(errno));
+ return -1;
+ }
+
+ return 0;
}
static int files_reflog_exists(struct ref_store *ref_store,
@@ -2284,6 +2288,7 @@ static int split_head_update(struct ref_update *update,
struct ref_update *new_update;
if ((update->flags & REF_LOG_ONLY) ||
+ (update->flags & REF_SKIP_CREATE_REFLOG) ||
(update->flags & REF_IS_PRUNING) ||
(update->flags & REF_UPDATE_VIA_HEAD))
return 0;
@@ -2309,7 +2314,7 @@ static int split_head_update(struct ref_update *update,
transaction, "HEAD",
update->flags | REF_LOG_ONLY | REF_NO_DEREF,
&update->new_oid, &update->old_oid,
- update->msg);
+ NULL, NULL, update->msg);
/*
* Add "HEAD". This insertion is O(N) in the transaction
@@ -2371,8 +2376,9 @@ static int split_symref_update(struct ref_update *update,
new_update = ref_transaction_add_update(
transaction, referent, new_flags,
- &update->new_oid, &update->old_oid,
- update->msg);
+ update->new_target ? NULL : &update->new_oid,
+ update->old_target ? NULL : &update->old_oid,
+ update->new_target, update->old_target, update->msg);
new_update->parent_update = update;
@@ -2401,17 +2407,6 @@ static int split_symref_update(struct ref_update *update,
}
/*
- * Return the refname under which update was originally requested.
- */
-static const char *original_update_refname(struct ref_update *update)
-{
- while (update->parent_update)
- update = update->parent_update;
-
- return update->refname;
-}
-
-/*
* Check whether the REF_HAVE_OLD and old_oid values stored in update
* are consistent with oid, which is the reference's current value. If
* everything is OK, return 0; otherwise, write an error message to
@@ -2427,16 +2422,16 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
if (is_null_oid(&update->old_oid))
strbuf_addf(err, "cannot lock ref '%s': "
"reference already exists",
- original_update_refname(update));
+ ref_update_original_update_refname(update));
else if (is_null_oid(oid))
strbuf_addf(err, "cannot lock ref '%s': "
"reference is missing but expected %s",
- original_update_refname(update),
+ ref_update_original_update_refname(update),
oid_to_hex(&update->old_oid));
else
strbuf_addf(err, "cannot lock ref '%s': "
"is at %s but expected %s",
- original_update_refname(update),
+ ref_update_original_update_refname(update),
oid_to_hex(oid),
oid_to_hex(&update->old_oid));
@@ -2464,14 +2459,13 @@ static int lock_ref_for_update(struct files_ref_store *refs,
struct strbuf *err)
{
struct strbuf referent = STRBUF_INIT;
- int mustexist = (update->flags & REF_HAVE_OLD) &&
- !is_null_oid(&update->old_oid);
+ int mustexist = ref_update_expects_existing_old_ref(update);
int ret = 0;
struct ref_lock *lock;
files_assert_main_repository(refs, "lock_ref_for_update");
- if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
+ if ((update->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(update))
update->flags |= REF_DELETING;
if (head_ref) {
@@ -2490,7 +2484,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
reason = strbuf_detach(err, NULL);
strbuf_addf(err, "cannot lock ref '%s': %s",
- original_update_refname(update), reason);
+ ref_update_original_update_refname(update), reason);
free(reason);
goto out;
}
@@ -2510,11 +2504,18 @@ static int lock_ref_for_update(struct files_ref_store *refs,
if (update->flags & REF_HAVE_OLD) {
strbuf_addf(err, "cannot lock ref '%s': "
"error reading reference",
- original_update_refname(update));
+ ref_update_original_update_refname(update));
ret = TRANSACTION_GENERIC_ERROR;
goto out;
}
- } else if (check_old_oid(update, &lock->old_oid, err)) {
+ }
+
+ if (update->old_target) {
+ if (ref_update_check_old_target(referent.buf, update, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ }
+ } else if (check_old_oid(update, &lock->old_oid, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto out;
}
@@ -2535,7 +2536,19 @@ static int lock_ref_for_update(struct files_ref_store *refs,
} else {
struct ref_update *parent_update;
- if (check_old_oid(update, &lock->old_oid, err)) {
+ /*
+ * Even if the ref is a regular ref, if `old_target` is set, we
+ * fail with an error.
+ */
+ if (update->old_target) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "expected symref with target '%s': "
+ "but is a regular ref"),
+ ref_update_original_update_refname(update),
+ update->old_target);
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ } else if (check_old_oid(update, &lock->old_oid, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto out;
}
@@ -2553,9 +2566,28 @@ static int lock_ref_for_update(struct files_ref_store *refs,
}
}
- if ((update->flags & REF_HAVE_NEW) &&
- !(update->flags & REF_DELETING) &&
- !(update->flags & REF_LOG_ONLY)) {
+ if (update->new_target && !(update->flags & REF_LOG_ONLY)) {
+ if (create_symref_lock(refs, lock, update->refname,
+ update->new_target, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ }
+
+ if (close_ref_gently(lock)) {
+ strbuf_addf(err, "couldn't close '%s.lock'",
+ update->refname);
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ }
+
+ /*
+ * Once we have created the symref lock, the commit
+ * phase of the transaction only needs to commit the lock.
+ */
+ update->flags |= REF_NEEDS_COMMIT;
+ } else if ((update->flags & REF_HAVE_NEW) &&
+ !(update->flags & REF_DELETING) &&
+ !(update->flags & REF_LOG_ONLY)) {
if (!(update->type & REF_ISSYMREF) &&
oideq(&lock->old_oid, &update->new_oid)) {
/*
@@ -2563,7 +2595,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* value, so we don't need to write it.
*/
} else if (write_ref_to_lockfile(
- lock, &update->new_oid,
+ refs, lock, &update->new_oid,
update->flags & REF_SKIP_OID_VERIFICATION,
err)) {
char *write_err = strbuf_detach(err, NULL);
@@ -2763,7 +2795,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
packed_transaction, update->refname,
REF_HAVE_NEW | REF_NO_DEREF,
&update->new_oid, NULL,
- NULL);
+ NULL, NULL, NULL);
}
}
@@ -2818,6 +2850,43 @@ static int files_transaction_prepare(struct ref_store *ref_store,
return ret;
}
+static int parse_and_write_reflog(struct files_ref_store *refs,
+ struct ref_update *update,
+ struct ref_lock *lock,
+ struct strbuf *err)
+{
+ if (update->new_target) {
+ /*
+ * We want to get the resolved OID for the target, to ensure
+ * that the correct value is added to the reflog.
+ */
+ if (!refs_resolve_ref_unsafe(&refs->base, update->new_target,
+ RESOLVE_REF_READING,
+ &update->new_oid, NULL)) {
+ /*
+ * TODO: currently we skip creating reflogs for dangling
+ * symref updates. It would be nice to capture this as
+ * zero oid updates however.
+ */
+ return 0;
+ }
+ }
+
+ if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
+ &update->new_oid, update->msg, update->flags, err)) {
+ char *old_msg = strbuf_detach(err, NULL);
+
+ strbuf_addf(err, "cannot update the ref '%s': %s",
+ lock->ref_name, old_msg);
+ free(old_msg);
+ unlock_ref(lock);
+ update->backend_data = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
static int files_transaction_finish(struct ref_store *ref_store,
struct ref_transaction *transaction,
struct strbuf *err)
@@ -2848,23 +2917,20 @@ static int files_transaction_finish(struct ref_store *ref_store,
if (update->flags & REF_NEEDS_COMMIT ||
update->flags & REF_LOG_ONLY) {
- if (files_log_ref_write(refs,
- lock->ref_name,
- &lock->old_oid,
- &update->new_oid,
- update->msg, update->flags,
- err)) {
- char *old_msg = strbuf_detach(err, NULL);
-
- strbuf_addf(err, "cannot update the ref '%s': %s",
- lock->ref_name, old_msg);
- free(old_msg);
- unlock_ref(lock);
- update->backend_data = NULL;
+ if (parse_and_write_reflog(refs, update, lock, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
}
+
+ /*
+ * We try creating a symlink, if that succeeds we continue to the
+ * next update. If not, we try and create a regular symref.
+ */
+ if (update->new_target && prefer_symlink_refs)
+ if (!create_ref_symlink(lock, update->new_target))
+ continue;
+
if (update->flags & REF_NEEDS_COMMIT) {
clear_loose_ref_cache(refs);
if (commit_ref(lock)) {
@@ -3048,7 +3114,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
ref_transaction_add_update(packed_transaction, update->refname,
update->flags & ~REF_HAVE_OLD,
&update->new_oid, &update->old_oid,
- NULL);
+ NULL, NULL, NULL);
}
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
@@ -3212,7 +3278,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
rollback_lock_file(&reflog_lock);
} else if (update &&
(write_in_full(get_lock_file_fd(&lock->lk),
- oid_to_hex(&cb.last_kept_oid), the_hash_algo->hexsz) < 0 ||
+ oid_to_hex(&cb.last_kept_oid), refs->base.repo->hash_algo->hexsz) < 0 ||
write_str_in_full(get_lock_file_fd(&lock->lk), "\n") < 0 ||
close_ref_gently(lock) < 0)) {
status |= error("couldn't write %s",
@@ -3236,12 +3302,12 @@ static int files_reflog_expire(struct ref_store *ref_store,
return -1;
}
-static int files_init_db(struct ref_store *ref_store,
- int flags,
- struct strbuf *err UNUSED)
+static int files_ref_store_create_on_disk(struct ref_store *ref_store,
+ int flags,
+ struct strbuf *err UNUSED)
{
struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_WRITE, "init_db");
+ files_downcast(ref_store, REF_STORE_WRITE, "create");
struct strbuf sb = STRBUF_INIT;
/*
@@ -3264,7 +3330,7 @@ static int files_init_db(struct ref_store *ref_store,
* There is no need to create directories for common refs when creating
* a worktree ref store.
*/
- if (!(flags & REFS_INIT_DB_IS_WORKTREE)) {
+ if (!(flags & REF_STORE_CREATE_ON_DISK_IS_WORKTREE)) {
/*
* Create .git/refs/{heads,tags}
*/
@@ -3281,17 +3347,80 @@ static int files_init_db(struct ref_store *ref_store,
return 0;
}
+struct remove_one_root_ref_data {
+ const char *gitdir;
+ struct strbuf *err;
+};
+
+static int remove_one_root_ref(const char *refname,
+ void *cb_data)
+{
+ struct remove_one_root_ref_data *data = cb_data;
+ struct strbuf buf = STRBUF_INIT;
+ int ret = 0;
+
+ strbuf_addf(&buf, "%s/%s", data->gitdir, refname);
+
+ ret = unlink(buf.buf);
+ if (ret < 0)
+ strbuf_addf(data->err, "could not delete %s: %s\n",
+ refname, strerror(errno));
+
+ strbuf_release(&buf);
+ return ret;
+}
+
+static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct files_ref_store *refs =
+ files_downcast(ref_store, REF_STORE_WRITE, "remove");
+ struct remove_one_root_ref_data data = {
+ .gitdir = refs->base.gitdir,
+ .err = err,
+ };
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+
+ strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete refs: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/logs", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete logs: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ if (for_each_root_ref(refs, remove_one_root_ref, &data) < 0)
+ ret = -1;
+
+ if (ref_store_remove_on_disk(refs->packed_ref_store, err) < 0)
+ ret = -1;
+
+ strbuf_release(&sb);
+ return ret;
+}
+
struct ref_storage_be refs_be_files = {
.name = "files",
- .init = files_ref_store_create,
- .init_db = files_init_db,
+ .init = files_ref_store_init,
+ .release = files_ref_store_release,
+ .create_on_disk = files_ref_store_create_on_disk,
+ .remove_on_disk = files_ref_store_remove_on_disk,
+
.transaction_prepare = files_transaction_prepare,
.transaction_finish = files_transaction_finish,
.transaction_abort = files_transaction_abort,
.initial_transaction_commit = files_initial_transaction_commit,
.pack_refs = files_pack_refs,
- .create_symref = files_create_symref,
.rename_ref = files_rename_ref,
.copy_ref = files_copy_ref,
diff --git a/refs/iterator.c b/refs/iterator.c
index 9db8b05..d355ebf 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -440,15 +440,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
struct ref_iterator *current_ref_iter = NULL;
-int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
- each_repo_ref_fn fn, void *cb_data)
+int do_for_each_ref_iterator(struct ref_iterator *iter,
+ each_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
struct ref_iterator *old_ref_iter = current_ref_iter;
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
+ retval = fn(iter->refname, iter->oid, iter->flags, cb_data);
if (retval) {
/*
* If ref_iterator_abort() returns ITER_ERROR,
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 4e826c0..a066640 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1,5 +1,8 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "../git-compat-util.h"
#include "../config.h"
+#include "../dir.h"
#include "../gettext.h"
#include "../hash.h"
#include "../hex.h"
@@ -200,9 +203,14 @@ static int release_snapshot(struct snapshot *snapshot)
}
}
-struct ref_store *packed_ref_store_create(struct repository *repo,
- const char *gitdir,
- unsigned int store_flags)
+static size_t snapshot_hexsz(const struct snapshot *snapshot)
+{
+ return snapshot->refs->base.repo->hash_algo->hexsz;
+}
+
+struct ref_store *packed_ref_store_init(struct repository *repo,
+ const char *gitdir,
+ unsigned int store_flags)
{
struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
@@ -252,6 +260,15 @@ static void clear_snapshot(struct packed_ref_store *refs)
}
}
+static void packed_ref_store_release(struct ref_store *ref_store)
+{
+ struct packed_ref_store *refs = packed_downcast(ref_store, 0, "release");
+ clear_snapshot(refs);
+ rollback_lock_file(&refs->lock);
+ delete_tempfile(&refs->tempfile);
+ free(refs->path);
+}
+
static NORETURN void die_unterminated_line(const char *path,
const char *p, size_t len)
{
@@ -280,11 +297,13 @@ struct snapshot_record {
size_t len;
};
-static int cmp_packed_ref_records(const void *v1, const void *v2)
+static int cmp_packed_ref_records(const void *v1, const void *v2,
+ void *cb_data)
{
+ const struct snapshot *snapshot = cb_data;
const struct snapshot_record *e1 = v1, *e2 = v2;
- const char *r1 = e1->start + the_hash_algo->hexsz + 1;
- const char *r2 = e2->start + the_hash_algo->hexsz + 1;
+ const char *r1 = e1->start + snapshot_hexsz(snapshot) + 1;
+ const char *r2 = e2->start + snapshot_hexsz(snapshot) + 1;
while (1) {
if (*r1 == '\n')
@@ -305,9 +324,9 @@ static int cmp_packed_ref_records(const void *v1, const void *v2)
* refname.
*/
static int cmp_record_to_refname(const char *rec, const char *refname,
- int start)
+ int start, const struct snapshot *snapshot)
{
- const char *r1 = rec + the_hash_algo->hexsz + 1;
+ const char *r1 = rec + snapshot_hexsz(snapshot) + 1;
const char *r2 = refname;
while (1) {
@@ -354,7 +373,7 @@ static void sort_snapshot(struct snapshot *snapshot)
if (!eol)
/* The safety check should prevent this. */
BUG("unterminated line found in packed-refs");
- if (eol - pos < the_hash_algo->hexsz + 2)
+ if (eol - pos < snapshot_hexsz(snapshot) + 2)
die_invalid_line(snapshot->refs->path,
pos, eof - pos);
eol++;
@@ -380,7 +399,7 @@ static void sort_snapshot(struct snapshot *snapshot)
if (sorted &&
nr > 1 &&
cmp_packed_ref_records(&records[nr - 2],
- &records[nr - 1]) >= 0)
+ &records[nr - 1], snapshot) >= 0)
sorted = 0;
pos = eol;
@@ -390,7 +409,7 @@ static void sort_snapshot(struct snapshot *snapshot)
goto cleanup;
/* We need to sort the memory. First we sort the records array: */
- QSORT(records, nr, cmp_packed_ref_records);
+ QSORT_S(records, nr, cmp_packed_ref_records, snapshot);
/*
* Allocate a new chunk of memory, and copy the old memory to
@@ -466,7 +485,8 @@ static void verify_buffer_safe(struct snapshot *snapshot)
return;
last_line = find_start_of_record(start, eof - 1);
- if (*(eof - 1) != '\n' || eof - last_line < the_hash_algo->hexsz + 2)
+ if (*(eof - 1) != '\n' ||
+ eof - last_line < snapshot_hexsz(snapshot) + 2)
die_invalid_line(snapshot->refs->path,
last_line, eof - last_line);
}
@@ -561,7 +581,7 @@ static const char *find_reference_location_1(struct snapshot *snapshot,
mid = lo + (hi - lo) / 2;
rec = find_start_of_record(lo, mid);
- cmp = cmp_record_to_refname(rec, refname, start);
+ cmp = cmp_record_to_refname(rec, refname, start, snapshot);
if (cmp < 0) {
lo = find_end_of_record(mid, hi);
} else if (cmp > 0) {
@@ -858,7 +878,7 @@ static int next_record(struct packed_ref_iterator *iter)
iter->base.flags = REF_ISPACKED;
p = iter->pos;
- if (iter->eof - p < the_hash_algo->hexsz + 2 ||
+ if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 2 ||
parse_oid_hex(p, &iter->oid, &p) ||
!isspace(*p++))
die_invalid_line(iter->snapshot->refs->path,
@@ -876,7 +896,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (!refname_is_safe(iter->base.refname))
die("packed refname is dangerous: %s",
iter->base.refname);
- oidclr(&iter->oid);
+ oidclr(&iter->oid, the_repository->hash_algo);
iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN;
}
if (iter->snapshot->peeled == PEELED_FULLY ||
@@ -888,7 +908,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (iter->pos < iter->eof && *iter->pos == '^') {
p = iter->pos + 1;
- if (iter->eof - p < the_hash_algo->hexsz + 1 ||
+ if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 1 ||
parse_oid_hex(p, &iter->peeled, &p) ||
*p++ != '\n')
die_invalid_line(iter->snapshot->refs->path,
@@ -901,13 +921,13 @@ static int next_record(struct packed_ref_iterator *iter)
* we suppress it if the reference is broken:
*/
if ((iter->base.flags & REF_ISBROKEN)) {
- oidclr(&iter->peeled);
+ oidclr(&iter->peeled, the_repository->hash_algo);
iter->base.flags &= ~REF_KNOWS_PEELED;
} else {
iter->base.flags |= REF_KNOWS_PEELED;
}
} else {
- oidclr(&iter->peeled);
+ oidclr(&iter->peeled, the_repository->hash_algo);
}
return ITER_OK;
@@ -944,16 +964,13 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- if (iter->repo != the_repository)
- BUG("peeling for non-the_repository is not supported");
-
if ((iter->base.flags & REF_KNOWS_PEELED)) {
oidcpy(peeled, &iter->peeled);
return is_null_oid(&iter->peeled) ? -1 : 0;
} else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
return -1;
} else {
- return peel_object(&iter->oid, peeled) ? -1 : 0;
+ return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
}
}
@@ -1244,14 +1261,27 @@ int packed_refs_is_locked(struct ref_store *ref_store)
static const char PACKED_REFS_HEADER[] =
"# pack-refs with: peeled fully-peeled sorted \n";
-static int packed_init_db(struct ref_store *ref_store UNUSED,
- int flags UNUSED,
- struct strbuf *err UNUSED)
+static int packed_ref_store_create_on_disk(struct ref_store *ref_store UNUSED,
+ int flags UNUSED,
+ struct strbuf *err UNUSED)
{
/* Nothing to do. */
return 0;
}
+static int packed_ref_store_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct packed_ref_store *refs = packed_downcast(ref_store, 0, "remove");
+
+ if (remove_path(refs->path) < 0) {
+ strbuf_addstr(err, "could not delete packed-refs");
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Write the packed refs from the current snapshot to the packed-refs
* tempfile, incorporating any changes from `updates`. `updates` must
@@ -1412,7 +1442,8 @@ static int write_with_updates(struct packed_ref_store *refs,
i++;
} else {
struct object_id peeled;
- int peel_error = peel_object(&update->new_oid,
+ int peel_error = peel_object(refs->base.repo,
+ &update->new_oid,
&peeled);
if (write_packed_entry(out, update->refname,
@@ -1706,15 +1737,17 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
struct ref_storage_be refs_be_packed = {
.name = "packed",
- .init = packed_ref_store_create,
- .init_db = packed_init_db,
+ .init = packed_ref_store_init,
+ .release = packed_ref_store_release,
+ .create_on_disk = packed_ref_store_create_on_disk,
+ .remove_on_disk = packed_ref_store_remove_on_disk,
+
.transaction_prepare = packed_transaction_prepare,
.transaction_finish = packed_transaction_finish,
.transaction_abort = packed_transaction_abort,
.initial_transaction_commit = packed_initial_transaction_commit,
.pack_refs = packed_pack_refs,
- .create_symref = NULL,
.rename_ref = NULL,
.copy_ref = NULL,
diff --git a/refs/packed-backend.h b/refs/packed-backend.h
index 9dd8a34..09437ad 100644
--- a/refs/packed-backend.h
+++ b/refs/packed-backend.h
@@ -13,9 +13,9 @@ struct ref_transaction;
* even among packed refs.
*/
-struct ref_store *packed_ref_store_create(struct repository *repo,
- const char *gitdir,
- unsigned int store_flags);
+struct ref_store *packed_ref_store_init(struct repository *repo,
+ const char *gitdir,
+ unsigned int store_flags);
/*
* Lock the packed-refs file for writing. Flags is passed to
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 9f97972..4ce519b 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -71,6 +71,8 @@ static void free_ref_entry(struct ref_entry *entry)
void free_ref_cache(struct ref_cache *cache)
{
+ if (!cache)
+ return;
free_ref_entry(cache->root);
free(cache);
}
@@ -441,10 +443,7 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
-
- if (iter->repo != the_repository)
- BUG("peeling for non-the_repository is not supported");
- return peel_object(ref_iterator->oid, peeled) ? -1 : 0;
+ return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
}
static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
index 95c76e2..31ebe24 100644
--- a/refs/ref-cache.h
+++ b/refs/ref-cache.h
@@ -1,7 +1,7 @@
#ifndef REFS_REF_CACHE_H
#define REFS_REF_CACHE_H
-#include "hash-ll.h"
+#include "hash.h"
struct ref_dir;
struct ref_store;
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 56641aa..fa975d6 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -69,40 +69,6 @@ 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,
-
- /*
- * object cannot be peeled because the named object (or an
- * object referred to by a tag in the peel chain), does not
- * exist.
- */
- PEEL_INVALID = -1,
-
- /* object cannot be peeled because it is not a tag: */
- PEEL_NON_TAG = -2,
-
- /* ref_entry contains no peeled value because it is a symref: */
- PEEL_IS_SYMREF = -3,
-
- /*
- * ref_entry cannot be peeled because it is broken (i.e., the
- * symbolic reference cannot even be resolved to an object
- * name):
- */
- PEEL_BROKEN = -4
-};
-
-/*
- * Peel the named object; i.e., if the object is a tag, resolve the
- * tag recursively until a non-tag is found. If successful, store the
- * result to oid and return PEEL_PEELED. If the object is not a tag
- * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
- * and leave oid unchanged.
- */
-enum peel_status peel_object(const struct object_id *name, struct object_id *oid);
-
/**
* Information needed for a single ref update. Set new_oid to the new
* value or to null_oid to delete the ref. To check the old value
@@ -125,6 +91,19 @@ struct ref_update {
struct object_id old_oid;
/*
+ * If set, point the reference to this value. This can also be
+ * used to convert regular references to become symbolic refs.
+ * Cannot be set together with `new_oid`.
+ */
+ const char *new_target;
+
+ /*
+ * If set, check that the reference previously pointed to this
+ * value. Cannot be set together with `old_oid`.
+ */
+ const char *old_target;
+
+ /*
* One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
* REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
*/
@@ -173,6 +152,7 @@ struct ref_update *ref_transaction_add_update(
const char *refname, unsigned int flags,
const struct object_id *new_oid,
const struct object_id *old_oid,
+ const char *new_target, const char *old_target,
const char *msg);
/*
@@ -503,9 +483,8 @@ extern struct ref_iterator *current_ref_iter;
* adapter between the callback style of reference iteration and the
* iterator style.
*/
-int do_for_each_repo_ref_iterator(struct repository *r,
- struct ref_iterator *iter,
- each_repo_ref_fn fn, void *cb_data);
+int do_for_each_ref_iterator(struct ref_iterator *iter,
+ each_ref_fn fn, void *cb_data);
struct ref_store;
@@ -529,10 +508,20 @@ struct ref_store;
typedef struct ref_store *ref_store_init_fn(struct repository *repo,
const char *gitdir,
unsigned int flags);
+/*
+ * Release all memory and resources associated with the ref store.
+ */
+typedef void ref_store_release_fn(struct ref_store *refs);
-typedef int ref_init_db_fn(struct ref_store *refs,
- int flags,
- struct strbuf *err);
+typedef int ref_store_create_on_disk_fn(struct ref_store *refs,
+ int flags,
+ struct strbuf *err);
+
+/*
+ * Remove the reference store from disk.
+ */
+typedef int ref_store_remove_on_disk_fn(struct ref_store *refs,
+ struct strbuf *err);
typedef int ref_transaction_prepare_fn(struct ref_store *refs,
struct ref_transaction *transaction,
@@ -552,10 +541,6 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
typedef int pack_refs_fn(struct ref_store *ref_store,
struct pack_refs_opts *opts);
-typedef int create_symref_fn(struct ref_store *ref_store,
- const char *ref_target,
- const char *refs_heads_master,
- const char *logmsg);
typedef int rename_ref_fn(struct ref_store *ref_store,
const char *oldref, const char *newref,
const char *logmsg);
@@ -668,7 +653,9 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam
struct ref_storage_be {
const char *name;
ref_store_init_fn *init;
- ref_init_db_fn *init_db;
+ ref_store_release_fn *release;
+ ref_store_create_on_disk_fn *create_on_disk;
+ ref_store_remove_on_disk_fn *remove_on_disk;
ref_transaction_prepare_fn *transaction_prepare;
ref_transaction_finish_fn *transaction_finish;
@@ -676,7 +663,6 @@ struct ref_storage_be {
ref_transaction_commit_fn *initial_transaction_commit;
pack_refs_fn *pack_refs;
- create_symref_fn *create_symref;
rename_ref_fn *rename_ref;
copy_ref_fn *copy_ref;
@@ -700,7 +686,7 @@ extern struct ref_storage_be refs_be_packed;
/*
* A representation of the reference store for the main repository or
* a submodule. The ref_store instances for submodules are kept in a
- * hash map; see get_submodule_ref_store() for more info.
+ * hash map; see repo_get_submodule_ref_store() for more info.
*/
struct ref_store {
/* The backend describing this ref_store's storage scheme: */
@@ -735,4 +721,31 @@ void base_ref_store_init(struct ref_store *refs, struct repository *repo,
*/
struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_store *store);
+/*
+ * Return the refname under which update was originally requested.
+ */
+const char *ref_update_original_update_refname(struct ref_update *update);
+
+/*
+ * Helper function to check if the new value is null, this
+ * takes into consideration that the update could be a regular
+ * ref or a symbolic ref.
+ */
+int ref_update_has_null_new_value(struct ref_update *update);
+
+/*
+ * Check whether the old_target values stored in update are consistent
+ * with the referent, which is the symbolic reference's current value.
+ * If everything is OK, return 0; otherwise, write an error message to
+ * err and return -1.
+ */
+int ref_update_check_old_target(const char *referent, struct ref_update *update,
+ struct strbuf *err);
+
+/*
+ * Check if the ref must exist, this means that the old_oid or
+ * old_target is non NULL.
+ */
+int ref_update_expects_existing_old_ref(struct ref_update *update);
+
#endif /* REFS_REFS_INTERNAL_H */
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1cda48c..fbe74c2 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1,6 +1,10 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "../git-compat-util.h"
#include "../abspath.h"
#include "../chdir-notify.h"
+#include "../config.h"
+#include "../dir.h"
#include "../environment.h"
#include "../gettext.h"
#include "../hash.h"
@@ -15,7 +19,6 @@
#include "../reftable/reftable-record.h"
#include "../reftable/reftable-error.h"
#include "../reftable/reftable-iterator.h"
-#include "../reftable/reftable-merged.h"
#include "../setup.h"
#include "../strmap.h"
#include "parse.h"
@@ -129,7 +132,7 @@ static struct reftable_stack *stack_for(struct reftable_ref_store *store,
store->base.repo->commondir, wtname_buf.buf);
store->err = reftable_new_stack(&stack, wt_dir.buf,
- store->write_options);
+ &store->write_options);
assert(store->err != REFTABLE_API_ERROR);
strmap_put(&store->worktree_stacks, wtname_buf.buf, stack);
}
@@ -172,32 +175,30 @@ static int should_write_log(struct ref_store *refs, const char *refname)
}
}
-static void fill_reftable_log_record(struct reftable_log_record *log)
+static void fill_reftable_log_record(struct reftable_log_record *log, const struct ident_split *split)
{
- const char *info = git_committer_info(0);
- struct ident_split split = {0};
+ const char *tz_begin;
int sign = 1;
- if (split_ident_line(&split, info, strlen(info)))
- BUG("failed splitting committer info");
-
reftable_log_record_release(log);
log->value_type = REFTABLE_LOG_UPDATE;
log->value.update.name =
- xstrndup(split.name_begin, split.name_end - split.name_begin);
+ xstrndup(split->name_begin, split->name_end - split->name_begin);
log->value.update.email =
- xstrndup(split.mail_begin, split.mail_end - split.mail_begin);
- log->value.update.time = atol(split.date_begin);
- if (*split.tz_begin == '-') {
+ xstrndup(split->mail_begin, split->mail_end - split->mail_begin);
+ log->value.update.time = atol(split->date_begin);
+
+ tz_begin = split->tz_begin;
+ if (*tz_begin == '-') {
sign = -1;
- split.tz_begin++;
+ tz_begin++;
}
- if (*split.tz_begin == '+') {
+ if (*tz_begin == '+') {
sign = 1;
- split.tz_begin++;
+ tz_begin++;
}
- log->value.update.tz_offset = sign * atoi(split.tz_begin);
+ log->value.update.tz_offset = sign * atoi(tz_begin);
}
static int read_ref_without_reload(struct reftable_stack *stack,
@@ -218,7 +219,8 @@ static int read_ref_without_reload(struct reftable_stack *stack,
strbuf_addstr(referent, ref.value.symref);
*type |= REF_ISSYMREF;
} else if (reftable_ref_record_val1(&ref)) {
- oidread(oid, reftable_ref_record_val1(&ref));
+ oidread(oid, reftable_ref_record_val1(&ref),
+ the_repository->hash_algo);
} else {
/* We got a tombstone, which should not happen. */
BUG("unhandled reference value type %d", ref.value_type);
@@ -230,6 +232,34 @@ static int read_ref_without_reload(struct reftable_stack *stack,
return ret;
}
+static int reftable_be_config(const char *var, const char *value,
+ const struct config_context *ctx,
+ void *_opts)
+{
+ struct reftable_write_options *opts = _opts;
+
+ if (!strcmp(var, "reftable.blocksize")) {
+ unsigned long block_size = git_config_ulong(var, value, ctx->kvi);
+ if (block_size > 16777215)
+ die("reftable block size cannot exceed 16MB");
+ opts->block_size = block_size;
+ } else if (!strcmp(var, "reftable.restartinterval")) {
+ unsigned long restart_interval = git_config_ulong(var, value, ctx->kvi);
+ if (restart_interval > UINT16_MAX)
+ die("reftable block size cannot exceed %u", (unsigned)UINT16_MAX);
+ opts->restart_interval = restart_interval;
+ } else if (!strcmp(var, "reftable.indexobjects")) {
+ opts->skip_index_objects = !git_config_bool(var, value);
+ } else if (!strcmp(var, "reftable.geometricfactor")) {
+ unsigned long factor = git_config_ulong(var, value, ctx->kvi);
+ if (factor > UINT8_MAX)
+ die("reftable geometric factor cannot exceed %u", (unsigned)UINT8_MAX);
+ opts->auto_compaction_factor = factor;
+ }
+
+ return 0;
+}
+
static struct ref_store *reftable_be_init(struct repository *repo,
const char *gitdir,
unsigned int store_flags)
@@ -245,12 +275,24 @@ static struct ref_store *reftable_be_init(struct repository *repo,
base_ref_store_init(&refs->base, repo, gitdir, &refs_be_reftable);
strmap_init(&refs->worktree_stacks);
refs->store_flags = store_flags;
- refs->write_options.block_size = 4096;
+
refs->write_options.hash_id = repo->hash_algo->format_id;
refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
refs->write_options.disable_auto_compact =
!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
+ git_config(reftable_be_config, &refs->write_options);
+
+ /*
+ * It is somewhat unfortunate that we have to mirror the default block
+ * size of the reftable library here. But given that the write options
+ * wouldn't be updated by the library here, and given that we require
+ * the proper block size to trim reflog message so that they fit, we
+ * must set up a proper value here.
+ */
+ if (!refs->write_options.block_size)
+ refs->write_options.block_size = 4096;
+
/*
* Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
* This stack contains both the shared and the main worktree refs.
@@ -265,7 +307,7 @@ static struct ref_store *reftable_be_init(struct repository *repo,
}
strbuf_addstr(&path, "/reftable");
refs->err = reftable_new_stack(&refs->main_stack, path.buf,
- refs->write_options);
+ &refs->write_options);
if (refs->err)
goto done;
@@ -282,7 +324,7 @@ static struct ref_store *reftable_be_init(struct repository *repo,
strbuf_addf(&path, "%s/reftable", gitdir);
refs->err = reftable_new_stack(&refs->worktree_stack, path.buf,
- refs->write_options);
+ &refs->write_options);
if (refs->err)
goto done;
}
@@ -295,12 +337,33 @@ static struct ref_store *reftable_be_init(struct repository *repo,
return &refs->base;
}
-static int reftable_be_init_db(struct ref_store *ref_store,
- int flags UNUSED,
- struct strbuf *err UNUSED)
+static void reftable_be_release(struct ref_store *ref_store)
+{
+ struct reftable_ref_store *refs = reftable_be_downcast(ref_store, 0, "release");
+ struct strmap_entry *entry;
+ struct hashmap_iter iter;
+
+ if (refs->main_stack) {
+ reftable_stack_destroy(refs->main_stack);
+ refs->main_stack = NULL;
+ }
+
+ if (refs->worktree_stack) {
+ reftable_stack_destroy(refs->worktree_stack);
+ refs->worktree_stack = NULL;
+ }
+
+ strmap_for_each_entry(&refs->worktree_stacks, &iter, entry)
+ reftable_stack_destroy(entry->value);
+ strmap_clear(&refs->worktree_stacks, 0);
+}
+
+static int reftable_be_create_on_disk(struct ref_store *ref_store,
+ int flags UNUSED,
+ struct strbuf *err UNUSED)
{
struct reftable_ref_store *refs =
- reftable_be_downcast(ref_store, REF_STORE_WRITE, "init_db");
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "create");
struct strbuf sb = STRBUF_INIT;
strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
@@ -324,6 +387,56 @@ static int reftable_be_init_db(struct ref_store *ref_store,
return 0;
}
+static int reftable_be_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "remove");
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+
+ /*
+ * Release the ref store such that all stacks are closed. This is
+ * required so that the "tables.list" file is not open anymore, which
+ * would otherwise make it impossible to remove the file on Windows.
+ */
+ reftable_be_release(ref_store);
+
+ strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete reftables: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir);
+ if (unlink(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete stub HEAD: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir);
+ if (unlink(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete stub heads: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
+ if (rmdir(sb.buf) < 0) {
+ strbuf_addf(err, "could not delete refs directory: %s",
+ strerror(errno));
+ ret = -1;
+ }
+
+ strbuf_release(&sb);
+ return ret;
+}
+
struct reftable_ref_iterator {
struct ref_iterator base;
struct reftable_ref_store *refs;
@@ -356,8 +469,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
*/
if (!starts_with(iter->ref.refname, "refs/") &&
!(iter->flags & DO_FOR_EACH_INCLUDE_ROOT_REFS &&
- (is_pseudoref(&iter->refs->base, iter->ref.refname) ||
- is_headref(&iter->refs->base, iter->ref.refname)))) {
+ is_root_ref(iter->ref.refname))) {
continue;
}
@@ -374,15 +486,17 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
switch (iter->ref.value_type) {
case REFTABLE_REF_VAL1:
- oidread(&iter->oid, iter->ref.value.val1);
+ oidread(&iter->oid, iter->ref.value.val1,
+ the_repository->hash_algo);
break;
case REFTABLE_REF_VAL2:
- oidread(&iter->oid, iter->ref.value.val2.value);
+ oidread(&iter->oid, iter->ref.value.val2.value,
+ the_repository->hash_algo);
break;
case REFTABLE_REF_SYMREF:
if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname,
RESOLVE_REF_READING, &iter->oid, &flags))
- oidclr(&iter->oid);
+ oidclr(&iter->oid, the_repository->hash_algo);
break;
default:
BUG("unhandled reference value type %d", iter->ref.value_type);
@@ -394,7 +508,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (check_refname_format(iter->ref.refname, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(iter->ref.refname))
die(_("refname is dangerous: %s"), iter->ref.refname);
- oidclr(&iter->oid);
+ oidclr(&iter->oid, the_repository->hash_algo);
flags |= REF_BAD_NAME | REF_ISBROKEN;
}
@@ -436,7 +550,8 @@ static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
(struct reftable_ref_iterator *)ref_iterator;
if (iter->ref.value_type == REFTABLE_REF_VAL2) {
- oidread(peeled, iter->ref.value.val2.target_value);
+ oidread(peeled, iter->ref.value.val2.target_value,
+ the_repository->hash_algo);
return 0;
}
@@ -464,7 +579,6 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
const char *prefix,
int flags)
{
- struct reftable_merged_table *merged_table;
struct reftable_ref_iterator *iter;
int ret;
@@ -484,9 +598,8 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
if (ret)
goto done;
- merged_table = reftable_stack_merged_table(stack);
-
- ret = reftable_merged_table_seek_ref(merged_table, &iter->iter, prefix);
+ reftable_stack_init_ref_iterator(stack, &iter->iter);
+ ret = reftable_iterator_seek_ref(&iter->iter, prefix);
if (ret)
goto done;
@@ -581,16 +694,6 @@ static int reftable_be_read_symbolic_ref(struct ref_store *ref_store,
return ret;
}
-/*
- * Return the refname under which update was originally requested.
- */
-static const char *original_update_refname(struct ref_update *update)
-{
- while (update->parent_update)
- update = update->parent_update;
- return update->refname;
-}
-
struct reftable_transaction_update {
struct ref_update *update;
struct object_id current_oid;
@@ -829,7 +932,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
new_update = ref_transaction_add_update(
transaction, "HEAD",
u->flags | REF_LOG_ONLY | REF_NO_DEREF,
- &u->new_oid, &u->old_oid, u->msg);
+ &u->new_oid, &u->old_oid, NULL, NULL, u->msg);
string_list_insert(&affected_refnames, new_update->refname);
}
@@ -837,7 +940,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
¤t_oid, &referent, &u->type);
if (ret < 0)
goto done;
- if (ret > 0 && (!(u->flags & REF_HAVE_OLD) || is_null_oid(&u->old_oid))) {
+ if (ret > 0 && !ref_update_expects_existing_old_ref(u)) {
/*
* The reference does not exist, and we either have no
* old object ID or expect the reference to not exist.
@@ -856,7 +959,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* There is no need to write the reference deletion
* when the reference in question doesn't exist.
*/
- if (u->flags & REF_HAVE_NEW && !is_null_oid(&u->new_oid)) {
+ if ((u->flags & REF_HAVE_NEW) && !ref_update_has_null_new_value(u)) {
ret = queue_transaction_update(refs, tx_data, u,
¤t_oid, err);
if (ret)
@@ -869,7 +972,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
/* The reference does not exist, but we expected it to. */
strbuf_addf(err, _("cannot lock ref '%s': "
"unable to resolve reference '%s'"),
- original_update_refname(u), u->refname);
+ ref_update_original_update_refname(u), u->refname);
ret = -1;
goto done;
}
@@ -907,8 +1010,11 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* intertwined with the locking in files-backend.c.
*/
new_update = ref_transaction_add_update(
- transaction, referent.buf, new_flags,
- &u->new_oid, &u->old_oid, u->msg);
+ transaction, referent.buf, new_flags,
+ u->new_target ? NULL : &u->new_oid,
+ u->old_target ? NULL : &u->old_oid,
+ u->new_target, u->old_target, u->msg);
+
new_update->parent_update = u;
/*
@@ -938,20 +1044,35 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* individual refs. But the error messages match what the files
* backend returns, which keeps our tests happy.
*/
- if (u->flags & REF_HAVE_OLD && !oideq(¤t_oid, &u->old_oid)) {
+ if (u->old_target) {
+ if (!(u->type & REF_ISSYMREF)) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "expected symref with target '%s': "
+ "but is a regular ref"),
+ ref_update_original_update_refname(u),
+ u->old_target);
+ ret = -1;
+ goto done;
+ }
+
+ if (ref_update_check_old_target(referent.buf, u, err)) {
+ ret = -1;
+ goto done;
+ }
+ } else if ((u->flags & REF_HAVE_OLD) && !oideq(¤t_oid, &u->old_oid)) {
if (is_null_oid(&u->old_oid))
strbuf_addf(err, _("cannot lock ref '%s': "
- "reference already exists"),
- original_update_refname(u));
+ "reference already exists"),
+ ref_update_original_update_refname(u));
else if (is_null_oid(¤t_oid))
strbuf_addf(err, _("cannot lock ref '%s': "
- "reference is missing but expected %s"),
- original_update_refname(u),
+ "reference is missing but expected %s"),
+ ref_update_original_update_refname(u),
oid_to_hex(&u->old_oid));
else
strbuf_addf(err, _("cannot lock ref '%s': "
- "is at %s but expected %s"),
- original_update_refname(u),
+ "is at %s but expected %s"),
+ ref_update_original_update_refname(u),
oid_to_hex(¤t_oid),
oid_to_hex(&u->old_oid));
ret = -1;
@@ -1017,13 +1138,17 @@ static int transaction_update_cmp(const void *a, const void *b)
static int write_transaction_table(struct reftable_writer *writer, void *cb_data)
{
struct write_transaction_table_arg *arg = cb_data;
- struct reftable_merged_table *mt =
- reftable_stack_merged_table(arg->stack);
uint64_t ts = reftable_stack_next_update_index(arg->stack);
struct reftable_log_record *logs = NULL;
+ struct ident_split committer_ident = {0};
size_t logs_nr = 0, logs_alloc = 0, i;
+ const char *committer_info;
int ret = 0;
+ committer_info = git_committer_info(0);
+ if (split_ident_line(&committer_ident, committer_info, strlen(committer_info)))
+ BUG("failed splitting committer info");
+
QSORT(arg->updates, arg->updates_nr, transaction_update_cmp);
reftable_writer_set_limits(writer, ts, ts);
@@ -1043,10 +1168,14 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
* - `core.logAllRefUpdates` tells us to create the reflog for
* the given ref.
*/
- if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && is_null_oid(&u->new_oid)) {
+ if ((u->flags & REF_HAVE_NEW) &&
+ !(u->type & REF_ISSYMREF) &&
+ ref_update_has_null_new_value(u)) {
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
+ reftable_stack_init_log_iterator(arg->stack, &it);
+
/*
* When deleting refs we also delete all reflog entries
* with them. While it is not strictly required to
@@ -1056,7 +1185,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
* Unfortunately, we have no better way than to delete
* all reflog entries one by one.
*/
- ret = reftable_merged_table_seek_log(mt, &it, u->refname);
+ ret = reftable_iterator_seek_log(&it, u->refname);
while (ret == 0) {
struct reftable_log_record *tombstone;
@@ -1080,28 +1209,57 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
if (ret)
goto done;
- } else if (u->flags & REF_HAVE_NEW &&
+ } else if (!(u->flags & REF_SKIP_CREATE_REFLOG) &&
+ (u->flags & REF_HAVE_NEW) &&
(u->flags & REF_FORCE_CREATE_REFLOG ||
should_write_log(&arg->refs->base, u->refname))) {
struct reftable_log_record *log;
+ int create_reflog = 1;
- ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
- log = &logs[logs_nr++];
- memset(log, 0, sizeof(*log));
+ if (u->new_target) {
+ if (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target,
+ RESOLVE_REF_READING, &u->new_oid, NULL)) {
+ /*
+ * TODO: currently we skip creating reflogs for dangling
+ * symref updates. It would be nice to capture this as
+ * zero oid updates however.
+ */
+ create_reflog = 0;
+ }
+ }
- fill_reftable_log_record(log);
- log->update_index = ts;
- log->refname = xstrdup(u->refname);
- memcpy(log->value.update.new_hash, u->new_oid.hash, GIT_MAX_RAWSZ);
- memcpy(log->value.update.old_hash, tx_update->current_oid.hash, GIT_MAX_RAWSZ);
- log->value.update.message =
- xstrndup(u->msg, arg->refs->write_options.block_size / 2);
+ if (create_reflog) {
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ log = &logs[logs_nr++];
+ memset(log, 0, sizeof(*log));
+
+ fill_reftable_log_record(log, &committer_ident);
+ log->update_index = ts;
+ log->refname = xstrdup(u->refname);
+ memcpy(log->value.update.new_hash,
+ u->new_oid.hash, GIT_MAX_RAWSZ);
+ memcpy(log->value.update.old_hash,
+ tx_update->current_oid.hash, GIT_MAX_RAWSZ);
+ log->value.update.message =
+ xstrndup(u->msg, arg->refs->write_options.block_size / 2);
+ }
}
if (u->flags & REF_LOG_ONLY)
continue;
- if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
+ if (u->new_target) {
+ struct reftable_ref_record ref = {
+ .refname = (char *)u->refname,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *)u->new_target,
+ .update_index = ts,
+ };
+
+ ret = reftable_writer_add_ref(writer, &ref);
+ if (ret < 0)
+ goto done;
+ } else if ((u->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(u)) {
struct reftable_ref_record ref = {
.refname = (char *)u->refname,
.update_index = ts,
@@ -1119,7 +1277,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
- peel_error = peel_object(&u->new_oid, &peeled);
+ peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
@@ -1227,96 +1385,12 @@ static int reftable_be_pack_refs(struct ref_store *ref_store,
struct write_create_symref_arg {
struct reftable_ref_store *refs;
struct reftable_stack *stack;
+ struct strbuf *err;
const char *refname;
const char *target;
const char *logmsg;
};
-static int write_create_symref_table(struct reftable_writer *writer, void *cb_data)
-{
- struct write_create_symref_arg *create = cb_data;
- uint64_t ts = reftable_stack_next_update_index(create->stack);
- struct reftable_ref_record ref = {
- .refname = (char *)create->refname,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *)create->target,
- .update_index = ts,
- };
- struct reftable_log_record log = {0};
- struct object_id new_oid;
- struct object_id old_oid;
- int ret;
-
- reftable_writer_set_limits(writer, ts, ts);
-
- ret = reftable_writer_add_ref(writer, &ref);
- if (ret)
- return ret;
-
- /*
- * Note that it is important to try and resolve the reference before we
- * write the log entry. This is because `should_write_log()` will munge
- * `core.logAllRefUpdates`, which is undesirable when we create a new
- * repository because it would be written into the config. As HEAD will
- * not resolve for new repositories this ordering will ensure that this
- * never happens.
- */
- if (!create->logmsg ||
- !refs_resolve_ref_unsafe(&create->refs->base, create->target,
- RESOLVE_REF_READING, &new_oid, NULL) ||
- !should_write_log(&create->refs->base, create->refname))
- return 0;
-
- fill_reftable_log_record(&log);
- log.refname = xstrdup(create->refname);
- log.update_index = ts;
- log.value.update.message = xstrndup(create->logmsg,
- create->refs->write_options.block_size / 2);
- memcpy(log.value.update.new_hash, new_oid.hash, GIT_MAX_RAWSZ);
- if (refs_resolve_ref_unsafe(&create->refs->base, create->refname,
- RESOLVE_REF_READING, &old_oid, NULL))
- memcpy(log.value.update.old_hash, old_oid.hash, GIT_MAX_RAWSZ);
-
- ret = reftable_writer_add_log(writer, &log);
- reftable_log_record_release(&log);
- return ret;
-}
-
-static int reftable_be_create_symref(struct ref_store *ref_store,
- const char *refname,
- const char *target,
- const char *logmsg)
-{
- struct reftable_ref_store *refs =
- reftable_be_downcast(ref_store, REF_STORE_WRITE, "create_symref");
- struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct write_create_symref_arg arg = {
- .refs = refs,
- .stack = stack,
- .refname = refname,
- .target = target,
- .logmsg = logmsg,
- };
- int ret;
-
- ret = refs->err;
- if (ret < 0)
- goto done;
-
- ret = reftable_stack_reload(stack);
- if (ret)
- goto done;
-
- ret = reftable_stack_add(stack, &write_create_symref_table, &arg);
-
-done:
- assert(ret != REFTABLE_API_ERROR);
- if (ret)
- error("unable to write symref for %s: %s", refname,
- reftable_error_str(ret));
- return ret;
-}
-
struct write_copy_arg {
struct reftable_ref_store *refs;
struct reftable_stack *stack;
@@ -1330,15 +1404,20 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
{
struct write_copy_arg *arg = cb_data;
uint64_t deletion_ts, creation_ts;
- struct reftable_merged_table *mt = reftable_stack_merged_table(arg->stack);
struct reftable_ref_record old_ref = {0}, refs[2] = {0};
struct reftable_log_record old_log = {0}, *logs = NULL;
struct reftable_iterator it = {0};
struct string_list skip = STRING_LIST_INIT_NODUP;
+ struct ident_split committer_ident = {0};
struct strbuf errbuf = STRBUF_INIT;
size_t logs_nr = 0, logs_alloc = 0, i;
+ const char *committer_info;
int ret;
+ committer_info = git_committer_info(0);
+ if (split_ident_line(&committer_ident, committer_info, strlen(committer_info)))
+ BUG("failed splitting committer info");
+
if (reftable_stack_read_ref(arg->stack, arg->oldname, &old_ref)) {
ret = error(_("refname %s not found"), arg->oldname);
goto done;
@@ -1361,7 +1440,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
/*
* Verify that the new refname is available.
*/
- string_list_insert(&skip, arg->oldname);
+ if (arg->delete_old)
+ string_list_insert(&skip, arg->oldname);
ret = refs_verify_refname_available(&arg->refs->base, arg->newname,
NULL, &skip, &errbuf);
if (ret < 0) {
@@ -1387,10 +1467,10 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
* old reference.
*/
refs[0] = old_ref;
- refs[0].refname = (char *)arg->newname;
+ refs[0].refname = xstrdup(arg->newname);
refs[0].update_index = creation_ts;
if (arg->delete_old) {
- refs[1].refname = (char *)arg->oldname;
+ refs[1].refname = xstrdup(arg->oldname);
refs[1].value_type = REFTABLE_REF_DELETION;
refs[1].update_index = deletion_ts;
}
@@ -1412,8 +1492,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
- fill_reftable_log_record(&logs[logs_nr]);
- logs[logs_nr].refname = (char *)arg->newname;
+ fill_reftable_log_record(&logs[logs_nr], &committer_ident);
+ logs[logs_nr].refname = xstrdup(arg->newname);
logs[logs_nr].update_index = deletion_ts;
logs[logs_nr].value.update.message =
xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1434,7 +1514,13 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
if (append_head_reflog) {
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
logs[logs_nr] = logs[logs_nr - 1];
- logs[logs_nr].refname = "HEAD";
+ logs[logs_nr].refname = xstrdup("HEAD");
+ logs[logs_nr].value.update.name =
+ xstrdup(logs[logs_nr].value.update.name);
+ logs[logs_nr].value.update.email =
+ xstrdup(logs[logs_nr].value.update.email);
+ logs[logs_nr].value.update.message =
+ xstrdup(logs[logs_nr].value.update.message);
logs_nr++;
}
}
@@ -1444,8 +1530,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
*/
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
- fill_reftable_log_record(&logs[logs_nr]);
- logs[logs_nr].refname = (char *)arg->newname;
+ fill_reftable_log_record(&logs[logs_nr], &committer_ident);
+ logs[logs_nr].refname = xstrdup(arg->newname);
logs[logs_nr].update_index = creation_ts;
logs[logs_nr].value.update.message =
xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1457,7 +1543,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
* copy over all log entries from the old reflog. Last but not least,
* when renaming we also have to delete all the old reflog entries.
*/
- ret = reftable_merged_table_seek_log(mt, &it, arg->oldname);
+ reftable_stack_init_log_iterator(arg->stack, &it);
+ ret = reftable_iterator_seek_log(&it, arg->oldname);
if (ret < 0)
goto done;
@@ -1477,7 +1564,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
*/
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
logs[logs_nr] = old_log;
- logs[logs_nr].refname = (char *)arg->newname;
+ logs[logs_nr].refname = xstrdup(arg->newname);
logs_nr++;
/*
@@ -1486,7 +1573,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
if (arg->delete_old) {
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
- logs[logs_nr].refname = (char *)arg->oldname;
+ logs[logs_nr].refname = xstrdup(arg->oldname);
logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
logs[logs_nr].update_index = old_log.update_index;
logs_nr++;
@@ -1509,13 +1596,11 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
reftable_iterator_destroy(&it);
string_list_clear(&skip, 0);
strbuf_release(&errbuf);
- for (i = 0; i < logs_nr; i++) {
- if (!strcmp(logs[i].refname, "HEAD"))
- continue;
- logs[i].refname = NULL;
+ for (i = 0; i < logs_nr; i++)
reftable_log_record_release(&logs[i]);
- }
free(logs);
+ for (i = 0; i < ARRAY_SIZE(refs); i++)
+ reftable_ref_record_release(&refs[i]);
reftable_ref_record_release(&old_ref);
reftable_log_record_release(&old_log);
return ret;
@@ -1663,7 +1748,6 @@ static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,
struct reftable_stack *stack)
{
- struct reftable_merged_table *merged_table;
struct reftable_reflog_iterator *iter;
int ret;
@@ -1680,9 +1764,8 @@ static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftabl
if (ret < 0)
goto done;
- merged_table = reftable_stack_merged_table(stack);
-
- ret = reftable_merged_table_seek_log(merged_table, &iter->iter, "");
+ reftable_stack_init_log_iterator(stack, &iter->iter);
+ ret = reftable_iterator_seek_log(&iter->iter, "");
if (ret < 0)
goto done;
@@ -1714,8 +1797,8 @@ static int yield_log_record(struct reftable_log_record *log,
struct object_id old_oid, new_oid;
const char *full_committer;
- oidread(&old_oid, log->value.update.old_hash);
- oidread(&new_oid, log->value.update.new_hash);
+ oidread(&old_oid, log->value.update.old_hash, the_repository->hash_algo);
+ oidread(&new_oid, log->value.update.new_hash, the_repository->hash_algo);
/*
* When both the old object ID and the new object ID are null
@@ -1740,7 +1823,6 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent_reverse");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct reftable_merged_table *mt = NULL;
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
int ret;
@@ -1748,8 +1830,8 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,
if (refs->err < 0)
return refs->err;
- mt = reftable_stack_merged_table(stack);
- ret = reftable_merged_table_seek_log(mt, &it, refname);
+ reftable_stack_init_log_iterator(stack, &it);
+ ret = reftable_iterator_seek_log(&it, refname);
while (!ret) {
ret = reftable_iterator_next_log(&it, &log);
if (ret < 0)
@@ -1777,7 +1859,6 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct reftable_merged_table *mt = NULL;
struct reftable_log_record *logs = NULL;
struct reftable_iterator it = {0};
size_t logs_alloc = 0, logs_nr = 0, i;
@@ -1786,8 +1867,8 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,
if (refs->err < 0)
return refs->err;
- mt = reftable_stack_merged_table(stack);
- ret = reftable_merged_table_seek_log(mt, &it, refname);
+ reftable_stack_init_log_iterator(stack, &it);
+ ret = reftable_iterator_seek_log(&it, refname);
while (!ret) {
struct reftable_log_record log = {0};
@@ -1824,7 +1905,6 @@ static int reftable_be_reflog_exists(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "reflog_exists");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
int ret;
@@ -1837,7 +1917,8 @@ static int reftable_be_reflog_exists(struct ref_store *ref_store,
if (ret < 0)
goto done;
- ret = reftable_merged_table_seek_log(mt, &it, refname);
+ reftable_stack_init_log_iterator(stack, &it);
+ ret = reftable_iterator_seek_log(&it, refname);
if (ret < 0)
goto done;
@@ -1935,8 +2016,6 @@ struct write_reflog_delete_arg {
static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_data)
{
struct write_reflog_delete_arg *arg = cb_data;
- struct reftable_merged_table *mt =
- reftable_stack_merged_table(arg->stack);
struct reftable_log_record log = {0}, tombstone = {0};
struct reftable_iterator it = {0};
uint64_t ts = reftable_stack_next_update_index(arg->stack);
@@ -1944,12 +2023,14 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_da
reftable_writer_set_limits(writer, ts, ts);
+ reftable_stack_init_log_iterator(arg->stack, &it);
+
/*
* In order to delete a table we need to delete all reflog entries one
* by one. This is inefficient, but the reftable format does not have a
* better marker right now.
*/
- ret = reftable_merged_table_seek_log(mt, &it, arg->refname);
+ ret = reftable_iterator_seek_log(&it, arg->refname);
while (ret == 0) {
ret = reftable_iterator_next_log(&it, &log);
if (ret < 0)
@@ -1993,6 +2074,7 @@ static int reftable_be_delete_reflog(struct ref_store *ref_store,
}
struct reflog_expiry_arg {
+ struct reftable_ref_store *refs;
struct reftable_stack *stack;
struct reftable_log_record *records;
struct object_id update_oid;
@@ -2021,7 +2103,7 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
ref.refname = (char *)arg->refname;
ref.update_index = ts;
- if (!peel_object(&arg->update_oid, &peeled)) {
+ if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled)) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);
@@ -2085,7 +2167,6 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_WRITE, "reflog_expire");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
- struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
struct reftable_log_record *logs = NULL;
struct reftable_log_record *rewritten = NULL;
struct reftable_ref_record ref_record = {0};
@@ -2104,7 +2185,9 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
if (ret < 0)
goto done;
- ret = reftable_merged_table_seek_log(mt, &it, refname);
+ reftable_stack_init_log_iterator(stack, &it);
+
+ ret = reftable_iterator_seek_log(&it, refname);
if (ret < 0)
goto done;
@@ -2116,7 +2199,8 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
if (ret < 0)
goto done;
if (reftable_ref_record_val1(&ref_record))
- oidread(&oid, reftable_ref_record_val1(&ref_record));
+ oidread(&oid, reftable_ref_record_val1(&ref_record),
+ the_repository->hash_algo);
prepare_fn(refname, &oid, policy_cb_data);
while (1) {
@@ -2131,8 +2215,10 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
break;
}
- oidread(&old_oid, log.value.update.old_hash);
- oidread(&new_oid, log.value.update.new_hash);
+ oidread(&old_oid, log.value.update.old_hash,
+ the_repository->hash_algo);
+ oidread(&new_oid, log.value.update.new_hash,
+ the_repository->hash_algo);
/*
* Skip over the reflog existence marker. We will add it back
@@ -2163,8 +2249,10 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
struct object_id old_oid, new_oid;
*dest = logs[i];
- oidread(&old_oid, logs[i].value.update.old_hash);
- oidread(&new_oid, logs[i].value.update.new_hash);
+ oidread(&old_oid, logs[i].value.update.old_hash,
+ the_repository->hash_algo);
+ oidread(&new_oid, logs[i].value.update.new_hash,
+ the_repository->hash_algo);
if (should_prune_fn(&old_oid, &new_oid, logs[i].value.update.email,
(timestamp_t)logs[i].value.update.time,
@@ -2181,8 +2269,9 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
if (flags & EXPIRE_REFLOGS_UPDATE_REF && last_hash &&
reftable_ref_record_val1(&ref_record))
- oidread(&arg.update_oid, last_hash);
+ oidread(&arg.update_oid, last_hash, the_repository->hash_algo);
+ arg.refs = refs;
arg.records = rewritten;
arg.len = logs_nr;
arg.stack = stack,
@@ -2217,14 +2306,16 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
struct ref_storage_be refs_be_reftable = {
.name = "reftable",
.init = reftable_be_init,
- .init_db = reftable_be_init_db,
+ .release = reftable_be_release,
+ .create_on_disk = reftable_be_create_on_disk,
+ .remove_on_disk = reftable_be_remove_on_disk,
+
.transaction_prepare = reftable_be_transaction_prepare,
.transaction_finish = reftable_be_transaction_finish,
.transaction_abort = reftable_be_transaction_abort,
.initial_transaction_commit = reftable_be_initial_transaction_commit,
.pack_refs = reftable_be_pack_refs,
- .create_symref = reftable_be_create_symref,
.rename_ref = reftable_be_rename_ref,
.copy_ref = reftable_be_copy_ref,
diff --git a/refspec.c b/refspec.c
index d60932f..ec90ab3 100644
--- a/refspec.c
+++ b/refspec.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hash.h"
@@ -7,19 +9,6 @@
#include "refspec.h"
#include "strbuf.h"
-static struct refspec_item s_tag_refspec = {
- .force = 0,
- .pattern = 1,
- .matching = 0,
- .exact_sha1 = 0,
- .negative = 0,
- .src = "refs/tags/*",
- .dst = "refs/tags/*",
-};
-
-/* See TAG_REFSPEC for the string version */
-const struct refspec_item *tag_refspec = &s_tag_refspec;
-
/*
* Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
* Returns 1 if successful and 0 if the refspec is invalid.
diff --git a/refspec.h b/refspec.h
index 8c0c446..754be45 100644
--- a/refspec.h
+++ b/refspec.h
@@ -2,7 +2,6 @@
#define REFSPEC_H
#define TAG_REFSPEC "refs/tags/*:refs/tags/*"
-extern const struct refspec_item *tag_refspec;
/**
* A struct refspec_item holds the parsed interpretation of a refspec. If it
diff --git a/reftable/basics.c b/reftable/basics.c
index fea711d..0058619 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -67,9 +67,9 @@ void free_names(char **a)
reftable_free(a);
}
-size_t names_length(char **names)
+size_t names_length(const char **names)
{
- char **p = names;
+ const char **p = names;
while (*p)
p++;
return p - names;
@@ -102,15 +102,12 @@ void parse_names(char *buf, int size, char ***namesp)
*namesp = names;
}
-int names_equal(char **a, char **b)
+int names_equal(const char **a, const char **b)
{
- int i = 0;
- for (; a[i] && b[i]; i++) {
- if (strcmp(a[i], b[i])) {
+ size_t i = 0;
+ for (; a[i] && b[i]; i++)
+ if (strcmp(a[i], b[i]))
return 0;
- }
- }
-
return a[i] == b[i];
}
diff --git a/reftable/basics.h b/reftable/basics.h
index 523ecd5..c8fec68 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -42,10 +42,10 @@ void free_names(char **a);
void parse_names(char *buf, int size, char ***namesp);
/* compares two NULL-terminated arrays of strings. */
-int names_equal(char **a, char **b);
+int names_equal(const char **a, const char **b);
/* returns the array size of a NULL-terminated array of strings. */
-size_t names_length(char **names);
+size_t names_length(const char **names);
/* Allocation routines; they invoke the functions set through
* reftable_set_alloc() */
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
deleted file mode 100644
index 997c4d9..0000000
--- a/reftable/basics_test.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "system.h"
-
-#include "basics.h"
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-struct integer_needle_lesseq_args {
- int needle;
- int *haystack;
-};
-
-static int integer_needle_lesseq(size_t i, void *_args)
-{
- struct integer_needle_lesseq_args *args = _args;
- return args->needle <= args->haystack[i];
-}
-
-static void test_binsearch(void)
-{
- int haystack[] = { 2, 4, 6, 8, 10 };
- struct {
- int needle;
- size_t expected_idx;
- } testcases[] = {
- {-9000, 0},
- {-1, 0},
- {0, 0},
- {2, 0},
- {3, 1},
- {4, 1},
- {7, 3},
- {9, 4},
- {10, 4},
- {11, 5},
- {9000, 5},
- };
- size_t i = 0;
-
- for (i = 0; i < ARRAY_SIZE(testcases); i++) {
- struct integer_needle_lesseq_args args = {
- .haystack = haystack,
- .needle = testcases[i].needle,
- };
- size_t idx;
-
- idx = binsearch(ARRAY_SIZE(haystack), &integer_needle_lesseq, &args);
- EXPECT(idx == testcases[i].expected_idx);
- }
-}
-
-static void test_names_length(void)
-{
- char *a[] = { "a", "b", NULL };
- EXPECT(names_length(a) == 2);
-}
-
-static void test_parse_names_normal(void)
-{
- char in[] = "a\nb\n";
- char **out = NULL;
- parse_names(in, strlen(in), &out);
- EXPECT(!strcmp(out[0], "a"));
- EXPECT(!strcmp(out[1], "b"));
- EXPECT(!out[2]);
- free_names(out);
-}
-
-static void test_parse_names_drop_empty(void)
-{
- char in[] = "a\n\n";
- char **out = NULL;
- parse_names(in, strlen(in), &out);
- EXPECT(!strcmp(out[0], "a"));
- EXPECT(!out[1]);
- free_names(out);
-}
-
-static void test_common_prefix(void)
-{
- struct strbuf s1 = STRBUF_INIT;
- struct strbuf s2 = STRBUF_INIT;
- strbuf_addstr(&s1, "abcdef");
- strbuf_addstr(&s2, "abc");
- EXPECT(common_prefix_size(&s1, &s2) == 3);
- strbuf_release(&s1);
- strbuf_release(&s2);
-}
-
-int basics_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_common_prefix);
- RUN_TEST(test_parse_names_normal);
- RUN_TEST(test_parse_names_drop_empty);
- RUN_TEST(test_binsearch);
- RUN_TEST(test_names_length);
- return 0;
-}
diff --git a/reftable/block.c b/reftable/block.c
index 3e87460..00030ee 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -76,6 +76,10 @@ void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf,
bw->entries = 0;
bw->restart_len = 0;
bw->last_key.len = 0;
+ if (!bw->zstream) {
+ REFTABLE_CALLOC_ARRAY(bw->zstream, 1);
+ deflateInit(bw->zstream, 9);
+ }
}
uint8_t block_writer_type(struct block_writer *bw)
@@ -139,39 +143,52 @@ int block_writer_finish(struct block_writer *w)
w->next += 2;
put_be24(w->buf + 1 + w->header_off, w->next);
+ /*
+ * Log records are stored zlib-compressed. Note that the compression
+ * also spans over the restart points we have just written.
+ */
if (block_writer_type(w) == BLOCK_TYPE_LOG) {
int block_header_skip = 4 + w->header_off;
- uLongf src_len = w->next - block_header_skip;
- uLongf dest_cap = src_len * 1.001 + 12;
- uint8_t *compressed;
+ uLongf src_len = w->next - block_header_skip, compressed_len;
+ int ret;
- REFTABLE_ALLOC_ARRAY(compressed, dest_cap);
+ ret = deflateReset(w->zstream);
+ if (ret != Z_OK)
+ return REFTABLE_ZLIB_ERROR;
- while (1) {
- uLongf out_dest_len = dest_cap;
- int zresult = compress2(compressed, &out_dest_len,
- w->buf + block_header_skip,
- src_len, 9);
- if (zresult == Z_BUF_ERROR && dest_cap < LONG_MAX) {
- dest_cap *= 2;
- compressed =
- reftable_realloc(compressed, dest_cap);
- if (compressed)
- continue;
- }
+ /*
+ * Precompute the upper bound of how many bytes the compressed
+ * data may end up with. Combined with `Z_FINISH`, `deflate()`
+ * is guaranteed to return `Z_STREAM_END`.
+ */
+ compressed_len = deflateBound(w->zstream, src_len);
+ REFTABLE_ALLOC_GROW(w->compressed, compressed_len, w->compressed_cap);
- if (Z_OK != zresult) {
- reftable_free(compressed);
- return REFTABLE_ZLIB_ERROR;
- }
+ w->zstream->next_out = w->compressed;
+ w->zstream->avail_out = compressed_len;
+ w->zstream->next_in = w->buf + block_header_skip;
+ w->zstream->avail_in = src_len;
- memcpy(w->buf + block_header_skip, compressed,
- out_dest_len);
- w->next = out_dest_len + block_header_skip;
- reftable_free(compressed);
- break;
- }
+ /*
+ * We want to perform all decompression in a single step, which
+ * is why we can pass Z_FINISH here. As we have precomputed the
+ * deflated buffer's size via `deflateBound()` this function is
+ * guaranteed to succeed according to the zlib documentation.
+ */
+ ret = deflate(w->zstream, Z_FINISH);
+ if (ret != Z_STREAM_END)
+ return REFTABLE_ZLIB_ERROR;
+
+ /*
+ * Overwrite the uncompressed data we have already written and
+ * adjust the `next` pointer to point right after the
+ * compressed data.
+ */
+ memcpy(w->buf + block_header_skip, w->compressed,
+ w->zstream->total_out);
+ w->next = w->zstream->total_out + block_header_skip;
}
+
return w->next;
}
@@ -309,9 +326,9 @@ int block_reader_first_key(const struct block_reader *br, struct strbuf *key)
return 0;
}
-static uint32_t block_reader_restart_offset(const struct block_reader *br, int i)
+static uint32_t block_reader_restart_offset(const struct block_reader *br, size_t idx)
{
- return get_be24(br->restart_bytes + 3 * i);
+ return get_be24(br->restart_bytes + 3 * idx);
}
void block_iter_seek_start(struct block_iter *it, const struct block_reader *br)
@@ -514,7 +531,10 @@ int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
void block_writer_release(struct block_writer *bw)
{
+ deflateEnd(bw->zstream);
+ FREE_AND_NULL(bw->zstream);
FREE_AND_NULL(bw->restarts);
+ FREE_AND_NULL(bw->compressed);
strbuf_release(&bw->last_key);
/* the block is not owned. */
}
diff --git a/reftable/block.h b/reftable/block.h
index ea4384a..1c8f25e 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -18,6 +18,10 @@ license that can be found in the LICENSE file or at
* allocation overhead.
*/
struct block_writer {
+ z_stream *zstream;
+ unsigned char *compressed;
+ size_t compressed_cap;
+
uint8_t *buf;
uint32_t block_size;
@@ -25,7 +29,7 @@ struct block_writer {
uint32_t header_off;
/* How often to restart keys. */
- int restart_interval;
+ uint16_t restart_interval;
int hash_size;
/* Offset of next uint8_t to write. */
diff --git a/reftable/block_test.c b/reftable/block_test.c
index 26a9cfb..90aecd5 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
header_off, hash_size(GIT_SHA1_FORMAT_ID));
- rec.u.ref.refname = "";
+ rec.u.ref.refname = (char *) "";
rec.u.ref.value_type = REFTABLE_REF_DELETION;
n = block_writer_add(&bw, &rec);
EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/constants.h b/reftable/constants.h
index 5eee72c..f6beb84 100644
--- a/reftable/constants.h
+++ b/reftable/constants.h
@@ -17,5 +17,6 @@ license that can be found in the LICENSE file or at
#define MAX_RESTARTS ((1 << 16) - 1)
#define DEFAULT_BLOCK_SIZE 4096
+#define DEFAULT_GEOMETRIC_FACTOR 2
#endif
diff --git a/reftable/dump.c b/reftable/dump.c
index 26e0393..dd65d9e 100644
--- a/reftable/dump.c
+++ b/reftable/dump.c
@@ -7,7 +7,7 @@ license that can be found in the LICENSE file or at
*/
#include "git-compat-util.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "reftable-blocksource.h"
#include "reftable-error.h"
@@ -27,9 +27,9 @@ license that can be found in the LICENSE file or at
static int compact_stack(const char *stackdir)
{
struct reftable_stack *stack = NULL;
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
- int err = reftable_new_stack(&stack, stackdir, cfg);
+ int err = reftable_new_stack(&stack, stackdir, &opts);
if (err < 0)
goto done;
@@ -48,6 +48,7 @@ static void print_help(void)
printf("usage: dump [-cst] arg\n\n"
"options: \n"
" -c compact\n"
+ " -b dump blocks\n"
" -t dump table\n"
" -s dump stack\n"
" -6 sha256 hash format\n"
@@ -58,6 +59,7 @@ static void print_help(void)
int reftable_dump_main(int argc, char *const *argv)
{
int err = 0;
+ int opt_dump_blocks = 0;
int opt_dump_table = 0;
int opt_dump_stack = 0;
int opt_compact = 0;
@@ -67,6 +69,8 @@ int reftable_dump_main(int argc, char *const *argv)
for (; argc > 1; argv++, argc--)
if (*argv[1] != '-')
break;
+ else if (!strcmp("-b", argv[1]))
+ opt_dump_blocks = 1;
else if (!strcmp("-t", argv[1]))
opt_dump_table = 1;
else if (!strcmp("-6", argv[1]))
@@ -88,7 +92,9 @@ int reftable_dump_main(int argc, char *const *argv)
arg = argv[1];
- if (opt_dump_table) {
+ if (opt_dump_blocks) {
+ err = reftable_reader_print_blocks(arg);
+ } else if (opt_dump_table) {
err = reftable_reader_print_file(arg);
} else if (opt_dump_stack) {
err = reftable_stack_print_directory(arg, opt_hash_id);
diff --git a/reftable/error.c b/reftable/error.c
index cfb7a0f..a25f28a 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -27,8 +27,6 @@ const char *reftable_error_str(int err)
return "misuse of the reftable API";
case REFTABLE_ZLIB_ERROR:
return "zlib failure";
- case REFTABLE_NAME_CONFLICT:
- return "file/directory conflict";
case REFTABLE_EMPTY_TABLE_ERROR:
return "wrote empty table";
case REFTABLE_REFNAME_ERROR:
diff --git a/reftable/generic.c b/reftable/generic.c
index b9f1c7c..28ae261 100644
--- a/reftable/generic.c
+++ b/reftable/generic.c
@@ -12,32 +12,66 @@ license that can be found in the LICENSE file or at
#include "reftable-iterator.h"
#include "reftable-generic.h"
-int reftable_table_seek_ref(struct reftable_table *tab,
- struct reftable_iterator *it, const char *name)
+void table_init_iter(struct reftable_table *tab,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- struct reftable_record rec = { .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = (char *)name,
- } };
- return tab->ops->seek_record(tab->table_arg, it, &rec);
+
+ tab->ops->init_iter(tab->table_arg, it, typ);
}
-int reftable_table_seek_log(struct reftable_table *tab,
- struct reftable_iterator *it, const char *name)
+void reftable_table_init_ref_iter(struct reftable_table *tab,
+ struct reftable_iterator *it)
{
- struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = (char *)name,
- .update_index = ~((uint64_t)0),
- } };
- return tab->ops->seek_record(tab->table_arg, it, &rec);
+ table_init_iter(tab, it, BLOCK_TYPE_REF);
+}
+
+void reftable_table_init_log_iter(struct reftable_table *tab,
+ struct reftable_iterator *it)
+{
+ table_init_iter(tab, it, BLOCK_TYPE_LOG);
+}
+
+int reftable_iterator_seek_ref(struct reftable_iterator *it,
+ const char *name)
+{
+ struct reftable_record want = {
+ .type = BLOCK_TYPE_REF,
+ .u.ref = {
+ .refname = (char *)name,
+ },
+ };
+ return it->ops->seek(it->iter_arg, &want);
+}
+
+int reftable_iterator_seek_log_at(struct reftable_iterator *it,
+ const char *name, uint64_t update_index)
+{
+ struct reftable_record want = {
+ .type = BLOCK_TYPE_LOG,
+ .u.log = {
+ .refname = (char *)name,
+ .update_index = update_index,
+ },
+ };
+ return it->ops->seek(it->iter_arg, &want);
+}
+
+int reftable_iterator_seek_log(struct reftable_iterator *it,
+ const char *name)
+{
+ return reftable_iterator_seek_log_at(it, name, ~((uint64_t) 0));
}
int reftable_table_read_ref(struct reftable_table *tab, const char *name,
struct reftable_ref_record *ref)
{
struct reftable_iterator it = { NULL };
- int err = reftable_table_seek_ref(tab, &it, name);
+ int err;
+
+ reftable_table_init_ref_iter(tab, &it);
+
+ err = reftable_iterator_seek_ref(&it, name);
if (err)
goto done;
@@ -62,10 +96,13 @@ int reftable_table_print(struct reftable_table *tab) {
struct reftable_ref_record ref = { NULL };
struct reftable_log_record log = { NULL };
uint32_t hash_id = reftable_table_hash_id(tab);
- int err = reftable_table_seek_ref(tab, &it, "");
- if (err < 0) {
+ int err;
+
+ reftable_table_init_ref_iter(tab, &it);
+
+ err = reftable_iterator_seek_ref(&it, "");
+ if (err < 0)
return err;
- }
while (1) {
err = reftable_iterator_next_ref(&it, &ref);
@@ -80,10 +117,12 @@ int reftable_table_print(struct reftable_table *tab) {
reftable_iterator_destroy(&it);
reftable_ref_record_release(&ref);
- err = reftable_table_seek_log(tab, &it, "");
- if (err < 0) {
+ reftable_table_init_log_iter(tab, &it);
+
+ err = reftable_iterator_seek_log(&it, "");
+ if (err < 0)
return err;
- }
+
while (1) {
err = reftable_iterator_next_log(&it, &log);
if (err > 0) {
@@ -152,11 +191,21 @@ int reftable_iterator_next_log(struct reftable_iterator *it,
return err;
}
+int iterator_seek(struct reftable_iterator *it, struct reftable_record *want)
+{
+ return it->ops->seek(it->iter_arg, want);
+}
+
int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
{
return it->ops->next(it->iter_arg, rec);
}
+static int empty_iterator_seek(void *arg, struct reftable_record *want)
+{
+ return 0;
+}
+
static int empty_iterator_next(void *arg, struct reftable_record *rec)
{
return 1;
@@ -167,6 +216,7 @@ static void empty_iterator_close(void *arg)
}
static struct reftable_iterator_vtable empty_vtable = {
+ .seek = &empty_iterator_seek,
.next = &empty_iterator_next,
.close = &empty_iterator_close,
};
diff --git a/reftable/generic.h b/reftable/generic.h
index 98886a0..8341fa5 100644
--- a/reftable/generic.h
+++ b/reftable/generic.h
@@ -14,19 +14,24 @@ license that can be found in the LICENSE file or at
/* generic interface to reftables */
struct reftable_table_vtable {
- int (*seek_record)(void *tab, struct reftable_iterator *it,
- struct reftable_record *);
+ void (*init_iter)(void *tab, struct reftable_iterator *it, uint8_t typ);
uint32_t (*hash_id)(void *tab);
uint64_t (*min_update_index)(void *tab);
uint64_t (*max_update_index)(void *tab);
};
+void table_init_iter(struct reftable_table *tab,
+ struct reftable_iterator *it,
+ uint8_t typ);
+
struct reftable_iterator_vtable {
+ int (*seek)(void *iter_arg, struct reftable_record *want);
int (*next)(void *iter_arg, struct reftable_record *rec);
void (*close)(void *iter_arg);
};
void iterator_set_empty(struct reftable_iterator *it);
+int iterator_seek(struct reftable_iterator *it, struct reftable_record *want);
int iterator_next(struct reftable_iterator *it, struct reftable_record *rec);
#endif
diff --git a/reftable/iter.c b/reftable/iter.c
index aa9ac19..fddea31 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -23,6 +23,13 @@ static void filtering_ref_iterator_close(void *iter_arg)
reftable_iterator_destroy(&fri->it);
}
+static int filtering_ref_iterator_seek(void *iter_arg,
+ struct reftable_record *want)
+{
+ struct filtering_ref_iterator *fri = iter_arg;
+ return iterator_seek(&fri->it, want);
+}
+
static int filtering_ref_iterator_next(void *iter_arg,
struct reftable_record *rec)
{
@@ -38,11 +45,11 @@ static int filtering_ref_iterator_next(void *iter_arg,
if (fri->double_check) {
struct reftable_iterator it = { NULL };
- err = reftable_table_seek_ref(&fri->tab, &it,
- ref->refname);
- if (err == 0) {
+ reftable_table_init_ref_iter(&fri->tab, &it);
+
+ err = reftable_iterator_seek_ref(&it, ref->refname);
+ if (err == 0)
err = reftable_iterator_next_ref(&it, ref);
- }
reftable_iterator_destroy(&it);
@@ -73,6 +80,7 @@ static int filtering_ref_iterator_next(void *iter_arg,
}
static struct reftable_iterator_vtable filtering_ref_iterator_vtable = {
+ .seek = &filtering_ref_iterator_seek,
.next = &filtering_ref_iterator_next,
.close = &filtering_ref_iterator_close,
};
@@ -119,6 +127,12 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
return 0;
}
+static int indexed_table_ref_iter_seek(void *p, struct reftable_record *want)
+{
+ BUG("seeking indexed table is not supported");
+ return -1;
+}
+
static int indexed_table_ref_iter_next(void *p, struct reftable_record *rec)
{
struct indexed_table_ref_iter *it = p;
@@ -175,6 +189,7 @@ int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
}
static struct reftable_iterator_vtable indexed_table_ref_iter_vtable = {
+ .seek = &indexed_table_ref_iter_seek,
.next = &indexed_table_ref_iter_next,
.close = &indexed_table_ref_iter_close,
};
diff --git a/reftable/merged.c b/reftable/merged.c
index f85a24c..6adce44 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -25,34 +25,25 @@ struct merged_subiter {
struct merged_iter {
struct merged_subiter *subiters;
struct merged_iter_pqueue pq;
- uint32_t hash_id;
size_t stack_len;
- uint8_t typ;
int suppress_deletions;
ssize_t advance_index;
};
-static int merged_iter_init(struct merged_iter *mi)
+static void merged_iter_init(struct merged_iter *mi,
+ struct reftable_merged_table *mt,
+ uint8_t typ)
{
- for (size_t i = 0; i < mi->stack_len; i++) {
- struct pq_entry e = {
- .index = i,
- .rec = &mi->subiters[i].rec,
- };
- int err;
+ memset(mi, 0, sizeof(*mi));
+ mi->advance_index = -1;
+ mi->suppress_deletions = mt->suppress_deletions;
- reftable_record_init(&mi->subiters[i].rec, mi->typ);
- err = iterator_next(&mi->subiters[i].iter,
- &mi->subiters[i].rec);
- if (err < 0)
- return err;
- if (err > 0)
- continue;
-
- merged_iter_pqueue_add(&mi->pq, &e);
+ REFTABLE_CALLOC_ARRAY(mi->subiters, mt->stack_len);
+ for (size_t i = 0; i < mt->stack_len; i++) {
+ reftable_record_init(&mi->subiters[i].rec, typ);
+ table_init_iter(&mt->stack[i], &mi->subiters[i].iter, typ);
}
-
- return 0;
+ mi->stack_len = mt->stack_len;
}
static void merged_iter_close(void *p)
@@ -83,6 +74,27 @@ static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
return 0;
}
+static int merged_iter_seek(struct merged_iter *mi, struct reftable_record *want)
+{
+ int err;
+
+ mi->advance_index = -1;
+
+ for (size_t i = 0; i < mi->stack_len; i++) {
+ err = iterator_seek(&mi->subiters[i].iter, want);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ continue;
+
+ err = merged_iter_advance_subiter(mi, i);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
static int merged_iter_next_entry(struct merged_iter *mi,
struct reftable_record *rec)
{
@@ -148,6 +160,11 @@ static int merged_iter_next_entry(struct merged_iter *mi,
return 0;
}
+static int merged_iter_seek_void(void *it, struct reftable_record *want)
+{
+ return merged_iter_seek(it, want);
+}
+
static int merged_iter_next_void(void *p, struct reftable_record *rec)
{
struct merged_iter *mi = p;
@@ -162,6 +179,7 @@ static int merged_iter_next_void(void *p, struct reftable_record *rec)
}
static struct reftable_iterator_vtable merged_iter_vtable = {
+ .seek = merged_iter_seek_void,
.next = &merged_iter_next_void,
.close = &merged_iter_close,
};
@@ -207,19 +225,11 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
return 0;
}
-/* clears the list of subtable, without affecting the readers themselves. */
-void merged_table_release(struct reftable_merged_table *mt)
-{
- FREE_AND_NULL(mt->stack);
- mt->stack_len = 0;
-}
-
void reftable_merged_table_free(struct reftable_merged_table *mt)
{
- if (!mt) {
+ if (!mt)
return;
- }
- merged_table_release(mt);
+ FREE_AND_NULL(mt->stack);
reftable_free(mt);
}
@@ -235,81 +245,13 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
-static int reftable_table_seek_record(struct reftable_table *tab,
- struct reftable_iterator *it,
- struct reftable_record *rec)
+void merged_table_init_iter(struct reftable_merged_table *mt,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- return tab->ops->seek_record(tab->table_arg, it, rec);
-}
-
-static int merged_table_seek_record(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- struct reftable_record *rec)
-{
- struct merged_iter merged = {
- .typ = reftable_record_type(rec),
- .hash_id = mt->hash_id,
- .suppress_deletions = mt->suppress_deletions,
- .advance_index = -1,
- };
- struct merged_iter *p;
- int err;
-
- REFTABLE_CALLOC_ARRAY(merged.subiters, mt->stack_len);
- for (size_t i = 0; i < mt->stack_len; i++) {
- err = reftable_table_seek_record(&mt->stack[i],
- &merged.subiters[merged.stack_len].iter, rec);
- if (err < 0)
- goto out;
- if (!err)
- merged.stack_len++;
- }
-
- err = merged_iter_init(&merged);
- if (err < 0)
- goto out;
-
- p = reftable_malloc(sizeof(struct merged_iter));
- *p = merged;
- iterator_from_merged_iter(it, p);
-
-out:
- if (err < 0)
- merged_iter_close(&merged);
- return err;
-}
-
-int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name)
-{
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = (char *)name,
- },
- };
- return merged_table_seek_record(mt, it, &rec);
-}
-
-int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name, uint64_t update_index)
-{
- struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = (char *)name,
- .update_index = update_index,
- } };
- return merged_table_seek_record(mt, it, &rec);
-}
-
-int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name)
-{
- uint64_t max = ~((uint64_t)0);
- return reftable_merged_table_seek_log_at(mt, it, name, max);
+ struct merged_iter *mi = reftable_malloc(sizeof(*mi));
+ merged_iter_init(mi, mt, typ);
+ iterator_from_merged_iter(it, mi);
}
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
@@ -317,11 +259,11 @@ uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
return mt->hash_id;
}
-static int reftable_merged_table_seek_void(void *tab,
- struct reftable_iterator *it,
- struct reftable_record *rec)
+static void reftable_merged_table_init_iter_void(void *tab,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- return merged_table_seek_record(tab, it, rec);
+ merged_table_init_iter(tab, it, typ);
}
static uint32_t reftable_merged_table_hash_id_void(void *tab)
@@ -340,7 +282,7 @@ static uint64_t reftable_merged_table_max_update_index_void(void *tab)
}
static struct reftable_table_vtable merged_table_vtable = {
- .seek_record = reftable_merged_table_seek_void,
+ .init_iter = reftable_merged_table_init_iter_void,
.hash_id = reftable_merged_table_hash_id_void,
.min_update_index = reftable_merged_table_min_update_index_void,
.max_update_index = reftable_merged_table_max_update_index_void,
diff --git a/reftable/merged.h b/reftable/merged.h
index a2571db..2efe571 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -24,6 +24,10 @@ struct reftable_merged_table {
uint64_t max;
};
-void merged_table_release(struct reftable_merged_table *mt);
+struct reftable_iterator;
+
+void merged_table_init_iter(struct reftable_merged_table *mt,
+ struct reftable_iterator *it,
+ uint8_t typ);
#endif
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index 530fc82..a9d6661 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -12,6 +12,7 @@ license that can be found in the LICENSE file or at
#include "basics.h"
#include "blocksource.h"
+#include "constants.h"
#include "reader.h"
#include "record.h"
#include "test_framework.h"
@@ -124,13 +125,13 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
static void test_merged_between(void)
{
struct reftable_ref_record r1[] = { {
- .refname = "b",
+ .refname = (char *) "b",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1, 2, 3, 0 },
} };
struct reftable_ref_record r2[] = { {
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 2,
.value_type = REFTABLE_REF_DELETION,
} };
@@ -145,7 +146,10 @@ static void test_merged_between(void)
int i;
struct reftable_ref_record ref = { NULL };
struct reftable_iterator it = { NULL };
- int err = reftable_merged_table_seek_ref(mt, &it, "a");
+ int err;
+
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ err = reftable_iterator_seek_ref(&it, "a");
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
@@ -165,38 +169,38 @@ static void test_merged(void)
{
struct reftable_ref_record r1[] = {
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1 },
},
{
- .refname = "b",
+ .refname = (char *) "b",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1 },
},
{
- .refname = "c",
+ .refname = (char *) "c",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1 },
}
};
struct reftable_ref_record r2[] = { {
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 2,
.value_type = REFTABLE_REF_DELETION,
} };
struct reftable_ref_record r3[] = {
{
- .refname = "c",
+ .refname = (char *) "c",
.update_index = 3,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 2 },
},
{
- .refname = "d",
+ .refname = (char *) "d",
.update_index = 3,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = { 1 },
@@ -217,14 +221,15 @@ static void test_merged(void)
struct reftable_reader **readers = NULL;
struct reftable_merged_table *mt =
merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3);
-
struct reftable_iterator it = { NULL };
- int err = reftable_merged_table_seek_ref(mt, &it, "a");
+ int err;
struct reftable_ref_record *out = NULL;
size_t len = 0;
size_t cap = 0;
int i = 0;
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ err = reftable_iterator_seek_ref(&it, "a");
EXPECT_ERR(err);
EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
EXPECT(reftable_merged_table_min_update_index(mt) == 1);
@@ -291,46 +296,46 @@ static void test_merged_logs(void)
{
struct reftable_log_record r1[] = {
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 2,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
.old_hash = { 2 },
/* deletion */
- .name = "jane doe",
- .email = "jane@invalid",
- .message = "message2",
+ .name = (char *) "jane doe",
+ .email = (char *) "jane@invalid",
+ .message = (char *) "message2",
}
},
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 1,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
.old_hash = { 1 },
.new_hash = { 2 },
- .name = "jane doe",
- .email = "jane@invalid",
- .message = "message1",
+ .name = (char *) "jane doe",
+ .email = (char *) "jane@invalid",
+ .message = (char *) "message1",
}
},
};
struct reftable_log_record r2[] = {
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 3,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
.new_hash = { 3 },
- .name = "jane doe",
- .email = "jane@invalid",
- .message = "message3",
+ .name = (char *) "jane doe",
+ .email = (char *) "jane@invalid",
+ .message = (char *) "message3",
}
},
};
struct reftable_log_record r3[] = {
{
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 2,
.value_type = REFTABLE_LOG_DELETION,
},
@@ -348,14 +353,15 @@ static void test_merged_logs(void)
struct reftable_reader **readers = NULL;
struct reftable_merged_table *mt = merged_table_from_log_records(
logs, &bs, &readers, sizes, bufs, 3);
-
struct reftable_iterator it = { NULL };
- int err = reftable_merged_table_seek_log(mt, &it, "a");
+ int err;
struct reftable_log_record *out = NULL;
size_t len = 0;
size_t cap = 0;
int i = 0;
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
+ err = reftable_iterator_seek_log(&it, "a");
EXPECT_ERR(err);
EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
EXPECT(reftable_merged_table_min_update_index(mt) == 1);
@@ -377,7 +383,8 @@ static void test_merged_logs(void)
GIT_SHA1_RAWSZ));
}
- err = reftable_merged_table_seek_log_at(mt, &it, "a", 2);
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
+ err = reftable_iterator_seek_log_at(&it, "a", 2);
EXPECT_ERR(err);
reftable_log_record_release(&out[0]);
err = reftable_iterator_next_log(&it, &out[0]);
@@ -406,7 +413,7 @@ static void test_default_write_opts(void)
reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record rec = {
- .refname = "master",
+ .refname = (char *) "master",
.update_index = 1,
};
int err;
diff --git a/reftable/reader.c b/reftable/reader.c
index 481dff1..29c99e2 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -224,8 +224,14 @@ struct table_iter {
struct block_iter bi;
int is_finished;
};
-#define TABLE_ITER_INIT { \
- .bi = BLOCK_ITER_INIT \
+
+static int table_iter_init(struct table_iter *ti, struct reftable_reader *r)
+{
+ struct block_iter bi = BLOCK_ITER_INIT;
+ memset(ti, 0, sizeof(*ti));
+ ti->r = r;
+ ti->bi = bi;
+ return 0;
}
static int table_iter_next_in_block(struct table_iter *ti,
@@ -363,50 +369,23 @@ static int table_iter_next(struct table_iter *ti, struct reftable_record *rec)
}
}
-static int table_iter_next_void(void *ti, struct reftable_record *rec)
-{
- return table_iter_next(ti, rec);
-}
-
-static void table_iter_close_void(void *ti)
-{
- table_iter_close(ti);
-}
-
-static struct reftable_iterator_vtable table_iter_vtable = {
- .next = &table_iter_next_void,
- .close = &table_iter_close_void,
-};
-
-static void iterator_from_table_iter(struct reftable_iterator *it,
- struct table_iter *ti)
-{
- assert(!it->ops);
- it->iter_arg = ti;
- it->ops = &table_iter_vtable;
-}
-
-static int reader_table_iter_at(struct reftable_reader *r,
- struct table_iter *ti, uint64_t off,
- uint8_t typ)
+static int table_iter_seek_to(struct table_iter *ti, uint64_t off, uint8_t typ)
{
int err;
- err = reader_init_block_reader(r, &ti->br, off, typ);
+ err = reader_init_block_reader(ti->r, &ti->br, off, typ);
if (err != 0)
return err;
- ti->r = r;
ti->typ = block_reader_type(&ti->br);
ti->block_off = off;
block_iter_seek_start(&ti->bi, &ti->br);
return 0;
}
-static int reader_start(struct reftable_reader *r, struct table_iter *ti,
- uint8_t typ, int index)
+static int table_iter_seek_start(struct table_iter *ti, uint8_t typ, int index)
{
- struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
+ struct reftable_reader_offsets *offs = reader_offsets_for(ti->r, typ);
uint64_t off = offs->offset;
if (index) {
off = offs->index_offset;
@@ -416,16 +395,16 @@ static int reader_start(struct reftable_reader *r, struct table_iter *ti,
typ = BLOCK_TYPE_INDEX;
}
- return reader_table_iter_at(r, ti, off, typ);
+ return table_iter_seek_to(ti, off, typ);
}
-static int reader_seek_linear(struct table_iter *ti,
- struct reftable_record *want)
+static int table_iter_seek_linear(struct table_iter *ti,
+ struct reftable_record *want)
{
struct strbuf want_key = STRBUF_INIT;
struct strbuf got_key = STRBUF_INIT;
struct reftable_record rec;
- int err = -1;
+ int err;
reftable_record_init(&rec, reftable_record_type(want));
reftable_record_key(want, &want_key);
@@ -499,9 +478,8 @@ static int reader_seek_linear(struct table_iter *ti,
return err;
}
-static int reader_seek_indexed(struct reftable_reader *r,
- struct reftable_iterator *it,
- struct reftable_record *rec)
+static int table_iter_seek_indexed(struct table_iter *ti,
+ struct reftable_record *rec)
{
struct reftable_record want_index = {
.type = BLOCK_TYPE_INDEX, .u.idx = { .last_key = STRBUF_INIT }
@@ -510,15 +488,9 @@ static int reader_seek_indexed(struct reftable_reader *r,
.type = BLOCK_TYPE_INDEX,
.u.idx = { .last_key = STRBUF_INIT },
};
- struct table_iter index_iter = TABLE_ITER_INIT;
- struct table_iter empty = TABLE_ITER_INIT;
- struct table_iter next = TABLE_ITER_INIT;
- int err = 0;
+ int err;
reftable_record_key(rec, &want_index.u.idx.last_key);
- err = reader_start(r, &index_iter, reftable_record_type(rec), 1);
- if (err < 0)
- goto done;
/*
* The index may consist of multiple levels, where each level may have
@@ -526,7 +498,7 @@ static int reader_seek_indexed(struct reftable_reader *r,
* highest layer that identifies the relevant index block as well as
* the record inside that block that corresponds to our wanted key.
*/
- err = reader_seek_linear(&index_iter, &want_index);
+ err = table_iter_seek_linear(ti, &want_index);
if (err < 0)
goto done;
@@ -552,123 +524,113 @@ static int reader_seek_indexed(struct reftable_reader *r,
* all levels of the index only to find out that the key does
* not exist.
*/
- err = table_iter_next(&index_iter, &index_result);
+ err = table_iter_next(ti, &index_result);
if (err != 0)
goto done;
- err = reader_table_iter_at(r, &next, index_result.u.idx.offset,
- 0);
+ err = table_iter_seek_to(ti, index_result.u.idx.offset, 0);
if (err != 0)
goto done;
- err = block_iter_seek_key(&next.bi, &next.br, &want_index.u.idx.last_key);
+ err = block_iter_seek_key(&ti->bi, &ti->br, &want_index.u.idx.last_key);
if (err < 0)
goto done;
- if (next.typ == reftable_record_type(rec)) {
+ if (ti->typ == reftable_record_type(rec)) {
err = 0;
break;
}
- if (next.typ != BLOCK_TYPE_INDEX) {
+ if (ti->typ != BLOCK_TYPE_INDEX) {
err = REFTABLE_FORMAT_ERROR;
- break;
+ goto done;
}
-
- table_iter_close(&index_iter);
- index_iter = next;
- next = empty;
- }
-
- if (err == 0) {
- struct table_iter *malloced = reftable_calloc(1, sizeof(*malloced));
- *malloced = next;
- next = empty;
- iterator_from_table_iter(it, malloced);
}
done:
- table_iter_close(&next);
- table_iter_close(&index_iter);
reftable_record_release(&want_index);
reftable_record_release(&index_result);
return err;
}
-static int reader_seek_internal(struct reftable_reader *r,
- struct reftable_iterator *it,
- struct reftable_record *rec)
+static int table_iter_seek(struct table_iter *ti,
+ struct reftable_record *want)
{
- struct reftable_reader_offsets *offs =
- reader_offsets_for(r, reftable_record_type(rec));
- uint64_t idx = offs->index_offset;
- struct table_iter ti = TABLE_ITER_INIT, *p;
+ uint8_t typ = reftable_record_type(want);
+ struct reftable_reader_offsets *offs = reader_offsets_for(ti->r, typ);
int err;
- if (idx > 0)
- return reader_seek_indexed(r, it, rec);
-
- err = reader_start(r, &ti, reftable_record_type(rec), 0);
+ err = table_iter_seek_start(ti, reftable_record_type(want),
+ !!offs->index_offset);
if (err < 0)
goto out;
- err = reader_seek_linear(&ti, rec);
- if (err < 0)
+ if (offs->index_offset)
+ err = table_iter_seek_indexed(ti, want);
+ else
+ err = table_iter_seek_linear(ti, want);
+ if (err)
goto out;
- REFTABLE_ALLOC_ARRAY(p, 1);
- *p = ti;
- iterator_from_table_iter(it, p);
-
out:
- if (err)
- table_iter_close(&ti);
return err;
}
-static int reader_seek(struct reftable_reader *r, struct reftable_iterator *it,
- struct reftable_record *rec)
+static int table_iter_seek_void(void *ti, struct reftable_record *want)
{
- uint8_t typ = reftable_record_type(rec);
+ return table_iter_seek(ti, want);
+}
+static int table_iter_next_void(void *ti, struct reftable_record *rec)
+{
+ return table_iter_next(ti, rec);
+}
+
+static void table_iter_close_void(void *ti)
+{
+ table_iter_close(ti);
+}
+
+static struct reftable_iterator_vtable table_iter_vtable = {
+ .seek = &table_iter_seek_void,
+ .next = &table_iter_next_void,
+ .close = &table_iter_close_void,
+};
+
+static void iterator_from_table_iter(struct reftable_iterator *it,
+ struct table_iter *ti)
+{
+ assert(!it->ops);
+ it->iter_arg = ti;
+ it->ops = &table_iter_vtable;
+}
+
+static void reader_init_iter(struct reftable_reader *r,
+ struct reftable_iterator *it,
+ uint8_t typ)
+{
struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
- if (!offs->is_present) {
+
+ if (offs->is_present) {
+ struct table_iter *ti;
+ REFTABLE_ALLOC_ARRAY(ti, 1);
+ table_iter_init(ti, r);
+ iterator_from_table_iter(it, ti);
+ } else {
iterator_set_empty(it);
- return 0;
}
-
- return reader_seek_internal(r, it, rec);
}
-int reftable_reader_seek_ref(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name)
+void reftable_reader_init_ref_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it)
{
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = (char *)name,
- },
- };
- return reader_seek(r, it, &rec);
+ reader_init_iter(r, it, BLOCK_TYPE_REF);
}
-int reftable_reader_seek_log_at(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name,
- uint64_t update_index)
+void reftable_reader_init_log_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it)
{
- struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = (char *)name,
- .update_index = update_index,
- } };
- return reader_seek(r, it, &rec);
-}
-
-int reftable_reader_seek_log(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name)
-{
- uint64_t max = ~((uint64_t)0);
- return reftable_reader_seek_log_at(r, it, name, max);
+ reader_init_iter(r, it, BLOCK_TYPE_LOG);
}
void reader_close(struct reftable_reader *r)
@@ -719,7 +681,8 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
struct indexed_table_ref_iter *itr = NULL;
/* Look through the reverse index. */
- err = reader_seek(r, &oit, &want);
+ reader_init_iter(r, &oit, BLOCK_TYPE_OBJ);
+ err = iterator_seek(&oit, &want);
if (err != 0)
goto done;
@@ -754,15 +717,15 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
struct reftable_iterator *it,
uint8_t *oid)
{
- struct table_iter ti_empty = TABLE_ITER_INIT;
- struct table_iter *ti = reftable_calloc(1, sizeof(*ti));
+ struct table_iter *ti;
struct filtering_ref_iterator *filter = NULL;
struct filtering_ref_iterator empty = FILTERING_REF_ITERATOR_INIT;
int oid_len = hash_size(r->hash_id);
int err;
- *ti = ti_empty;
- err = reader_start(r, ti, BLOCK_TYPE_REF, 0);
+ REFTABLE_ALLOC_ARRAY(ti, 1);
+ table_iter_init(ti, r);
+ err = table_iter_seek_start(ti, BLOCK_TYPE_REF, 0);
if (err < 0) {
reftable_free(ti);
return err;
@@ -800,10 +763,11 @@ uint64_t reftable_reader_min_update_index(struct reftable_reader *r)
/* generic table interface. */
-static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
- struct reftable_record *rec)
+static void reftable_reader_init_iter_void(void *tab,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- return reader_seek(tab, it, rec);
+ reader_init_iter(tab, it, typ);
}
static uint32_t reftable_reader_hash_id_void(void *tab)
@@ -822,7 +786,7 @@ static uint64_t reftable_reader_max_update_index_void(void *tab)
}
static struct reftable_table_vtable reader_vtable = {
- .seek_record = reftable_reader_seek_void,
+ .init_iter = reftable_reader_init_iter_void,
.hash_id = reftable_reader_hash_id_void,
.min_update_index = reftable_reader_min_update_index_void,
.max_update_index = reftable_reader_max_update_index_void,
@@ -856,3 +820,68 @@ int reftable_reader_print_file(const char *tablename)
reftable_reader_free(r);
return err;
}
+
+int reftable_reader_print_blocks(const char *tablename)
+{
+ struct {
+ const char *name;
+ int type;
+ } sections[] = {
+ {
+ .name = "ref",
+ .type = BLOCK_TYPE_REF,
+ },
+ {
+ .name = "obj",
+ .type = BLOCK_TYPE_OBJ,
+ },
+ {
+ .name = "log",
+ .type = BLOCK_TYPE_LOG,
+ },
+ };
+ struct reftable_block_source src = { 0 };
+ struct reftable_reader *r = NULL;
+ struct table_iter ti = { 0 };
+ size_t i;
+ int err;
+
+ err = reftable_block_source_from_file(&src, tablename);
+ if (err < 0)
+ goto done;
+
+ err = reftable_new_reader(&r, &src, tablename);
+ if (err < 0)
+ goto done;
+
+ table_iter_init(&ti, r);
+
+ printf("header:\n");
+ printf(" block_size: %d\n", r->block_size);
+
+ for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ err = table_iter_seek_start(&ti, sections[i].type, 0);
+ if (err < 0)
+ goto done;
+ if (err > 0)
+ continue;
+
+ printf("%s:\n", sections[i].name);
+
+ while (1) {
+ printf(" - length: %u\n", ti.br.block_len);
+ printf(" restarts: %u\n", ti.br.restart_count);
+
+ err = table_iter_next_block(&ti);
+ if (err < 0)
+ goto done;
+ if (err > 0)
+ break;
+ }
+ }
+
+done:
+ reftable_reader_free(r);
+ table_iter_close(&ti);
+ return err;
+}
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index a6dbd21..f411abf 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -86,7 +86,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
log.update_index = update_index;
log.value_type = REFTABLE_LOG_UPDATE;
set_test_hash(log.value.update.new_hash, i);
- log.value.update.message = "message";
+ log.value.update.message = (char *) "message";
n = reftable_writer_add_log(w, &log);
EXPECT(n == 0);
@@ -118,15 +118,15 @@ static void test_log_buffer_size(void)
int err;
int i;
struct reftable_log_record
- log = { .refname = "refs/heads/master",
+ log = { .refname = (char *) "refs/heads/master",
.update_index = 0xa,
.value_type = REFTABLE_LOG_UPDATE,
.value = { .update = {
- .name = "Han-Wen Nienhuys",
- .email = "hanwen@google.com",
+ .name = (char *) "Han-Wen Nienhuys",
+ .email = (char *) "hanwen@google.com",
.tz_offset = 100,
.time = 0x5e430672,
- .message = "commit: 9\n",
+ .message = (char *) "commit: 9\n",
} } };
struct reftable_writer *w =
reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
@@ -156,15 +156,15 @@ static void test_log_overflow(void)
};
int err;
struct reftable_log_record log = {
- .refname = "refs/heads/master",
+ .refname = (char *) "refs/heads/master",
.update_index = 0xa,
.value_type = REFTABLE_LOG_UPDATE,
.value = {
.update = {
.old_hash = { 1 },
.new_hash = { 2 },
- .name = "Han-Wen Nienhuys",
- .email = "hanwen@google.com",
+ .name = (char *) "Han-Wen Nienhuys",
+ .email = (char *) "hanwen@google.com",
.tz_offset = 100,
.time = 0x5e430672,
.message = msg,
@@ -239,7 +239,9 @@ static void test_log_write_read(void)
err = init_reader(&rd, &source, "file.log");
EXPECT_ERR(err);
- err = reftable_reader_seek_ref(&rd, &it, names[N - 1]);
+ reftable_reader_init_ref_iterator(&rd, &it);
+
+ err = reftable_iterator_seek_ref(&it, names[N - 1]);
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
@@ -252,7 +254,9 @@ static void test_log_write_read(void)
reftable_iterator_destroy(&it);
reftable_ref_record_release(&ref);
- err = reftable_reader_seek_log(&rd, &it, "");
+ reftable_reader_init_log_iterator(&rd, &it);
+
+ err = reftable_iterator_seek_log(&it, "");
EXPECT_ERR(err);
i = 0;
@@ -293,14 +297,14 @@ static void test_log_zlib_corruption(void)
char message[100] = { 0 };
int err, i, n;
struct reftable_log_record log = {
- .refname = "refname",
+ .refname = (char *) "refname",
.value_type = REFTABLE_LOG_UPDATE,
.value = {
.update = {
.new_hash = { 1 },
.old_hash = { 2 },
- .name = "My Name",
- .email = "myname@invalid",
+ .name = (char *) "My Name",
+ .email = (char *) "myname@invalid",
.message = message,
},
},
@@ -330,7 +334,8 @@ static void test_log_zlib_corruption(void)
err = init_reader(&rd, &source, "file.log");
EXPECT_ERR(err);
- err = reftable_reader_seek_log(&rd, &it, "refname");
+ reftable_reader_init_log_iterator(&rd, &it);
+ err = reftable_iterator_seek_log(&it, "refname");
EXPECT(err == REFTABLE_ZLIB_ERROR);
reftable_iterator_destroy(&it);
@@ -358,7 +363,8 @@ static void test_table_read_write_sequential(void)
err = init_reader(&rd, &source, "file.ref");
EXPECT_ERR(err);
- err = reftable_reader_seek_ref(&rd, &it, "");
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
while (1) {
@@ -412,7 +418,8 @@ static void test_table_read_api(void)
err = init_reader(&rd, &source, "file.ref");
EXPECT_ERR(err);
- err = reftable_reader_seek_ref(&rd, &it, names[0]);
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, names[0]);
EXPECT_ERR(err);
err = reftable_iterator_next_log(&it, &log);
@@ -457,7 +464,8 @@ static void test_table_read_write_seek(int index, int hash_id)
}
for (i = 1; i < N; i++) {
- int err = reftable_reader_seek_ref(&rd, &it, names[i]);
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, names[i]);
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
EXPECT_ERR(err);
@@ -472,7 +480,8 @@ static void test_table_read_write_seek(int index, int hash_id)
strbuf_addstr(&pastLast, names[N - 1]);
strbuf_addstr(&pastLast, "/");
- err = reftable_reader_seek_ref(&rd, &it, pastLast.buf);
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, pastLast.buf);
if (err == 0) {
struct reftable_ref_record ref = { NULL };
int err = reftable_iterator_next_ref(&it, &ref);
@@ -576,7 +585,8 @@ static void test_table_refs_for(int indexed)
rd.obj_offsets.is_present = 0;
}
- err = reftable_reader_seek_ref(&rd, &it, "");
+ reftable_reader_init_ref_iterator(&rd, &it);
+ err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);
@@ -639,7 +649,8 @@ static void test_write_empty_table(void)
err = reftable_new_reader(&rd, &source, "filename");
EXPECT_ERR(err);
- err = reftable_reader_seek_ref(rd, &it, "");
+ reftable_reader_init_ref_iterator(rd, &it);
+ err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &rec);
@@ -728,7 +739,7 @@ static void test_write_empty_key(void)
struct reftable_writer *w =
reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record ref = {
- .refname = "",
+ .refname = (char *) "",
.update_index = 1,
.value_type = REFTABLE_REF_DELETION,
};
@@ -752,18 +763,18 @@ static void test_write_key_order(void)
reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record refs[2] = {
{
- .refname = "b",
+ .refname = (char *) "b",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
.value = {
- .symref = "target",
+ .symref = (char *) "target",
},
}, {
- .refname = "a",
+ .refname = (char *) "a",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
.value = {
- .symref = "target",
+ .symref = (char *) "target",
},
}
};
@@ -846,7 +857,8 @@ static void test_write_multiple_indices(void)
* Seeking the log uses the log index now. In case there is any
* confusion regarding indices we would notice here.
*/
- err = reftable_reader_seek_log(reader, &it, "");
+ reftable_reader_init_log_iterator(reader, &it);
+ err = reftable_iterator_seek_log(&it, "");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);
@@ -901,7 +913,8 @@ static void test_write_multi_level_index(void)
/*
* Seeking the last ref should work as expected.
*/
- err = reftable_reader_seek_ref(reader, &it, "refs/heads/199");
+ reftable_reader_init_ref_iterator(reader, &it);
+ err = reftable_iterator_seek_ref(&it, "refs/heads/199");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);
diff --git a/reftable/record.c b/reftable/record.c
index 5506f3e..a2cba5e 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -116,7 +116,7 @@ static int decode_string(struct strbuf *dest, struct string_view in)
return start_len - in.len;
}
-static int encode_string(char *str, struct string_view s)
+static int encode_string(const char *str, struct string_view s)
{
struct string_view start = s;
int l = strlen(str);
@@ -969,9 +969,9 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
return REFTABLE_FORMAT_ERROR;
}
-static int null_streq(char *a, char *b)
+static int null_streq(const char *a, const char *b)
{
- char *empty = "";
+ const char *empty = "";
if (!a)
a = empty;
diff --git a/reftable/record_test.c b/reftable/record_test.c
index c158ee7..58290bd 100644
--- a/reftable/record_test.c
+++ b/reftable/record_test.c
@@ -64,31 +64,6 @@ static void test_varint_roundtrip(void)
}
}
-static void test_common_prefix(void)
-{
- struct {
- const char *a, *b;
- int want;
- } cases[] = {
- { "abc", "ab", 2 },
- { "", "abc", 0 },
- { "abc", "abd", 2 },
- { "abc", "pqr", 0 },
- };
-
- int i = 0;
- for (i = 0; i < ARRAY_SIZE(cases); i++) {
- struct strbuf a = STRBUF_INIT;
- struct strbuf b = STRBUF_INIT;
- strbuf_addstr(&a, cases[i].a);
- strbuf_addstr(&b, cases[i].b);
- EXPECT(common_prefix_size(&a, &b) == cases[i].want);
-
- strbuf_release(&a);
- strbuf_release(&b);
- }
-}
-
static void set_hash(uint8_t *h, int j)
{
int i = 0;
@@ -258,16 +233,6 @@ static void test_reftable_log_record_roundtrip(void)
strbuf_release(&scratch);
}
-static void test_u24_roundtrip(void)
-{
- uint32_t in = 0x112233;
- uint8_t dest[3];
- uint32_t out;
- put_be24(dest, in);
- out = get_be24(dest);
- EXPECT(in == out);
-}
-
static void test_key_roundtrip(void)
{
uint8_t buffer[1024] = { 0 };
@@ -411,9 +376,7 @@ int record_test_main(int argc, const char *argv[])
RUN_TEST(test_reftable_ref_record_roundtrip);
RUN_TEST(test_varint_roundtrip);
RUN_TEST(test_key_roundtrip);
- RUN_TEST(test_common_prefix);
RUN_TEST(test_reftable_obj_record_roundtrip);
RUN_TEST(test_reftable_index_record_roundtrip);
- RUN_TEST(test_u24_roundtrip);
return 0;
}
diff --git a/reftable/refname.c b/reftable/refname.c
deleted file mode 100644
index bbfde15..0000000
--- a/reftable/refname.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- Copyright 2020 Google LLC
-
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file or at
- https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "system.h"
-#include "reftable-error.h"
-#include "basics.h"
-#include "refname.h"
-#include "reftable-iterator.h"
-
-struct refname_needle_lesseq_args {
- char **haystack;
- const char *needle;
-};
-
-static int refname_needle_lesseq(size_t k, void *_args)
-{
- struct refname_needle_lesseq_args *args = _args;
- return strcmp(args->needle, args->haystack[k]) <= 0;
-}
-
-static int modification_has_ref(struct modification *mod, const char *name)
-{
- struct reftable_ref_record ref = { NULL };
- int err = 0;
-
- if (mod->add_len > 0) {
- struct refname_needle_lesseq_args args = {
- .haystack = mod->add,
- .needle = name,
- };
- size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
- if (idx < mod->add_len && !strcmp(mod->add[idx], name))
- return 0;
- }
-
- if (mod->del_len > 0) {
- struct refname_needle_lesseq_args args = {
- .haystack = mod->del,
- .needle = name,
- };
- size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
- if (idx < mod->del_len && !strcmp(mod->del[idx], name))
- return 1;
- }
-
- err = reftable_table_read_ref(&mod->tab, name, &ref);
- reftable_ref_record_release(&ref);
- return err;
-}
-
-static void modification_release(struct modification *mod)
-{
- /* don't delete the strings themselves; they're owned by ref records.
- */
- FREE_AND_NULL(mod->add);
- FREE_AND_NULL(mod->del);
- mod->add_len = 0;
- mod->del_len = 0;
-}
-
-static int modification_has_ref_with_prefix(struct modification *mod,
- const char *prefix)
-{
- struct reftable_iterator it = { NULL };
- struct reftable_ref_record ref = { NULL };
- int err = 0;
-
- if (mod->add_len > 0) {
- struct refname_needle_lesseq_args args = {
- .haystack = mod->add,
- .needle = prefix,
- };
- size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
- if (idx < mod->add_len &&
- !strncmp(prefix, mod->add[idx], strlen(prefix)))
- goto done;
- }
- err = reftable_table_seek_ref(&mod->tab, &it, prefix);
- if (err)
- goto done;
-
- while (1) {
- err = reftable_iterator_next_ref(&it, &ref);
- if (err)
- goto done;
-
- if (mod->del_len > 0) {
- struct refname_needle_lesseq_args args = {
- .haystack = mod->del,
- .needle = ref.refname,
- };
- size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
- if (idx < mod->del_len &&
- !strcmp(ref.refname, mod->del[idx]))
- continue;
- }
-
- if (strncmp(ref.refname, prefix, strlen(prefix))) {
- err = 1;
- goto done;
- }
- err = 0;
- goto done;
- }
-
-done:
- reftable_ref_record_release(&ref);
- reftable_iterator_destroy(&it);
- return err;
-}
-
-static int validate_refname(const char *name)
-{
- while (1) {
- char *next = strchr(name, '/');
- if (!*name) {
- return REFTABLE_REFNAME_ERROR;
- }
- if (!next) {
- return 0;
- }
- if (next - name == 0 || (next - name == 1 && *name == '.') ||
- (next - name == 2 && name[0] == '.' && name[1] == '.'))
- return REFTABLE_REFNAME_ERROR;
- name = next + 1;
- }
- return 0;
-}
-
-int validate_ref_record_addition(struct reftable_table tab,
- struct reftable_ref_record *recs, size_t sz)
-{
- struct modification mod = {
- .tab = tab,
- .add = reftable_calloc(sz, sizeof(*mod.add)),
- .del = reftable_calloc(sz, sizeof(*mod.del)),
- };
- int i = 0;
- int err = 0;
- for (; i < sz; i++) {
- if (reftable_ref_record_is_deletion(&recs[i])) {
- mod.del[mod.del_len++] = recs[i].refname;
- } else {
- mod.add[mod.add_len++] = recs[i].refname;
- }
- }
-
- err = modification_validate(&mod);
- modification_release(&mod);
- return err;
-}
-
-static void strbuf_trim_component(struct strbuf *sl)
-{
- while (sl->len > 0) {
- int is_slash = (sl->buf[sl->len - 1] == '/');
- strbuf_setlen(sl, sl->len - 1);
- if (is_slash)
- break;
- }
-}
-
-int modification_validate(struct modification *mod)
-{
- struct strbuf slashed = STRBUF_INIT;
- int err = 0;
- int i = 0;
- for (; i < mod->add_len; i++) {
- err = validate_refname(mod->add[i]);
- if (err)
- goto done;
- strbuf_reset(&slashed);
- strbuf_addstr(&slashed, mod->add[i]);
- strbuf_addstr(&slashed, "/");
-
- err = modification_has_ref_with_prefix(mod, slashed.buf);
- if (err == 0) {
- err = REFTABLE_NAME_CONFLICT;
- goto done;
- }
- if (err < 0)
- goto done;
-
- strbuf_reset(&slashed);
- strbuf_addstr(&slashed, mod->add[i]);
- while (slashed.len) {
- strbuf_trim_component(&slashed);
- err = modification_has_ref(mod, slashed.buf);
- if (err == 0) {
- err = REFTABLE_NAME_CONFLICT;
- goto done;
- }
- if (err < 0)
- goto done;
- }
- }
- err = 0;
-done:
- strbuf_release(&slashed);
- return err;
-}
diff --git a/reftable/refname.h b/reftable/refname.h
deleted file mode 100644
index a24b40f..0000000
--- a/reftable/refname.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- Copyright 2020 Google LLC
-
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file or at
- https://developers.google.com/open-source/licenses/bsd
-*/
-#ifndef REFNAME_H
-#define REFNAME_H
-
-#include "reftable-record.h"
-#include "reftable-generic.h"
-
-struct modification {
- struct reftable_table tab;
-
- char **add;
- size_t add_len;
-
- char **del;
- size_t del_len;
-};
-
-int validate_ref_record_addition(struct reftable_table tab,
- struct reftable_ref_record *recs, size_t sz);
-
-int modification_validate(struct modification *mod);
-
-#endif
diff --git a/reftable/refname_test.c b/reftable/refname_test.c
deleted file mode 100644
index b9cc625..0000000
--- a/reftable/refname_test.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "basics.h"
-#include "block.h"
-#include "blocksource.h"
-#include "reader.h"
-#include "record.h"
-#include "refname.h"
-#include "reftable-error.h"
-#include "reftable-writer.h"
-#include "system.h"
-
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-struct testcase {
- char *add;
- char *del;
- int error_code;
-};
-
-static void test_conflict(void)
-{
- struct reftable_write_options opts = { 0 };
- struct strbuf buf = STRBUF_INIT;
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
- struct reftable_ref_record rec = {
- .refname = "a/b",
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = "destination", /* make sure it's not a symref.
- */
- .update_index = 1,
- };
- int err;
- int i;
- struct reftable_block_source source = { NULL };
- struct reftable_reader *rd = NULL;
- struct reftable_table tab = { NULL };
- struct testcase cases[] = {
- { "a/b/c", NULL, REFTABLE_NAME_CONFLICT },
- { "b", NULL, 0 },
- { "a", NULL, REFTABLE_NAME_CONFLICT },
- { "a", "a/b", 0 },
-
- { "p/", NULL, REFTABLE_REFNAME_ERROR },
- { "p//q", NULL, REFTABLE_REFNAME_ERROR },
- { "p/./q", NULL, REFTABLE_REFNAME_ERROR },
- { "p/../q", NULL, REFTABLE_REFNAME_ERROR },
-
- { "a/b/c", "a/b", 0 },
- { NULL, "a//b", 0 },
- };
- reftable_writer_set_limits(w, 1, 1);
-
- err = reftable_writer_add_ref(w, &rec);
- EXPECT_ERR(err);
-
- err = reftable_writer_close(w);
- EXPECT_ERR(err);
- reftable_writer_free(w);
-
- block_source_from_strbuf(&source, &buf);
- err = reftable_new_reader(&rd, &source, "filename");
- EXPECT_ERR(err);
-
- reftable_table_from_reader(&tab, rd);
-
- for (i = 0; i < ARRAY_SIZE(cases); i++) {
- struct modification mod = {
- .tab = tab,
- };
-
- if (cases[i].add) {
- mod.add = &cases[i].add;
- mod.add_len = 1;
- }
- if (cases[i].del) {
- mod.del = &cases[i].del;
- mod.del_len = 1;
- }
-
- err = modification_validate(&mod);
- EXPECT(err == cases[i].error_code);
- }
-
- reftable_reader_free(rd);
- strbuf_release(&buf);
-}
-
-int refname_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_conflict);
- return 0;
-}
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index e9b07c9..6368cd9 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -48,9 +48,6 @@ enum reftable_error {
/* Wrote a table without blocks. */
REFTABLE_EMPTY_TABLE_ERROR = -8,
- /* Dir/file conflict. */
- REFTABLE_NAME_CONFLICT = -9,
-
/* Invalid ref name. */
REFTABLE_REFNAME_ERROR = -10,
diff --git a/reftable/reftable-generic.h b/reftable/reftable-generic.h
index d239751..65670ea 100644
--- a/reftable/reftable-generic.h
+++ b/reftable/reftable-generic.h
@@ -21,11 +21,11 @@ struct reftable_table {
void *table_arg;
};
-int reftable_table_seek_log(struct reftable_table *tab,
- struct reftable_iterator *it, const char *name);
+void reftable_table_init_ref_iter(struct reftable_table *tab,
+ struct reftable_iterator *it);
-int reftable_table_seek_ref(struct reftable_table *tab,
- struct reftable_iterator *it, const char *name);
+void reftable_table_init_log_iter(struct reftable_table *tab,
+ struct reftable_iterator *it);
/* returns the hash ID from a generic reftable_table */
uint32_t reftable_table_hash_id(struct reftable_table *tab);
diff --git a/reftable/reftable-iterator.h b/reftable/reftable-iterator.h
index d3eee7a..e3bf688 100644
--- a/reftable/reftable-iterator.h
+++ b/reftable/reftable-iterator.h
@@ -21,12 +21,33 @@ struct reftable_iterator {
void *iter_arg;
};
+/*
+ * Position the iterator at the ref record with given name such that the next
+ * call to `next_ref()` would yield the record.
+ */
+int reftable_iterator_seek_ref(struct reftable_iterator *it,
+ const char *name);
+
/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0:
* end of iteration.
*/
int reftable_iterator_next_ref(struct reftable_iterator *it,
struct reftable_ref_record *ref);
+/*
+ * Position the iterator at the log record with given name and update index
+ * such that the next call to `next_log()` would yield the record.
+ */
+int reftable_iterator_seek_log_at(struct reftable_iterator *it,
+ const char *name, uint64_t update_index);
+
+/*
+ * Position the iterator at the newest log record with given name such that the
+ * next call to `next_log()` would yield the record.
+ */
+int reftable_iterator_seek_log(struct reftable_iterator *it,
+ const char *name);
+
/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0:
* end of iteration.
*/
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index c91a2d8..14d5fc9 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -36,21 +36,6 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
struct reftable_table *stack, size_t n,
uint32_t hash_id);
-/* returns an iterator positioned just before 'name' */
-int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name);
-
-/* returns an iterator for log entry, at given update_index */
-int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name, uint64_t update_index);
-
-/* like reftable_merged_table_seek_log_at but look for the newest entry. */
-int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- const char *name);
-
/* returns the max update_index covered by this merged table. */
uint64_t
reftable_merged_table_max_update_index(struct reftable_merged_table *mt);
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index 4a4bc2f..a32f31d 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -36,48 +36,17 @@ struct reftable_table;
int reftable_new_reader(struct reftable_reader **pp,
struct reftable_block_source *src, const char *name);
-/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted
- in the table. To seek to the start of the table, use name = "".
+/* Initialize a reftable iterator for reading refs. */
+void reftable_reader_init_ref_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it);
- example:
-
- struct reftable_reader *r = NULL;
- int err = reftable_new_reader(&r, &src, "filename");
- if (err < 0) { ... }
- struct reftable_iterator it = {0};
- err = reftable_reader_seek_ref(r, &it, "refs/heads/master");
- if (err < 0) { ... }
- struct reftable_ref_record ref = {0};
- while (1) {
- err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0) {
- break;
- }
- if (err < 0) {
- ..error handling..
- }
- ..found..
- }
- reftable_iterator_destroy(&it);
- reftable_ref_record_release(&ref);
-*/
-int reftable_reader_seek_ref(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name);
+/* Initialize a reftable iterator for reading logs. */
+void reftable_reader_init_log_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it);
/* returns the hash ID used in this table. */
uint32_t reftable_reader_hash_id(struct reftable_reader *r);
-/* seek to logs for the given name, older than update_index. To seek to the
- start of the table, use name = "".
-*/
-int reftable_reader_seek_log_at(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name,
- uint64_t update_index);
-
-/* seek to newest log entry for given name. */
-int reftable_reader_seek_log(struct reftable_reader *r,
- struct reftable_iterator *it, const char *name);
-
/* closes and deallocates a reader. */
void reftable_reader_free(struct reftable_reader *);
@@ -97,5 +66,7 @@ void reftable_table_from_reader(struct reftable_table *tab,
/* print table onto stdout for debugging. */
int reftable_reader_print_file(const char *tablename);
+/* print blocks onto stdout for debugging. */
+int reftable_reader_print_blocks(const char *tablename);
#endif
diff --git a/reftable/reftable-record.h b/reftable/reftable-record.h
index 2a2943c..ff486eb 100644
--- a/reftable/reftable-record.h
+++ b/reftable/reftable-record.h
@@ -9,7 +9,7 @@ license that can be found in the LICENSE file or at
#ifndef REFTABLE_RECORD_H
#define REFTABLE_RECORD_H
-#include "hash-ll.h"
+#include "hash.h"
#include <stdint.h>
/*
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index 1b602dd..09e97c9 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -29,7 +29,7 @@ struct reftable_stack;
* stored in 'dir'. Typically, this should be .git/reftables.
*/
int reftable_new_stack(struct reftable_stack **dest, const char *dir,
- struct reftable_write_options config);
+ const struct reftable_write_options *opts);
/* returns the update_index at which a next table should be written. */
uint64_t reftable_stack_next_update_index(struct reftable_stack *st);
@@ -66,6 +66,24 @@ int reftable_stack_add(struct reftable_stack *st,
void *write_arg),
void *write_arg);
+struct reftable_iterator;
+
+/*
+ * Initialize an iterator for the merged tables contained in the stack that can
+ * be used to iterate through refs. The iterator is valid until the next reload
+ * or write.
+ */
+void reftable_stack_init_ref_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it);
+
+/*
+ * Initialize an iterator for the merged tables contained in the stack that can
+ * be used to iterate through logs. The iterator is valid until the next reload
+ * or write.
+ */
+void reftable_stack_init_log_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it);
+
/* returns the merged_table for seeking. This table is valid until the
* next write or reload, and should not be closed or deleted.
*/
diff --git a/reftable/reftable-tests.h b/reftable/reftable-tests.h
index 0019cbc..114cc3d 100644
--- a/reftable/reftable-tests.h
+++ b/reftable/reftable-tests.h
@@ -14,7 +14,6 @@ int block_test_main(int argc, const char **argv);
int merged_test_main(int argc, const char **argv);
int pq_test_main(int argc, const char **argv);
int record_test_main(int argc, const char **argv);
-int refname_test_main(int argc, const char **argv);
int readwrite_test_main(int argc, const char **argv);
int stack_test_main(int argc, const char **argv);
int tree_test_main(int argc, const char **argv);
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 155bf0b..189b1f4 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -28,7 +28,7 @@ struct reftable_write_options {
unsigned skip_index_objects : 1;
/* how often to write complete keys in each block. */
- int restart_interval;
+ uint16_t restart_interval;
/* 4-byte identifier ("sha1", "s256") of the hash.
* Defaults to SHA1 if unset
@@ -38,10 +38,6 @@ struct reftable_write_options {
/* Default mode for creating files. If unset, use 0666 (+umask) */
unsigned int default_permissions;
- /* boolean: do not check ref names for validity or dir/file conflicts.
- */
- unsigned skip_name_check : 1;
-
/* boolean: copy log messages exactly. If unset, check that the message
* is a single line, and add '\n' if missing.
*/
@@ -49,6 +45,12 @@ struct reftable_write_options {
/* boolean: Prevent auto-compaction of tables. */
unsigned disable_auto_compact : 1;
+
+ /*
+ * Geometric sequence factor used by auto-compaction to decide which
+ * tables to compact. Defaults to 2 if unset.
+ */
+ uint8_t auto_compaction_factor;
};
/* reftable_block_stats holds statistics for a single block type */
@@ -92,7 +94,7 @@ struct reftable_stats {
struct reftable_writer *
reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
int (*flush_func)(void *),
- void *writer_arg, struct reftable_write_options *opts);
+ void *writer_arg, const struct reftable_write_options *opts);
/* Set the range of update indices for the records we will add. When writing a
table into a stack, the min should be at least
diff --git a/reftable/stack.c b/reftable/stack.c
index 80266bc..7375911 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -10,10 +10,11 @@ license that can be found in the LICENSE file or at
#include "../write-or-die.h"
#include "system.h"
+#include "constants.h"
#include "merged.h"
#include "reader.h"
-#include "refname.h"
#include "reftable-error.h"
+#include "reftable-generic.h"
#include "reftable-record.h"
#include "reftable-merged.h"
#include "writer.h"
@@ -27,8 +28,6 @@ static int stack_write_compact(struct reftable_stack *st,
struct reftable_writer *wr,
size_t first, size_t last,
struct reftable_log_expiry_config *config);
-static int stack_check_addition(struct reftable_stack *st,
- const char *new_tab_name);
static void reftable_addition_close(struct reftable_addition *add);
static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
int reuse_open);
@@ -56,15 +55,17 @@ static int reftable_fd_flush(void *arg)
}
int reftable_new_stack(struct reftable_stack **dest, const char *dir,
- struct reftable_write_options config)
+ const struct reftable_write_options *_opts)
{
struct reftable_stack *p = reftable_calloc(1, sizeof(*p));
struct strbuf list_file_name = STRBUF_INIT;
+ struct reftable_write_options opts = {0};
int err = 0;
- if (config.hash_id == 0) {
- config.hash_id = GIT_SHA1_FORMAT_ID;
- }
+ if (_opts)
+ opts = *_opts;
+ if (opts.hash_id == 0)
+ opts.hash_id = GIT_SHA1_FORMAT_ID;
*dest = NULL;
@@ -75,7 +76,7 @@ int reftable_new_stack(struct reftable_stack **dest, const char *dir,
p->list_file = strbuf_detach(&list_file_name, NULL);
p->list_fd = -1;
p->reftable_dir = xstrdup(dir);
- p->config = config;
+ p->opts = opts;
err = reftable_stack_reload_maybe_reuse(p, 1);
if (err < 0) {
@@ -132,6 +133,20 @@ int read_lines(const char *filename, char ***namesp)
return err;
}
+void reftable_stack_init_ref_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it)
+{
+ merged_table_init_iter(reftable_stack_merged_table(st),
+ it, BLOCK_TYPE_REF);
+}
+
+void reftable_stack_init_log_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it)
+{
+ merged_table_init_iter(reftable_stack_merged_table(st),
+ it, BLOCK_TYPE_LOG);
+}
+
struct reftable_merged_table *
reftable_stack_merged_table(struct reftable_stack *st)
{
@@ -206,7 +221,8 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
return cur;
}
-static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
+static int reftable_stack_reload_once(struct reftable_stack *st,
+ const char **names,
int reuse_open)
{
size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
@@ -224,7 +240,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
while (*names) {
struct reftable_reader *rd = NULL;
- char *name = *names++;
+ const char *name = *names++;
/* this is linear; we assume compaction keeps the number of
tables under control so this is not quadratic. */
@@ -257,16 +273,14 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
/* success! */
err = reftable_new_merged_table(&new_merged, new_tables,
- new_readers_len, st->config.hash_id);
+ new_readers_len, st->opts.hash_id);
if (err < 0)
goto done;
new_tables = NULL;
st->readers_len = new_readers_len;
- if (st->merged) {
- merged_table_release(st->merged);
+ if (st->merged)
reftable_merged_table_free(st->merged);
- }
if (st->readers) {
reftable_free(st->readers);
}
@@ -356,7 +370,7 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
goto out;
}
- err = reftable_stack_reload_once(st, names, reuse_open);
+ err = reftable_stack_reload_once(st, (const char **) names, reuse_open);
if (!err)
break;
if (err != REFTABLE_NOT_EXIST_ERROR)
@@ -370,7 +384,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
err = read_lines(st->list_file, &names_after);
if (err < 0)
goto out;
- if (names_equal(names_after, names)) {
+ if (names_equal((const char **) names_after,
+ (const char **) names)) {
err = REFTABLE_NOT_EXIST_ERROR;
goto out;
}
@@ -580,8 +595,8 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
}
goto done;
}
- if (st->config.default_permissions) {
- if (chmod(add->lock_file->filename.buf, st->config.default_permissions) < 0) {
+ if (st->opts.default_permissions) {
+ if (chmod(add->lock_file->filename.buf, st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
@@ -680,7 +695,7 @@ int reftable_addition_commit(struct reftable_addition *add)
if (err)
goto done;
- if (!add->stack->config.disable_auto_compact) {
+ if (!add->stack->opts.disable_auto_compact) {
/*
* Auto-compact the stack to keep the number of tables in
* control. It is possible that a concurrent writer is already
@@ -758,9 +773,9 @@ int reftable_addition_add(struct reftable_addition *add,
err = REFTABLE_IO_ERROR;
goto done;
}
- if (add->stack->config.default_permissions) {
+ if (add->stack->opts.default_permissions) {
if (chmod(get_tempfile_path(tab_file),
- add->stack->config.default_permissions)) {
+ add->stack->opts.default_permissions)) {
err = REFTABLE_IO_ERROR;
goto done;
}
@@ -768,7 +783,7 @@ int reftable_addition_add(struct reftable_addition *add,
tab_fd = get_tempfile_fd(tab_file);
wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush, &tab_fd,
- &add->stack->config);
+ &add->stack->opts);
err = write_table(wr, arg);
if (err < 0)
goto done;
@@ -787,10 +802,6 @@ int reftable_addition_add(struct reftable_addition *add,
goto done;
}
- err = stack_check_addition(add->stack, get_tempfile_path(tab_file));
- if (err < 0)
- goto done;
-
if (wr->min_update_index < add->next_update_index) {
err = REFTABLE_API_ERROR;
goto done;
@@ -855,14 +866,14 @@ static int stack_compact_locked(struct reftable_stack *st,
}
tab_fd = get_tempfile_fd(tab_file);
- if (st->config.default_permissions &&
- chmod(get_tempfile_path(tab_file), st->config.default_permissions) < 0) {
+ if (st->opts.default_permissions &&
+ chmod(get_tempfile_path(tab_file), st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush,
- &tab_fd, &st->config);
+ &tab_fd, &st->opts);
err = stack_write_compact(st, wr, first, last, config);
if (err < 0)
goto done;
@@ -910,13 +921,14 @@ static int stack_write_compact(struct reftable_stack *st,
st->readers[last]->max_update_index);
err = reftable_new_merged_table(&mt, subtabs, subtabs_len,
- st->config.hash_id);
+ st->opts.hash_id);
if (err < 0) {
reftable_free(subtabs);
goto done;
}
- err = reftable_merged_table_seek_ref(mt, &it, "");
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ err = reftable_iterator_seek_ref(&it, "");
if (err < 0)
goto done;
@@ -940,7 +952,8 @@ static int stack_write_compact(struct reftable_stack *st,
}
reftable_iterator_destroy(&it);
- err = reftable_merged_table_seek_log(mt, &it, "");
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
+ err = reftable_iterator_seek_log(&it, "");
if (err < 0)
goto done;
@@ -974,10 +987,8 @@ static int stack_write_compact(struct reftable_stack *st,
done:
reftable_iterator_destroy(&it);
- if (mt) {
- merged_table_release(mt);
+ if (mt)
reftable_merged_table_free(mt);
- }
reftable_ref_record_release(&ref);
reftable_log_record_release(&log);
st->stats.entries_written += entries;
@@ -1100,9 +1111,9 @@ static int stack_compact_range(struct reftable_stack *st,
goto done;
}
- if (st->config.default_permissions) {
+ if (st->opts.default_permissions) {
if (chmod(get_lock_file_path(&tables_list_lock),
- st->config.default_permissions) < 0) {
+ st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
@@ -1216,12 +1227,16 @@ static int segment_size(struct segment *s)
return s->end - s->start;
}
-struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
+struct segment suggest_compaction_segment(uint64_t *sizes, size_t n,
+ uint8_t factor)
{
struct segment seg = { 0 };
uint64_t bytes;
size_t i;
+ if (!factor)
+ factor = DEFAULT_GEOMETRIC_FACTOR;
+
/*
* If there are no tables or only a single one then we don't have to
* compact anything. The sequence is geometric by definition already.
@@ -1253,7 +1268,7 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
* 64, 32, 16, 8, 4, 3, 1
*/
for (i = n - 1; i > 0; i--) {
- if (sizes[i - 1] < sizes[i] * 2) {
+ if (sizes[i - 1] < sizes[i] * factor) {
seg.end = i + 1;
bytes = sizes[i];
break;
@@ -1279,7 +1294,7 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
uint64_t curr = bytes;
bytes += sizes[i - 1];
- if (sizes[i - 1] < curr * 2) {
+ if (sizes[i - 1] < curr * factor) {
seg.start = i - 1;
seg.bytes = bytes;
}
@@ -1292,7 +1307,7 @@ static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
{
uint64_t *sizes =
reftable_calloc(st->merged->stack_len, sizeof(*sizes));
- int version = (st->config.hash_id == GIT_SHA1_FORMAT_ID) ? 1 : 2;
+ int version = (st->opts.hash_id == GIT_SHA1_FORMAT_ID) ? 1 : 2;
int overhead = header_size(version) - 1;
int i = 0;
for (i = 0; i < st->merged->stack_len; i++) {
@@ -1305,7 +1320,8 @@ int reftable_stack_auto_compact(struct reftable_stack *st)
{
uint64_t *sizes = stack_table_sizes_for_compaction(st);
struct segment seg =
- suggest_compaction_segment(sizes, st->merged->stack_len);
+ suggest_compaction_segment(sizes, st->merged->stack_len,
+ st->opts.auto_compaction_factor);
reftable_free(sizes);
if (segment_size(&seg) > 0)
return stack_compact_range_stats(st, seg.start, seg.end - 1,
@@ -1331,9 +1347,11 @@ int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
struct reftable_log_record *log)
{
- struct reftable_iterator it = { NULL };
- struct reftable_merged_table *mt = reftable_stack_merged_table(st);
- int err = reftable_merged_table_seek_log(mt, &it, refname);
+ struct reftable_iterator it = {0};
+ int err;
+
+ reftable_stack_init_log_iterator(st, &it);
+ err = reftable_iterator_seek_log(&it, refname);
if (err)
goto done;
@@ -1355,65 +1373,6 @@ int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
return err;
}
-static int stack_check_addition(struct reftable_stack *st,
- const char *new_tab_name)
-{
- int err = 0;
- struct reftable_block_source src = { NULL };
- struct reftable_reader *rd = NULL;
- struct reftable_table tab = { NULL };
- struct reftable_ref_record *refs = NULL;
- struct reftable_iterator it = { NULL };
- int cap = 0;
- int len = 0;
- int i = 0;
-
- if (st->config.skip_name_check)
- return 0;
-
- err = reftable_block_source_from_file(&src, new_tab_name);
- if (err < 0)
- goto done;
-
- err = reftable_new_reader(&rd, &src, new_tab_name);
- if (err < 0)
- goto done;
-
- err = reftable_reader_seek_ref(rd, &it, "");
- if (err > 0) {
- err = 0;
- goto done;
- }
- if (err < 0)
- goto done;
-
- while (1) {
- struct reftable_ref_record ref = { NULL };
- err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0)
- break;
- if (err < 0)
- goto done;
-
- REFTABLE_ALLOC_GROW(refs, len + 1, cap);
- refs[len++] = ref;
- }
-
- reftable_table_from_merged_table(&tab, reftable_stack_merged_table(st));
-
- err = validate_ref_record_addition(tab, refs, len);
-
-done:
- for (i = 0; i < len; i++) {
- reftable_ref_record_release(&refs[i]);
- }
-
- free(refs);
- reftable_iterator_destroy(&it);
- reftable_reader_free(rd);
- return err;
-}
-
static int is_table_name(const char *s)
{
const char *dot = strrchr(s, '.');
@@ -1500,11 +1459,11 @@ int reftable_stack_clean(struct reftable_stack *st)
int reftable_stack_print_directory(const char *stackdir, uint32_t hash_id)
{
struct reftable_stack *stack = NULL;
- struct reftable_write_options cfg = { .hash_id = hash_id };
+ struct reftable_write_options opts = { .hash_id = hash_id };
struct reftable_merged_table *merged = NULL;
struct reftable_table table = { NULL };
- int err = reftable_new_stack(&stack, stackdir, cfg);
+ int err = reftable_new_stack(&stack, stackdir, &opts);
if (err < 0)
goto done;
diff --git a/reftable/stack.h b/reftable/stack.h
index d43efa4..5b45cff 100644
--- a/reftable/stack.h
+++ b/reftable/stack.h
@@ -20,7 +20,7 @@ struct reftable_stack {
char *reftable_dir;
- struct reftable_write_options config;
+ struct reftable_write_options opts;
struct reftable_reader **readers;
size_t readers_len;
@@ -35,6 +35,7 @@ struct segment {
uint64_t bytes;
};
-struct segment suggest_compaction_segment(uint64_t *sizes, size_t n);
+struct segment suggest_compaction_segment(uint64_t *sizes, size_t n,
+ uint8_t factor);
#endif
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 1df3ffc..e3c11e6 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -83,7 +83,7 @@ static void test_read_file(void)
char out[1024] = "line1\n\nline2\nline3";
int n, err;
char **names = NULL;
- char *want[] = { "line1", "line2", "line3" };
+ const char *want[] = { "line1", "line2", "line3" };
int i = 0;
EXPECT(fd > 0);
@@ -102,29 +102,6 @@ static void test_read_file(void)
(void) remove(fn);
}
-static void test_parse_names(void)
-{
- char buf[] = "line\n";
- char **names = NULL;
- parse_names(buf, strlen(buf), &names);
-
- EXPECT(NULL != names[0]);
- EXPECT(0 == strcmp(names[0], "line"));
- EXPECT(NULL == names[1]);
- free_names(names);
-}
-
-static void test_names_equal(void)
-{
- char *a[] = { "a", "b", "c", NULL };
- char *b[] = { "a", "b", "d", NULL };
- char *c[] = { "a", "b", NULL };
-
- EXPECT(names_equal(a, a));
- EXPECT(!names_equal(a, b));
- EXPECT(!names_equal(a, c));
-}
-
static int write_test_ref(struct reftable_writer *wr, void *arg)
{
struct reftable_ref_record *ref = arg;
@@ -150,20 +127,20 @@ static void test_reftable_stack_add_one(void)
char *dir = get_tmp_dir(__LINE__);
struct strbuf scratch = STRBUF_INIT;
int mask = umask(002);
- struct reftable_write_options cfg = {
+ struct reftable_write_options opts = {
.default_permissions = 0660,
};
struct reftable_stack *st = NULL;
int err;
struct reftable_ref_record ref = {
- .refname = "HEAD",
+ .refname = (char *) "HEAD",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
struct reftable_ref_record dest = { NULL };
struct stat stat_result = { 0 };
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, &write_test_ref, &ref);
@@ -186,7 +163,7 @@ static void test_reftable_stack_add_one(void)
strbuf_addstr(&scratch, "/tables.list");
err = stat(scratch.buf, &stat_result);
EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+ EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
strbuf_reset(&scratch);
strbuf_addstr(&scratch, dir);
@@ -195,7 +172,7 @@ static void test_reftable_stack_add_one(void)
strbuf_addstr(&scratch, st->readers[0]->name);
err = stat(scratch.buf, &stat_result);
EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+ EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
#else
(void) stat_result;
#endif
@@ -209,33 +186,33 @@ static void test_reftable_stack_add_one(void)
static void test_reftable_stack_uptodate(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL;
struct reftable_stack *st2 = NULL;
char *dir = get_tmp_dir(__LINE__);
int err;
struct reftable_ref_record ref1 = {
- .refname = "HEAD",
+ .refname = (char *) "HEAD",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
struct reftable_ref_record ref2 = {
- .refname = "branch2",
+ .refname = (char *) "branch2",
.update_index = 2,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
/* simulate multi-process access to the same stack
by creating two stacks for the same directory.
*/
- err = reftable_new_stack(&st1, dir, cfg);
+ err = reftable_new_stack(&st1, dir, &opts);
EXPECT_ERR(err);
- err = reftable_new_stack(&st2, dir, cfg);
+ err = reftable_new_stack(&st2, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st1, &write_test_ref, &ref1);
@@ -257,22 +234,20 @@ static void test_reftable_stack_uptodate(void)
static void test_reftable_stack_transaction_api(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
struct reftable_addition *add = NULL;
struct reftable_ref_record ref = {
- .refname = "HEAD",
+ .refname = (char *) "HEAD",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
struct reftable_ref_record dest = { NULL };
-
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
reftable_addition_destroy(add);
@@ -301,19 +276,19 @@ static void test_reftable_stack_transaction_api(void)
static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
{
char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options cfg = {0};
+ struct reftable_write_options opts = {0};
struct reftable_addition *add = NULL;
struct reftable_stack *st = NULL;
int i, n = 20, err;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i <= n; i++) {
struct reftable_ref_record ref = {
.update_index = reftable_stack_next_update_index(st),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
char name[100];
@@ -325,7 +300,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
* we can ensure that we indeed honor this setting and have
* better control over when exactly auto compaction runs.
*/
- st->config.disable_auto_compact = i != n;
+ st->opts.disable_auto_compact = i != n;
err = reftable_stack_new_addition(&add, st);
EXPECT_ERR(err);
@@ -356,18 +331,18 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
static void test_reftable_stack_auto_compaction_fails_gracefully(void)
{
struct reftable_ref_record ref = {
- .refname = "refs/heads/master",
+ .refname = (char *) "refs/heads/master",
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
.value.val1 = {0x01},
};
- struct reftable_write_options cfg = {0};
+ struct reftable_write_options opts = {0};
struct reftable_stack *st;
struct strbuf table_path = STRBUF_INIT;
char *dir = get_tmp_dir(__LINE__);
int err;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, write_test_ref, &ref);
@@ -396,44 +371,6 @@ static void test_reftable_stack_auto_compaction_fails_gracefully(void)
clear_dir(dir);
}
-static void test_reftable_stack_validate_refname(void)
-{
- struct reftable_write_options cfg = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- char *dir = get_tmp_dir(__LINE__);
-
- int i;
- struct reftable_ref_record ref = {
- .refname = "a/b",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
- };
- char *additions[] = { "a", "a/b/c" };
-
- err = reftable_new_stack(&st, dir, cfg);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- EXPECT_ERR(err);
-
- for (i = 0; i < ARRAY_SIZE(additions); i++) {
- struct reftable_ref_record ref = {
- .refname = additions[i],
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
- };
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- EXPECT(err == REFTABLE_NAME_CONFLICT);
- }
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
static int write_error(struct reftable_writer *wr, void *arg)
{
return *((int *)arg);
@@ -442,24 +379,23 @@ static int write_error(struct reftable_writer *wr, void *arg)
static void test_reftable_stack_update_index_check(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
struct reftable_ref_record ref1 = {
- .refname = "name1",
+ .refname = (char *) "name1",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
struct reftable_ref_record ref2 = {
- .refname = "name2",
+ .refname = (char *) "name2",
.update_index = 1,
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, &write_test_ref, &ref1);
@@ -474,12 +410,11 @@ static void test_reftable_stack_update_index_check(void)
static void test_reftable_stack_lock_failure(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err, i;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
err = reftable_stack_add(st, &write_error, &i);
@@ -494,7 +429,7 @@ static void test_reftable_stack_add(void)
{
int i = 0;
int err = 0;
- struct reftable_write_options cfg = {
+ struct reftable_write_options opts = {
.exact_log_message = 1,
.default_permissions = 0660,
.disable_auto_compact = 1,
@@ -507,7 +442,7 @@ static void test_reftable_stack_add(void)
struct stat stat_result;
int N = ARRAY_SIZE(refs);
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i < N; i++) {
@@ -566,7 +501,7 @@ static void test_reftable_stack_add(void)
strbuf_addstr(&path, "/tables.list");
err = stat(path.buf, &stat_result);
EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+ EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
strbuf_reset(&path);
strbuf_addstr(&path, dir);
@@ -575,7 +510,7 @@ static void test_reftable_stack_add(void)
strbuf_addstr(&path, st->readers[0]->name);
err = stat(path.buf, &stat_result);
EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+ EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
#else
(void) stat_result;
#endif
@@ -593,13 +528,13 @@ static void test_reftable_stack_add(void)
static void test_reftable_stack_log_normalize(void)
{
int err = 0;
- struct reftable_write_options cfg = {
+ struct reftable_write_options opts = {
0,
};
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
struct reftable_log_record input = {
- .refname = "branch",
+ .refname = (char *) "branch",
.update_index = 1,
.value_type = REFTABLE_LOG_UPDATE,
.value = {
@@ -617,14 +552,14 @@ static void test_reftable_stack_log_normalize(void)
.update_index = 1,
};
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
- input.value.update.message = "one\ntwo";
+ input.value.update.message = (char *) "one\ntwo";
err = reftable_stack_add(st, &write_test_log, &arg);
EXPECT(err == REFTABLE_API_ERROR);
- input.value.update.message = "one";
+ input.value.update.message = (char *) "one";
err = reftable_stack_add(st, &write_test_log, &arg);
EXPECT_ERR(err);
@@ -632,7 +567,7 @@ static void test_reftable_stack_log_normalize(void)
EXPECT_ERR(err);
EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
- input.value.update.message = "two\n";
+ input.value.update.message = (char *) "two\n";
arg.update_index = 2;
err = reftable_stack_add(st, &write_test_log, &arg);
EXPECT_ERR(err);
@@ -650,8 +585,7 @@ static void test_reftable_stack_tombstone(void)
{
int i = 0;
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
struct reftable_ref_record refs[2] = { { NULL } };
@@ -660,8 +594,7 @@ static void test_reftable_stack_tombstone(void)
struct reftable_ref_record dest = { NULL };
struct reftable_log_record log_dest = { NULL };
-
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
/* even entries add the refs, odd entries delete them. */
@@ -729,35 +662,34 @@ static void test_reftable_stack_tombstone(void)
static void test_reftable_stack_hash_id(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
struct reftable_ref_record ref = {
- .refname = "master",
+ .refname = (char *) "master",
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "target",
+ .value.symref = (char *) "target",
.update_index = 1,
};
- struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
+ struct reftable_write_options opts32 = { .hash_id = GIT_SHA256_FORMAT_ID };
struct reftable_stack *st32 = NULL;
- struct reftable_write_options cfg_default = { 0 };
+ struct reftable_write_options opts_default = { 0 };
struct reftable_stack *st_default = NULL;
struct reftable_ref_record dest = { NULL };
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, &write_test_ref, &ref);
EXPECT_ERR(err);
/* can't read it with the wrong hash ID. */
- err = reftable_new_stack(&st32, dir, cfg32);
+ err = reftable_new_stack(&st32, dir, &opts32);
EXPECT(err == REFTABLE_FORMAT_ERROR);
- /* check that we can read it back with default config too. */
- err = reftable_new_stack(&st_default, dir, cfg_default);
+ /* check that we can read it back with default opts too. */
+ err = reftable_new_stack(&st_default, dir, &opts_default);
EXPECT_ERR(err);
err = reftable_stack_read_ref(st_default, "master", &dest);
@@ -774,7 +706,7 @@ static void test_suggest_compaction_segment(void)
{
uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
struct segment min =
- suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
+ suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
EXPECT(min.start == 1);
EXPECT(min.end == 10);
}
@@ -783,15 +715,14 @@ static void test_suggest_compaction_segment_nothing(void)
{
uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
struct segment result =
- suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
+ suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
EXPECT(result.start == result.end);
}
static void test_reflog_expire(void)
{
char *dir = get_tmp_dir(__LINE__);
-
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct reftable_log_record logs[20] = { { NULL } };
int N = ARRAY_SIZE(logs) - 1;
@@ -802,8 +733,7 @@ static void test_reflog_expire(void)
};
struct reftable_log_record log = { NULL };
-
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 1; i <= N; i++) {
@@ -866,21 +796,19 @@ static int write_nothing(struct reftable_writer *wr, void *arg)
static void test_empty_add(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int err;
char *dir = get_tmp_dir(__LINE__);
-
struct reftable_stack *st2 = NULL;
-
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_add(st, &write_nothing, NULL);
EXPECT_ERR(err);
- err = reftable_new_stack(&st2, dir, cfg);
+ err = reftable_new_stack(&st2, dir, &opts);
EXPECT_ERR(err);
clear_dir(dir);
reftable_stack_destroy(st);
@@ -899,16 +827,15 @@ static int fastlog2(uint64_t sz)
static void test_reftable_stack_auto_compaction(void)
{
- struct reftable_write_options cfg = {
+ struct reftable_write_options opts = {
.disable_auto_compact = 1,
};
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
-
int err, i;
int N = 100;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i < N; i++) {
@@ -917,7 +844,7 @@ static void test_reftable_stack_auto_compaction(void)
.refname = name,
.update_index = reftable_stack_next_update_index(st),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
snprintf(name, sizeof(name), "branch%04d", i);
@@ -938,20 +865,20 @@ static void test_reftable_stack_auto_compaction(void)
static void test_reftable_stack_add_performs_auto_compaction(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct strbuf refname = STRBUF_INIT;
char *dir = get_tmp_dir(__LINE__);
int err, i, n = 20;
- err = reftable_new_stack(&st, dir, cfg);
+ err = reftable_new_stack(&st, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i <= n; i++) {
struct reftable_ref_record ref = {
.update_index = reftable_stack_next_update_index(st),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
/*
@@ -959,7 +886,7 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
* we can ensure that we indeed honor this setting and have
* better control over when exactly auto compaction runs.
*/
- st->config.disable_auto_compact = i != n;
+ st->opts.disable_auto_compact = i != n;
strbuf_reset(&refname);
strbuf_addf(&refname, "branch-%04d", i);
@@ -986,14 +913,13 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
static void test_reftable_stack_compaction_concurrent(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL, *st2 = NULL;
char *dir = get_tmp_dir(__LINE__);
-
int err, i;
int N = 3;
- err = reftable_new_stack(&st1, dir, cfg);
+ err = reftable_new_stack(&st1, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i < N; i++) {
@@ -1002,7 +928,7 @@ static void test_reftable_stack_compaction_concurrent(void)
.refname = name,
.update_index = reftable_stack_next_update_index(st1),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
snprintf(name, sizeof(name), "branch%04d", i);
@@ -1010,7 +936,7 @@ static void test_reftable_stack_compaction_concurrent(void)
EXPECT_ERR(err);
}
- err = reftable_new_stack(&st2, dir, cfg);
+ err = reftable_new_stack(&st2, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_compact_all(st1, NULL);
@@ -1036,14 +962,13 @@ static void unclean_stack_close(struct reftable_stack *st)
static void test_reftable_stack_compaction_concurrent_clean(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
char *dir = get_tmp_dir(__LINE__);
-
int err, i;
int N = 3;
- err = reftable_new_stack(&st1, dir, cfg);
+ err = reftable_new_stack(&st1, dir, &opts);
EXPECT_ERR(err);
for (i = 0; i < N; i++) {
@@ -1052,7 +977,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
.refname = name,
.update_index = reftable_stack_next_update_index(st1),
.value_type = REFTABLE_REF_SYMREF,
- .value.symref = "master",
+ .value.symref = (char *) "master",
};
snprintf(name, sizeof(name), "branch%04d", i);
@@ -1060,7 +985,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
EXPECT_ERR(err);
}
- err = reftable_new_stack(&st2, dir, cfg);
+ err = reftable_new_stack(&st2, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_compact_all(st1, NULL);
@@ -1069,7 +994,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
unclean_stack_close(st1);
unclean_stack_close(st2);
- err = reftable_new_stack(&st3, dir, cfg);
+ err = reftable_new_stack(&st3, dir, &opts);
EXPECT_ERR(err);
err = reftable_stack_clean(st3);
@@ -1086,8 +1011,6 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
int stack_test_main(int argc, const char *argv[])
{
RUN_TEST(test_empty_add);
- RUN_TEST(test_names_equal);
- RUN_TEST(test_parse_names);
RUN_TEST(test_read_file);
RUN_TEST(test_reflog_expire);
RUN_TEST(test_reftable_stack_add);
@@ -1105,7 +1028,6 @@ int stack_test_main(int argc, const char *argv[])
RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
RUN_TEST(test_reftable_stack_update_index_check);
RUN_TEST(test_reftable_stack_uptodate);
- RUN_TEST(test_reftable_stack_validate_refname);
RUN_TEST(test_suggest_compaction_segment);
RUN_TEST(test_suggest_compaction_segment_nothing);
return 0;
diff --git a/reftable/system.h b/reftable/system.h
index 5d8b6de..d0cabd5 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -15,7 +15,7 @@ license that can be found in the LICENSE file or at
#include "lockfile.h"
#include "strbuf.h"
#include "tempfile.h"
-#include "hash-ll.h" /* hash ID, sizes.*/
+#include "hash.h" /* hash ID, sizes.*/
#include "dir.h" /* remove_dir_recursively, for tests.*/
int hash_size(uint32_t id);
diff --git a/reftable/writer.c b/reftable/writer.c
index 1d9ff0f..45b3e9c 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -109,7 +109,7 @@ static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
block_start = header_size(writer_version(w));
}
- strbuf_release(&w->last_key);
+ strbuf_reset(&w->last_key);
block_writer_init(&w->block_writer_data, typ, w->block,
w->opts.block_size, block_start,
hash_size(w->opts.hash_id));
@@ -117,25 +117,26 @@ static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
w->block_writer->restart_interval = w->opts.restart_interval;
}
-static struct strbuf reftable_empty_strbuf = STRBUF_INIT;
-
struct reftable_writer *
reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
int (*flush_func)(void *),
- void *writer_arg, struct reftable_write_options *opts)
+ void *writer_arg, const struct reftable_write_options *_opts)
{
struct reftable_writer *wp = reftable_calloc(1, sizeof(*wp));
+ struct reftable_write_options opts = {0};
+
+ if (_opts)
+ opts = *_opts;
+ options_set_defaults(&opts);
+ if (opts.block_size >= (1 << 24))
+ BUG("configured block size exceeds 16MB");
+
strbuf_init(&wp->block_writer_data.last_key, 0);
- options_set_defaults(opts);
- if (opts->block_size >= (1 << 24)) {
- /* TODO - error return? */
- abort();
- }
- wp->last_key = reftable_empty_strbuf;
- REFTABLE_CALLOC_ARRAY(wp->block, opts->block_size);
+ strbuf_init(&wp->last_key, 0);
+ REFTABLE_CALLOC_ARRAY(wp->block, opts.block_size);
wp->write = writer_func;
wp->write_arg = writer_arg;
- wp->opts = *opts;
+ wp->opts = opts;
wp->flush = flush_func;
writer_reinit_block_writer(wp, BLOCK_TYPE_REF);
@@ -149,11 +150,21 @@ void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
w->max_update_index = max;
}
+static void writer_release(struct reftable_writer *w)
+{
+ if (w) {
+ reftable_free(w->block);
+ w->block = NULL;
+ block_writer_release(&w->block_writer_data);
+ w->block_writer = NULL;
+ writer_clear_index(w);
+ strbuf_release(&w->last_key);
+ }
+}
+
void reftable_writer_free(struct reftable_writer *w)
{
- if (!w)
- return;
- reftable_free(w->block);
+ writer_release(w);
reftable_free(w);
}
@@ -209,7 +220,8 @@ static int writer_add_record(struct reftable_writer *w,
struct reftable_record *rec)
{
struct strbuf key = STRBUF_INIT;
- int err = -1;
+ int err;
+
reftable_record_key(rec, &key);
if (strbuf_cmp(&w->last_key, &key) >= 0) {
err = REFTABLE_API_ERROR;
@@ -218,27 +230,42 @@ static int writer_add_record(struct reftable_writer *w,
strbuf_reset(&w->last_key);
strbuf_addbuf(&w->last_key, &key);
- if (!w->block_writer) {
+ if (!w->block_writer)
writer_reinit_block_writer(w, reftable_record_type(rec));
- }
- assert(block_writer_type(w->block_writer) == reftable_record_type(rec));
+ if (block_writer_type(w->block_writer) != reftable_record_type(rec))
+ BUG("record of type %d added to writer of type %d",
+ reftable_record_type(rec), block_writer_type(w->block_writer));
- if (block_writer_add(w->block_writer, rec) == 0) {
+ /*
+ * Try to add the record to the writer. If this succeeds then we're
+ * done. Otherwise the block writer may have hit the block size limit
+ * and needs to be flushed.
+ */
+ if (!block_writer_add(w->block_writer, rec)) {
err = 0;
goto done;
}
+ /*
+ * The current block is full, so we need to flush and reinitialize the
+ * writer to start writing the next block.
+ */
err = writer_flush_block(w);
- if (err < 0) {
+ if (err < 0)
goto done;
- }
-
writer_reinit_block_writer(w, reftable_record_type(rec));
+
+ /*
+ * Try to add the record to the writer again. If this still fails then
+ * the record does not fit into the block size.
+ *
+ * TODO: it would be great to have `block_writer_add()` return proper
+ * error codes so that we don't have to second-guess the failure
+ * mode here.
+ */
err = block_writer_add(w->block_writer, rec);
- if (err == -1) {
- /* we are writing into memory, so an error can only mean it
- * doesn't fit. */
+ if (err) {
err = REFTABLE_ENTRY_TOO_BIG_ERROR;
goto done;
}
@@ -452,7 +479,7 @@ static int writer_finish_section(struct reftable_writer *w)
bstats->max_index_level = max_level;
/* Reinit lastKey, as the next section can start with any key. */
- w->last_key.len = 0;
+ strbuf_reset(&w->last_key);
return 0;
}
@@ -627,74 +654,87 @@ int reftable_writer_close(struct reftable_writer *w)
}
done:
- /* free up memory. */
- block_writer_release(&w->block_writer_data);
- writer_clear_index(w);
- strbuf_release(&w->last_key);
+ writer_release(w);
return err;
}
static void writer_clear_index(struct reftable_writer *w)
{
- for (size_t i = 0; i < w->index_len; i++)
+ for (size_t i = 0; w->index && i < w->index_len; i++)
strbuf_release(&w->index[i].last_key);
FREE_AND_NULL(w->index);
w->index_len = 0;
w->index_cap = 0;
}
-static const int debug = 0;
-
static int writer_flush_nonempty_block(struct reftable_writer *w)
{
+ struct reftable_index_record index_record = {
+ .last_key = STRBUF_INIT,
+ };
uint8_t typ = block_writer_type(w->block_writer);
- struct reftable_block_stats *bstats =
- writer_reftable_block_stats(w, typ);
- uint64_t block_typ_off = (bstats->blocks == 0) ? w->next : 0;
- int raw_bytes = block_writer_finish(w->block_writer);
- int padding = 0;
- int err = 0;
- struct reftable_index_record ir = { .last_key = STRBUF_INIT };
+ struct reftable_block_stats *bstats;
+ int raw_bytes, padding = 0, err;
+ uint64_t block_typ_off;
+
+ /*
+ * Finish the current block. This will cause the block writer to emit
+ * restart points and potentially compress records in case we are
+ * writing a log block.
+ *
+ * Note that this is still happening in memory.
+ */
+ raw_bytes = block_writer_finish(w->block_writer);
if (raw_bytes < 0)
return raw_bytes;
- if (!w->opts.unpadded && typ != BLOCK_TYPE_LOG) {
+ /*
+ * By default, all records except for log records are padded to the
+ * block size.
+ */
+ if (!w->opts.unpadded && typ != BLOCK_TYPE_LOG)
padding = w->opts.block_size - raw_bytes;
- }
- if (block_typ_off > 0) {
+ bstats = writer_reftable_block_stats(w, typ);
+ block_typ_off = (bstats->blocks == 0) ? w->next : 0;
+ if (block_typ_off > 0)
bstats->offset = block_typ_off;
- }
-
bstats->entries += w->block_writer->entries;
bstats->restarts += w->block_writer->restart_len;
bstats->blocks++;
w->stats.blocks++;
- if (debug) {
- fprintf(stderr, "block %c off %" PRIu64 " sz %d (%d)\n", typ,
- w->next, raw_bytes,
- get_be24(w->block + w->block_writer->header_off + 1));
- }
-
- if (w->next == 0) {
+ /*
+ * If this is the first block we're writing to the table then we need
+ * to also write the reftable header.
+ */
+ if (!w->next)
writer_write_header(w, w->block);
- }
err = padded_write(w, w->block, raw_bytes, padding);
if (err < 0)
return err;
+ /*
+ * Add an index record for every block that we're writing. If we end up
+ * having more than a threshold of index records we will end up writing
+ * an index section in `writer_finish_section()`. Each index record
+ * contains the last record key of the block it is indexing as well as
+ * the offset of that block.
+ *
+ * Note that this also applies when flushing index blocks, in which
+ * case we will end up with a multi-level index.
+ */
REFTABLE_ALLOC_GROW(w->index, w->index_len + 1, w->index_cap);
-
- ir.offset = w->next;
- strbuf_reset(&ir.last_key);
- strbuf_addbuf(&ir.last_key, &w->block_writer->last_key);
- w->index[w->index_len] = ir;
-
+ index_record.offset = w->next;
+ strbuf_reset(&index_record.last_key);
+ strbuf_addbuf(&index_record.last_key, &w->block_writer->last_key);
+ w->index[w->index_len] = index_record;
w->index_len++;
+
w->next += padding + raw_bytes;
w->block_writer = NULL;
+
return 0;
}
diff --git a/remote-curl.c b/remote-curl.c
index 0b6d781..4adcf25 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "git-curl-compat.h"
#include "config.h"
@@ -58,9 +60,9 @@ struct options {
static struct options options;
static struct string_list cas_options = STRING_LIST_INIT_DUP;
-static int set_option(const char *name, const char *value)
+static int set_option(const char *name, size_t namelen, const char *value)
{
- if (!strcmp(name, "verbosity")) {
+ if (!strncmp(name, "verbosity", namelen)) {
char *end;
int v = strtol(value, &end, 10);
if (value == end || *end)
@@ -68,7 +70,7 @@ static int set_option(const char *name, const char *value)
options.verbosity = v;
return 0;
}
- else if (!strcmp(name, "progress")) {
+ else if (!strncmp(name, "progress", namelen)) {
if (!strcmp(value, "true"))
options.progress = 1;
else if (!strcmp(value, "false"))
@@ -77,7 +79,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "depth")) {
+ else if (!strncmp(name, "depth", namelen)) {
char *end;
unsigned long v = strtoul(value, &end, 10);
if (value == end || *end)
@@ -85,15 +87,15 @@ static int set_option(const char *name, const char *value)
options.depth = v;
return 0;
}
- else if (!strcmp(name, "deepen-since")) {
+ else if (!strncmp(name, "deepen-since", namelen)) {
options.deepen_since = xstrdup(value);
return 0;
}
- else if (!strcmp(name, "deepen-not")) {
+ else if (!strncmp(name, "deepen-not", namelen)) {
string_list_append(&options.deepen_not, value);
return 0;
}
- else if (!strcmp(name, "deepen-relative")) {
+ else if (!strncmp(name, "deepen-relative", namelen)) {
if (!strcmp(value, "true"))
options.deepen_relative = 1;
else if (!strcmp(value, "false"))
@@ -102,7 +104,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "followtags")) {
+ else if (!strncmp(name, "followtags", namelen)) {
if (!strcmp(value, "true"))
options.followtags = 1;
else if (!strcmp(value, "false"))
@@ -111,7 +113,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "dry-run")) {
+ else if (!strncmp(name, "dry-run", namelen)) {
if (!strcmp(value, "true"))
options.dry_run = 1;
else if (!strcmp(value, "false"))
@@ -120,7 +122,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "check-connectivity")) {
+ else if (!strncmp(name, "check-connectivity", namelen)) {
if (!strcmp(value, "true"))
options.check_self_contained_and_connected = 1;
else if (!strcmp(value, "false"))
@@ -129,7 +131,7 @@ static int set_option(const char *name, const char *value)
return -1;
return 0;
}
- else if (!strcmp(name, "cas")) {
+ else if (!strncmp(name, "cas", namelen)) {
struct strbuf val = STRBUF_INIT;
strbuf_addstr(&val, "--force-with-lease=");
if (*value != '"')
@@ -139,7 +141,7 @@ static int set_option(const char *name, const char *value)
string_list_append(&cas_options, val.buf);
strbuf_release(&val);
return 0;
- } else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+ } else if (!strncmp(name, TRANS_OPT_FORCE_IF_INCLUDES, namelen)) {
if (!strcmp(value, "true"))
options.force_if_includes = 1;
else if (!strcmp(value, "false"))
@@ -147,7 +149,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "cloning")) {
+ } else if (!strncmp(name, "cloning", namelen)) {
if (!strcmp(value, "true"))
options.cloning = 1;
else if (!strcmp(value, "false"))
@@ -155,7 +157,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "update-shallow")) {
+ } else if (!strncmp(name, "update-shallow", namelen)) {
if (!strcmp(value, "true"))
options.update_shallow = 1;
else if (!strcmp(value, "false"))
@@ -163,7 +165,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "pushcert")) {
+ } else if (!strncmp(name, "pushcert", namelen)) {
if (!strcmp(value, "true"))
options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
else if (!strcmp(value, "false"))
@@ -173,7 +175,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "atomic")) {
+ } else if (!strncmp(name, "atomic", namelen)) {
if (!strcmp(value, "true"))
options.atomic = 1;
else if (!strcmp(value, "false"))
@@ -181,7 +183,7 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "push-option")) {
+ } else if (!strncmp(name, "push-option", namelen)) {
if (*value != '"')
string_list_append(&options.push_options, value);
else {
@@ -192,7 +194,7 @@ static int set_option(const char *name, const char *value)
strbuf_detach(&unquoted, NULL));
}
return 0;
- } else if (!strcmp(name, "family")) {
+ } else if (!strncmp(name, "family", namelen)) {
if (!strcmp(value, "ipv4"))
git_curl_ipresolve = CURL_IPRESOLVE_V4;
else if (!strcmp(value, "ipv6"))
@@ -202,16 +204,16 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
- } else if (!strcmp(name, "from-promisor")) {
+ } else if (!strncmp(name, "from-promisor", namelen)) {
options.from_promisor = 1;
return 0;
- } else if (!strcmp(name, "refetch")) {
+ } else if (!strncmp(name, "refetch", namelen)) {
options.refetch = 1;
return 0;
- } else if (!strcmp(name, "filter")) {
+ } else if (!strncmp(name, "filter", namelen)) {
options.filter = xstrdup(value);
return 0;
- } else if (!strcmp(name, "object-format")) {
+ } else if (!strncmp(name, "object-format", namelen)) {
options.object_format = 1;
if (strcmp(value, "true"))
die(_("unknown value for object-format: %s"), value);
@@ -266,12 +268,23 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
return list;
}
+/*
+ * Try to detect the hash algorithm used by the remote repository when using
+ * the dumb HTTP transport. As dumb transports cannot tell us the object hash
+ * directly have to derive it from the advertised ref lengths.
+ */
static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
{
const char *p = memchr(heads->buf, '\t', heads->len);
int algo;
+
+ /*
+ * In case the remote has no refs we have no way to reliably determine
+ * the object hash used by that repository. In that case we simply fall
+ * back to SHA1, which may or may not be correct.
+ */
if (!p)
- return the_hash_algo;
+ return &hash_algos[GIT_HASH_SHA1];
algo = hash_algo_by_length((p - heads->buf) / 2);
if (algo == GIT_HASH_UNKNOWN)
@@ -295,6 +308,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
"is this a git repository?",
transport_anonymize_url(url.buf));
+ /*
+ * Set the repository's hash algo to whatever we have just detected.
+ * This ensures that we can correctly parse the remote references.
+ */
+ repo_set_hash_algo(the_repository, hash_algo_by_ptr(options.hash_algo));
+
data = heads->buf;
start = NULL;
mid = data;
@@ -889,7 +908,7 @@ static curl_off_t xcurl_off_t(size_t len)
static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_received)
{
struct active_request_slot *slot;
- struct curl_slist *headers = http_copy_default_headers();
+ struct curl_slist *headers = NULL;
int use_gzip = rpc->gzip_request;
char *gzip_body = NULL;
size_t gzip_size = 0;
@@ -922,20 +941,24 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
do {
err = probe_rpc(rpc, &results);
if (err == HTTP_REAUTH)
- credential_fill(&http_auth);
+ credential_fill(&http_auth, 0);
} while (err == HTTP_REAUTH);
if (err != HTTP_OK)
return -1;
- if (results.auth_avail & CURLAUTH_GSSNEGOTIATE)
+ if (results.auth_avail & CURLAUTH_GSSNEGOTIATE || http_auth.authtype)
needs_100_continue = 1;
}
+retry:
+ headers = http_copy_default_headers();
headers = curl_slist_append(headers, rpc->hdr_content_type);
headers = curl_slist_append(headers, rpc->hdr_accept);
headers = curl_slist_append(headers, needs_100_continue ?
"Expect: 100-continue" : "Expect:");
+ headers = http_append_auth_header(&http_auth, headers);
+
/* Add Accept-Language header */
if (rpc->hdr_accept_language)
headers = curl_slist_append(headers, rpc->hdr_accept_language);
@@ -944,7 +967,6 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
if (rpc->protocol_header)
headers = curl_slist_append(headers, rpc->protocol_header);
-retry:
slot = get_active_slot();
curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
@@ -1041,7 +1063,8 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
rpc->any_written = 0;
err = run_slot(slot, NULL);
if (err == HTTP_REAUTH && !large_request) {
- credential_fill(&http_auth);
+ credential_fill(&http_auth, 0);
+ curl_slist_free_all(headers);
goto retry;
}
if (err != HTTP_OK)
@@ -1553,7 +1576,7 @@ int cmd_main(int argc, const char **argv)
if (argc > 2) {
end_url_with_slash(&url, argv[2]);
} else {
- end_url_with_slash(&url, remote->url[0]);
+ end_url_with_slash(&url, remote->url.v[0]);
}
http_init(remote, url.buf, 0);
@@ -1584,15 +1607,16 @@ int cmd_main(int argc, const char **argv)
parse_push(&buf);
} else if (skip_prefix(buf.buf, "option ", &arg)) {
- char *value = strchr(arg, ' ');
+ const char *value = strchrnul(arg, ' ');
+ size_t arglen = value - arg;
int result;
- if (value)
- *value++ = '\0';
+ if (*value)
+ value++; /* skip over SP */
else
value = "true";
- result = set_option(arg, value);
+ result = set_option(arg, arglen, value);
if (!result)
printf("ok\n");
else if (result < 0)
diff --git a/remote.c b/remote.c
index 2b650b8..f43cf5e 100644
--- a/remote.c
+++ b/remote.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
@@ -32,10 +34,10 @@ struct counted_string {
static int valid_remote(const struct remote *remote)
{
- return (!!remote->url) || (!!remote->foreign_vcs);
+ return !!remote->url.nr;
}
-static const char *alias_url(const char *url, struct rewrites *r)
+static char *alias_url(const char *url, struct rewrites *r)
{
int i, j;
struct counted_string *longest;
@@ -56,36 +58,43 @@ static const char *alias_url(const char *url, struct rewrites *r)
}
}
if (!longest)
- return url;
+ return NULL;
return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len);
}
static void add_url(struct remote *remote, const char *url)
{
- ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
- remote->url[remote->url_nr++] = url;
+ if (*url)
+ strvec_push(&remote->url, url);
+ else
+ strvec_clear(&remote->url);
}
static void add_pushurl(struct remote *remote, const char *pushurl)
{
- ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
- remote->pushurl[remote->pushurl_nr++] = pushurl;
+ if (*pushurl)
+ strvec_push(&remote->pushurl, pushurl);
+ else
+ strvec_clear(&remote->pushurl);
}
static void add_pushurl_alias(struct remote_state *remote_state,
struct remote *remote, const char *url)
{
- const char *pushurl = alias_url(url, &remote_state->rewrites_push);
- if (pushurl != url)
- add_pushurl(remote, pushurl);
+ char *alias = alias_url(url, &remote_state->rewrites_push);
+ if (alias)
+ add_pushurl(remote, alias);
+ free(alias);
}
static void add_url_alias(struct remote_state *remote_state,
struct remote *remote, const char *url)
{
- add_url(remote, alias_url(url, &remote_state->rewrites));
+ char *alias = alias_url(url, &remote_state->rewrites);
+ add_url(remote, alias ? alias : url);
add_pushurl_alias(remote_state, remote, url);
+ free(alias);
}
struct remotes_hash_key {
@@ -147,18 +156,12 @@ static struct remote *make_remote(struct remote_state *remote_state,
static void remote_clear(struct remote *remote)
{
- int i;
-
free((char *)remote->name);
free((char *)remote->foreign_vcs);
- for (i = 0; i < remote->url_nr; i++)
- free((char *)remote->url[i]);
- FREE_AND_NULL(remote->url);
+ strvec_clear(&remote->url);
+ strvec_clear(&remote->pushurl);
- for (i = 0; i < remote->pushurl_nr; i++)
- free((char *)remote->pushurl[i]);
- FREE_AND_NULL(remote->pushurl);
free((char *)remote->receivepack);
free((char *)remote->uploadpack);
FREE_AND_NULL(remote->http_proxy);
@@ -292,7 +295,7 @@ static void read_remotes_file(struct remote_state *remote_state,
if (skip_prefix(buf.buf, "URL:", &v))
add_url_alias(remote_state, remote,
- xstrdup(skip_spaces(v)));
+ skip_spaces(v));
else if (skip_prefix(buf.buf, "Push:", &v))
refspec_append(&remote->push, skip_spaces(v));
else if (skip_prefix(buf.buf, "Pull:", &v))
@@ -305,7 +308,7 @@ static void read_remotes_file(struct remote_state *remote_state,
static void read_branches_file(struct remote_state *remote_state,
struct remote *remote)
{
- char *frag;
+ char *frag, *to_free = NULL;
struct strbuf buf = STRBUF_INIT;
FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r");
@@ -333,9 +336,9 @@ static void read_branches_file(struct remote_state *remote_state,
if (frag)
*(frag++) = '\0';
else
- frag = (char *)git_default_branch_name(0);
+ frag = to_free = repo_default_branch_name(the_repository, 0);
- add_url_alias(remote_state, remote, strbuf_detach(&buf, NULL));
+ add_url_alias(remote_state, remote, buf.buf);
refspec_appendf(&remote->fetch, "refs/heads/%s:refs/heads/%s",
frag, remote->name);
@@ -345,6 +348,9 @@ static void read_branches_file(struct remote_state *remote_state,
*/
refspec_appendf(&remote->push, "HEAD:refs/heads/%s", frag);
remote->fetch_tags = 1; /* always auto-follow */
+
+ strbuf_release(&buf);
+ free(to_free);
}
static int handle_config(const char *key, const char *value,
@@ -428,29 +434,27 @@ static int handle_config(const char *key, const char *value,
else if (!strcmp(subkey, "prunetags"))
remote->prune_tags = git_config_bool(key, value);
else if (!strcmp(subkey, "url")) {
- const char *v;
- if (git_config_string(&v, key, value))
- return -1;
- add_url(remote, v);
+ if (!value)
+ return config_error_nonbool(key);
+ add_url(remote, value);
} else if (!strcmp(subkey, "pushurl")) {
- const char *v;
- if (git_config_string(&v, key, value))
- return -1;
- add_pushurl(remote, v);
+ if (!value)
+ return config_error_nonbool(key);
+ add_pushurl(remote, value);
} else if (!strcmp(subkey, "push")) {
- const char *v;
+ char *v;
if (git_config_string(&v, key, value))
return -1;
refspec_append(&remote->push, v);
- free((char *)v);
+ free(v);
} else if (!strcmp(subkey, "fetch")) {
- const char *v;
+ char *v;
if (git_config_string(&v, key, value))
return -1;
refspec_append(&remote->fetch, v);
- free((char *)v);
+ free(v);
} else if (!strcmp(subkey, "receivepack")) {
- const char *v;
+ char *v;
if (git_config_string(&v, key, value))
return -1;
if (!remote->receivepack)
@@ -458,7 +462,7 @@ static int handle_config(const char *key, const char *value,
else
error(_("more than one receivepack given, using the first"));
} else if (!strcmp(subkey, "uploadpack")) {
- const char *v;
+ char *v;
if (git_config_string(&v, key, value))
return -1;
if (!remote->uploadpack)
@@ -471,10 +475,10 @@ static int handle_config(const char *key, const char *value,
else if (!strcmp(value, "--tags"))
remote->fetch_tags = 2;
} else if (!strcmp(subkey, "proxy")) {
- return git_config_string((const char **)&remote->http_proxy,
+ return git_config_string(&remote->http_proxy,
key, value);
} else if (!strcmp(subkey, "proxyauthmethod")) {
- return git_config_string((const char **)&remote->http_proxy_authmethod,
+ return git_config_string(&remote->http_proxy_authmethod,
key, value);
} else if (!strcmp(subkey, "vcs")) {
return git_config_string(&remote->foreign_vcs, key, value);
@@ -489,20 +493,25 @@ static void alias_all_urls(struct remote_state *remote_state)
int add_pushurl_aliases;
if (!remote_state->remotes[i])
continue;
- for (j = 0; j < remote_state->remotes[i]->pushurl_nr; j++) {
- remote_state->remotes[i]->pushurl[j] =
- alias_url(remote_state->remotes[i]->pushurl[j],
- &remote_state->rewrites);
+ for (j = 0; j < remote_state->remotes[i]->pushurl.nr; j++) {
+ char *alias = alias_url(remote_state->remotes[i]->pushurl.v[j],
+ &remote_state->rewrites);
+ if (alias)
+ strvec_replace(&remote_state->remotes[i]->pushurl,
+ j, alias);
}
- add_pushurl_aliases = remote_state->remotes[i]->pushurl_nr == 0;
- for (j = 0; j < remote_state->remotes[i]->url_nr; j++) {
+ add_pushurl_aliases = remote_state->remotes[i]->pushurl.nr == 0;
+ for (j = 0; j < remote_state->remotes[i]->url.nr; j++) {
+ char *alias;
if (add_pushurl_aliases)
add_pushurl_alias(
remote_state, remote_state->remotes[i],
- remote_state->remotes[i]->url[j]);
- remote_state->remotes[i]->url[j] =
- alias_url(remote_state->remotes[i]->url[j],
+ remote_state->remotes[i]->url.v[j]);
+ alias = alias_url(remote_state->remotes[i]->url.v[j],
&remote_state->rewrites);
+ if (alias)
+ strvec_replace(&remote_state->remotes[i]->url,
+ j, alias);
}
}
}
@@ -642,10 +651,10 @@ static void validate_remote_url(struct remote *remote)
else
die(_("unrecognized value transfer.credentialsInUrl: '%s'"), value);
- for (i = 0; i < remote->url_nr; i++) {
+ for (i = 0; i < remote->url.nr; i++) {
struct url_info url_info = { 0 };
- if (!url_normalize(remote->url[i], &url_info) ||
+ if (!url_normalize(remote->url.v[i], &url_info) ||
!url_info.passwd_off)
goto loop_cleanup;
@@ -819,13 +828,18 @@ struct ref *ref_remove_duplicates(struct ref *ref_map)
int remote_has_url(struct remote *remote, const char *url)
{
int i;
- for (i = 0; i < remote->url_nr; i++) {
- if (!strcmp(remote->url[i], url))
+ for (i = 0; i < remote->url.nr; i++) {
+ if (!strcmp(remote->url.v[i], url))
return 1;
}
return 0;
}
+struct strvec *push_url_of_remote(struct remote *remote)
+{
+ return remote->pushurl.nr ? &remote->pushurl : &remote->url;
+}
+
static int match_name_with_pattern(const char *key, const char *name,
const char *value, char **result)
{
@@ -1162,7 +1176,7 @@ static void tail_link_ref(struct ref *ref, struct ref ***tail)
static struct ref *alloc_delete_ref(void)
{
struct ref *ref = alloc_ref("(delete)");
- oidclr(&ref->new_oid);
+ oidclr(&ref->new_oid, the_repository->hash_algo);
return ref;
}
@@ -1198,8 +1212,10 @@ static char *guess_ref(const char *name, struct ref *peer)
{
struct strbuf buf = STRBUF_INIT;
- const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
- NULL, NULL);
+ const char *r = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ peer->name,
+ RESOLVE_REF_READING,
+ NULL, NULL);
if (!r)
return NULL;
@@ -1316,9 +1332,10 @@ static int match_explicit(struct ref *src, struct ref *dst,
if (!dst_value) {
int flag;
- dst_value = resolve_ref_unsafe(matched_src->name,
- RESOLVE_REF_READING,
- NULL, &flag);
+ dst_value = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ matched_src->name,
+ RESOLVE_REF_READING,
+ NULL, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
!starts_with(dst_value, "refs/heads/")))
@@ -1773,7 +1790,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
if (starts_with(ref->name, "refs/tags/"))
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
- else if (!repo_has_object_file(the_repository, &ref->old_oid))
+ else if (!repo_has_object_file_with_flags(the_repository, &ref->old_oid, OBJECT_INFO_SKIP_FETCH_OBJECT))
reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) ||
!lookup_commit_reference_gently(the_repository, &ref->new_oid, 1))
@@ -1882,7 +1899,7 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
* or because it is not a real branch, and get_branch
* auto-vivified it?
*/
- if (!ref_exists(branch->refname))
+ if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname))
return error_buf(err, _("no such branch: '%s'"),
branch->name);
return error_buf(err,
@@ -2168,13 +2185,13 @@ static int stat_branch_pair(const char *branch_name, const char *base,
struct strvec argv = STRVEC_INIT;
/* Cannot stat if what we used to build on no longer exists */
- if (read_ref(base, &oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), base, &oid))
return -1;
theirs = lookup_commit_reference(the_repository, &oid);
if (!theirs)
return -1;
- if (read_ref(branch_name, &oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), branch_name, &oid))
return -1;
ours = lookup_commit_reference(the_repository, &oid);
if (!ours)
@@ -2278,7 +2295,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
upstream_is_gone = 1;
}
- base = shorten_unambiguous_ref(full_base, 0);
+ base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ full_base, 0);
if (upstream_is_gone) {
strbuf_addf(sb,
_("Your branch is based on '%s', but the upstream is gone.\n"),
@@ -2358,7 +2376,8 @@ struct ref *get_local_heads(void)
{
struct ref *local_refs = NULL, **local_tail = &local_refs;
- for_each_ref(one_local_ref, &local_tail);
+ refs_for_each_ref(get_main_ref_store(the_repository), one_local_ref,
+ &local_tail);
return local_refs;
}
@@ -2383,11 +2402,13 @@ struct ref *guess_remote_head(const struct ref *head,
/* If a remote branch exists with the default branch name, let's use it. */
if (!all) {
- char *ref = xstrfmt("refs/heads/%s",
- git_default_branch_name(0));
+ char *default_branch = repo_default_branch_name(the_repository, 0);
+ char *ref = xstrfmt("refs/heads/%s", default_branch);
r = find_ref_by_name(refs, ref);
free(ref);
+ free(default_branch);
+
if (r && oideq(&r->old_oid, &head->old_oid))
return copy_ref(r);
@@ -2468,7 +2489,8 @@ struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map)
for (ref = fetch_map; ref; ref = ref->next)
string_list_append(&ref_names, ref->name);
string_list_sort(&ref_names);
- for_each_ref(get_stale_heads_cb, &info);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ get_stale_heads_cb, &info);
string_list_clear(&ref_names, 0);
return stale_refs;
}
@@ -2521,7 +2543,7 @@ 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])
- oidclr(&entry->expect);
+ oidclr(&entry->expect, the_repository->hash_algo);
else if (repo_get_oid(the_repository, colon + 1, &entry->expect))
return error(_("cannot parse expected object name '%s'"),
colon + 1);
@@ -2553,7 +2575,7 @@ static int remote_tracking(struct remote *remote, const char *refname,
dst = apply_refspecs(&remote->fetch, refname);
if (!dst)
return -1; /* no tracking ref for refname at remote */
- if (read_ref(dst, oid))
+ if (refs_read_ref(get_main_ref_store(the_repository), dst, oid))
return -1; /* we know what the tracking ref is but we cannot read it */
*dst_refname = dst;
@@ -2659,12 +2681,16 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
* Get the timestamp from the latest entry
* of the remote-tracking ref's reflog.
*/
- for_each_reflog_ent_reverse(remote->tracking_ref, peek_reflog, &date);
+ refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
+ remote->tracking_ref, peek_reflog,
+ &date);
cb.remote_commit = commit;
cb.local_commits = &arr;
cb.remote_reflog_timestamp = date;
- ret = for_each_reflog_ent_reverse(local, check_and_collect_until, &cb);
+ ret = refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
+ local, check_and_collect_until,
+ &cb);
/* We found an entry in the reflog. */
if (ret > 0)
@@ -2719,7 +2745,7 @@ static void apply_cas(struct push_cas_option *cas,
else if (remote_tracking(remote, ref->name,
&ref->old_oid_expect,
&ref->tracking_ref))
- oidclr(&ref->old_oid_expect);
+ oidclr(&ref->old_oid_expect, the_repository->hash_algo);
else
ref->check_reachable = cas->use_force_if_includes;
return;
@@ -2733,7 +2759,7 @@ static void apply_cas(struct push_cas_option *cas,
if (remote_tracking(remote, ref->name,
&ref->old_oid_expect,
&ref->tracking_ref))
- oidclr(&ref->old_oid_expect);
+ oidclr(&ref->old_oid_expect, the_repository->hash_algo);
else
ref->check_reachable = cas->use_force_if_includes;
}
diff --git a/remote.h b/remote.h
index dfd4837..b901b56 100644
--- a/remote.h
+++ b/remote.h
@@ -1,9 +1,10 @@
#ifndef REMOTE_H
#define REMOTE_H
-#include "hash-ll.h"
+#include "hash.h"
#include "hashmap.h"
#include "refspec.h"
+#include "strvec.h"
struct option;
struct transport_ls_refs_options;
@@ -46,7 +47,7 @@ struct remote_state {
struct hashmap branches_hash;
struct branch *current_branch;
- const char *pushremote_name;
+ char *pushremote_name;
struct rewrites rewrites;
struct rewrites rewrites_push;
@@ -65,19 +66,12 @@ struct remote {
int origin, configured_in_repo;
- const char *foreign_vcs;
+ char *foreign_vcs;
/* An array of all of the url_nr URLs configured for the remote */
- const char **url;
-
- int url_nr;
- int url_alloc;
-
+ struct strvec url;
/* An array of all of the pushurl_nr push URLs configured for the remote */
- const char **pushurl;
-
- int pushurl_nr;
- int pushurl_alloc;
+ struct strvec pushurl;
struct refspec push;
@@ -129,6 +123,7 @@ typedef int each_remote_fn(struct remote *remote, void *priv);
int for_each_remote(each_remote_fn fn, void *priv);
int remote_has_url(struct remote *remote, const char *url);
+struct strvec *push_url_of_remote(struct remote *remote);
struct ref_push_report {
const char *ref_name;
@@ -200,7 +195,7 @@ struct ref {
};
#define REF_NORMAL (1u << 0)
-#define REF_HEADS (1u << 1)
+#define REF_BRANCHES (1u << 1)
#define REF_TAGS (1u << 2)
struct ref *find_ref_by_name(const struct ref *list, const char *name);
@@ -309,9 +304,9 @@ struct branch {
const char *refname;
/* The name of the remote listed in the configuration. */
- const char *remote_name;
+ char *remote_name;
- const char *pushremote_name;
+ char *pushremote_name;
/* An array of the "merge" lines in the configuration. */
const char **merge_name;
diff --git a/replace-object.c b/replace-object.c
index 5232155..59252d5 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -8,18 +8,19 @@
#include "repository.h"
#include "commit.h"
-static int register_replace_ref(struct repository *r,
- const char *refname,
+static int register_replace_ref(const char *refname,
const struct object_id *oid,
int flag UNUSED,
- void *cb_data UNUSED)
+ void *cb_data)
{
+ struct repository *r = cb_data;
+
/* Get sha1 from refname */
const char *slash = strrchr(refname, '/');
const char *hash = slash ? slash + 1 : refname;
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
- if (get_oid_hex(hash, &repl_obj->original.oid)) {
+ if (get_oid_hex_algop(hash, &repl_obj->original.oid, r->hash_algo)) {
free(repl_obj);
warning(_("bad replace ref name: %s"), refname);
return 0;
@@ -50,7 +51,8 @@ void prepare_replace_object(struct repository *r)
xmalloc(sizeof(*r->objects->replace_map));
oidmap_init(r->objects->replace_map, 0);
- for_each_replace_ref(r, register_replace_ref, NULL);
+ refs_for_each_replace_ref(get_main_ref_store(r),
+ register_replace_ref, r);
r->objects->replace_map_initialized = 1;
pthread_mutex_unlock(&r->objects->replace_mutex);
diff --git a/repository.c b/repository.c
index da42ff5..9825a30 100644
--- a/repository.c
+++ b/repository.c
@@ -1,8 +1,3 @@
-/*
- * not really _using_ the compat macros, just make sure the_index
- * declaration matches the definition in this file.
- */
-#define USE_THE_INDEX_VARIABLE
#include "git-compat-util.h"
#include "abspath.h"
#include "repository.h"
@@ -19,24 +14,71 @@
#include "sparse-index.h"
#include "trace2.h"
#include "promisor-remote.h"
+#include "refs.h"
+
+/*
+ * We do not define `USE_THE_REPOSITORY_VARIABLE` in this file because we do
+ * not want to rely on functions that implicitly use `the_repository`. This
+ * means that the `extern` declaration of `the_repository` isn't visible here,
+ * which makes sparse unhappy. We thus declare it here.
+ */
+extern struct repository *the_repository;
/* The main repository */
static struct repository the_repo;
-struct repository *the_repository;
-struct index_state the_index;
+struct repository *the_repository = &the_repo;
-void initialize_the_repository(void)
+/*
+ * An escape hatch: if we hit a bug in the production code that fails
+ * to set an appropriate hash algorithm (most likely to happen when
+ * running outside a repository), we can tell the user who reported
+ * the crash to set the environment variable to "sha1" (all lowercase)
+ * to revert to the historical behaviour of defaulting to SHA-1.
+ */
+static void set_default_hash_algo(struct repository *repo)
{
- the_repository = &the_repo;
+ const char *hash_name;
+ int algo;
- the_repo.index = &the_index;
- the_repo.objects = raw_object_store_new();
- the_repo.remote_state = remote_state_new();
- the_repo.parsed_objects = parsed_object_pool_new();
+ hash_name = getenv("GIT_TEST_DEFAULT_HASH_ALGO");
+ if (!hash_name)
+ return;
+ algo = hash_algo_by_name(hash_name);
+ if (algo == GIT_HASH_UNKNOWN)
+ return;
- index_state_init(&the_index, the_repository);
+ repo_set_hash_algo(repo, algo);
+}
- repo_set_hash_algo(&the_repo, GIT_HASH_SHA1);
+void initialize_repository(struct repository *repo)
+{
+ repo->objects = raw_object_store_new();
+ repo->remote_state = remote_state_new();
+ repo->parsed_objects = parsed_object_pool_new();
+ ALLOC_ARRAY(repo->index, 1);
+ index_state_init(repo->index, repo);
+
+ /*
+ * When a command runs inside a repository, it learns what
+ * hash algorithm is in use from the repository, but some
+ * commands are designed to work outside a repository, yet
+ * they want to access the_hash_algo, if only for the length
+ * of the hashed value to see if their input looks like a
+ * plausible hash value.
+ *
+ * We are in the process of identifying such code paths and
+ * giving them an appropriate default individually; any
+ * unconverted code paths that try to access the_hash_algo
+ * will thus fail. The end-users however have an escape hatch
+ * to set GIT_TEST_DEFAULT_HASH_ALGO environment variable to
+ * "sha1" to get back the old behaviour of defaulting to SHA-1.
+ *
+ * This escape hatch is deliberately kept unadvertised, so
+ * that they see crashes and we can get a report before
+ * telling them about it.
+ */
+ if (repo == the_repository)
+ set_default_hash_algo(repo);
}
static void expand_base_dir(char **out, const char *in,
@@ -114,7 +156,8 @@ void repo_set_compat_hash_algo(struct repository *repo, int algo)
repo_read_loose_object_map(repo);
}
-void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+void repo_set_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format)
{
repo->ref_storage_format = format;
}
@@ -188,9 +231,7 @@ int repo_init(struct repository *repo,
struct repository_format format = REPOSITORY_FORMAT_INIT;
memset(repo, 0, sizeof(*repo));
- repo->objects = raw_object_store_new();
- repo->parsed_objects = parsed_object_pool_new();
- repo->remote_state = remote_state_new();
+ initialize_repository(repo);
if (repo_init_gitdir(repo, gitdir))
goto error;
@@ -282,6 +323,9 @@ static void repo_clear_path_cache(struct repo_path_cache *cache)
void repo_clear(struct repository *repo)
{
+ struct hashmap_iter iter;
+ struct strmap_entry *e;
+
FREE_AND_NULL(repo->gitdir);
FREE_AND_NULL(repo->commondir);
FREE_AND_NULL(repo->graft_file);
@@ -309,8 +353,7 @@ void repo_clear(struct repository *repo)
if (repo->index) {
discard_index(repo->index);
- if (repo->index != &the_index)
- FREE_AND_NULL(repo->index);
+ FREE_AND_NULL(repo->index);
}
if (repo->promisor_remote_config) {
@@ -323,6 +366,14 @@ void repo_clear(struct repository *repo)
FREE_AND_NULL(repo->remote_state);
}
+ strmap_for_each_entry(&repo->submodule_ref_stores, &iter, e)
+ ref_store_release(e->value);
+ strmap_clear(&repo->submodule_ref_stores, 1);
+
+ strmap_for_each_entry(&repo->worktree_ref_stores, &iter, e)
+ ref_store_release(e->value);
+ strmap_clear(&repo->worktree_ref_stores, 1);
+
repo_clear_path_cache(&repo->cached_paths);
}
diff --git a/repository.h b/repository.h
index 2684367..6ce6826 100644
--- a/repository.h
+++ b/repository.h
@@ -1,6 +1,8 @@
#ifndef REPOSITORY_H
#define REPOSITORY_H
+#include "strmap.h"
+
struct config_set;
struct fsmonitor_settings;
struct git_hash_algo;
@@ -24,9 +26,11 @@ enum fetch_negotiation_setting {
FETCH_NEGOTIATION_NOOP,
};
-#define REF_STORAGE_FORMAT_UNKNOWN 0
-#define REF_STORAGE_FORMAT_FILES 1
-#define REF_STORAGE_FORMAT_REFTABLE 2
+enum ref_storage_format {
+ REF_STORAGE_FORMAT_UNKNOWN,
+ REF_STORAGE_FORMAT_FILES,
+ REF_STORAGE_FORMAT_REFTABLE,
+};
struct repo_settings {
int initialized;
@@ -109,6 +113,18 @@ struct repository {
struct ref_store *refs_private;
/*
+ * A strmap of ref_stores, stored by submodule name, accessible via
+ * `repo_get_submodule_ref_store()`.
+ */
+ struct strmap submodule_ref_stores;
+
+ /*
+ * A strmap of ref_stores, stored by worktree id, accessible via
+ * `get_worktree_ref_store()`.
+ */
+ struct strmap worktree_ref_stores;
+
+ /*
* Contains path to often used file names.
*/
struct repo_path_cache cached_paths;
@@ -167,7 +183,7 @@ struct repository {
const struct git_hash_algo *compat_hash_algo;
/* Repository's reference storage format, as serialized on disk. */
- unsigned int ref_storage_format;
+ enum ref_storage_format ref_storage_format;
/* A unique-id for tracing purposes. */
int trace2_repo_id;
@@ -186,9 +202,8 @@ struct repository {
unsigned different_commondir:1;
};
+#ifdef USE_THE_REPOSITORY_VARIABLE
extern struct repository *the_repository;
-#ifdef USE_THE_INDEX_VARIABLE
-extern struct index_state the_index;
#endif
/*
@@ -209,8 +224,9 @@ void repo_set_gitdir(struct repository *repo, const char *root,
void repo_set_worktree(struct repository *repo, const char *path);
void repo_set_hash_algo(struct repository *repo, int algo);
void repo_set_compat_hash_algo(struct repository *repo, int compat_algo);
-void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
-void initialize_the_repository(void);
+void repo_set_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format);
+void initialize_repository(struct repository *repo);
RESULT_MUST_BE_USED
int repo_init(struct repository *r, const char *gitdir, const char *worktree);
diff --git a/rerere.c b/rerere.c
index c7e1f8f..597256f 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
diff --git a/reset.c b/reset.c
index d619cb7..9550dea 100644
--- a/reset.c
+++ b/reset.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "cache-tree.h"
#include "gettext.h"
@@ -47,11 +49,13 @@ static int update_refs(const struct reset_head_opts *opts,
strbuf_addstr(&msg, "updating ORIG_HEAD");
reflog_orig_head = msg.buf;
}
- update_ref(reflog_orig_head, "ORIG_HEAD",
- orig_head ? orig_head : head,
- old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+ refs_update_ref(get_main_ref_store(the_repository),
+ reflog_orig_head, "ORIG_HEAD",
+ orig_head ? orig_head : head,
+ old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
} else if (old_orig)
- delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, "ORIG_HEAD", old_orig, 0);
}
if (!reflog_head) {
@@ -60,16 +64,19 @@ static int update_refs(const struct reset_head_opts *opts,
reflog_head = msg.buf;
}
if (!switch_to_branch)
- ret = update_ref(reflog_head, "HEAD", oid, head,
- detach_head ? REF_NO_DEREF : 0,
- UPDATE_REFS_MSG_ON_ERR);
+ ret = refs_update_ref(get_main_ref_store(the_repository),
+ reflog_head, "HEAD", oid, head,
+ detach_head ? REF_NO_DEREF : 0,
+ UPDATE_REFS_MSG_ON_ERR);
else {
- ret = update_ref(reflog_branch ? reflog_branch : reflog_head,
- switch_to_branch, oid, NULL, 0,
- UPDATE_REFS_MSG_ON_ERR);
+ ret = refs_update_ref(get_main_ref_store(the_repository),
+ reflog_branch ? reflog_branch : reflog_head,
+ switch_to_branch, oid, NULL, 0,
+ UPDATE_REFS_MSG_ON_ERR);
if (!ret)
- ret = create_symref("HEAD", switch_to_branch,
- reflog_head);
+ ret = refs_update_symref(get_main_ref_store(the_repository),
+ "HEAD", switch_to_branch,
+ reflog_head);
}
if (!ret && run_hook)
run_hooks_l("post-checkout",
diff --git a/reset.h b/reset.h
index 10708d8..a28f818 100644
--- a/reset.h
+++ b/reset.h
@@ -1,7 +1,7 @@
#ifndef RESET_H
#define RESET_H
-#include "hash-ll.h"
+#include "hash.h"
#include "repository.h"
#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
diff --git a/resolve-undo.c b/resolve-undo.c
index cd02dc9..8c9911a 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "dir.h"
#include "hash.h"
@@ -93,7 +95,8 @@ struct string_list *resolve_undo_read(const char *data, unsigned long size)
continue;
if (size < rawsz)
goto error;
- oidread(&ui->oid[i], (const unsigned char *)data);
+ oidread(&ui->oid[i], (const unsigned char *)data,
+ the_repository->hash_algo);
size -= rawsz;
data += rawsz;
}
diff --git a/resolve-undo.h b/resolve-undo.h
index f3f8462..89a3227 100644
--- a/resolve-undo.h
+++ b/resolve-undo.h
@@ -6,7 +6,7 @@ struct index_state;
struct pathspec;
struct string_list;
-#include "hash-ll.h"
+#include "hash.h"
struct resolve_undo_info {
unsigned int mode[3];
diff --git a/revision.c b/revision.c
index 9db534f..3959ccc 100644
--- a/revision.c
+++ b/revision.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -1739,7 +1741,8 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
cb.all_revs = revs;
cb.all_flags = flags;
cb.wt = NULL;
- for_each_reflog(handle_one_reflog, &cb);
+ refs_for_each_reflog(get_main_ref_store(the_repository),
+ handle_one_reflog, &cb);
if (!revs->single_worktree)
add_other_reflogs_to_pending(&cb);
@@ -1980,9 +1983,9 @@ static const char *lookup_other_head(struct object_id *oid)
};
for (i = 0; i < ARRAY_SIZE(other_head); i++)
- if (!read_ref_full(other_head[i],
- RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- oid, NULL)) {
+ if (!refs_read_ref_full(get_main_ref_store(the_repository), other_head[i],
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ oid, NULL)) {
if (is_null_oid(oid))
die(_("%s exists but is a symbolic ref"), other_head[i]);
return other_head[i];
@@ -2650,10 +2653,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--invert-grep")) {
revs->grep_filter.no_body_match = 1;
} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+ free(git_log_output_encoding);
if (strcmp(optarg, "none"))
git_log_output_encoding = xstrdup(optarg);
else
- git_log_output_encoding = "";
+ git_log_output_encoding = xstrdup("");
return argcount;
} else if (!strcmp(arg, "--reverse")) {
revs->reverse ^= 1;
@@ -2790,7 +2794,8 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
struct all_refs_cb cb;
init_all_refs_cb(&cb, revs, *flags);
- for_each_glob_ref(handle_one_ref, optarg, &cb);
+ refs_for_each_glob_ref(get_main_ref_store(the_repository),
+ handle_one_ref, optarg, &cb);
clear_ref_exclusions(&revs->ref_excludes);
return argcount;
} else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
@@ -2805,7 +2810,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--branches");
init_all_refs_cb(&cb, revs, *flags);
- for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ handle_one_ref, optarg,
+ "refs/heads/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--tags=", &optarg)) {
struct all_refs_cb cb;
@@ -2813,7 +2820,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--tags");
init_all_refs_cb(&cb, revs, *flags);
- for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ handle_one_ref, optarg,
+ "refs/tags/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--remotes=", &optarg)) {
struct all_refs_cb cb;
@@ -2821,7 +2830,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--remotes");
init_all_refs_cb(&cb, revs, *flags);
- for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
+ refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+ handle_one_ref, optarg,
+ "refs/remotes/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--reflog")) {
add_reflogs_to_pending(revs, *flags);
@@ -2912,7 +2923,8 @@ static void NORETURN diagnose_missing_default(const char *def)
int flags;
const char *refname;
- refname = resolve_ref_unsafe(def, 0, NULL, &flags);
+ refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ def, 0, NULL, &flags);
if (!refname || !(flags & REF_ISSYMREF) || (flags & REF_ISBROKEN))
die(_("your current branch appears to be broken"));
diff --git a/run-command.c b/run-command.c
index ec2c12e..d9f80fa 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "run-command.h"
#include "environment.h"
@@ -663,7 +665,7 @@ int start_command(struct child_process *cmd)
int need_in, need_out, need_err;
int fdin[2], fdout[2], fderr[2];
int failed_errno;
- char *str;
+ const char *str;
/*
* In case of errors we must keep the promise to close FDs
@@ -746,6 +748,8 @@ int start_command(struct child_process *cmd)
goto end_of_spawn;
}
+ trace_argv_printf(&argv.v[1], "trace: start_command:");
+
if (pipe(notify_pipe))
notify_pipe[0] = notify_pipe[1] = -1;
@@ -913,6 +917,7 @@ int start_command(struct child_process *cmd)
else if (cmd->use_shell)
cmd->args.v = prepare_shell_cmd(&nargv, sargv);
+ trace_argv_printf(cmd->args.v, "trace: start_command:");
cmd->pid = mingw_spawnvpe(cmd->args.v[0], cmd->args.v,
(char**) cmd->env.v,
cmd->dir, fhin, fhout, fherr);
diff --git a/scalar.c b/scalar.c
index 5359b1a..1fe8a93 100644
--- a/scalar.c
+++ b/scalar.c
@@ -2,6 +2,8 @@
* The Scalar command-line interface.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "gettext.h"
@@ -363,16 +365,13 @@ static char *remote_default_branch(const char *url)
static int delete_enlistment(struct strbuf *enlistment)
{
-#ifdef WIN32
struct strbuf parent = STRBUF_INIT;
size_t offset;
char *path_sep;
-#endif
if (unregister_dir())
return error(_("failed to unregister repository"));
-#ifdef WIN32
/*
* Change the current directory to one outside of the enlistment so
* that we may delete everything underneath it.
@@ -387,7 +386,6 @@ static int delete_enlistment(struct strbuf *enlistment)
return res;
}
strbuf_release(&parent);
-#endif
if (have_fsmonitor_support() && stop_fsmonitor_daemon())
return error(_("failed to stop the FSMonitor daemon"));
diff --git a/send-pack.c b/send-pack.c
index 37f59d4..713da58 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
@@ -259,7 +261,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
if (p)
hint->remote_status = xstrdup(p);
else
- hint->remote_status = "failed";
+ hint->remote_status = xstrdup("failed");
} else {
hint->status = REF_STATUS_OK;
hint->remote_status = xstrdup_or_null(p);
diff --git a/sequencer.c b/sequencer.c
index cf45070..b4f055e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
@@ -266,7 +268,7 @@ static struct update_ref_record *init_update_ref_record(const char *ref)
oidcpy(&rec->after, null_oid());
/* This may fail, but that's fine, we will keep the null OID. */
- read_ref(ref, &rec->before);
+ refs_read_ref(get_main_ref_store(the_repository), ref, &rec->before);
return rec;
}
@@ -306,7 +308,7 @@ static int git_sequencer_config(const char *k, const char *v,
}
if (!opts->default_strategy && !strcmp(k, "pull.twohead")) {
- int ret = git_config_string((const char**)&opts->default_strategy, k, v);
+ int ret = git_config_string(&opts->default_strategy, k, v);
if (ret == 0) {
/*
* pull.twohead is allowed to be multi-valued; we only
@@ -359,35 +361,32 @@ static const char *get_todo_path(const struct replay_opts *opts)
static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
size_t ignore_footer)
{
- struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
- struct trailer_info info;
- size_t i;
+ struct trailer_iterator iter;
+ size_t i = 0;
int found_sob = 0, found_sob_last = 0;
char saved_char;
- opts.no_divider = 1;
-
if (ignore_footer) {
saved_char = sb->buf[sb->len - ignore_footer];
sb->buf[sb->len - ignore_footer] = '\0';
}
- trailer_info_get(&opts, sb->buf, &info);
+ trailer_iterator_init(&iter, sb->buf);
if (ignore_footer)
sb->buf[sb->len - ignore_footer] = saved_char;
- if (info.trailer_block_start == info.trailer_block_end)
+ while (trailer_iterator_advance(&iter)) {
+ i++;
+ if (sob && !strncmp(iter.raw, sob->buf, sob->len))
+ found_sob = i;
+ }
+ trailer_iterator_release(&iter);
+
+ if (!i)
return 0;
- for (i = 0; i < info.trailer_nr; i++)
- if (sob && !strncmp(info.trailers[i], sob->buf, sob->len)) {
- found_sob = 1;
- if (i == info.trailer_nr - 1)
- found_sob_last = 1;
- }
-
- trailer_info_release(&info);
+ found_sob_last = (int)i == found_sob;
if (found_sob_last)
return 3;
@@ -440,7 +439,7 @@ int sequencer_remove_state(struct replay_opts *opts)
char *eol = strchr(p, '\n');
if (eol)
*eol = '\0';
- if (delete_ref("(rebase) cleanup", p, NULL, 0) < 0) {
+ if (refs_delete_ref(get_main_ref_store(the_repository), "(rebase) cleanup", p, NULL, 0) < 0) {
warning(_("could not delete '%s'"), p);
ret = -1;
}
@@ -661,11 +660,12 @@ static int fast_forward_to(struct repository *r,
strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
to, unborn && !is_rebase_i(opts) ?
- null_oid() : from,
+ null_oid() : from, NULL, NULL,
0, sb.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
@@ -841,11 +841,12 @@ static int is_index_unchanged(struct repository *r)
struct index_state *istate = r->index;
const char *head_name;
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
/* Check to see if this is an unborn branch */
- head_name = resolve_ref_unsafe("HEAD",
- RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &head_oid, NULL);
+ head_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &head_oid, NULL);
if (!head_name ||
!starts_with(head_name, "refs/heads/") ||
!is_null_oid(&head_oid))
@@ -1294,11 +1295,12 @@ int update_head_with_reflog(const struct commit *old_head,
strbuf_addch(&sb, '\n');
}
- transaction = ref_transaction_begin(err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD", new_head,
old_head ? &old_head->object.oid : null_oid(),
- 0, sb.buf, err) ||
+ NULL, NULL, 0, sb.buf, err) ||
ref_transaction_commit(transaction, err)) {
ret = -1;
}
@@ -1720,8 +1722,8 @@ static int try_to_commit(struct repository *r,
static int write_rebase_head(struct object_id *oid)
{
- if (update_ref("rebase", "REBASE_HEAD", oid,
- NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ if (refs_update_ref(get_main_ref_store(the_repository), "rebase", "REBASE_HEAD", oid,
+ NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
return error(_("could not update %s"), "REBASE_HEAD");
return 0;
@@ -2263,7 +2265,7 @@ static int do_pick_commit(struct repository *r,
unborn = 1;
} else if (unborn)
oidcpy(&head, the_hash_algo->empty_tree);
- if (index_differs_from(r, unborn ? empty_tree_oid_hex() : "HEAD",
+ if (index_differs_from(r, unborn ? empty_tree_oid_hex(the_repository->hash_algo) : "HEAD",
NULL, 0))
return error_dirty_index(r, opts);
}
@@ -2455,12 +2457,12 @@ static int do_pick_commit(struct repository *r,
if ((command == TODO_PICK || command == TODO_REWORD ||
command == TODO_EDIT) && !opts->no_commit &&
(res == 0 || res == 1) &&
- update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ refs_update_ref(get_main_ref_store(the_repository), NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
- update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ refs_update_ref(get_main_ref_store(the_repository), NULL, "REVERT_HEAD", &commit->object.oid, NULL,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (res) {
@@ -3394,12 +3396,12 @@ static int rollback_is_safe(void)
strbuf_release(&sb);
}
else if (errno == ENOENT)
- oidclr(&expected_head);
+ oidclr(&expected_head, the_repository->hash_algo);
else
die_errno(_("could not read '%s'"), git_path_abort_safety_file());
if (repo_get_oid(the_repository, "HEAD", &actual_head))
- oidclr(&actual_head);
+ oidclr(&actual_head, the_repository->hash_algo);
return oideq(&actual_head, &expected_head);
}
@@ -3424,7 +3426,7 @@ static int rollback_single_pick(struct repository *r)
if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
!refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", 0, &head_oid, NULL))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &head_oid, NULL))
return error(_("cannot resolve HEAD"));
if (is_null_oid(&head_oid))
return error(_("cannot abort from a branch yet to be born"));
@@ -3435,7 +3437,7 @@ static int skip_single_pick(void)
{
struct object_id head;
- if (read_ref_full("HEAD", 0, &head, NULL))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &head, NULL))
return error(_("cannot resolve HEAD"));
return reset_merge(&head);
}
@@ -3892,8 +3894,9 @@ static int do_label(struct repository *r, const char *name, int len)
} else if (repo_get_oid(r, "HEAD", &head_oid)) {
error(_("could not read HEAD"));
ret = -1;
- } else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
- NULL, 0, msg.buf, &err) < 0 ||
+ } else if (ref_transaction_update(transaction, ref_name.buf,
+ &head_oid, NULL, NULL, NULL,
+ 0, msg.buf, &err) < 0 ||
ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ret = -1;
@@ -3951,7 +3954,7 @@ static struct commit *lookup_label(struct repository *r, const char *label,
strbuf_reset(buf);
strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
- if (!read_ref(buf->buf, &oid)) {
+ if (!refs_read_ref(get_main_ref_store(the_repository), buf->buf, &oid)) {
commit = lookup_commit_object(r, &oid);
} else {
/* fall back to non-rewritten ref or commit */
@@ -4047,9 +4050,10 @@ static int do_reset(struct repository *r,
ret = error(_("could not write index"));
if (!ret)
- ret = update_ref(reflog_message(opts, "reset", "'%.*s'",
- len, name), "HEAD", &oid,
- NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+ ret = refs_update_ref(get_main_ref_store(the_repository), reflog_message(opts, "reset", "'%.*s'",
+ len, name),
+ "HEAD", &oid,
+ NULL, 0, UPDATE_REFS_MSG_ON_ERR);
cleanup:
free((void *)desc.buffer);
if (ret < 0)
@@ -4531,7 +4535,7 @@ static int do_update_ref(struct repository *r, const char *refname)
for_each_string_list_item(item, &list) {
if (!strcmp(item->string, refname)) {
struct update_ref_record *rec = item->util;
- if (read_ref("HEAD", &rec->after))
+ if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &rec->after))
return -1;
break;
}
@@ -5091,15 +5095,15 @@ static int pick_commits(struct repository *r,
}
msg = reflog_message(opts, "finish", "%s onto %s",
head_ref.buf, buf.buf);
- if (update_ref(msg, head_ref.buf, &head, &orig,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), msg, head_ref.buf, &head, &orig,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
res = error(_("could not update %s"),
head_ref.buf);
goto cleanup_head_ref;
}
msg = reflog_message(opts, "finish", "returning to %s",
head_ref.buf);
- if (create_symref("HEAD", head_ref.buf, msg)) {
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", head_ref.buf, msg)) {
res = error(_("could not update HEAD to %s"),
head_ref.buf);
goto cleanup_head_ref;
@@ -6270,10 +6274,11 @@ static int add_decorations_to_list(const struct commit *commit,
struct todo_add_branch_context *ctx)
{
const struct name_decoration *decoration = get_name_decoration(&commit->object);
- const char *head_ref = resolve_ref_unsafe("HEAD",
- RESOLVE_REF_READING,
- NULL,
- NULL);
+ const char *head_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING,
+ NULL,
+ NULL);
while (decoration) {
struct todo_item *item;
diff --git a/serve.c b/serve.c
index aa651b7..884cd84 100644
--- a/serve.c
+++ b/serve.c
@@ -1,7 +1,9 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "repository.h"
#include "config.h"
-#include "hash-ll.h"
+#include "hash.h"
#include "pkt-line.h"
#include "version.h"
#include "ls-refs.h"
diff --git a/server-info.c b/server-info.c
index e2fe0f9..f61296a 100644
--- a/server-info.c
+++ b/server-info.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "dir.h"
#include "environment.h"
@@ -13,6 +15,7 @@
#include "object-store-ll.h"
#include "server-info.h"
#include "strbuf.h"
+#include "tempfile.h"
struct update_info_ctx {
FILE *cur_fp;
@@ -75,9 +78,8 @@ static int update_info_file(char *path,
int force)
{
char *tmp = mkpathdup("%s_XXXXXX", path);
+ struct tempfile *f = NULL;
int ret = -1;
- int fd = -1;
- FILE *to_close;
struct update_info_ctx uic = {
.cur_fp = NULL,
.old_fp = NULL,
@@ -86,13 +88,12 @@ static int update_info_file(char *path,
};
safe_create_leading_directories(path);
- fd = git_mkstemp_mode(tmp, 0666);
- if (fd < 0)
+ f = mks_tempfile_m(tmp, 0666);
+ if (!f)
goto out;
- to_close = uic.cur_fp = fdopen(fd, "w");
+ uic.cur_fp = fdopen_tempfile(f, "w");
if (!uic.cur_fp)
goto out;
- fd = -1;
/* no problem on ENOENT and old_fp == NULL, it's stale, now */
if (!force)
@@ -121,27 +122,22 @@ static int update_info_file(char *path,
}
uic.cur_fp = NULL;
- if (fclose(to_close))
- goto out;
if (uic_is_stale(&uic)) {
- if (adjust_shared_perm(tmp) < 0)
+ if (adjust_shared_perm(get_tempfile_path(f)) < 0)
goto out;
- if (rename(tmp, path) < 0)
+ if (rename_tempfile(&f, path) < 0)
goto out;
} else {
- unlink(tmp);
+ delete_tempfile(&f);
}
ret = 0;
out:
if (ret) {
error_errno("unable to update %s", path);
- if (uic.cur_fp)
- fclose(uic.cur_fp);
- else if (fd >= 0)
- close(fd);
- unlink(tmp);
+ if (f)
+ delete_tempfile(&f);
}
free(tmp);
if (uic.old_fp)
@@ -175,7 +171,8 @@ static int add_info_ref(const char *path, const struct object_id *oid,
static int generate_info_refs(struct update_info_ctx *uic)
{
- return for_each_ref(add_info_ref, uic);
+ return refs_for_each_ref(get_main_ref_store(the_repository),
+ add_info_ref, uic);
}
static int update_info_refs(int force)
diff --git a/setup.c b/setup.c
index 3ef7b68..d458edc 100644
--- a/setup.c
+++ b/setup.c
@@ -1,9 +1,12 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "copy.h"
#include "environment.h"
#include "exec-cmd.h"
#include "gettext.h"
+#include "hex.h"
#include "object-name.h"
#include "refs.h"
#include "repository.h"
@@ -342,6 +345,58 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
return ret;
}
+static int validate_headref(const char *path)
+{
+ struct stat st;
+ char buffer[256];
+ const char *refname;
+ struct object_id oid;
+ int fd;
+ ssize_t len;
+
+ if (lstat(path, &st) < 0)
+ return -1;
+
+ /* Make sure it is a "refs/.." symlink */
+ if (S_ISLNK(st.st_mode)) {
+ len = readlink(path, buffer, sizeof(buffer)-1);
+ if (len >= 5 && !memcmp("refs/", buffer, 5))
+ return 0;
+ return -1;
+ }
+
+ /*
+ * Anything else, just open it and try to see if it is a symbolic ref.
+ */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ len = read_in_full(fd, buffer, sizeof(buffer)-1);
+ close(fd);
+
+ if (len < 0)
+ return -1;
+ buffer[len] = '\0';
+
+ /*
+ * Is it a symbolic ref?
+ */
+ if (skip_prefix(buffer, "ref:", &refname)) {
+ while (isspace(*refname))
+ refname++;
+ if (starts_with(refname, "refs/"))
+ return 0;
+ }
+
+ /*
+ * Is this a detached HEAD?
+ */
+ if (get_oid_hex_any(buffer, &oid) != GIT_HASH_UNKNOWN)
+ return 0;
+
+ return -1;
+}
+
/*
* Test if it looks like we're at a git directory.
* We want to see:
@@ -1177,21 +1232,20 @@ static int safe_directory_cb(const char *key, const char *value,
} else if (!strcmp(value, "*")) {
data->is_safe = 1;
} else {
- const char *allowed = NULL;
+ char *allowed = NULL;
if (!git_config_pathname(&allowed, key, value)) {
- if (!allowed)
- allowed = value;
- if (ends_with(allowed, "/*")) {
- size_t len = strlen(allowed);
- if (!fspathncmp(allowed, data->path, len - 1))
+ const char *check = allowed ? allowed : value;
+ if (ends_with(check, "/*")) {
+ size_t len = strlen(check);
+ if (!fspathncmp(check, data->path, len - 1))
data->is_safe = 1;
- } else if (!fspathcmp(data->path, allowed)) {
+ } else if (!fspathcmp(data->path, check)) {
data->is_safe = 1;
}
}
if (allowed != value)
- free((char *)allowed);
+ free(allowed);
}
return 0;
@@ -1830,7 +1884,7 @@ static int template_dir_cb(const char *key, const char *value,
char *path = NULL;
FREE_AND_NULL(data->path);
- if (!git_config_pathname((const char **)&path, key, value))
+ if (!git_config_pathname(&path, key, value))
data->path = path ? path : xstrdup(value);
}
@@ -2005,7 +2059,7 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
}
void initialize_repository_version(int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
int reinit)
{
char repo_version_string[10];
@@ -2036,6 +2090,8 @@ void initialize_repository_version(int hash_algo,
if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
git_config_set("extensions.refstorage",
ref_storage_format_to_name(ref_storage_format));
+ else if (reinit)
+ git_config_set_gently("extensions.refstorage", NULL);
}
static int is_reinit(void)
@@ -2050,14 +2106,15 @@ static int is_reinit(void)
return ret;
}
-void create_reference_database(unsigned int ref_storage_format,
+void create_reference_database(enum ref_storage_format ref_storage_format,
const char *initial_branch, int quiet)
{
struct strbuf err = STRBUF_INIT;
+ char *to_free = NULL;
int reinit = is_reinit();
repo_set_ref_storage_format(the_repository, ref_storage_format);
- if (refs_init_db(get_main_ref_store(the_repository), 0, &err))
+ if (ref_store_create_on_disk(get_main_ref_store(the_repository), 0, &err))
die("failed to set up refs db: %s", err.buf);
/*
@@ -2068,14 +2125,15 @@ void create_reference_database(unsigned int ref_storage_format,
char *ref;
if (!initial_branch)
- initial_branch = git_default_branch_name(quiet);
+ initial_branch = to_free =
+ repo_default_branch_name(the_repository, quiet);
ref = xstrfmt("refs/heads/%s", initial_branch);
if (check_refname_format(ref, 0) < 0)
die(_("invalid initial branch name: '%s'"),
initial_branch);
- if (create_symref("HEAD", ref, NULL) < 0)
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", ref, NULL) < 0)
exit(1);
free(ref);
}
@@ -2085,6 +2143,7 @@ void create_reference_database(unsigned int ref_storage_format,
initial_branch);
strbuf_release(&err);
+ free(to_free);
}
static int create_default_files(const char *template_path,
@@ -2246,7 +2305,7 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
}
static void validate_ref_storage_format(struct repository_format *repo_fmt,
- unsigned int format)
+ enum ref_storage_format format)
{
const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
@@ -2266,7 +2325,7 @@ static void validate_ref_storage_format(struct repository_format *repo_fmt,
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
const char *initial_branch,
int init_shared_repository, unsigned int flags)
{
diff --git a/setup.h b/setup.h
index b3fd3bf..cd8dbc2 100644
--- a/setup.h
+++ b/setup.h
@@ -1,6 +1,7 @@
#ifndef SETUP_H
#define SETUP_H
+#include "refs.h"
#include "string-list.h"
int is_inside_git_dir(void);
@@ -128,7 +129,7 @@ struct repository_format {
int is_bare;
int hash_algo;
int compat_hash_algo;
- unsigned int ref_storage_format;
+ enum ref_storage_format ref_storage_format;
int sparse_index;
char *work_tree;
struct string_list unknown_extensions;
@@ -192,13 +193,13 @@ const char *get_template_dir(const char *option_template);
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
const char *initial_branch, int init_shared_repository,
unsigned int flags);
void initialize_repository_version(int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
int reinit);
-void create_reference_database(unsigned int ref_storage_format,
+void create_reference_database(enum ref_storage_format ref_storage_format,
const char *initial_branch, int quiet);
/*
diff --git a/shallow.c b/shallow.c
index 7ff50dd..31a6ca4 100644
--- a/shallow.c
+++ b/shallow.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hex.h"
#include "repository.h"
@@ -678,8 +680,10 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
* connect to old refs. If not (e.g. force ref updates) it'll
* have to go down to the current shallow commits.
*/
- head_ref(mark_uninteresting, NULL);
- for_each_ref(mark_uninteresting, NULL);
+ refs_head_ref(get_main_ref_store(the_repository), mark_uninteresting,
+ NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ mark_uninteresting, NULL);
/* Mark potential bottoms so we won't go out of bound */
for (i = 0; i < nr_shallow; i++) {
@@ -782,8 +786,8 @@ static void post_assign_shallow(struct shallow_info *info,
info->nr_theirs = dst;
memset(&ca, 0, sizeof(ca));
- head_ref(add_ref, &ca);
- for_each_ref(add_ref, &ca);
+ refs_head_ref(get_main_ref_store(the_repository), add_ref, &ca);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_ref, &ca);
/* Remove unreachable shallow commits from "ours" */
for (i = dst = 0; i < info->nr_ours; i++) {
@@ -822,8 +826,10 @@ int delayed_reachability_test(struct shallow_info *si, int c)
struct commit_array ca;
memset(&ca, 0, sizeof(ca));
- head_ref(add_ref, &ca);
- for_each_ref(add_ref, &ca);
+ refs_head_ref(get_main_ref_store(the_repository),
+ add_ref, &ca);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ add_ref, &ca);
si->commits = ca.commits;
si->nr_commits = ca.nr;
}
diff --git a/split-index.c b/split-index.c
index 8c38687..120c819 100644
--- a/split-index.c
+++ b/split-index.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hash.h"
@@ -29,7 +31,7 @@ int read_link_extension(struct index_state *istate,
if (sz < the_hash_algo->rawsz)
return error("corrupt link extension (too short)");
si = init_split_index(istate);
- oidread(&si->base_oid, data);
+ oidread(&si->base_oid, data, the_repository->hash_algo);
data += the_hash_algo->rawsz;
sz -= the_hash_algo->rawsz;
if (!sz)
diff --git a/split-index.h b/split-index.h
index 15a29cd..1a153f4 100644
--- a/split-index.h
+++ b/split-index.h
@@ -1,7 +1,7 @@
#ifndef SPLIT_INDEX_H
#define SPLIT_INDEX_H
-#include "hash-ll.h"
+#include "hash.h"
struct index_state;
struct strbuf;
diff --git a/strbuf.c b/strbuf.c
index 0d929e4..3d2189a 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -313,6 +313,15 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
strbuf_setlen(sb, sb->len + len);
}
+void strbuf_addstrings(struct strbuf *sb, const char *s, size_t n)
+{
+ size_t len = strlen(s);
+
+ strbuf_grow(sb, st_mult(len, n));
+ for (size_t i = 0; i < n; i++)
+ strbuf_add(sb, s, len);
+}
+
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
{
strbuf_grow(sb, sb2->len);
@@ -691,8 +700,10 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
int strbuf_appendwholeline(struct strbuf *sb, FILE *fp, int term)
{
struct strbuf line = STRBUF_INIT;
- if (strbuf_getwholeline(&line, fp, term))
+ if (strbuf_getwholeline(&line, fp, term)) {
+ strbuf_release(&line);
return EOF;
+ }
strbuf_addbuf(sb, &line);
strbuf_release(&line);
return 0;
diff --git a/strbuf.h b/strbuf.h
index 97fa4a3..003f880 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -311,6 +311,11 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)
}
/**
+ * Add a NUL-terminated string the specified number of times to the buffer.
+ */
+void strbuf_addstrings(struct strbuf *sb, const char *s, size_t n);
+
+/**
* Copy the contents of another buffer at the end of the current one.
*/
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
diff --git a/streaming.c b/streaming.c
index 10adf62..3883951 100644
--- a/streaming.c
+++ b/streaming.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2011, Google Inc.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "convert.h"
#include "environment.h"
diff --git a/strvec.c b/strvec.c
index 178f4f3..d4073ec 100644
--- a/strvec.c
+++ b/strvec.c
@@ -56,6 +56,26 @@ void strvec_pushv(struct strvec *array, const char **items)
strvec_push(array, *items);
}
+const char *strvec_replace(struct strvec *array, size_t idx, const char *replacement)
+{
+ char *to_free;
+ if (idx >= array->nr)
+ BUG("index outside of array boundary");
+ to_free = (char *) array->v[idx];
+ array->v[idx] = xstrdup(replacement);
+ free(to_free);
+ return array->v[idx];
+}
+
+void strvec_remove(struct strvec *array, size_t idx)
+{
+ if (idx >= array->nr)
+ BUG("index outside of array boundary");
+ free((char *)array->v[idx]);
+ memmove(array->v + idx, array->v + idx + 1, (array->nr - idx) * sizeof(char *));
+ array->nr--;
+}
+
void strvec_pop(struct strvec *array)
{
if (!array->nr)
diff --git a/strvec.h b/strvec.h
index 4715d3e..6c7e8b7 100644
--- a/strvec.h
+++ b/strvec.h
@@ -65,6 +65,19 @@ void strvec_pushl(struct strvec *, ...);
void strvec_pushv(struct strvec *, const char **);
/**
+ * Replace the value at the given index with a new value. The index must be
+ * valid. Returns a pointer to the inserted value.
+ */
+const char *strvec_replace(struct strvec *array, size_t idx, const char *replacement);
+
+/*
+ * Remove the value at the given index. The remainder of the array will be
+ * moved to fill the resulting gap. The provided index must point into the
+ * array.
+ */
+void strvec_remove(struct strvec *array, size_t idx);
+
+/**
* Remove the final element from the array. If there are no
* elements in the array, do nothing.
*/
diff --git a/submodule-config.c b/submodule-config.c
index 11428b4..9b0bb0b 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "dir.h"
#include "environment.h"
@@ -91,6 +93,8 @@ static void free_one_config(struct submodule_entry *entry)
free((void *) entry->config->path);
free((void *) entry->config->name);
free((void *) entry->config->branch);
+ free((void *) entry->config->url);
+ free((void *) entry->config->ignore);
free((void *) entry->config->update_strategy.command);
free(entry->config);
}
@@ -680,7 +684,7 @@ static int gitmodule_oid_from_commit(const struct object_id *treeish_name,
int ret = 0;
if (is_null_oid(treeish_name)) {
- oidclr(gitmodules_oid);
+ oidclr(gitmodules_oid, the_repository->hash_algo);
return 1;
}
diff --git a/submodule.c b/submodule.c
index 05d6db9..ab99a30 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "repository.h"
@@ -99,7 +101,8 @@ int is_staging_gitmodules_ok(struct index_state *istate)
static int for_each_remote_ref_submodule(const char *submodule,
each_ref_fn fn, void *cb_data)
{
- return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+ return refs_for_each_remote_ref(repo_get_submodule_ref_store(the_repository,
+ submodule),
fn, cb_data);
}
@@ -1242,7 +1245,8 @@ int push_unpushed_submodules(struct repository *r,
char *head;
struct object_id head_oid;
- head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, &head_oid, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
@@ -1280,7 +1284,8 @@ static int append_oid_to_array(const char *ref UNUSED,
void check_for_new_submodule_commits(struct object_id *oid)
{
if (!initialized_fetch_ref_tips) {
- for_each_ref(append_oid_to_array, &ref_tips_before_fetch);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ append_oid_to_array, &ref_tips_before_fetch);
initialized_fetch_ref_tips = 1;
}
@@ -2116,7 +2121,7 @@ static void submodule_reset_index(const char *path, const char *super_prefix)
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
(super_prefix ? super_prefix : ""), path);
- strvec_push(&cp.args, empty_tree_oid_hex());
+ strvec_push(&cp.args, empty_tree_oid_hex(the_repository->hash_algo));
if (run_command(&cp))
die(_("could not reset submodule index"));
@@ -2226,9 +2231,9 @@ int submodule_move_head(const char *path, const char *super_prefix,
strvec_push(&cp.args, "-m");
if (!(flags & SUBMODULE_MOVE_HEAD_FORCE))
- strvec_push(&cp.args, old_head ? old_head : empty_tree_oid_hex());
+ strvec_push(&cp.args, old_head ? old_head : empty_tree_oid_hex(the_repository->hash_algo));
- strvec_push(&cp.args, new_head ? new_head : empty_tree_oid_hex());
+ strvec_push(&cp.args, new_head ? new_head : empty_tree_oid_hex(the_repository->hash_algo));
if (run_command(&cp)) {
ret = error(_("Submodule '%s' could not be updated."), path);
diff --git a/t/Makefile b/t/Makefile
index 2d95046..b2eb9f7 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -48,7 +48,8 @@
CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c)
UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES))
-UNIT_TESTS = $(sort $(filter-out unit-tests/bin/t-basic%,$(UNIT_TEST_PROGRAMS)))
+UNIT_TESTS = $(sort $(UNIT_TEST_PROGRAMS))
+UNIT_TESTS_NO_DIR = $(notdir $(UNIT_TESTS))
# `test-chainlint` (which is a dependency of `test-lint`, `test` and `prove`)
# checks all tests in all scripts via a single invocation, so tell individual
@@ -67,7 +68,7 @@
test -z "$$failed" || $(MAKE) $$failed
prove: pre-clean check-chainlint $(TEST_LINT)
- @echo "*** prove ***"; $(CHAINLINTSUPPRESS) $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+ @echo "*** prove (shell & unit tests) ***"; $(CHAINLINTSUPPRESS) TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) :: $(GIT_TEST_OPTS)
$(MAKE) clean-except-prove-cache
$(T):
@@ -76,7 +77,7 @@
$(UNIT_TESTS):
@echo "*** $@ ***"; $@
-.PHONY: unit-tests unit-tests-raw unit-tests-prove
+.PHONY: unit-tests unit-tests-raw unit-tests-prove unit-tests-test-tool
unit-tests: $(DEFAULT_UNIT_TEST_TARGET)
unit-tests-raw: $(UNIT_TESTS)
@@ -84,6 +85,13 @@
unit-tests-prove:
@echo "*** prove - unit tests ***"; $(PROVE) $(GIT_PROVE_OPTS) $(UNIT_TESTS)
+unit-tests-test-tool:
+ @echo "*** test-tool - unit tests **"
+ ( \
+ cd unit-tests/bin && \
+ ../../helper/test-tool$X run-command testsuite $(UNIT_TESTS_NO_DIR)\
+ )
+
pre-clean:
$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c
index af43ee1..3f23f21 100644
--- a/t/helper/test-bitmap.c
+++ b/t/helper/test-bitmap.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "git-compat-util.h"
#include "pack-bitmap.h"
@@ -13,21 +15,41 @@ static int bitmap_dump_hashes(void)
return test_bitmap_hashes(the_repository);
}
+static int bitmap_dump_pseudo_merges(void)
+{
+ return test_bitmap_pseudo_merges(the_repository);
+}
+
+static int bitmap_dump_pseudo_merge_commits(uint32_t n)
+{
+ return test_bitmap_pseudo_merge_commits(the_repository, n);
+}
+
+static int bitmap_dump_pseudo_merge_objects(uint32_t n)
+{
+ return test_bitmap_pseudo_merge_objects(the_repository, n);
+}
+
int cmd__bitmap(int argc, const char **argv)
{
setup_git_directory();
- if (argc != 2)
- goto usage;
-
- if (!strcmp(argv[1], "list-commits"))
+ if (argc == 2 && !strcmp(argv[1], "list-commits"))
return bitmap_list_commits();
- if (!strcmp(argv[1], "dump-hashes"))
+ if (argc == 2 && !strcmp(argv[1], "dump-hashes"))
return bitmap_dump_hashes();
+ if (argc == 2 && !strcmp(argv[1], "dump-pseudo-merges"))
+ return bitmap_dump_pseudo_merges();
+ if (argc == 3 && !strcmp(argv[1], "dump-pseudo-merge-commits"))
+ return bitmap_dump_pseudo_merge_commits(atoi(argv[2]));
+ if (argc == 3 && !strcmp(argv[1], "dump-pseudo-merge-objects"))
+ return bitmap_dump_pseudo_merge_objects(atoi(argv[2]));
-usage:
usage("\ttest-tool bitmap list-commits\n"
- "\ttest-tool bitmap dump-hashes");
+ "\ttest-tool bitmap dump-hashes\n"
+ "\ttest-tool bitmap dump-pseudo-merges\n"
+ "\ttest-tool bitmap dump-pseudo-merge-commits <n>\n"
+ "\ttest-tool bitmap dump-pseudo-merge-objects <n>");
return -1;
}
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index 1281e66..f7f9b62 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "bloom.h"
#include "hex.h"
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
index 09dc787..0c5fa72 100644
--- a/t/helper/test-bundle-uri.c
+++ b/t/helper/test-bundle-uri.c
@@ -88,8 +88,6 @@ static int cmd_ls_remote(int argc, const char **argv)
die(_("bad repository '%s'"), dest);
die(_("no remote configured to get bundle URIs from"));
}
- if (!remote->url_nr)
- die(_("remote '%s' has no configured URL"), dest);
transport = transport_get(remote, NULL);
if (transport_get_remote_bundle_uri(transport) < 0) {
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
index e723639..5cdef3e 100644
--- a/t/helper/test-cache-tree.c
+++ b/t/helper/test-cache-tree.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "gettext.h"
#include "hex.h"
@@ -38,29 +39,29 @@ int cmd__cache_tree(int argc, const char **argv)
if (repo_read_index(the_repository) < 0)
die(_("unable to read index file"));
- oidcpy(&oid, &the_index.cache_tree->oid);
+ oidcpy(&oid, &the_repository->index->cache_tree->oid);
tree = parse_tree_indirect(&oid);
if (!tree)
die(_("not a tree object: %s"), oid_to_hex(&oid));
if (empty) {
/* clear the cache tree & allocate a new one */
- cache_tree_free(&the_index.cache_tree);
- the_index.cache_tree = cache_tree();
+ cache_tree_free(&the_repository->index->cache_tree);
+ the_repository->index->cache_tree = cache_tree();
} else if (invalidate_qty) {
/* invalidate the specified number of unique paths */
- float f_interval = (float)the_index.cache_nr / invalidate_qty;
+ float f_interval = (float)the_repository->index->cache_nr / invalidate_qty;
int interval = f_interval < 1.0 ? 1 : (int)f_interval;
- for (i = 0; i < invalidate_qty && i * interval < the_index.cache_nr; i++)
- cache_tree_invalidate_path(&the_index, the_index.cache[i * interval]->name);
+ for (i = 0; i < invalidate_qty && i * interval < the_repository->index->cache_nr; i++)
+ cache_tree_invalidate_path(the_repository->index, the_repository->index->cache[i * interval]->name);
}
if (argc != 1)
usage_with_options(test_cache_tree_usage, options);
else if (!strcmp(argv[0], "prime"))
- prime_cache_tree(the_repository, &the_index, tree);
+ prime_cache_tree(the_repository, the_repository->index, tree);
else if (!strcmp(argv[0], "update"))
- cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+ cache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
/* use "control" subcommand to specify no-op */
else if (!!strcmp(argv[0], "control"))
die(_("Unhandled subcommand '%s'"), argv[0]);
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index c38f546..3f0c7d0 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hash.h"
#include "hex.h"
@@ -68,10 +69,10 @@ int cmd__dump_cache_tree(int ac UNUSED, const char **av UNUSED)
setup_git_directory();
if (repo_read_index(the_repository) < 0)
die("unable to read index file");
- istate = the_index;
+ istate = *the_repository->index;
istate.cache_tree = another;
cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
- ret = dump_cache_tree(the_index.cache_tree, another, "");
+ ret = dump_cache_tree(the_repository->index->cache_tree, another, "");
cache_tree_free(&another);
return ret;
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
index 4f215fe..1b7f37a 100644
--- a/t/helper/test-dump-fsmonitor.c
+++ b/t/helper/test-dump-fsmonitor.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "read-cache-ll.h"
#include "repository.h"
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index f29d18e..a6720fa 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "read-cache-ll.h"
@@ -19,16 +20,16 @@ int cmd__dump_split_index(int ac UNUSED, const char **av)
setup_git_directory();
- do_read_index(&the_index, av[1], 1);
- printf("own %s\n", oid_to_hex(&the_index.oid));
- si = the_index.split_index;
+ do_read_index(the_repository->index, av[1], 1);
+ printf("own %s\n", oid_to_hex(&the_repository->index->oid));
+ si = the_repository->index->split_index;
if (!si) {
printf("not a split index\n");
return 0;
}
printf("base %s\n", oid_to_hex(&si->base_oid));
- for (i = 0; i < the_index.cache_nr; i++) {
- struct cache_entry *ce = the_index.cache[i];
+ for (i = 0; i < the_repository->index->cache_nr; i++) {
+ struct cache_entry *ce = the_repository->index->cache[i];
printf("%06o %s %d\t%s\n", ce->ce_mode,
oid_to_hex(&ce->oid), ce_stage(ce), ce->name);
}
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index b4af971..4f010d5 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "dir.h"
#include "hex.h"
@@ -56,7 +57,7 @@ int cmd__dump_untracked_cache(int ac UNUSED, const char **av UNUSED)
setup_git_directory();
if (repo_read_index(the_repository) < 0)
die("unable to read index file");
- uc = the_index.untracked;
+ uc = the_repository->index->untracked;
if (!uc) {
printf("no untracked cache\n");
return 0;
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
deleted file mode 100644
index 8f59f6b..0000000
--- a/t/helper/test-example-decorate.c
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "test-tool.h"
-#include "git-compat-util.h"
-#include "object.h"
-#include "decorate.h"
-#include "repository.h"
-
-int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED)
-{
- struct decoration n;
- struct object_id one_oid = { {1} };
- struct object_id two_oid = { {2} };
- struct object_id three_oid = { {3} };
- struct object *one, *two, *three;
-
- int decoration_a, decoration_b;
-
- void *ret;
-
- int i, objects_noticed = 0;
-
- /*
- * The struct must be zero-initialized.
- */
- memset(&n, 0, sizeof(n));
-
- /*
- * Add 2 objects, one with a non-NULL decoration and one with a NULL
- * decoration.
- */
- one = lookup_unknown_object(the_repository, &one_oid);
- two = lookup_unknown_object(the_repository, &two_oid);
- ret = add_decoration(&n, one, &decoration_a);
- if (ret)
- BUG("when adding a brand-new object, NULL should be returned");
- ret = add_decoration(&n, two, NULL);
- if (ret)
- BUG("when adding a brand-new object, NULL should be returned");
-
- /*
- * When re-adding an already existing object, the old decoration is
- * returned.
- */
- ret = add_decoration(&n, one, NULL);
- if (ret != &decoration_a)
- BUG("when readding an already existing object, existing decoration should be returned");
- ret = add_decoration(&n, two, &decoration_b);
- if (ret)
- BUG("when readding an already existing object, existing decoration should be returned");
-
- /*
- * Lookup returns the added declarations, or NULL if the object was
- * never added.
- */
- ret = lookup_decoration(&n, one);
- if (ret)
- BUG("lookup should return added declaration");
- ret = lookup_decoration(&n, two);
- if (ret != &decoration_b)
- BUG("lookup should return added declaration");
- three = lookup_unknown_object(the_repository, &three_oid);
- ret = lookup_decoration(&n, three);
- if (ret)
- BUG("lookup for unknown object should return NULL");
-
- /*
- * The user can also loop through all entries.
- */
- for (i = 0; i < n.size; i++) {
- if (n.entries[i].base)
- objects_noticed++;
- }
- if (objects_noticed != 2)
- BUG("should have 2 objects");
-
- clear_decoration(&n, NULL);
-
- return 0;
-}
diff --git a/t/unit-tests/t-basic.c b/t/helper/test-example-tap.c
similarity index 95%
rename from t/unit-tests/t-basic.c
rename to t/helper/test-example-tap.c
index fda1ae5..d072ad5 100644
--- a/t/unit-tests/t-basic.c
+++ b/t/helper/test-example-tap.c
@@ -1,4 +1,5 @@
-#include "test-lib.h"
+#include "test-tool.h"
+#include "t/unit-tests/test-lib.h"
/*
* The purpose of this "unit test" is to verify a few invariants of the unit
@@ -69,7 +70,7 @@ static void t_empty(void)
; /* empty */
}
-int cmd_main(int argc, const char **argv)
+int cmd__example_tap(int argc, const char **argv)
{
test_res = TEST(check_res = check_int(1, ==, 1), "passing test");
TEST(t_res(1), "passing test and assertion return 1");
diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c
index e8bd793..14b2b0c 100644
--- a/t/helper/test-find-pack.c
+++ b/t/helper/test-find-pack.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "object-name.h"
#include "object-store.h"
diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c
index 8280984..02bfe92 100644
--- a/t/helper/test-fsmonitor-client.c
+++ b/t/helper/test-fsmonitor-client.c
@@ -3,6 +3,8 @@
* a `git fsmonitor--daemon` daemon.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "parse-options.h"
#include "fsmonitor-ipc.h"
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c
index b235da5..7de822a 100644
--- a/t/helper/test-hash-speed.c
+++ b/t/helper/test-hash-speed.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "hash-ll.h"
+#include "hash.h"
#define NUM_SECONDS 3
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 0eb0b3d..2912899 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -36,7 +36,8 @@ static int test_entry_cmp(const void *cmp_data,
}
static struct test_entry *alloc_test_entry(unsigned int hash,
- char *key, char *value)
+ const char *key,
+ const char *value)
{
size_t klen = strlen(key);
size_t vlen = strlen(value);
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index afe393f..ed52eb7 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -174,7 +174,7 @@ static void make_arr4(int pretty)
jw_end(&arr4);
}
-static char *expect_nest1 =
+static const char *expect_nest1 =
"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
static struct json_writer nest1 = JSON_WRITER_INIT;
@@ -195,10 +195,10 @@ static void make_nest1(int pretty)
jw_release(&arr1);
}
-static char *expect_inline1 =
+static const char *expect_inline1 =
"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
-static char *pretty_inline1 =
+static const char *pretty_inline1 =
("{\n"
" \"obj1\": {\n"
" \"a\": \"abc\",\n"
@@ -236,10 +236,10 @@ static void make_inline1(int pretty)
jw_end(&inline1);
}
-static char *expect_inline2 =
+static const char *expect_inline2 =
"[[1,2],[3,4],{\"a\":\"abc\"}]";
-static char *pretty_inline2 =
+static const char *pretty_inline2 =
("[\n"
" [\n"
" 1,\n"
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index 187a115..40f5df4 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "environment.h"
#include "name-hash.h"
@@ -40,22 +41,22 @@ static void dump_run(void)
repo_read_index(the_repository);
if (single) {
- test_lazy_init_name_hash(&the_index, 0);
+ test_lazy_init_name_hash(the_repository->index, 0);
} else {
- int nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
+ int nr_threads_used = test_lazy_init_name_hash(the_repository->index, 1);
if (!nr_threads_used)
die("non-threaded code path used");
}
- hashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir,
+ hashmap_for_each_entry(&the_repository->index->dir_hash, &iter_dir, dir,
ent /* member name */)
printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name);
- hashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce,
+ hashmap_for_each_entry(&the_repository->index->name_hash, &iter_cache, ce,
ent /* member name */)
printf("name %08x %s\n", ce->ent.hash, ce->name);
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
/*
@@ -74,7 +75,7 @@ static uint64_t time_runs(int try_threaded)
t0 = getnanotime();
repo_read_index(the_repository);
t1 = getnanotime();
- nr_threads_used = test_lazy_init_name_hash(&the_index, try_threaded);
+ nr_threads_used = test_lazy_init_name_hash(the_repository->index, try_threaded);
t2 = getnanotime();
sum += (t2 - t1);
@@ -86,16 +87,16 @@ static uint64_t time_runs(int try_threaded)
printf("%f %f %d multi %d\n",
((double)(t1 - t0))/1000000000,
((double)(t2 - t1))/1000000000,
- the_index.cache_nr,
+ the_repository->index->cache_nr,
nr_threads_used);
else
printf("%f %f %d single\n",
((double)(t1 - t0))/1000000000,
((double)(t2 - t1))/1000000000,
- the_index.cache_nr);
+ the_repository->index->cache_nr);
fflush(stdout);
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
avg = sum / count;
@@ -120,8 +121,8 @@ static void analyze_run(void)
int nr;
repo_read_index(the_repository);
- cache_nr_limit = the_index.cache_nr;
- discard_index(&the_index);
+ cache_nr_limit = the_repository->index->cache_nr;
+ discard_index(the_repository->index);
nr = analyze;
while (1) {
@@ -135,22 +136,22 @@ static void analyze_run(void)
for (i = 0; i < count; i++) {
repo_read_index(the_repository);
- the_index.cache_nr = nr; /* cheap truncate of index */
+ the_repository->index->cache_nr = nr; /* cheap truncate of index */
t1s = getnanotime();
- test_lazy_init_name_hash(&the_index, 0);
+ test_lazy_init_name_hash(the_repository->index, 0);
t2s = getnanotime();
sum_single += (t2s - t1s);
- the_index.cache_nr = cache_nr_limit;
- discard_index(&the_index);
+ the_repository->index->cache_nr = cache_nr_limit;
+ discard_index(the_repository->index);
repo_read_index(the_repository);
- the_index.cache_nr = nr; /* cheap truncate of index */
+ the_repository->index->cache_nr = nr; /* cheap truncate of index */
t1m = getnanotime();
- nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
+ nr_threads_used = test_lazy_init_name_hash(the_repository->index, 1);
t2m = getnanotime();
sum_multi += (t2m - t1m);
- the_index.cache_nr = cache_nr_limit;
- discard_index(&the_index);
+ the_repository->index->cache_nr = cache_nr_limit;
+ discard_index(the_repository->index);
if (!nr_threads_used)
printf(" [size %8d] [single %f] non-threaded code path used\n",
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index d0db5ff..e0e2048 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "match-trees.h"
diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c
index aafe398..076b849 100644
--- a/t/helper/test-oid-array.c
+++ b/t/helper/test-oid-array.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "oid-array.h"
@@ -17,6 +19,8 @@ int cmd__oid_array(int argc UNUSED, const char **argv UNUSED)
int nongit_ok;
setup_git_directory_gently(&nongit_ok);
+ if (nongit_ok)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
while (strbuf_getline(&line, stdin) != EOF) {
const char *arg;
diff --git a/t/helper/test-oidmap.c b/t/helper/test-oidmap.c
index bd30244..c03cfa5 100644
--- a/t/helper/test-oidmap.c
+++ b/t/helper/test-oidmap.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "object-name.h"
diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c
deleted file mode 100644
index c7a1d4c..0000000
--- a/t/helper/test-oidtree.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "test-tool.h"
-#include "hex.h"
-#include "oidtree.h"
-#include "setup.h"
-#include "strbuf.h"
-
-static enum cb_next print_oid(const struct object_id *oid, void *data UNUSED)
-{
- puts(oid_to_hex(oid));
- return CB_CONTINUE;
-}
-
-int cmd__oidtree(int argc UNUSED, const char **argv UNUSED)
-{
- struct oidtree ot;
- struct strbuf line = STRBUF_INIT;
- int nongit_ok;
- int algo = GIT_HASH_UNKNOWN;
-
- oidtree_init(&ot);
- setup_git_directory_gently(&nongit_ok);
-
- while (strbuf_getline(&line, stdin) != EOF) {
- const char *arg;
- struct object_id oid;
-
- if (skip_prefix(line.buf, "insert ", &arg)) {
- if (get_oid_hex_any(arg, &oid) == GIT_HASH_UNKNOWN)
- die("insert not a hexadecimal oid: %s", arg);
- algo = oid.algo;
- oidtree_insert(&ot, &oid);
- } else if (skip_prefix(line.buf, "contains ", &arg)) {
- if (get_oid_hex(arg, &oid))
- die("contains not a hexadecimal oid: %s", arg);
- printf("%d\n", oidtree_contains(&ot, &oid));
- } else if (skip_prefix(line.buf, "each ", &arg)) {
- char buf[GIT_MAX_HEXSZ + 1] = { '0' };
- memset(&oid, 0, sizeof(oid));
- memcpy(buf, arg, strlen(arg));
- buf[hash_algos[algo].hexsz] = '\0';
- get_oid_hex_any(buf, &oid);
- oid.algo = algo;
- oidtree_each(&ot, &oid, strlen(arg), print_oid, NULL);
- } else if (!strcmp(line.buf, "clear")) {
- oidtree_clear(&ot);
- } else {
- die("unknown command: %s", line.buf);
- }
- }
-
- strbuf_release(&line);
-
- return 0;
-}
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
index 67a964e..f8f9afb 100644
--- a/t/helper/test-pack-mtimes.c
+++ b/t/helper/test-pack-mtimes.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "strbuf.h"
diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c
index 910a128..0ead5291 100644
--- a/t/helper/test-partial-clone.c
+++ b/t/helper/test-partial-clone.c
@@ -21,7 +21,7 @@ static void object_info(const char *gitdir, const char *oid_hex)
if (repo_init(&r, gitdir, NULL))
die("could not init repo");
- if (parse_oid_hex(oid_hex, &oid, &p))
+ if (parse_oid_hex_algop(oid_hex, &oid, &p, r.hash_algo))
die("could not parse oid");
if (oid_object_info_extended(&r, &oid, &oi, 0))
die("could not obtain object info");
diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c
index f30022d..29361c7 100644
--- a/t/helper/test-proc-receive.c
+++ b/t/helper/test-proc-receive.c
@@ -3,8 +3,8 @@
#include "hex.h"
#include "parse-options.h"
#include "pkt-line.h"
-#include "setup.h"
#include "sigchain.h"
+#include "string-list.h"
static const char *proc_receive_usage[] = {
"test-tool proc-receive [<options>]",
@@ -92,9 +92,9 @@ static void proc_receive_read_commands(struct packet_reader *reader,
if (die_read_commands)
die("die with the --die-read-commands option");
- if (parse_oid_hex(reader->line, &old_oid, &p) ||
+ if (parse_oid_hex_any(reader->line, &old_oid, &p) == GIT_HASH_UNKNOWN ||
*p++ != ' ' ||
- parse_oid_hex(p, &new_oid, &p) ||
+ parse_oid_hex_any(p, &new_oid, &p) == GIT_HASH_UNKNOWN ||
*p++ != ' ')
die("protocol error: expected 'old new ref', got '%s'",
reader->line);
@@ -128,7 +128,6 @@ static void proc_receive_read_push_options(struct packet_reader *reader,
int cmd__proc_receive(int argc, const char **argv)
{
- int nongit_ok = 0;
struct packet_reader reader;
struct command *commands = NULL;
struct string_list push_options = STRING_LIST_INIT_DUP;
@@ -154,8 +153,6 @@ int cmd__proc_receive(int argc, const char **argv)
OPT_END()
};
- setup_git_directory_gently(&nongit_ok);
-
argc = parse_options(argc, argv, "test-tools", options, proc_receive_usage, 0);
if (argc > 0)
usage_msg_opt("Too many arguments.", proc_receive_usage, options);
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 1e3b431..5dd3743 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "commit.h"
#include "commit-reach.h"
@@ -62,7 +64,7 @@ int cmd__reach(int ac, const char **av)
die("failed to resolve %s", buf.buf + 2);
orig = parse_object(r, &oid);
- peeled = deref_tag_noverify(orig);
+ peeled = deref_tag_noverify(the_repository, orig);
if (!peeled)
die("failed to load commit for input %s resulting in oid %s\n",
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index 1acd362..d285c65 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "config.h"
#include "read-cache-ll.h"
@@ -10,7 +11,7 @@ int cmd__read_cache(int argc, const char **argv)
int i, cnt = 1;
const char *name = NULL;
- initialize_the_repository();
+ initialize_repository(the_repository);
if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) {
argc--;
@@ -27,16 +28,16 @@ int cmd__read_cache(int argc, const char **argv)
if (name) {
int pos;
- refresh_index(&the_index, REFRESH_QUIET,
+ refresh_index(the_repository->index, REFRESH_QUIET,
NULL, NULL, NULL);
- pos = index_name_pos(&the_index, name, strlen(name));
+ pos = index_name_pos(the_repository->index, name, strlen(name));
if (pos < 0)
die("%s not in index", name);
printf("%s is%s up to date\n", name,
- ce_uptodate(the_index.cache[pos]) ? "" : " not");
+ ce_uptodate(the_repository->index->cache[pos]) ? "" : " not");
write_file(name, "%d\n", i);
}
- discard_index(&the_index);
+ discard_index(the_repository->index);
}
return 0;
}
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 8c7a83f..d9e980d 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "commit-graph.h"
#include "repository.h"
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index 4acae41..83effc2 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "midx.h"
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 82bbf6e..637b8b2 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "hex.h"
#include "refs.h"
@@ -82,7 +84,7 @@ static const char **get_store(const char **argv, struct ref_store **refs)
add_to_alternates_memory(sb.buf);
strbuf_release(&sb);
- *refs = get_submodule_ref_store(gitdir);
+ *refs = repo_get_submodule_ref_store(the_repository, gitdir);
} else if (skip_prefix(argv[0], "worktree:", &gitdir)) {
struct worktree **p, **worktrees = get_worktrees();
@@ -118,7 +120,7 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv)
const char *target = notnull(*argv++, "target");
const char *logmsg = *argv++;
- return refs_create_symref(refs, refname, target, logmsg);
+ return refs_update_symref(refs, refname, target, logmsg);
}
static struct flag_definition transaction_flags[] = {
@@ -126,6 +128,7 @@ static struct flag_definition transaction_flags[] = {
FLAG_DEF(REF_FORCE_CREATE_REFLOG),
FLAG_DEF(REF_SKIP_OID_VERIFICATION),
FLAG_DEF(REF_SKIP_REFNAME_VERIFICATION),
+ FLAG_DEF(REF_SKIP_CREATE_REFLOG),
{ NULL, 0 }
};
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 00237ef..9160bc5 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -5,7 +5,6 @@
int cmd__reftable(int argc, const char **argv)
{
/* test from simple to complex. */
- basics_test_main(argc, argv);
record_test_main(argc, argv);
block_test_main(argc, argv);
tree_test_main(argc, argv);
@@ -13,7 +12,6 @@ int cmd__reftable(int argc, const char **argv)
readwrite_test_main(argc, argv);
merged_test_main(argc, argv);
stack_test_main(argc, argv);
- refname_test_main(argc, argv);
return 0;
}
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index 80042ea..366bd70 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -20,8 +20,8 @@ static struct reg_flag reg_flags[] = {
static int test_regex_bug(void)
{
- char *pat = "[^={} \t]+";
- char *str = "={}\nfred";
+ const char *pat = "[^={} \t]+";
+ const char *str = "={}\nfred";
regex_t r;
regmatch_t m[1];
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 0c7c5aa..c6a074d 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "commit-graph.h"
#include "commit.h"
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index f346951..071f5bd 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -8,6 +8,8 @@
* published by the Free Software Foundation.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "commit.h"
#include "diff.h"
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index f8d564c..7e1d9e0 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -136,7 +136,7 @@ static void free_delay_entries(void)
strmap_clear(&delay, 0);
}
-static void add_delay_entry(char *pathname, int count, int requested)
+static void add_delay_entry(const char *pathname, int count, int requested)
{
struct delay_entry *entry = xcalloc(1, sizeof(*entry));
entry->count = count;
@@ -189,7 +189,8 @@ static void reply_list_available_blobs_cmd(void)
static void command_loop(void)
{
for (;;) {
- char *buf, *output;
+ char *buf;
+ const char *output;
char *pathname;
struct delay_entry *entry;
struct strbuf input = STRBUF_INIT;
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index c0ed872..61eb117 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -65,6 +65,7 @@ struct testsuite {
struct string_list tests, failed;
int next;
int quiet, immediate, verbose, verbose_log, trace, write_junit_xml;
+ const char *shell_path;
};
#define TESTSUITE_INIT { \
.tests = STRING_LIST_INIT_DUP, \
@@ -80,7 +81,9 @@ static int next_test(struct child_process *cp, struct strbuf *err, void *cb,
return 0;
test = suite->tests.items[suite->next++].string;
- strvec_pushl(&cp->args, "sh", test, NULL);
+ if (suite->shell_path)
+ strvec_push(&cp->args, suite->shell_path);
+ strvec_push(&cp->args, test);
if (suite->quiet)
strvec_push(&cp->args, "--quiet");
if (suite->immediate)
@@ -155,6 +158,8 @@ static int testsuite(int argc, const char **argv)
.task_finished = test_finished,
.data = &suite,
};
+ struct strbuf progpath = STRBUF_INIT;
+ size_t path_prefix_len;
argc = parse_options(argc, argv, NULL, options,
testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION);
@@ -162,26 +167,36 @@ static int testsuite(int argc, const char **argv)
if (max_jobs <= 0)
max_jobs = online_cpus();
+ /*
+ * If we run without a shell, execute the programs directly from CWD.
+ */
+ suite.shell_path = getenv("TEST_SHELL_PATH");
+ if (!suite.shell_path)
+ strbuf_addstr(&progpath, "./");
+ path_prefix_len = progpath.len;
+
dir = opendir(".");
if (!dir)
die("Could not open the current directory");
while ((d = readdir(dir))) {
const char *p = d->d_name;
- if (*p != 't' || !isdigit(p[1]) || !isdigit(p[2]) ||
- !isdigit(p[3]) || !isdigit(p[4]) || p[5] != '-' ||
- !ends_with(p, ".sh"))
+ if (!strcmp(p, ".") || !strcmp(p, ".."))
continue;
/* No pattern: match all */
if (!argc) {
- string_list_append(&suite.tests, p);
+ strbuf_setlen(&progpath, path_prefix_len);
+ strbuf_addstr(&progpath, p);
+ string_list_append(&suite.tests, progpath.buf);
continue;
}
for (i = 0; i < argc; i++)
if (!wildmatch(argv[i], p, 0)) {
- string_list_append(&suite.tests, p);
+ strbuf_setlen(&progpath, path_prefix_len);
+ strbuf_addstr(&progpath, p);
+ string_list_append(&suite.tests, progpath.buf);
break;
}
}
@@ -208,6 +223,7 @@ static int testsuite(int argc, const char **argv)
string_list_clear(&suite.tests, 0);
string_list_clear(&suite.failed, 0);
+ strbuf_release(&progpath);
return ret;
}
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index 0a816a9..64fff6e 100644
--- a/t/helper/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "lockfile.h"
#include "read-cache-ll.h"
@@ -15,9 +16,9 @@ int cmd__scrap_cache_tree(int ac UNUSED, const char **av UNUSED)
repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
if (repo_read_index(the_repository) < 0)
die("unable to read index file");
- cache_tree_free(&the_index.cache_tree);
- the_index.cache_tree = NULL;
- if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
+ cache_tree_free(&the_repository->index->cache_tree);
+ the_repository->index->cache_tree = NULL;
+ if (write_locked_index(the_repository->index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
return 0;
}
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
index dcb7f6c..e60d000 100644
--- a/t/helper/test-sha1.c
+++ b/t/helper/test-sha1.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "hash-ll.h"
+#include "hash.h"
int cmd__sha1(int ac, const char **av)
{
diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c
index 08cf149..2fb2043 100644
--- a/t/helper/test-sha256.c
+++ b/t/helper/test-sha256.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "hash-ll.h"
+#include "hash.h"
int cmd__sha256(int ac, const char **av)
{
diff --git a/t/helper/test-strcmp-offset.c b/t/helper/test-strcmp-offset.c
deleted file mode 100644
index d8473cf..0000000
--- a/t/helper/test-strcmp-offset.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "test-tool.h"
-#include "read-cache-ll.h"
-
-int cmd__strcmp_offset(int argc UNUSED, const char **argv)
-{
- int result;
- size_t offset;
-
- if (!argv[1] || !argv[2])
- die("usage: %s <string1> <string2>", argv[0]);
-
- result = strcmp_offset(argv[1], argv[2], &offset);
-
- /*
- * Because different CRTs behave differently, only rely on signs
- * of the result values.
- */
- result = (result < 0 ? -1 :
- result > 0 ? 1 :
- 0);
- printf("%d %"PRIuMAX"\n", result, (uintmax_t)offset);
- return 0;
-}
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index 9df2f03..cbe93f2 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "config.h"
#include "hash.h"
@@ -44,7 +46,7 @@ int cmd__submodule_config(int argc, const char **argv)
path_or_name = arg[1];
if (commit[0] == '\0')
- oidclr(&commit_oid);
+ oidclr(&commit_oid, the_repository->hash_algo);
else if (repo_get_oid(the_repository, commit, &commit_oid) < 0)
die_usage(argc, argv, "Commit not found.");
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index ecd40de..6ca069c 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "repository.h"
#include "setup.h"
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
index 7197969..22e518d 100644
--- a/t/helper/test-submodule.c
+++ b/t/helper/test-submodule.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "test-tool-utils.h"
#include "parse-options.h"
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 80a946b..93436a8 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -29,7 +29,7 @@ static struct test_cmd cmds[] = {
{ "dump-split-index", cmd__dump_split_index },
{ "dump-untracked-cache", cmd__dump_untracked_cache },
{ "env-helper", cmd__env_helper },
- { "example-decorate", cmd__example_decorate },
+ { "example-tap", cmd__example_tap },
{ "find-pack", cmd__find_pack },
{ "fsmonitor-client", cmd__fsmonitor_client },
{ "genrandom", cmd__genrandom },
@@ -45,7 +45,6 @@ static struct test_cmd cmds[] = {
{ "mktemp", cmd__mktemp },
{ "oid-array", cmd__oid_array },
{ "oidmap", cmd__oidmap },
- { "oidtree", cmd__oidtree },
{ "online-cpus", cmd__online_cpus },
{ "pack-mtimes", cmd__pack_mtimes },
{ "parse-options", cmd__parse_options },
@@ -77,7 +76,6 @@ static struct test_cmd cmds[] = {
{ "sha256", cmd__sha256 },
{ "sigchain", cmd__sigchain },
{ "simple-ipc", cmd__simple_ipc },
- { "strcmp-offset", cmd__strcmp_offset },
{ "string-list", cmd__string_list },
{ "submodule", cmd__submodule },
{ "submodule-config", cmd__submodule_config },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 2808b92..d9033d1 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -23,7 +23,7 @@ int cmd__dump_split_index(int argc, const char **argv);
int cmd__dump_untracked_cache(int argc, const char **argv);
int cmd__dump_reftable(int argc, const char **argv);
int cmd__env_helper(int argc, const char **argv);
-int cmd__example_decorate(int argc, const char **argv);
+int cmd__example_tap(int argc, const char **argv);
int cmd__find_pack(int argc, const char **argv);
int cmd__fsmonitor_client(int argc, const char **argv);
int cmd__genrandom(int argc, const char **argv);
@@ -38,7 +38,6 @@ int cmd__match_trees(int argc, const char **argv);
int cmd__mergesort(int argc, const char **argv);
int cmd__mktemp(int argc, const char **argv);
int cmd__oidmap(int argc, const char **argv);
-int cmd__oidtree(int argc, const char **argv);
int cmd__online_cpus(int argc, const char **argv);
int cmd__pack_mtimes(int argc, const char **argv);
int cmd__parse_options(int argc, const char **argv);
@@ -70,7 +69,6 @@ int cmd__oid_array(int argc, const char **argv);
int cmd__sha256(int argc, const char **argv);
int cmd__sigchain(int argc, const char **argv);
int cmd__simple_ipc(int argc, const char **argv);
-int cmd__strcmp_offset(int argc, const char **argv);
int cmd__string_list(int argc, const char **argv);
int cmd__submodule(int argc, const char **argv);
int cmd__submodule_config(int argc, const char **argv);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index 1adac29..cd955ec 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "strvec.h"
#include "run-command.h"
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
index f084034..b37dd2c 100644
--- a/t/helper/test-write-cache.c
+++ b/t/helper/test-write-cache.c
@@ -1,4 +1,5 @@
-#define USE_THE_INDEX_VARIABLE
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "test-tool.h"
#include "lockfile.h"
#include "read-cache-ll.h"
@@ -16,7 +17,7 @@ int cmd__write_cache(int argc, const char **argv)
for (i = 0; i < cnt; i++) {
repo_hold_locked_index(the_repository, &index_lock,
LOCK_DIE_ON_ERROR);
- if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
+ if (write_locked_index(the_repository->index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
}
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index 44799c0..58b9c74 100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -538,6 +538,129 @@
'
}
+helper_test_authtype() {
+ HELPER=$1
+
+ test_expect_success "helper ($HELPER) stores authtype and credential" '
+ check approve $HELPER <<-\EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=random-token
+ protocol=https
+ host=git.example.com
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) gets authtype and credential" '
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git.example.com
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=random-token
+ protocol=https
+ host=git.example.com
+ --
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) stores authtype and credential with username" '
+ check approve $HELPER <<-\EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=other-token
+ protocol=https
+ host=git.example.com
+ username=foobar
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) gets authtype and credential with username" '
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git.example.com
+ username=foobar
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=other-token
+ protocol=https
+ host=git.example.com
+ username=foobar
+ --
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) does not get authtype and credential with different username" '
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git.example.com
+ username=barbaz
+ --
+ protocol=https
+ host=git.example.com
+ username=barbaz
+ password=askpass-password
+ --
+ askpass: Password for '\''https://barbaz@git.example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) does not store ephemeral authtype and credential" '
+ check approve $HELPER <<-\EOF &&
+ capability[]=authtype
+ authtype=Bearer
+ credential=git2-token
+ protocol=https
+ host=git2.example.com
+ ephemeral=1
+ EOF
+
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git2.example.com
+ --
+ protocol=https
+ host=git2.example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://git2.example.com'\'':
+ askpass: Password for '\''https://askpass-username@git2.example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) does not store ephemeral username and password" '
+ check approve $HELPER <<-\EOF &&
+ capability[]=authtype
+ protocol=https
+ host=git2.example.com
+ user=barbaz
+ password=secret
+ ephemeral=1
+ EOF
+
+ check fill $HELPER <<-\EOF
+ capability[]=authtype
+ protocol=https
+ host=git2.example.com
+ --
+ protocol=https
+ host=git2.example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://git2.example.com'\'':
+ askpass: Password for '\''https://askpass-username@git2.example.com'\'':
+ EOF
+ '
+}
+
write_script askpass <<\EOF
echo >&2 askpass: $*
what=$(echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z)
diff --git a/t/lib-httpd/nph-custom-auth.sh b/t/lib-httpd/nph-custom-auth.sh
index f5345e7..d408d2c 100644
--- a/t/lib-httpd/nph-custom-auth.sh
+++ b/t/lib-httpd/nph-custom-auth.sh
@@ -19,21 +19,30 @@
#
if test -n "$HTTP_AUTHORIZATION" && \
- grep -Fqsx "${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
+ grep -Fqs "creds=${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
then
+ idno=$(grep -F "creds=${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE" | sed -e 's/^id=\([a-z0-9-][a-z0-9-]*\) .*$/\1/')
+ status=$(sed -ne "s/^id=$idno.*status=\\([0-9][0-9][0-9]\\).*\$/\\1/p" "$CHALLENGE_FILE" | head -n1)
# Note that although git-http-backend returns a status line, it
# does so using a CGI 'Status' header. Because this script is an
# No Parsed Headers (NPH) script, we must return a real HTTP
# status line.
# This is only a test script, so we don't bother to check for
# the actual status from git-http-backend and always return 200.
- echo 'HTTP/1.1 200 OK'
- exec "$GIT_EXEC_PATH"/git-http-backend
+ echo "HTTP/1.1 $status Nonspecific Reason Phrase"
+ if test "$status" -eq 200
+ then
+ exec "$GIT_EXEC_PATH"/git-http-backend
+ else
+ sed -ne "s/^id=$idno.*response=//p" "$CHALLENGE_FILE"
+ echo
+ exit
+ fi
fi
echo 'HTTP/1.1 401 Authorization Required'
if test -f "$CHALLENGE_FILE"
then
- cat "$CHALLENGE_FILE"
+ sed -ne 's/^id=default.*response=//p' "$CHALLENGE_FILE"
fi
echo
diff --git a/t/perf/p5333-pseudo-merge-bitmaps.sh b/t/perf/p5333-pseudo-merge-bitmaps.sh
new file mode 100755
index 0000000..2e8b1d2
--- /dev/null
+++ b/t/perf/p5333-pseudo-merge-bitmaps.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='pseudo-merge bitmaps'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup' '
+ git \
+ -c bitmapPseudoMerge.all.pattern="refs/" \
+ -c bitmapPseudoMerge.all.threshold=now \
+ -c bitmapPseudoMerge.all.stableThreshold=never \
+ -c bitmapPseudoMerge.all.maxMerges=64 \
+ -c pack.writeBitmapLookupTable=true \
+ repack -adb
+'
+
+test_perf 'git rev-list --count --all --objects (no bitmaps)' '
+ git rev-list --objects --all
+'
+
+test_perf 'git rev-list --count --all --objects (no pseudo-merges)' '
+ GIT_TEST_USE_PSEUDO_MERGES=0 \
+ git rev-list --objects --all --use-bitmap-index
+'
+
+test_perf 'git rev-list --count --all --objects (with pseudo-merges)' '
+ GIT_TEST_USE_PSEUDO_MERGES=1 \
+ git rev-list --objects --all --use-bitmap-index
+'
+
+test_done
diff --git a/t/run-test.sh b/t/run-test.sh
new file mode 100755
index 0000000..13c353b
--- /dev/null
+++ b/t/run-test.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# A simple wrapper to run shell tests via TEST_SHELL_PATH,
+# or exec unit tests directly.
+
+case "$1" in
+*.sh)
+ if test -z "${TEST_SHELL_PATH}"
+ then
+ echo >&2 "ERROR: TEST_SHELL_PATH is empty or not set"
+ exit 1
+ fi
+ exec "${TEST_SHELL_PATH}" "$@"
+ ;;
+*)
+ exec "$@"
+ ;;
+esac
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 71f0828..66ccb58 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -434,6 +434,21 @@
)
'
+test_expect_success 'diff without repository with attr source' '
+ mkdir -p "$TRASH_DIRECTORY/outside/nongit" &&
+ (
+ cd "$TRASH_DIRECTORY/outside/nongit" &&
+ GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/outside" &&
+ export GIT_CEILING_DIRECTORIES &&
+ touch file &&
+ cat >expect <<-EOF &&
+ fatal: cannot use --attr-source or GIT_ATTR_SOURCE without repo
+ EOF
+ test_must_fail env GIT_ATTR_SOURCE=HEAD git grep --no-index foo file 2>err &&
+ test_cmp expect err
+ )
+'
+
test_expect_success 'bare repository: with --source' '
(
cd bare.git &&
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 9556834..854d59e 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -44,4 +44,15 @@
test_cmp expect actual
'
+test_expect_success 'tracing a shell alias with arguments shows trace of prepared command' '
+ cat >expect <<-EOF &&
+ trace: start_command: SHELL -c ${SQ}echo \$* "\$@"${SQ} ${SQ}echo \$*${SQ} arg
+ EOF
+ git config alias.echo "!echo \$*" &&
+ env GIT_TRACE=1 git echo arg 2>output &&
+ # redact platform differences
+ sed -n -e "s/^\(trace: start_command:\) .* -c /\1 SHELL -c /p" output >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh
deleted file mode 100755
index 0a087a1..0000000
--- a/t/t0015-hash.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-test_description='test basic hash implementation'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-test_expect_success 'test basic SHA-1 hash values' '
- test-tool sha1 </dev/null >actual &&
- grep da39a3ee5e6b4b0d3255bfef95601890afd80709 actual &&
- printf "a" | test-tool sha1 >actual &&
- grep 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 actual &&
- printf "abc" | test-tool sha1 >actual &&
- grep a9993e364706816aba3e25717850c26c9cd0d89d actual &&
- printf "message digest" | test-tool sha1 >actual &&
- grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual &&
- printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual &&
- grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual &&
- perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" |
- test-tool sha1 >actual &&
- grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual &&
- printf "blob 0\0" | test-tool sha1 >actual &&
- grep e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 actual &&
- printf "blob 3\0abc" | test-tool sha1 >actual &&
- grep f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f actual &&
- printf "tree 0\0" | test-tool sha1 >actual &&
- grep 4b825dc642cb6eb9a060e54bf8d69288fbee4904 actual
-'
-
-test_expect_success 'test basic SHA-256 hash values' '
- test-tool sha256 </dev/null >actual &&
- grep e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual &&
- printf "a" | test-tool sha256 >actual &&
- grep ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb actual &&
- printf "abc" | test-tool sha256 >actual &&
- grep ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad actual &&
- printf "message digest" | test-tool sha256 >actual &&
- grep f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650 actual &&
- printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual &&
- grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual &&
- # Try to exercise the chunking code by turning autoflush on.
- perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" |
- test-tool sha256 >actual &&
- grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual &&
- perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" |
- test-tool sha256 >actual &&
- grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual &&
- printf "blob 0\0" | test-tool sha256 >actual &&
- grep 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 actual &&
- printf "blob 3\0abc" | test-tool sha256 >actual &&
- grep c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6 actual &&
- printf "tree 0\0" | test-tool sha256 >actual &&
- grep 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321 actual
-'
-
-test_done
diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh
index fc14ba0..f3a1685 100755
--- a/t/t0017-env-helper.sh
+++ b/t/t0017-env-helper.sh
@@ -91,9 +91,16 @@
git config -l 2>err &&
grep "exceeded maximum include depth" err &&
+ # This validates that the assumption that we attempt to
+ # read the configuration and fail very early in the start-up
+ # sequence (due to trace2 subsystem), even before we notice
+ # that the directory named with "test-tool -C" does not exist
+ # and die. It is a dubious thing to test, though.
test_must_fail \
env HOME="$(pwd)/home" GIT_TEST_ENV_HELPER=true \
- test-tool -C cycle env-helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
+ test-tool -C no-such-directory \
+ env-helper --type=bool --default=0 \
+ --exit-code GIT_TEST_ENV_HELPER 2>err &&
grep "exceeded maximum include depth" err
'
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index 0dcfb76..29306b3 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -2,6 +2,9 @@
test_description='Test advise_if_enabled functionality'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=trunk
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
@@ -29,4 +32,72 @@
test_must_be_empty actual
'
+test_expect_success 'advice should not be printed when --no-advice is used' '
+ q_to_tab >expect <<-\EOF &&
+ On branch trunk
+
+ No commits yet
+
+ Untracked files:
+ QREADME
+
+ nothing added to commit but untracked files present
+ EOF
+
+ test_when_finished "rm -fr advice-test" &&
+ git init advice-test &&
+ (
+ cd advice-test &&
+ >README &&
+ git --no-advice status
+ ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'advice should not be printed when GIT_ADVICE is set to false' '
+ q_to_tab >expect <<-\EOF &&
+ On branch trunk
+
+ No commits yet
+
+ Untracked files:
+ QREADME
+
+ nothing added to commit but untracked files present
+ EOF
+
+ test_when_finished "rm -fr advice-test" &&
+ git init advice-test &&
+ (
+ cd advice-test &&
+ >README &&
+ GIT_ADVICE=false git status
+ ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'advice should be printed when GIT_ADVICE is set to true' '
+ q_to_tab >expect <<-\EOF &&
+ On branch trunk
+
+ No commits yet
+
+ Untracked files:
+ (use "git add <file>..." to include in what will be committed)
+ QREADME
+
+ nothing added to commit but untracked files present (use "git add" to track)
+ EOF
+
+ test_when_finished "rm -fr advice-test" &&
+ git init advice-test &&
+ (
+ cd advice-test &&
+ >README &&
+ GIT_ADVICE=true git status
+ ) >actual &&
+ cat actual > /tmp/actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 8bb2a8b..45a7736 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -176,6 +176,23 @@
test_cmp expect output
'
+test_expect_success 'abbreviate to something longer than SHA1 length' '
+ cat >expect <<-EOF &&
+ boolean: 0
+ integer: 0
+ magnitude: 0
+ timestamp: 0
+ string: (not set)
+ abbrev: 100
+ verbose: -1
+ quiet: 0
+ dry run: no
+ file: (not set)
+ EOF
+ test-tool parse-options --abbrev=100 >output &&
+ test_cmp expect output
+'
+
test_expect_success 'missing required value' '
cat >expect <<-\EOF &&
error: switch `s'\'' requires a value
diff --git a/t/t0064-oid-array.sh b/t/t0064-oid-array.sh
index 88c89e8..de74b69 100755
--- a/t/t0064-oid-array.sh
+++ b/t/t0064-oid-array.sh
@@ -15,6 +15,24 @@
done
}
+test_expect_success 'without repository' '
+ cat >expect <<-EOF &&
+ 4444444444444444444444444444444444444444
+ 5555555555555555555555555555555555555555
+ 8888888888888888888888888888888888888888
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ EOF
+ cat >input <<-EOF &&
+ append 4444444444444444444444444444444444444444
+ append 5555555555555555555555555555555555555555
+ append 8888888888888888888888888888888888888888
+ append aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ for_each_unique
+ EOF
+ nongit test-tool oid-array <input >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'ordered enumeration' '
echoid "" 44 55 88 aa >expect &&
{
diff --git a/t/t0065-strcmp-offset.sh b/t/t0065-strcmp-offset.sh
deleted file mode 100755
index 94e34c8..0000000
--- a/t/t0065-strcmp-offset.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-test_description='Test strcmp_offset functionality'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-while read s1 s2 expect
-do
- test_expect_success "strcmp_offset($s1, $s2)" '
- echo "$expect" >expect &&
- test-tool strcmp-offset "$s1" "$s2" >actual &&
- test_cmp expect actual
- '
-done <<-EOF
-abc abc 0 3
-abc def -1 0
-abc abz -1 2
-abc abcdef -1 3
-EOF
-
-test_done
diff --git a/t/t0069-oidtree.sh b/t/t0069-oidtree.sh
deleted file mode 100755
index 889db50..0000000
--- a/t/t0069-oidtree.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/sh
-
-test_description='basic tests for the oidtree implementation'
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-maxhexsz=$(test_oid hexsz)
-echoid () {
- prefix="${1:+$1 }"
- shift
- while test $# -gt 0
- do
- shortoid="$1"
- shift
- difference=$(($maxhexsz - ${#shortoid}))
- printf "%s%s%0${difference}d\\n" "$prefix" "$shortoid" "0"
- done
-}
-
-test_expect_success 'oidtree insert and contains' '
- cat >expect <<-\EOF &&
- 0
- 0
- 0
- 1
- 1
- 0
- EOF
- {
- echoid insert 444 1 2 3 4 5 a b c d e &&
- echoid contains 44 441 440 444 4440 4444 &&
- echo clear
- } | test-tool oidtree >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'oidtree each' '
- echoid "" 123 321 321 >expect &&
- {
- echoid insert f 9 8 123 321 a b c d e &&
- echo each 12300 &&
- echo each 3211 &&
- echo each 3210 &&
- echo each 32100 &&
- echo clear
- } | test-tool oidtree >actual &&
- test_cmp expect actual
-'
-
-test_done
diff --git a/t/t0080-unit-test-output.sh b/t/t0080-unit-test-output.sh
index 6657c11..7bbb065 100755
--- a/t/t0080-unit-test-output.sh
+++ b/t/t0080-unit-test-output.sh
@@ -9,50 +9,50 @@
cat >expect <<-EOF &&
ok 1 - passing test
ok 2 - passing test and assertion return 1
- # check "1 == 2" failed at t/unit-tests/t-basic.c:76
+ # check "1 == 2" failed at t/helper/test-example-tap.c:77
# left: 1
# right: 2
not ok 3 - failing test
ok 4 - failing test and assertion return 0
not ok 5 - passing TEST_TODO() # TODO
ok 6 - passing TEST_TODO() returns 1
- # todo check ${SQ}check(x)${SQ} succeeded at t/unit-tests/t-basic.c:25
+ # todo check ${SQ}check(x)${SQ} succeeded at t/helper/test-example-tap.c:26
not ok 7 - failing TEST_TODO()
ok 8 - failing TEST_TODO() returns 0
- # check "0" failed at t/unit-tests/t-basic.c:30
+ # check "0" failed at t/helper/test-example-tap.c:31
# skipping test - missing prerequisite
- # skipping check ${SQ}1${SQ} at t/unit-tests/t-basic.c:32
+ # skipping check ${SQ}1${SQ} at t/helper/test-example-tap.c:33
ok 9 - test_skip() # SKIP
ok 10 - skipped test returns 1
# skipping test - missing prerequisite
ok 11 - test_skip() inside TEST_TODO() # SKIP
ok 12 - test_skip() inside TEST_TODO() returns 1
- # check "0" failed at t/unit-tests/t-basic.c:48
+ # check "0" failed at t/helper/test-example-tap.c:49
not ok 13 - TEST_TODO() after failing check
ok 14 - TEST_TODO() after failing check returns 0
- # check "0" failed at t/unit-tests/t-basic.c:56
+ # check "0" failed at t/helper/test-example-tap.c:57
not ok 15 - failing check after TEST_TODO()
ok 16 - failing check after TEST_TODO() returns 0
- # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/unit-tests/t-basic.c:61
+ # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/helper/test-example-tap.c:62
# left: "\011hello\\\\"
# right: "there\"\012"
- # check "!strcmp("NULL", NULL)" failed at t/unit-tests/t-basic.c:62
+ # check "!strcmp("NULL", NULL)" failed at t/helper/test-example-tap.c:63
# left: "NULL"
# right: NULL
- # check "${SQ}a${SQ} == ${SQ}\n${SQ}" failed at t/unit-tests/t-basic.c:63
+ # check "${SQ}a${SQ} == ${SQ}\n${SQ}" failed at t/helper/test-example-tap.c:64
# left: ${SQ}a${SQ}
# right: ${SQ}\012${SQ}
- # check "${SQ}\\\\${SQ} == ${SQ}\\${SQ}${SQ}" failed at t/unit-tests/t-basic.c:64
+ # check "${SQ}\\\\${SQ} == ${SQ}\\${SQ}${SQ}" failed at t/helper/test-example-tap.c:65
# left: ${SQ}\\\\${SQ}
# right: ${SQ}\\${SQ}${SQ}
not ok 17 - messages from failing string and char comparison
- # BUG: test has no checks at t/unit-tests/t-basic.c:91
+ # BUG: test has no checks at t/helper/test-example-tap.c:92
not ok 18 - test with no checks
ok 19 - test with no checks returns 0
1..19
EOF
- ! "$GIT_BUILD_DIR"/t/unit-tests/bin/t-basic >actual &&
+ ! test-tool example-tap >actual &&
test_cmp expect actual
'
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 400f6bd..6a76b7f 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic credential helper tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-credential.sh
@@ -12,7 +14,13 @@
IFS==
while read key value; do
echo >&2 "$whoami: $key=$value"
- eval "$key=$value"
+ if test -z "${key%%*\[\]}"
+ then
+ key=${key%%\[\]}
+ eval "$key=\"\$$key $value\""
+ else
+ eval "$key=$value"
+ fi
done
IFS=$OIFS
EOF
@@ -35,6 +43,30 @@
test -z "$pass" || echo password=$pass
EOF
+ write_script git-credential-verbatim-cred <<-\EOF &&
+ authtype=$1; shift
+ credential=$1; shift
+ . ./dump
+ echo capability[]=authtype
+ echo capability[]=state
+ test -z "${capability##*authtype*}" || exit 0
+ test -z "$authtype" || echo authtype=$authtype
+ test -z "$credential" || echo credential=$credential
+ test -z "${capability##*state*}" || exit 0
+ echo state[]=verbatim-cred:foo
+ EOF
+
+ write_script git-credential-verbatim-ephemeral <<-\EOF &&
+ authtype=$1; shift
+ credential=$1; shift
+ . ./dump
+ echo capability[]=authtype
+ test -z "${capability##*authtype*}" || exit 0
+ test -z "$authtype" || echo authtype=$authtype
+ test -z "$credential" || echo credential=$credential
+ echo "ephemeral=1"
+ EOF
+
write_script git-credential-verbatim-with-expiry <<-\EOF &&
user=$1; shift
pass=$1; shift
@@ -64,6 +96,67 @@
EOF
'
+test_expect_success 'credential_fill invokes helper with credential' '
+ check fill "verbatim-cred Bearer token" <<-\EOF
+ capability[]=authtype
+ protocol=http
+ host=example.com
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=token
+ protocol=http
+ host=example.com
+ --
+ verbatim-cred: get
+ verbatim-cred: capability[]=authtype
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
+test_expect_success 'credential_fill invokes helper with ephemeral credential' '
+ check fill "verbatim-ephemeral Bearer token" <<-\EOF
+ capability[]=authtype
+ protocol=http
+ host=example.com
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=token
+ ephemeral=1
+ protocol=http
+ host=example.com
+ --
+ verbatim-ephemeral: get
+ verbatim-ephemeral: capability[]=authtype
+ verbatim-ephemeral: protocol=http
+ verbatim-ephemeral: host=example.com
+ EOF
+'
+test_expect_success 'credential_fill invokes helper with credential and state' '
+ check fill "verbatim-cred Bearer token" <<-\EOF
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=example.com
+ --
+ capability[]=authtype
+ capability[]=state
+ authtype=Bearer
+ credential=token
+ protocol=http
+ host=example.com
+ state[]=verbatim-cred:foo
+ --
+ verbatim-cred: get
+ verbatim-cred: capability[]=authtype
+ verbatim-cred: capability[]=state
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
test_expect_success 'credential_fill invokes multiple helpers' '
check fill useless "verbatim foo bar" <<-\EOF
protocol=http
@@ -83,6 +176,45 @@
EOF
'
+test_expect_success 'credential_fill response does not get capabilities when helpers are incapable' '
+ check fill useless "verbatim foo bar" <<-\EOF
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ username=foo
+ password=bar
+ --
+ useless: get
+ useless: capability[]=authtype
+ useless: capability[]=state
+ useless: protocol=http
+ useless: host=example.com
+ verbatim: get
+ verbatim: capability[]=authtype
+ verbatim: capability[]=state
+ verbatim: protocol=http
+ verbatim: host=example.com
+ EOF
+'
+
+test_expect_success 'credential_fill response does not get capabilities when caller is incapable' '
+ check fill "verbatim-cred Bearer token" <<-\EOF
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ --
+ verbatim-cred: get
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
test_expect_success 'credential_fill stops when we get a full response' '
check fill "verbatim one two" "verbatim three four" <<-\EOF
protocol=http
@@ -99,6 +231,25 @@
EOF
'
+test_expect_success 'credential_fill thinks a credential is a full response' '
+ check fill "verbatim-cred Bearer token" "verbatim three four" <<-\EOF
+ capability[]=authtype
+ protocol=http
+ host=example.com
+ --
+ capability[]=authtype
+ authtype=Bearer
+ credential=token
+ protocol=http
+ host=example.com
+ --
+ verbatim-cred: get
+ verbatim-cred: capability[]=authtype
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
test_expect_success 'credential_fill continues through partial response' '
check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
protocol=http
@@ -175,6 +326,20 @@
EOF
'
+test_expect_success 'credential_fill produces no credential without capability' '
+ check fill "verbatim-cred Bearer token" <<-\EOF
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ --
+ verbatim-cred: get
+ verbatim-cred: protocol=http
+ verbatim-cred: host=example.com
+ EOF
+'
+
test_expect_success 'credential_approve calls all helpers' '
check approve useless "verbatim one two" <<-\EOF
protocol=http
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index f2c146f..c10e359 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -39,6 +39,7 @@
helper_test cache
helper_test_password_expiry_utc cache
helper_test_oauth_refresh_token cache
+helper_test_authtype cache
test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
test_when_finished "
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 88a66f0..2c30c86 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -3,6 +3,7 @@
test_description='partial clone'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
# missing promisor objects cause repacks which write bitmaps to fail
GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
@@ -689,6 +690,67 @@
! grep "[?]$FILE_HASH" out
'
+test_expect_success 'push should not fetch new commit objects' '
+ rm -rf server client &&
+ test_create_repo server &&
+ test_config -C server uploadpack.allowfilter 1 &&
+ test_config -C server uploadpack.allowanysha1inwant 1 &&
+ test_commit -C server server1 &&
+
+ git clone --filter=blob:none "file://$(pwd)/server" client &&
+ test_commit -C client client1 &&
+
+ test_commit -C server server2 &&
+ COMMIT=$(git -C server rev-parse server2) &&
+
+ test_must_fail git -C client push 2>err &&
+ grep "fetch first" err &&
+ git -C client rev-list --objects --missing=print "$COMMIT" >objects &&
+ grep "^[?]$COMMIT" objects
+'
+
+test_expect_success 'setup for promisor.quiet tests' '
+ rm -rf server &&
+ test_create_repo server &&
+ test_commit -C server foo &&
+ git -C server rm foo.t &&
+ git -C server commit -m remove &&
+ git -C server config uploadpack.allowanysha1inwant 1 &&
+ git -C server config uploadpack.allowfilter 1
+'
+
+test_expect_success TTY 'promisor.quiet=false shows progress messages' '
+ rm -rf repo &&
+ git clone --filter=blob:none "file://$(pwd)/server" repo &&
+ git -C repo config promisor.quiet "false" &&
+
+ test_terminal git -C repo cat-file -p foo:foo.t 2>err &&
+
+ # Ensure that progress messages are written
+ grep "Receiving objects" err
+'
+
+test_expect_success TTY 'promisor.quiet=true does not show progress messages' '
+ rm -rf repo &&
+ git clone --filter=blob:none "file://$(pwd)/server" repo &&
+ git -C repo config promisor.quiet "true" &&
+
+ test_terminal git -C repo cat-file -p foo:foo.t 2>err &&
+
+ # Ensure that no progress messages are written
+ ! grep "Receiving objects" err
+'
+
+test_expect_success TTY 'promisor.quiet=unconfigured shows progress messages' '
+ rm -rf repo &&
+ git clone --filter=blob:none "file://$(pwd)/server" repo &&
+
+ test_terminal git -C repo cat-file -p foo:foo.t 2>err &&
+
+ # Ensure that progress messages are written
+ grep "Receiving objects" err
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t0411-clone-from-partial.sh b/t/t0411-clone-from-partial.sh
index c98d501..932bf20 100755
--- a/t/t0411-clone-from-partial.sh
+++ b/t/t0411-clone-from-partial.sh
@@ -2,6 +2,7 @@
test_description='check that local clone does not fetch from promisor remotes'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create evil repo' '
diff --git a/t/t0450/txt-help-mismatches b/t/t0450/txt-help-mismatches
index a0777ac..28003f1 100644
--- a/t/t0450/txt-help-mismatches
+++ b/t/t0450/txt-help-mismatches
@@ -10,7 +10,6 @@
checkout-index
clone
column
-config
credential
credential-cache
credential-store
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
index 6421434..b2a771f 100755
--- a/t/t0600-reffiles-backend.sh
+++ b/t/t0600-reffiles-backend.sh
@@ -4,16 +4,12 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=files
+export GIT_TEST_DEFAULT_REF_FORMAT
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-if ! test_have_prereq REFFILES
-then
- skip_all='skipping reffiles specific tests'
- test_done
-fi
-
test_expect_success 'setup' '
git commit --allow-empty -m Initial &&
C=$(git rev-parse HEAD) &&
@@ -428,7 +424,7 @@
test_when_finished "rm -rf subdir" &&
git init --bare subdir &&
- rm -rfv subdir/refs subdir/objects subdir/packed-refs &&
+ rm -rf subdir/refs subdir/objects subdir/packed-refs &&
ln -s ../.git/refs subdir/refs &&
ln -s ../.git/objects subdir/objects &&
ln -s ../.git/packed-refs subdir/packed-refs &&
@@ -472,4 +468,36 @@
esac
'
+test_expect_success SYMLINKS 'symref transaction supports symlinks' '
+ test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" &&
+ git update-ref refs/heads/new @ &&
+ test_config core.prefersymlinkrefs true &&
+ cat >stdin <<-EOF &&
+ start
+ symref-create TEST_SYMREF_HEAD refs/heads/new
+ prepare
+ commit
+ EOF
+ git update-ref --no-deref --stdin <stdin &&
+ test_path_is_symlink .git/TEST_SYMREF_HEAD &&
+ test "$(test_readlink .git/TEST_SYMREF_HEAD)" = refs/heads/new
+'
+
+test_expect_success 'symref transaction supports false symlink config' '
+ test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" &&
+ git update-ref refs/heads/new @ &&
+ test_config core.prefersymlinkrefs false &&
+ cat >stdin <<-EOF &&
+ start
+ symref-create TEST_SYMREF_HEAD refs/heads/new
+ prepare
+ commit
+ EOF
+ git update-ref --no-deref --stdin <stdin &&
+ test_path_is_file .git/TEST_SYMREF_HEAD &&
+ git symbolic-ref TEST_SYMREF_HEAD >actual &&
+ echo refs/heads/new >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0601-reffiles-pack-refs.sh b/t/t0601-reffiles-pack-refs.sh
index 7d4ab0b..60a544b 100755
--- a/t/t0601-reffiles-pack-refs.sh
+++ b/t/t0601-reffiles-pack-refs.sh
@@ -9,18 +9,15 @@
This test runs git pack-refs and git show-ref and checks that the branch
semantic is still the same.
'
+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=files
+export GIT_TEST_DEFAULT_REF_FORMAT
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-if ! test_have_prereq REFFILES
-then
- skip_all='skipping reffiles specific tests'
- test_done
-fi
-
test_expect_success 'enable reflogs' '
git config core.logallrefupdates true
'
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index 178791e..b06c469 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -4,17 +4,15 @@
#
test_description='reftable basics'
+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=reftable
+export GIT_TEST_DEFAULT_REF_FORMAT
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-if ! test_have_prereq REFTABLE
-then
- skip_all='skipping reftable tests; set GIT_TEST_DEFAULT_REF_FORMAT=reftable'
- test_done
-fi
-
INVALID_OID=$(test_oid 001)
test_expect_success 'init: creates basic reftable structures' '
@@ -81,8 +79,8 @@
'
test_expect_perms () {
- local perms="$1"
- local file="$2"
+ local perms="$1" &&
+ local file="$2" &&
local actual="$(ls -l "$file")" &&
case "$actual" in
@@ -286,7 +284,7 @@
git init repo &&
test_commit -C repo A &&
cat >expect <<-EOF &&
- error: unable to write symref for refs/heads: file/directory conflict
+ error: ${SQ}refs/heads/main${SQ} exists; cannot create ${SQ}refs/heads${SQ}
EOF
test_must_fail git -C repo symbolic-ref refs/heads refs/heads/foo 2>err &&
test_cmp expect err
@@ -854,6 +852,39 @@
)
'
+test_expect_success 'branch: copying branch with D/F conflict' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git branch branch &&
+ cat >expect <<-EOF &&
+ error: ${SQ}refs/heads/branch${SQ} exists; cannot create ${SQ}refs/heads/branch/moved${SQ}
+ fatal: branch copy failed
+ EOF
+ test_must_fail git branch -c branch branch/moved 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'branch: moving branch with D/F conflict' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git branch branch &&
+ git branch conflict &&
+ cat >expect <<-EOF &&
+ error: ${SQ}refs/heads/conflict${SQ} exists; cannot create ${SQ}refs/heads/conflict/moved${SQ}
+ fatal: branch rename failed
+ EOF
+ test_must_fail git branch -m branch conflict/moved 2>err &&
+ test_cmp expect err
+ )
+'
+
test_expect_success 'worktree: adding worktree creates separate stack' '
test_when_finished "rm -rf repo worktree" &&
git init repo &&
diff --git a/t/t0611-reftable-httpd.sh b/t/t0611-reftable-httpd.sh
index 5e05b9c..2805995 100755
--- a/t/t0611-reftable-httpd.sh
+++ b/t/t0611-reftable-httpd.sh
@@ -2,6 +2,7 @@
test_description='reftable HTTPD tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t0612-reftable-jgit-compatibility.sh b/t/t0612-reftable-jgit-compatibility.sh
new file mode 100755
index 0000000..d0d7e80
--- /dev/null
+++ b/t/t0612-reftable-jgit-compatibility.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+
+test_description='reftables are compatible with JGit'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=reftable
+export GIT_TEST_DEFAULT_REF_FORMAT
+
+# JGit does not support the 'link' DIRC extension.
+GIT_TEST_SPLIT_INDEX=0
+export GIT_TEST_SPLIT_INDEX
+
+. ./test-lib.sh
+
+if ! test_have_prereq JGIT
+then
+ skip_all='skipping reftable JGit tests; JGit is not present in PATH'
+ test_done
+fi
+
+if ! test_have_prereq SHA1
+then
+ skip_all='skipping reftable JGit tests; JGit does not support SHA256 reftables'
+ test_done
+fi
+
+test_commit_jgit () {
+ touch "$1" &&
+ jgit add "$1" &&
+ jgit commit -m "$1"
+}
+
+test_same_refs () {
+ git show-ref --head >cgit.actual &&
+ jgit show-ref >jgit-tabs.actual &&
+ tr "\t" " " <jgit-tabs.actual >jgit.actual &&
+ test_cmp cgit.actual jgit.actual
+}
+
+test_same_ref () {
+ git rev-parse "$1" >cgit.actual &&
+ jgit rev-parse "$1" >jgit.actual &&
+ test_cmp cgit.actual jgit.actual
+}
+
+test_same_reflog () {
+ git reflog "$*" >cgit.actual &&
+ jgit reflog "$*" >jgit-newline.actual &&
+ sed '/^$/d' <jgit-newline.actual >jgit.actual &&
+ test_cmp cgit.actual jgit.actual
+}
+
+test_expect_success 'CGit repository can be read by JGit' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_same_refs &&
+ test_same_ref HEAD &&
+ test_same_reflog HEAD
+ )
+'
+
+test_expect_success 'JGit repository can be read by CGit' '
+ test_when_finished "rm -rf repo" &&
+ jgit init repo &&
+ (
+ cd repo &&
+
+ touch file &&
+ jgit add file &&
+ jgit commit -m "initial commit" &&
+
+ # Note that we must convert the ref storage after we have
+ # written the default branch. Otherwise JGit will end up with
+ # no HEAD at all.
+ jgit convert-ref-storage --format=reftable &&
+
+ test_same_refs &&
+ test_same_ref HEAD &&
+ # Interestingly, JGit cannot read its own reflog here. CGit can
+ # though.
+ printf "%s HEAD@{0}: commit (initial): initial commit" "$(git rev-parse --short HEAD)" >expect &&
+ git reflog HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'mixed writes from JGit and CGit' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ test_commit_jgit B &&
+ test_commit C &&
+ test_commit_jgit D &&
+
+ test_same_refs &&
+ test_same_ref HEAD &&
+ test_same_reflog HEAD
+ )
+'
+
+test_expect_success 'JGit can read multi-level index' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ awk "
+ BEGIN {
+ print \"start\";
+ for (i = 0; i < 10000; i++)
+ printf \"create refs/heads/branch-%d HEAD\n\", i;
+ print \"commit\";
+ }
+ " >input &&
+ git update-ref --stdin <input &&
+
+ test_same_refs &&
+ test_same_ref refs/heads/branch-1 &&
+ test_same_ref refs/heads/branch-5738 &&
+ test_same_ref refs/heads/branch-9999
+ )
+'
+
+test_done
diff --git a/t/t0613-reftable-write-options.sh b/t/t0613-reftable-write-options.sh
new file mode 100755
index 0000000..e2708e1
--- /dev/null
+++ b/t/t0613-reftable-write-options.sh
@@ -0,0 +1,286 @@
+#!/bin/sh
+
+test_description='reftable write options'
+
+GIT_TEST_DEFAULT_REF_FORMAT=reftable
+export GIT_TEST_DEFAULT_REF_FORMAT
+# Disable auto-compaction for all tests as we explicitly control repacking of
+# refs.
+GIT_TEST_REFTABLE_AUTOCOMPACTION=false
+export GIT_TEST_REFTABLE_AUTOCOMPACTION
+# Block sizes depend on the hash function, so we force SHA1 here.
+GIT_TEST_DEFAULT_HASH=sha1
+export GIT_TEST_DEFAULT_HASH
+# Block sizes also depend on the actual refs we write, so we force "master" to
+# be the default initial branch name.
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'default write options' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ git pack-refs &&
+ cat >expect <<-EOF &&
+ header:
+ block_size: 4096
+ ref:
+ - length: 129
+ restarts: 2
+ log:
+ - length: 262
+ restarts: 2
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'disabled reflog writes no log blocks' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ git pack-refs &&
+ cat >expect <<-EOF &&
+ header:
+ block_size: 4096
+ ref:
+ - length: 129
+ restarts: 2
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'many refs results in multiple blocks' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ for i in $(test_seq 200)
+ do
+ printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+ return 1
+ done >input &&
+ git update-ref --stdin <input &&
+ git pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 4096
+ ref:
+ - length: 4049
+ restarts: 11
+ - length: 1136
+ restarts: 3
+ log:
+ - length: 4041
+ restarts: 4
+ - length: 4015
+ restarts: 3
+ - length: 4014
+ restarts: 3
+ - length: 4012
+ restarts: 3
+ - length: 3289
+ restarts: 3
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'tiny block size leads to error' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ cat >expect <<-EOF &&
+ error: unable to compact stack: entry too large
+ EOF
+ test_must_fail git -c reftable.blockSize=50 pack-refs 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'small block size leads to multiple ref blocks' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit B &&
+ git -c reftable.blockSize=100 pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 100
+ ref:
+ - length: 53
+ restarts: 1
+ - length: 74
+ restarts: 1
+ - length: 38
+ restarts: 1
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'small block size fails with large reflog message' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ perl -e "print \"a\" x 500" >logmsg &&
+ cat >expect <<-EOF &&
+ fatal: update_ref failed for ref ${SQ}refs/heads/logme${SQ}: reftable: transaction failure: entry too large
+ EOF
+ test_must_fail git -c reftable.blockSize=100 \
+ update-ref -m "$(cat logmsg)" refs/heads/logme HEAD 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'block size exceeding maximum supported size' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit B &&
+ cat >expect <<-EOF &&
+ fatal: reftable block size cannot exceed 16MB
+ EOF
+ test_must_fail git -c reftable.blockSize=16777216 pack-refs 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'restart interval at every single record' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ for i in $(test_seq 10)
+ do
+ printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+ return 1
+ done >input &&
+ git update-ref --stdin <input &&
+ git -c reftable.restartInterval=1 pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 4096
+ ref:
+ - length: 566
+ restarts: 13
+ log:
+ - length: 1393
+ restarts: 12
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'restart interval exceeding maximum supported interval' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ cat >expect <<-EOF &&
+ fatal: reftable block size cannot exceed 65535
+ EOF
+ test_must_fail git -c reftable.restartInterval=65536 pack-refs 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'object index gets written by default with ref index' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ for i in $(test_seq 5)
+ do
+ printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+ return 1
+ done >input &&
+ git update-ref --stdin <input &&
+ git -c reftable.blockSize=100 pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 100
+ ref:
+ - length: 53
+ restarts: 1
+ - length: 95
+ restarts: 1
+ - length: 71
+ restarts: 1
+ - length: 80
+ restarts: 1
+ obj:
+ - length: 11
+ restarts: 1
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'object index can be disabled' '
+ test_config_global core.logAllRefUpdates false &&
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ for i in $(test_seq 5)
+ do
+ printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+ return 1
+ done >input &&
+ git update-ref --stdin <input &&
+ git -c reftable.blockSize=100 -c reftable.indexObjects=false pack-refs &&
+
+ cat >expect <<-EOF &&
+ header:
+ block_size: 100
+ ref:
+ - length: 53
+ restarts: 1
+ - length: 95
+ restarts: 1
+ - length: 71
+ restarts: 1
+ - length: 80
+ restarts: 1
+ EOF
+ test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index e12b221..ff9bf21 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -1294,4 +1294,34 @@
grep "^fatal:.*flush is only for --buffer mode.*" err
'
+script='
+use warnings;
+use strict;
+use IPC::Open2;
+my ($opt, $oid, $expect, @pfx) = @ARGV;
+my @cmd = (qw(git cat-file), $opt);
+my $pid = open2(my $out, my $in, @cmd) or die "open2: @cmd";
+print $in @pfx, $oid, "\n" or die "print $!";
+my $rvec = "";
+vec($rvec, fileno($out), 1) = 1;
+select($rvec, undef, undef, 30) or die "no response to `@pfx $oid` from @cmd";
+my $info = <$out>;
+chop($info) eq "\n" or die "no LF";
+$info eq $expect or die "`$info` != `$expect`";
+close $in or die "close in $!";
+close $out or die "close out $!";
+waitpid $pid, 0;
+$? == 0 or die "\$?=$?";
+'
+
+expect="$hello_oid blob $hello_size"
+
+test_expect_success PERL '--batch-check is unbuffered by default' '
+ perl -e "$script" -- --batch-check $hello_oid "$expect"
+'
+
+test_expect_success PERL '--batch-command info is unbuffered by default' '
+ perl -e "$script" -- --batch-command $hello_oid "$expect" "info "
+'
+
test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index 64aea38..d73a5cc 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -260,4 +260,10 @@
echo example | git hash-object -t $t --literally --stdin
'
+test_expect_success '--stdin outside of repository (uses SHA-1)' '
+ nongit git hash-object --stdin <hello >actual &&
+ echo "$(test_oid --hash=sha1 hello)" >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
index bfc90d4..cf8b94e 100755
--- a/t/t1013-read-tree-submodule.sh
+++ b/t/t1013-read-tree-submodule.sh
@@ -2,6 +2,7 @@
test_description='read-tree can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 3a14218..da0e771 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -6,6 +6,7 @@
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index ab3a105..8c5cd65 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -8,6 +8,7 @@
GIT_TEST_SPLIT_INDEX=false
export GIT_TEST_SPLIT_INDEX
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
list_files() {
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 9b65d9e..9de2d95 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,34 @@
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+for mode in legacy subcommands
+do
+
+case "$mode" in
+legacy)
+ mode_prefix="--"
+ mode_get=""
+ mode_get_all="--get-all"
+ mode_get_regexp="--get-regexp"
+ mode_set=""
+ mode_replace_all="--replace-all"
+ mode_unset="--unset"
+ mode_unset_all="--unset-all"
+ ;;
+subcommands)
+ mode_prefix=""
+ mode_get="get"
+ mode_get_all="get --all"
+ mode_get_regexp="get --regexp --all --show-names"
+ mode_set="set"
+ mode_replace_all="set --all"
+ mode_unset="unset"
+ mode_unset_all="unset --all"
+ ;;
+*)
+ BUG "unknown mode $mode";;
+esac
+
test_expect_success 'setup whitespace config' '
sed -e "s/^|//" \
-e "s/[$]$//" \
@@ -112,7 +140,7 @@
penguin = little blue
EOF
test_expect_success 'initial' '
- git config section.penguin "little blue" &&
+ git config ${mode_set} section.penguin "little blue" &&
test_cmp expect .git/config
'
@@ -122,7 +150,7 @@
Movie = BadPhysics
EOF
test_expect_success 'mixed case' '
- git config Section.Movie BadPhysics &&
+ git config ${mode_set} Section.Movie BadPhysics &&
test_cmp expect .git/config
'
@@ -134,7 +162,7 @@
WhatEver = Second
EOF
test_expect_success 'similar section' '
- git config Sections.WhatEver Second &&
+ git config ${mode_set} Sections.WhatEver Second &&
test_cmp expect .git/config
'
@@ -147,7 +175,7 @@
WhatEver = Second
EOF
test_expect_success 'uppercase section' '
- git config SECTION.UPPERCASE true &&
+ git config ${mode_set} SECTION.UPPERCASE true &&
test_cmp expect .git/config
'
@@ -174,8 +202,8 @@
test_expect_success 'append comments' '
git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
- git config --comment="find fish" section.disposition peckish &&
- git config --comment="#abc" section.foo bar &&
+ git config ${mode_set} --comment="find fish" section.disposition peckish &&
+ git config ${mode_set} --comment="#abc" section.foo bar &&
git config --comment="and comment" section.spsp value &&
git config --comment=" # and comment" section.htsp value &&
@@ -184,7 +212,7 @@
'
test_expect_success 'Prohibited LF in comment' '
- test_must_fail git config --comment="a${LF}b" section.k v
+ test_must_fail git config ${mode_set} --comment="a${LF}b" section.k v
'
test_expect_success 'non-match result' 'test_cmp expect .git/config'
@@ -235,7 +263,7 @@
EOF
test_expect_success 'unset with cont. lines' '
- git config --unset beta.baz
+ git config ${mode_unset} beta.baz
'
cat > expect <<\EOF
@@ -262,7 +290,7 @@
cp .git/config .git/config2
test_expect_success 'multiple unset' '
- git config --unset-all beta.haha
+ git config ${mode_unset_all} beta.haha
'
cat > expect << EOF
@@ -281,14 +309,14 @@
cp .git/config2 .git/config
test_expect_success '--replace-all missing value' '
- test_must_fail git config --replace-all beta.haha &&
+ test_must_fail git config ${mode_replace_all} beta.haha &&
test_cmp .git/config2 .git/config
'
rm .git/config2
test_expect_success '--replace-all' '
- git config --replace-all beta.haha gamma
+ git config ${mode_replace_all} beta.haha gamma
'
cat > expect << EOF
@@ -315,7 +343,7 @@
[nextSection] noNewline = ouch
EOF
test_expect_success 'really mean test' '
- git config beta.haha alpha &&
+ git config ${mode_set} beta.haha alpha &&
test_cmp expect .git/config
'
@@ -330,7 +358,7 @@
nonewline = wow
EOF
test_expect_success 'really really mean test' '
- git config nextsection.nonewline wow &&
+ git config ${mode_set} nextsection.nonewline wow &&
test_cmp expect .git/config
'
@@ -348,7 +376,7 @@
nonewline = wow
EOF
test_expect_success 'unset' '
- git config --unset beta.haha &&
+ git config ${mode_unset} beta.haha &&
test_cmp expect .git/config
'
@@ -384,7 +412,7 @@
wow
wow2 for me
EOF
- git config --get-all nextsection.nonewline >actual &&
+ git config ${mode_get_all} nextsection.nonewline >actual &&
test_cmp expect actual
'
@@ -404,11 +432,11 @@
'
test_expect_success 'ambiguous unset' '
- test_must_fail git config --unset nextsection.nonewline
+ test_must_fail git config ${mode_unset} nextsection.nonewline
'
test_expect_success 'invalid unset' '
- test_must_fail git config --unset somesection.nonewline
+ test_must_fail git config ${mode_unset} somesection.nonewline
'
cat > expect << EOF
@@ -422,7 +450,12 @@
EOF
test_expect_success 'multivar unset' '
- git config --unset nextsection.nonewline "wow3$" &&
+ case "$mode" in
+ legacy)
+ git config --unset nextsection.nonewline "wow3$";;
+ subcommands)
+ git config unset --value="wow3$" nextsection.nonewline;;
+ esac &&
test_cmp expect .git/config
'
@@ -460,11 +493,11 @@
EOF
test_expect_success 'working --list' '
- git config --list > output &&
+ git config ${mode_prefix}list > output &&
test_cmp expect output
'
test_expect_success '--list without repo produces empty output' '
- git --git-dir=nonexistent config --list >output &&
+ git --git-dir=nonexistent config ${mode_prefix}list >output &&
test_must_be_empty output
'
@@ -476,7 +509,7 @@
EOF
test_expect_success '--name-only --list' '
- git config --name-only --list >output &&
+ git config ${mode_prefix}list --name-only >output &&
test_cmp expect output
'
@@ -486,7 +519,7 @@
EOF
test_expect_success '--get-regexp' '
- git config --get-regexp in >output &&
+ git config ${mode_get_regexp} in >output &&
test_cmp expect output
'
@@ -496,7 +529,7 @@
EOF
test_expect_success '--name-only --get-regexp' '
- git config --name-only --get-regexp in >output &&
+ git config ${mode_get_regexp} --name-only in >output &&
test_cmp expect output
'
@@ -507,7 +540,7 @@
test_expect_success '--add' '
git config --add nextsection.nonewline "wow4 for you" &&
- git config --get-all nextsection.nonewline > output &&
+ git config ${mode_get_all} nextsection.nonewline > output &&
test_cmp expect output
'
@@ -529,21 +562,21 @@
echo novalue.variable > expect
test_expect_success 'get-regexp variable with no value' '
- git config --get-regexp novalue > output &&
+ git config ${mode_get_regexp} novalue > output &&
test_cmp expect output
'
echo 'novalue.variable true' > expect
test_expect_success 'get-regexp --bool variable with no value' '
- git config --bool --get-regexp novalue > output &&
+ git config ${mode_get_regexp} --bool novalue > output &&
test_cmp expect output
'
echo 'emptyvalue.variable ' > expect
test_expect_success 'get-regexp variable with empty value' '
- git config --get-regexp emptyvalue > output &&
+ git config ${mode_get_regexp} emptyvalue > output &&
test_cmp expect output
'
@@ -563,7 +596,8 @@
test_expect_success 'no arguments, but no crash' '
test_must_fail git config >output 2>&1 &&
- test_grep usage output
+ echo "error: no action specified" >expect &&
+ test_cmp expect output
'
cat > .git/config << EOF
@@ -614,17 +648,17 @@
EOF
test_expect_success 'alternative GIT_CONFIG' '
- GIT_CONFIG=other-config git config --list >output &&
+ GIT_CONFIG=other-config git config ${mode_prefix}list >output &&
test_cmp expect output
'
test_expect_success 'alternative GIT_CONFIG (--file)' '
- git config --file other-config --list >output &&
+ git config ${mode_prefix}list --file other-config >output &&
test_cmp expect output
'
test_expect_success 'alternative GIT_CONFIG (--file=-)' '
- git config --file - --list <other-config >output &&
+ git config ${mode_prefix}list --file - <other-config >output &&
test_cmp expect output
'
@@ -633,10 +667,11 @@
'
test_expect_success 'editing stdin is an error' '
- test_must_fail git config --file - --edit
+ test_must_fail git config ${mode_prefix}edit --file -
'
test_expect_success 'refer config from subdirectory' '
+ test_when_finished "rm -r x" &&
mkdir x &&
test_cmp_config -C x strasse --file=../other-config --get ein.bahn
'
@@ -665,7 +700,7 @@
EOF
test_expect_success 'rename section' '
- git config --rename-section branch.eins branch.zwei
+ git config ${mode_prefix}rename-section branch.eins branch.zwei
'
cat > expect << EOF
@@ -684,7 +719,7 @@
'
test_expect_success 'rename non-existing section' '
- test_must_fail git config --rename-section \
+ test_must_fail git config ${mode_prefix}rename-section \
branch."world domination" branch.drei
'
@@ -693,7 +728,7 @@
'
test_expect_success 'rename another section' '
- git config --rename-section branch."1 234 blabl/a" branch.drei
+ git config ${mode_prefix}rename-section branch."1 234 blabl/a" branch.drei
'
cat > expect << EOF
@@ -716,7 +751,7 @@
EOF
test_expect_success 'rename a section with a var on the same line' '
- git config --rename-section branch.vier branch.zwei
+ git config ${mode_prefix}rename-section branch.vier branch.zwei
'
cat > expect << EOF
@@ -737,11 +772,11 @@
'
test_expect_success 'renaming empty section name is rejected' '
- test_must_fail git config --rename-section branch.zwei ""
+ test_must_fail git config ${mode_prefix}rename-section branch.zwei ""
'
test_expect_success 'renaming to bogus section is rejected' '
- test_must_fail git config --rename-section branch.zwei "bogus name"
+ test_must_fail git config ${mode_prefix}rename-section branch.zwei "bogus name"
'
test_expect_success 'renaming a section with a long line' '
@@ -750,7 +785,7 @@
printf " c = d %1024s [a] e = f\\n" " " &&
printf "[a] g = h\\n"
} >y &&
- git config -f y --rename-section a xyz &&
+ git config ${mode_prefix}rename-section -f y a xyz &&
test_must_fail git config -f y b.e
'
@@ -760,7 +795,7 @@
printf " c = d %1024s [a] [foo] e = f\\n" " " &&
printf "[a] g = h\\n"
} >y &&
- git config -f y --rename-section a xyz &&
+ git config ${mode_prefix}rename-section -f y a xyz &&
test_must_fail git config -f y foo.e
'
@@ -770,7 +805,7 @@
printf " c = d %525000s e" " " &&
printf "[a] g = h\\n"
} >y &&
- test_must_fail git config -f y --rename-section a xyz 2>err &&
+ test_must_fail git config ${mode_prefix}rename-section -f y a xyz 2>err &&
grep "refusing to work with overly long line in .y. on line 2" err
'
@@ -779,7 +814,7 @@
EOF
test_expect_success 'remove section' '
- git config --remove-section branch.zwei
+ git config ${mode_prefix}remove-section branch.zwei
'
cat > expect << EOF
@@ -803,16 +838,16 @@
test_expect_success 'section ending' '
rm -f .git/config &&
- git config gitcvs.enabled true &&
- git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
- git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+ git config ${mode_set} gitcvs.enabled true &&
+ git config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+ git config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
test_cmp expect .git/config
'
test_expect_success numbers '
- git config kilo.gram 1k &&
- git config mega.ton 1m &&
+ git config ${mode_set} kilo.gram 1k &&
+ git config ${mode_set} mega.ton 1m &&
echo 1024 >expect &&
echo 1048576 >>expect &&
git config --int --get kilo.gram >actual &&
@@ -821,20 +856,20 @@
'
test_expect_success '--int is at least 64 bits' '
- git config giga.watts 121g &&
+ git config ${mode_set} giga.watts 121g &&
echo >expect &&
test_cmp_config 129922760704 --int --get giga.watts
'
test_expect_success 'invalid unit' '
- git config aninvalid.unit "1auto" &&
+ git config ${mode_set} aninvalid.unit "1auto" &&
test_cmp_config 1auto aninvalid.unit &&
test_must_fail git config --int --get aninvalid.unit 2>actual &&
test_grep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
'
test_expect_success 'invalid unit boolean' '
- git config commit.gpgsign "1true" &&
+ git config ${mode_set} commit.gpgsign "1true" &&
test_cmp_config 1true commit.gpgsign &&
test_must_fail git config --bool --get commit.gpgsign 2>actual &&
test_grep "bad boolean config value .1true. for .commit.gpgsign." actual
@@ -847,7 +882,7 @@
'
test_expect_success 'invalid stdin config' '
- echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
+ echo "[broken" | test_must_fail git config ${mode_prefix}list --file - >output 2>&1 &&
test_grep "bad config line 1 in standard input" output
'
@@ -864,14 +899,14 @@
test_expect_success bool '
- git config bool.true1 01 &&
- git config bool.true2 -1 &&
- git config bool.true3 YeS &&
- git config bool.true4 true &&
- git config bool.false1 000 &&
- git config bool.false2 "" &&
- git config bool.false3 nO &&
- git config bool.false4 FALSE &&
+ git config ${mode_set} bool.true1 01 &&
+ git config ${mode_set} bool.true2 -1 &&
+ git config ${mode_set} bool.true3 YeS &&
+ git config ${mode_set} bool.true4 true &&
+ git config ${mode_set} bool.false1 000 &&
+ git config ${mode_set} bool.false2 "" &&
+ git config ${mode_set} bool.false3 nO &&
+ git config ${mode_set} bool.false4 FALSE &&
rm -f result &&
for i in 1 2 3 4
do
@@ -882,7 +917,7 @@
test_expect_success 'invalid bool (--get)' '
- git config bool.nobool foobar &&
+ git config ${mode_set} bool.nobool foobar &&
test_must_fail git config --bool --get bool.nobool'
test_expect_success 'invalid bool (set)' '
@@ -1071,7 +1106,7 @@
test_expect_success 'get --type=color' '
rm .git/config &&
- git config foo.color "red" &&
+ git config ${mode_set} foo.color "red" &&
git config --get --type=color foo.color >actual.raw &&
test_decode_color <actual.raw >actual &&
echo "<RED>" >expect &&
@@ -1108,18 +1143,18 @@
EOF
test_expect_success 'quoting' '
rm -f .git/config &&
- git config quote.leading " test" &&
- git config quote.ending "test " &&
- git config quote.semicolon "test;test" &&
- git config quote.hash "test#test" &&
+ git config ${mode_set} quote.leading " test" &&
+ git config ${mode_set} quote.ending "test " &&
+ git config ${mode_set} quote.semicolon "test;test" &&
+ git config ${mode_set} quote.hash "test#test" &&
test_cmp expect .git/config
'
test_expect_success 'key with newline' '
- test_must_fail git config "key.with
+ test_must_fail git config ${mode_get} "key.with
newline" 123'
-test_expect_success 'value with newline' 'git config key.sub value.with\\\
+test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\
newline'
cat > .git/config <<\EOF
@@ -1139,7 +1174,7 @@
EOF
test_expect_success 'value continued on next line' '
- git config --list > result &&
+ git config ${mode_prefix}list > result &&
test_cmp expect result
'
@@ -1163,14 +1198,14 @@
Qsection.sub=section.val5Q
EOF
test_expect_success '--null --list' '
- git config --null --list >result.raw &&
+ git config ${mode_prefix}list --null >result.raw &&
nul_to_q <result.raw >result &&
echo >>result &&
test_cmp expect result
'
test_expect_success '--null --get-regexp' '
- git config --null --get-regexp "val[0-9]" >result.raw &&
+ git config ${mode_get_regexp} --null "val[0-9]" >result.raw &&
nul_to_q <result.raw >result &&
echo >>result &&
test_cmp expect result
@@ -1178,26 +1213,27 @@
test_expect_success 'inner whitespace kept verbatim, spaces only' '
echo "foo bar" >expect &&
- git config section.val "foo bar" &&
- git config --get section.val >actual &&
+ git config ${mode_set} section.val "foo bar" &&
+ git config ${mode_get} section.val >actual &&
test_cmp expect actual
'
test_expect_success 'inner whitespace kept verbatim, horizontal tabs only' '
echo "fooQQbar" | q_to_tab >expect &&
- git config section.val "$(cat expect)" &&
- git config --get section.val >actual &&
+ git config ${mode_set} section.val "$(cat expect)" &&
+ git config ${mode_get} section.val >actual &&
test_cmp expect actual
'
test_expect_success 'inner whitespace kept verbatim, horizontal tabs and spaces' '
echo "foo Q bar" | q_to_tab >expect &&
- git config section.val "$(cat expect)" &&
- git config --get section.val >actual &&
+ git config ${mode_set} section.val "$(cat expect)" &&
+ git config ${mode_get} section.val >actual &&
test_cmp expect actual
'
test_expect_success SYMLINKS 'symlinked configuration' '
+ test_when_finished "rm myconfig" &&
ln -s notyet myconfig &&
git config --file=myconfig test.frotz nitfol &&
test -h myconfig &&
@@ -1218,10 +1254,11 @@
'
test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
+ test_when_finished "rm linktonada linktolinktonada" &&
ln -s doesnotexist linktonada &&
ln -s linktonada linktolinktonada &&
- test_must_fail git config --file=linktonada --list &&
- test_must_fail git config --file=linktolinktonada --list
+ test_must_fail git config ${mode_prefix}list --file=linktonada &&
+ test_must_fail git config ${mode_prefix}list --file=linktolinktonada
'
test_expect_success 'check split_cmdline return' '
@@ -1229,12 +1266,12 @@
git init repo &&
(
cd repo &&
- git config alias.split-cmdline-fix "echo \"" &&
+ git config ${mode_set} alias.split-cmdline-fix "echo \"" &&
test_must_fail git split-cmdline-fix &&
echo foo >foo &&
git add foo &&
git commit -m "initial commit" &&
- git config branch.main.mergeoptions "echo \"" &&
+ git config ${mode_set} branch.main.mergeoptions "echo \"" &&
test_must_fail git merge main
)
'
@@ -1266,18 +1303,18 @@
'
test_expect_success 'key sanity-checking' '
- test_must_fail git config foo=bar &&
- test_must_fail git config foo=.bar &&
- test_must_fail git config foo.ba=r &&
- test_must_fail git config foo.1bar &&
- test_must_fail git config foo."ba
+ test_must_fail git config ${mode_get} foo=bar &&
+ test_must_fail git config ${mode_get} foo=.bar &&
+ test_must_fail git config ${mode_get} foo.ba=r &&
+ test_must_fail git config ${mode_get} foo.1bar &&
+ test_must_fail git config ${mode_get} foo."ba
z".bar &&
- test_must_fail git config . false &&
- test_must_fail git config .foo false &&
- test_must_fail git config foo. false &&
- test_must_fail git config .foo. false &&
- git config foo.bar true &&
- git config foo."ba =z".bar false
+ test_must_fail git config ${mode_set} . false &&
+ test_must_fail git config ${mode_set} .foo false &&
+ test_must_fail git config ${mode_set} foo. false &&
+ test_must_fail git config ${mode_set} .foo. false &&
+ git config ${mode_set} foo.bar true &&
+ git config ${mode_set} foo."ba =z".bar false
'
test_expect_success 'git -c works with aliases of builtins' '
@@ -1319,7 +1356,7 @@
'
test_expect_success 'multiple git -c appends config' '
- test_config alias.x "!git -c x.two=2 config --get-regexp ^x\.*" &&
+ test_config alias.x "!git -c x.two=2 config ${mode_get_regexp} ^x\.*" &&
cat >expect <<-\EOF &&
x.one 1
x.two 2
@@ -1478,14 +1515,14 @@
done
test_expect_success 'git -c is not confused by empty environment' '
- GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
+ GIT_CONFIG_PARAMETERS="" git -c x.one=1 config ${mode_prefix}list
'
test_expect_success 'GIT_CONFIG_PARAMETERS handles old-style entries' '
v="${SQ}key.one=foo${SQ}" &&
v="$v ${SQ}key.two=bar${SQ}" &&
v="$v ${SQ}key.ambiguous=section.whatever=value${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_get_regexp} "key.*" >actual &&
cat >expect <<-EOF &&
key.one foo
key.two bar
@@ -1498,7 +1535,7 @@
v="${SQ}key.one${SQ}=${SQ}foo${SQ}" &&
v="$v ${SQ}key.two${SQ}=${SQ}bar${SQ}" &&
v="$v ${SQ}key.ambiguous=section.whatever${SQ}=${SQ}value${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_get_regexp} "key.*" >actual &&
cat >expect <<-EOF &&
key.one foo
key.two bar
@@ -1512,7 +1549,7 @@
v="$v ${SQ}key.newone${SQ}=${SQ}newfoo${SQ}" &&
v="$v ${SQ}key.oldtwo=oldbar${SQ}" &&
v="$v ${SQ}key.newtwo${SQ}=${SQ}newbar${SQ}" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_get_regexp} "key.*" >actual &&
cat >expect <<-EOF &&
key.oldone oldfoo
key.newone newfoo
@@ -1525,7 +1562,7 @@
test_expect_success 'old and new bools with ambiguous subsection' '
v="${SQ}key.with=equals.oldbool${SQ}" &&
v="$v ${SQ}key.with=equals.newbool${SQ}=" &&
- GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+ GIT_CONFIG_PARAMETERS=$v git config ${mode_get_regexp} "key.*" >actual &&
cat >expect <<-EOF &&
key.with equals.oldbool
key.with=equals.newbool
@@ -1539,7 +1576,7 @@
env.two two
EOF
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ} ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*" >actual &&
+ git config ${mode_get_regexp} "env.*" >actual &&
test_cmp expect actual &&
cat >expect <<-EOF &&
@@ -1547,12 +1584,12 @@
env.two two
EOF
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ$SQ$SQ ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*" >actual &&
+ git config ${mode_get_regexp} "env.*" >actual &&
test_cmp expect actual &&
test_must_fail env \
GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ ${SQ}env.two=two${SQ}" \
- git config --get-regexp "env.*"
+ git config ${mode_get_regexp} "env.*"
'
test_expect_success 'git --config-env=key=envvar support' '
@@ -1600,7 +1637,7 @@
ENVVAR=env-value git \
-c bar.cmd=cmd-value \
--config-env=bar.env=ENVVAR \
- config --get-regexp "^bar.*" >actual &&
+ config ${mode_get_regexp} "^bar.*" >actual &&
test_cmp expect actual
'
@@ -1628,7 +1665,7 @@
GIT_CONFIG_COUNT=2 \
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="foo" \
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="bar" \
- git config --get-regexp "pair.*" >actual &&
+ git config ${mode_get_regexp} "pair.*" >actual &&
cat >expect <<-EOF &&
pair.one foo
pair.two bar
@@ -1638,7 +1675,7 @@
test_expect_success 'git config ignores pairs without count' '
test_must_fail env GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one 2>error &&
+ git config ${mode_get} pair.one 2>error &&
test_must_be_empty error
'
@@ -1646,7 +1683,7 @@
GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
- git config --get-regexp "pair.*" >actual 2>error &&
+ git config ${mode_get_regexp} "pair.*" >actual 2>error &&
cat >expect <<-EOF &&
pair.one value
EOF
@@ -1657,43 +1694,43 @@
test_expect_success 'git config ignores pairs with zero count' '
test_must_fail env \
GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one 2>error &&
+ git config ${mode_get} pair.one 2>error &&
test_must_be_empty error
'
test_expect_success 'git config ignores pairs with empty count' '
test_must_fail env \
GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one 2>error &&
+ git config ${mode_get} pair.one 2>error &&
test_must_be_empty error
'
test_expect_success 'git config fails with invalid count' '
- test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
+ test_must_fail env GIT_CONFIG_COUNT=10a git config ${mode_prefix}list 2>error &&
test_grep "bogus count" error &&
- test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
+ test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config ${mode_prefix}list 2>error &&
test_grep "too many entries" error
'
test_expect_success 'git config fails with missing config key' '
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
- git config --list 2>error &&
+ git config ${mode_prefix}list 2>error &&
test_grep "missing config key" error
'
test_expect_success 'git config fails with missing config value' '
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
- git config --list 2>error &&
+ git config ${mode_prefix}list 2>error &&
test_grep "missing config value" error
'
test_expect_success 'git config fails with invalid config pair key' '
test_must_fail env GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0= GIT_CONFIG_VALUE_0=value \
- git config --list &&
+ git config ${mode_prefix}list &&
test_must_fail env GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0=missing-section GIT_CONFIG_VALUE_0=value \
- git config --list
+ git config ${mode_prefix}list
'
test_expect_success 'environment overrides config file' '
@@ -1703,7 +1740,7 @@
one = value
EOF
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=override \
- git config pair.one >actual &&
+ git config ${mode_get} pair.one >actual &&
cat >expect <<-EOF &&
override
EOF
@@ -1713,7 +1750,7 @@
test_expect_success 'GIT_CONFIG_PARAMETERS overrides environment config' '
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
GIT_CONFIG_PARAMETERS="${SQ}pair.one=override${SQ}" \
- git config pair.one >actual &&
+ git config ${mode_get} pair.one >actual &&
cat >expect <<-EOF &&
override
EOF
@@ -1732,8 +1769,8 @@
test_expect_success 'git config --edit works' '
git config -f tmp test.value no &&
echo test.value=yes >expect &&
- GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit &&
- git config -f tmp --list >actual &&
+ GIT_EDITOR="echo [test]value=yes >" git config ${mode_prefix}edit -f tmp &&
+ git config ${mode_prefix}list -f tmp >actual &&
test_cmp expect actual
'
@@ -1741,8 +1778,8 @@
git config -f tmp test.value no &&
echo test.value=yes >expect &&
test_config core.editor "echo [test]value=yes >" &&
- git config -f tmp --edit &&
- git config -f tmp --list >actual &&
+ git config ${mode_prefix}edit -f tmp &&
+ git config ${mode_prefix}list -f tmp >actual &&
test_cmp expect actual
'
@@ -1788,20 +1825,28 @@
test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
test_must_be_empty actual &&
+ test_expect_code 1 git config get --url=https://good.example.com --bool doesnt.exist >actual &&
+ test_must_be_empty actual &&
echo true >expect &&
git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
test_cmp expect actual &&
+ git config get --bool --url=https://good.example.com http.SSLverify >actual &&
+ test_cmp expect actual &&
echo false >expect &&
git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
test_cmp expect actual &&
+ git config get --bool --url=https://weak.example.com http.sslverify >actual &&
+ test_cmp expect actual &&
{
echo http.cookiefile /tmp/cookie.txt &&
echo http.sslverify false
} >expect &&
git config --get-urlmatch HTTP https://weak.example.com >actual &&
+ test_cmp expect actual &&
+ git config get --url=https://weak.example.com HTTP >actual &&
test_cmp expect actual
'
@@ -1817,6 +1862,8 @@
local http.sslverify false
EOF
git config --get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
+ test_cmp expect actual &&
+ git config get --url=https://weak.example.com --show-scope HTTP >actual &&
test_cmp expect actual
'
@@ -1849,45 +1896,67 @@
echo http.cookiefile /tmp/root.txt >expect &&
git config --get-urlmatch HTTP https://example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
git config --get-urlmatch HTTP https://example.com/subdirectory >actual &&
test_cmp expect actual &&
+ git config get --url=https://example.com/subdirectory HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual &&
test_cmp expect actual &&
+ git config get --url=https://example.com/subdirectory/nested HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/user.txt >expect &&
git config --get-urlmatch HTTP https://user@example.com/ >actual &&
test_cmp expect actual &&
+ git config get --url=https://user@example.com/ HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/subdirectory.txt >expect &&
git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual &&
test_cmp expect actual &&
+ git config get --url=https://averylonguser@example.com/subdirectory HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/preceding.txt >expect &&
git config --get-urlmatch HTTP https://preceding.example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://preceding.example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/wildcard.txt >expect &&
git config --get-urlmatch HTTP https://wildcard.example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://wildcard.example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/sub.txt >expect &&
git config --get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual &&
test_cmp expect actual &&
+ git config get --url=https://sub.example.com/wildcardwithsubdomain HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/trailing.txt >expect &&
git config --get-urlmatch HTTP https://trailing.example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://trailing.example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/sub.txt >expect &&
git config --get-urlmatch HTTP https://user@sub.example.com >actual &&
test_cmp expect actual &&
+ git config get --url=https://user@sub.example.com HTTP >actual &&
+ test_cmp expect actual &&
echo http.cookiefile /tmp/multiwildcard.txt >expect &&
git config --get-urlmatch HTTP https://wildcard.example.org >actual &&
+ test_cmp expect actual &&
+ git config get --url=https://wildcard.example.org HTTP >actual &&
test_cmp expect actual
'
@@ -1954,7 +2023,7 @@
# please be careful when you update the above variable
EOF
- git config --unset section.key &&
+ git config ${mode_unset} section.key &&
test_cmp expect .git/config &&
cat >.git/config <<-\EOF &&
@@ -1967,7 +2036,7 @@
[next-section]
EOF
- git config --unset section.key &&
+ git config ${mode_unset} section.key &&
test_cmp expect .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1977,7 +2046,7 @@
[two]
key = true
EOF
- git config --unset two.key &&
+ git config ${mode_unset} two.key &&
! grep two .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1987,7 +2056,7 @@
[one]
key = true
EOF
- git config --unset-all one.key &&
+ git config ${mode_unset_all} one.key &&
test_line_count = 0 .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -1997,7 +2066,7 @@
[two]
Qkey = true
EOF
- git config --unset two.key &&
+ git config ${mode_unset} two.key &&
grep two .git/config &&
q_to_tab >.git/config <<-\EOF &&
@@ -2009,8 +2078,8 @@
[TWO "subsection"]
[one]
EOF
- git config --unset two.subsection.key &&
- test "not [two subsection]" = "$(git config one.key)" &&
+ git config ${mode_unset} two.subsection.key &&
+ test "not [two subsection]" = "$(git config ${mode_get} one.key)" &&
test_line_count = 3 .git/config
'
@@ -2021,7 +2090,7 @@
key = value2
EOF
- git config --unset-all section.key &&
+ git config ${mode_unset_all} section.key &&
test_line_count = 0 .git/config
'
@@ -2044,7 +2113,7 @@
git config imap.pass Hunter2 &&
perl -e \
"die q(badset) if ((stat(q(.git/config)))[2] & 07777) != 0600" &&
- git config --rename-section imap pop &&
+ git config ${mode_prefix}rename-section imap pop &&
perl -e \
"die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
'
@@ -2093,7 +2162,7 @@
command line: user.cmdline=true
EOF
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=user.environ GIT_CONFIG_VALUE_0=true\
- git -c user.cmdline=true config --list --show-origin >output &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-origin >output &&
test_cmp expect output
'
@@ -2110,7 +2179,7 @@
includeQcommand line:Quser.cmdline
trueQ
EOF
- git -c user.cmdline=true config --null --list --show-origin >output.raw &&
+ git -c user.cmdline=true config ${mode_prefix}list --null --show-origin >output.raw &&
nul_to_q <output.raw >output &&
# The here-doc above adds a newline that the --null output would not
# include. Add it here to make the two comparable.
@@ -2124,7 +2193,7 @@
file:.git/config user.override=local
file:.git/config include.path=../include/relative.include
EOF
- git config --local --list --show-origin >output &&
+ git config ${mode_prefix}list --local --show-origin >output &&
test_cmp expect output
'
@@ -2133,7 +2202,7 @@
file:$HOME/.gitconfig user.global true
file:.git/config user.local true
EOF
- git config --show-origin --get-regexp "user\.[g|l].*" >output &&
+ git config ${mode_get_regexp} --show-origin "user\.[g|l].*" >output &&
test_cmp expect output
'
@@ -2141,7 +2210,7 @@
cat >expect <<-\EOF &&
file:.git/config local
EOF
- git config --show-origin user.override >output &&
+ git config ${mode_get} --show-origin user.override >output &&
test_cmp expect output
'
@@ -2162,7 +2231,7 @@
cat >expect <<-\EOF &&
file:"file\" (dq) and spaces.conf" user.custom=true
EOF
- git config --file "$WEIRDLY_NAMED_FILE" --show-origin --list >output &&
+ git config ${mode_prefix}list --file "$WEIRDLY_NAMED_FILE" --show-origin >output &&
test_cmp expect output
'
@@ -2170,7 +2239,7 @@
cat >expect <<-\EOF &&
standard input: user.custom=true
EOF
- git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
+ git config ${mode_prefix}list --file - --show-origin <"$CUSTOM_CONFIG_FILE" >output &&
test_cmp expect output
'
@@ -2197,7 +2266,7 @@
cat >expect <<-EOF &&
blob:$blob user.custom=true
EOF
- git config --blob=$blob --show-origin --list >output &&
+ git config ${mode_prefix}list --blob=$blob --show-origin >output &&
test_cmp expect output
)
'
@@ -2213,7 +2282,7 @@
cp "$CUSTOM_CONFIG_FILE" custom.conf &&
git add custom.conf &&
git commit -m "new config file" &&
- git config --blob=main:custom.conf --show-origin --list >output &&
+ git config ${mode_prefix}list --blob=main:custom.conf --show-origin >output &&
test_cmp expect output
)
'
@@ -2239,13 +2308,14 @@
worktree user.worktree=true
command user.cmdline=true
EOF
+ test_when_finished "git worktree remove wt1" &&
git worktree add wt1 &&
# We need these to test for worktree scope, but outside of this
# test, this is just noise
test_config core.repositoryformatversion 1 &&
test_config extensions.worktreeConfig true &&
git config --worktree user.worktree true &&
- git -c user.cmdline=true config --list --show-scope >output &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-scope >output &&
test_cmp expect output
'
@@ -2254,7 +2324,7 @@
cat >expect <<-EOF &&
command user.custom=true
EOF
- git config --blob=$blob --show-scope --list >output &&
+ git config ${mode_prefix}list --blob=$blob --show-scope >output &&
test_cmp expect output
'
@@ -2264,7 +2334,7 @@
local user.override=local
local include.path=../include/relative.include
EOF
- git config --local --list --show-scope >output &&
+ git config ${mode_prefix}list --local --show-scope >output &&
test_cmp expect output
'
@@ -2272,7 +2342,7 @@
cat >expect <<-\EOF &&
local true
EOF
- git config --show-scope --get user.local >output &&
+ git config ${mode_get} --show-scope user.local >output &&
test_cmp expect output
'
@@ -2288,7 +2358,7 @@
local file:.git/../include/relative.include user.relative=include
command command line: user.cmdline=true
EOF
- git -c user.cmdline=true config --list --show-origin --show-scope >output &&
+ git -c user.cmdline=true config ${mode_prefix}list --show-origin --show-scope >output &&
test_cmp expect output
'
@@ -2329,7 +2399,7 @@
global home.config=true
local local.config=true
EOF
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output &&
cat >expect <<-EOF &&
@@ -2338,20 +2408,20 @@
local local.config=true
EOF
GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=custom-system-config GIT_CONFIG_GLOBAL=custom-global-config \
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output &&
cat >expect <<-EOF &&
local local.config=true
EOF
GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=/dev/null GIT_CONFIG_GLOBAL=/dev/null \
- git config --show-scope --list >output &&
+ git config ${mode_prefix}list --show-scope >output &&
test_cmp expect output
'
test_expect_success 'override global and system config with missing file' '
- test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config --global --list &&
- test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config --system --list &&
+ test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config ${mode_prefix}list --global &&
+ test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config ${mode_prefix}list --system &&
GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=does-not-exist git version
'
@@ -2467,7 +2537,7 @@
[abc]
Qkey = b
EOF
- git config --replace-all abc.key b &&
+ git config ${mode_replace_all} abc.key b &&
test_cmp expect .git/config
'
@@ -2478,7 +2548,7 @@
# no match => add new entry
cp initial config &&
git config --file=config abc.key two a+ &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2491,7 +2561,7 @@
# multiple values, no match => add
git config --file=config abc.key three a+ &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2501,7 +2571,7 @@
# single match => replace
git config --file=config abc.key four h+ &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=one
abc.key=two
@@ -2516,7 +2586,7 @@
git config --file=config --add abc.key two &&
git config --file=config --add abc.key three &&
git config --file=config --replace-all abc.key four "o+" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
abc.key=four
abc.key=three
@@ -2532,20 +2602,20 @@
test_must_fail git config --file=config --fixed-value --add dev.null bogus &&
test_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&
test_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --rename-section dev null &&
- test_must_fail git config --file=config --fixed-value --remove-section dev &&
- test_must_fail git config --file=config --fixed-value --list &&
+ test_must_fail git config ${mode_prefix}rename-section --file=config --fixed-value dev null &&
+ test_must_fail git config ${mode_prefix}remove-section --file=config --fixed-value dev &&
+ test_must_fail git config ${mode_prefix}list --file=config --fixed-value &&
test_must_fail git config --file=config --fixed-value --get-color dev.null &&
test_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&
# These modes complain when --fixed-value has no value-pattern
- test_must_fail git config --file=config --fixed-value dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&
- test_must_fail git config --file=config --fixed-value --get dev.null &&
- test_must_fail git config --file=config --fixed-value --get-all dev.null &&
- test_must_fail git config --file=config --fixed-value --get-regexp "dev.*" &&
- test_must_fail git config --file=config --fixed-value --unset dev.null &&
- test_must_fail git config --file=config --fixed-value --unset-all dev.null
+ test_must_fail git config ${mode_set} --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_replace_all} --file=config --fixed-value dev.null bogus &&
+ test_must_fail git config ${mode_prefix}get --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_get_all} --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_get_regexp} --file=config --fixed-value "dev.*" &&
+ test_must_fail git config ${mode_unset} --file=config --fixed-value dev.null &&
+ test_must_fail git config ${mode_unset_all} --file=config --fixed-value dev.null
'
test_expect_success '--fixed-value uses exact string matching' '
@@ -2555,7 +2625,7 @@
cp initial config &&
git config --file=config fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=$META
fixed.test=bogus
@@ -2564,7 +2634,7 @@
cp initial config &&
git config --file=config --fixed-value fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-\EOF &&
fixed.test=bogus
EOF
@@ -2573,16 +2643,21 @@
cp initial config &&
test_must_fail git config --file=config --unset fixed.test "$META" &&
git config --file=config --fixed-value --unset fixed.test "$META" &&
- test_must_fail git config --file=config fixed.test &&
+ test_must_fail git config ${mode_get} --file=config fixed.test &&
+
+ cp initial config &&
+ test_must_fail git config unset --file=config --value="$META" fixed.test &&
+ git config unset --file=config --fixed-value --value="$META" fixed.test &&
+ test_must_fail git config ${mode_get} --file=config fixed.test &&
cp initial config &&
test_must_fail git config --file=config --unset-all fixed.test "$META" &&
git config --file=config --fixed-value --unset-all fixed.test "$META" &&
- test_must_fail git config --file=config fixed.test &&
+ test_must_fail git config ${mode_get} --file=config fixed.test &&
cp initial config &&
- git config --file=config --replace-all fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config --file=config fixed.test bogus "$META" &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=$META
fixed.test=bogus
@@ -2590,7 +2665,7 @@
test_cmp expect actual &&
git config --file=config --fixed-value --replace-all fixed.test bogus "$META" &&
- git config --file=config --list >actual &&
+ git config ${mode_prefix}list --file=config >actual &&
cat >expect <<-EOF &&
fixed.test=bogus
fixed.test=bogus
@@ -2605,18 +2680,27 @@
git config --file=config --add fixed.test "$META" &&
git config --file=config --get fixed.test bogus &&
+ git config get --file=config --value=bogus fixed.test &&
test_must_fail git config --file=config --get fixed.test "$META" &&
+ test_must_fail git config get --file=config --value="$META" fixed.test &&
git config --file=config --get --fixed-value fixed.test "$META" &&
+ git config get --file=config --fixed-value --value="$META" fixed.test &&
test_must_fail git config --file=config --get --fixed-value fixed.test non-existent &&
git config --file=config --get-all fixed.test bogus &&
+ git config get --all --file=config --value=bogus fixed.test &&
test_must_fail git config --file=config --get-all fixed.test "$META" &&
+ test_must_fail git config get --all --file=config --value="$META" fixed.test &&
git config --file=config --get-all --fixed-value fixed.test "$META" &&
+ git config get --all --file=config --value="$META" --fixed-value fixed.test &&
test_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent &&
git config --file=config --get-regexp fixed+ bogus &&
+ git config get --regexp --file=config --value=bogus fixed+ &&
test_must_fail git config --file=config --get-regexp fixed+ "$META" &&
+ test_must_fail git config get --regexp --file=config --value="$META" fixed+ &&
git config --file=config --get-regexp --fixed-value fixed+ "$META" &&
+ git config get --regexp --file=config --fixed-value --value="$META" fixed+ &&
test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
'
@@ -2738,4 +2822,25 @@
grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err
'
+test_expect_success 'negated mode causes failure' '
+ test_must_fail git config --no-get 2>err &&
+ grep "unknown option \`no-get${SQ}" err
+'
+
+test_expect_success 'specifying multiple modes causes failure' '
+ cat >expect <<-EOF &&
+ error: options ${SQ}--get-all${SQ} and ${SQ}--get${SQ} cannot be used together
+ EOF
+ test_must_fail git config --get --get-all 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'writing to stdin is rejected' '
+ echo "fatal: writing to stdin is not supported" >expect &&
+ test_must_fail git config ${mode_set} --file - foo.bar baz 2>err &&
+ test_cmp expect err
+'
+
+done
+
test_done
diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh
index 40d3c42..53e5b29 100755
--- a/t/t1306-xdg-files.sh
+++ b/t/t1306-xdg-files.sh
@@ -7,6 +7,7 @@
test_description='Compatibility with $XDG_CONFIG_HOME/git/ files'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\''t' '
diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh
index 45a0492..ceeb7ac 100755
--- a/t/t1350-config-hooks-path.sh
+++ b/t/t1350-config-hooks-path.sh
@@ -2,6 +2,7 @@
test_description='Test the core.hooksPath configuration variable'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up a pre-commit hook in core.hooksPath' '
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index ec3443c..eb16918 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -4,6 +4,8 @@
#
test_description='Test git update-ref and basic ref logging'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
Z=$ZERO_OID
@@ -890,17 +892,23 @@
'
test_expect_success 'stdin verify succeeds for correct value' '
+ test-tool ref-store main for-each-reflog-ent $m >before &&
git rev-parse $m >expect &&
echo "verify $m $m" >stdin &&
git update-ref --stdin <stdin &&
git rev-parse $m >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent $m >after &&
+ test_cmp before after
'
test_expect_success 'stdin verify succeeds for missing reference' '
+ test-tool ref-store main for-each-reflog-ent $m >before &&
echo "verify refs/heads/missing $Z" >stdin &&
git update-ref --stdin <stdin &&
- test_must_fail git rev-parse --verify -q refs/heads/missing
+ test_must_fail git rev-parse --verify -q refs/heads/missing &&
+ test-tool ref-store main for-each-reflog-ent $m >after &&
+ test_cmp before after
'
test_expect_success 'stdin verify treats no value as missing' '
@@ -1354,6 +1362,7 @@
'
test_expect_success 'fails with duplicate ref update via symref' '
+ test_when_finished "git symbolic-ref -d refs/heads/symref2" &&
git branch target2 $A &&
git symbolic-ref refs/heads/symref2 refs/heads/target2 &&
cat >stdin <<-EOF &&
@@ -1641,4 +1650,423 @@
test_cmp expected actual
'
+format_command () {
+ if test "$1" = "-z"
+ then
+ shift
+ printf "$F" "$@"
+ else
+ echo "$@"
+ fi
+}
+
+for type in "" "-z"
+do
+
+ test_expect_success "stdin $type symref-verify fails without --no-deref" '
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-verify refs/heads/symref" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: symref-verify: cannot operate with deref mode" err
+ '
+
+ test_expect_success "stdin $type symref-verify fails with too many arguments" '
+ format_command $type "symref-verify refs/heads/symref" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ if test "$type" = "-z"
+ then
+ grep "fatal: unknown command: $a" err
+ else
+ grep "fatal: symref-verify refs/heads/symref: extra input: $a" err
+ fi
+ '
+
+ test_expect_success "stdin $type symref-verify succeeds for correct value" '
+ git symbolic-ref refs/heads/symref >expect &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
+ format_command $type "symref-verify refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
+ test_cmp before after
+ '
+
+ test_expect_success "stdin $type symref-verify fails with no value" '
+ git symbolic-ref refs/heads/symref >expect &&
+ format_command $type "symref-verify refs/heads/symref" "" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-verify succeeds for dangling reference" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref2" &&
+ test_must_fail git symbolic-ref refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
+ format_command $type "symref-verify refs/heads/symref2" "refs/heads/nonexistent" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-verify fails for missing reference" '
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
+ format_command $type "symref-verify refs/heads/missing" "refs/heads/unknown" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: cannot lock ref ${SQ}refs/heads/missing${SQ}: unable to resolve reference ${SQ}refs/heads/missing${SQ}" err &&
+ test_must_fail git rev-parse --verify -q refs/heads/missing &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
+ test_cmp before after
+ '
+
+ test_expect_success "stdin $type symref-verify fails for wrong value" '
+ git symbolic-ref refs/heads/symref >expect &&
+ format_command $type "symref-verify refs/heads/symref" "$b" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-verify fails for mistaken null value" '
+ git symbolic-ref refs/heads/symref >expect &&
+ format_command $type "symref-verify refs/heads/symref" "$Z" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-delete fails without --no-deref" '
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-delete refs/heads/symref" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: symref-delete: cannot operate with deref mode" err
+ '
+
+ test_expect_success "stdin $type symref-delete fails with no ref" '
+ format_command $type "symref-delete " >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: symref-delete: missing <ref>" err
+ '
+
+ test_expect_success "stdin $type symref-delete fails deleting regular ref" '
+ test_when_finished "git update-ref -d refs/heads/regularref" &&
+ git update-ref refs/heads/regularref $a &&
+ format_command $type "symref-delete refs/heads/regularref" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: expected symref with target ${SQ}$a${SQ}: but is a regular ref" err
+ '
+
+ test_expect_success "stdin $type symref-delete fails with too many arguments" '
+ format_command $type "symref-delete refs/heads/symref" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ if test "$type" = "-z"
+ then
+ grep "fatal: unknown command: $a" err
+ else
+ grep "fatal: symref-delete refs/heads/symref: extra input: $a" err
+ fi
+ '
+
+ test_expect_success "stdin $type symref-delete fails with wrong old value" '
+ format_command $type "symref-delete refs/heads/symref" "$m" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err &&
+ git symbolic-ref refs/heads/symref >expect &&
+ echo $a >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-delete works with right old value" '
+ format_command $type "symref-delete refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ test_must_fail git rev-parse --verify -q refs/heads/symref
+ '
+
+ test_expect_success "stdin $type symref-delete works with empty old value" '
+ git symbolic-ref refs/heads/symref $a >stdin &&
+ format_command $type "symref-delete refs/heads/symref" "" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ test_must_fail git rev-parse --verify -q $b
+ '
+
+ test_expect_success "stdin $type symref-delete succeeds for dangling reference" '
+ test_must_fail git symbolic-ref refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
+ format_command $type "symref-delete refs/heads/symref2" "refs/heads/nonexistent" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ test_must_fail git symbolic-ref -d refs/heads/symref2
+ '
+
+ test_expect_success "stdin $type symref-delete deletes regular ref without target" '
+ git update-ref refs/heads/regularref $a &&
+ format_command $type "symref-delete refs/heads/regularref" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-create fails with too many arguments" '
+ format_command $type "symref-create refs/heads/symref" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ if test "$type" = "-z"
+ then
+ grep "fatal: unknown command: $a" err
+ else
+ grep "fatal: symref-create refs/heads/symref: extra input: $a" err
+ fi
+ '
+
+ test_expect_success "stdin $type symref-create fails with no target" '
+ format_command $type "symref-create refs/heads/symref" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-create fails with empty target" '
+ format_command $type "symref-create refs/heads/symref" "" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin
+ '
+
+ test_expect_success "stdin $type symref-create works" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-create refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >expect &&
+ echo $a >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-create works with --no-deref" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-create refs/heads/symref" "$a" &&
+ git update-ref --stdin $type <stdin 2>err
+ '
+
+ test_expect_success "stdin $type create dangling symref ref works" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-create refs/heads/symref" "refs/heads/unkown" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >expect &&
+ echo refs/heads/unkown >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-create does not create reflogs by default" '
+ test_when_finished "git symbolic-ref -d refs/symref" &&
+ format_command $type "symref-create refs/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/symref >expect &&
+ echo $a >actual &&
+ test_cmp expect actual &&
+ test_must_fail git reflog exists refs/symref
+ '
+
+ test_expect_success "stdin $type symref-create reflogs with --create-reflog" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-create refs/heads/symref" "$a" >stdin &&
+ git update-ref --create-reflog --stdin $type --no-deref <stdin &&
+ git symbolic-ref refs/heads/symref >expect &&
+ echo $a >actual &&
+ test_cmp expect actual &&
+ git reflog exists refs/heads/symref
+ '
+
+ test_expect_success "stdin $type symref-update fails with too many arguments" '
+ format_command $type "symref-update refs/heads/symref" "$a" "ref" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ if test "$type" = "-z"
+ then
+ grep "fatal: unknown command: $a" err
+ else
+ grep "fatal: symref-update refs/heads/symref: extra input: $a" err
+ fi
+ '
+
+ test_expect_success "stdin $type symref-update fails with wrong old value argument" '
+ format_command $type "symref-update refs/heads/symref" "$a" "foo" "$a" "$a" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: symref-update refs/heads/symref: invalid arg ${SQ}foo${SQ} for old value" err
+ '
+
+ test_expect_success "stdin $type symref-update creates with zero old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update creates with no old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update creates dangling" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_must_fail git rev-parse refs/heads/nonexistent &&
+ format_command $type "symref-update refs/heads/symref" "refs/heads/nonexistent" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo refs/heads/nonexistent >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update fails with wrong old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-update refs/heads/symref" "$m" "ref" "$b" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
+ grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected $b" err &&
+ test_must_fail git rev-parse --verify -q $c
+ '
+
+ test_expect_success "stdin $type symref-update updates dangling ref" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_must_fail git rev-parse refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
+ format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update updates dangling ref with old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_must_fail git rev-parse refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
+ format_command $type "symref-update refs/heads/symref" "$a" "ref" "refs/heads/nonexistent" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update fails update dangling ref with wrong old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_must_fail git rev-parse refs/heads/nonexistent &&
+ git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
+ format_command $type "symref-update refs/heads/symref" "$a" "ref" "refs/heads/wrongref" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin &&
+ echo refs/heads/nonexistent >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update works with right old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-update refs/heads/symref" "$m" "ref" "$a" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $m >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update works with no old value" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-update refs/heads/symref" "$m" >stdin &&
+ git update-ref --stdin $type --no-deref <stdin &&
+ echo $m >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update fails with empty old ref-target" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref $a &&
+ format_command $type "symref-update refs/heads/symref" "$m" "ref" "" >stdin &&
+ test_must_fail git update-ref --stdin $type --no-deref <stdin &&
+ echo $a >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update creates (with deref)" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
+ git update-ref --stdin $type <stdin &&
+ echo $a >expect &&
+ git symbolic-ref --no-recurse refs/heads/symref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual &&
+ grep "$Z $(git rev-parse $a)" actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref with correct old-oid" '
+ test_when_finished "git symbolic-ref -d --no-recurse refs/heads/regularref" &&
+ git update-ref --no-deref refs/heads/regularref $a &&
+ format_command $type "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse $a)" >stdin &&
+ git update-ref --stdin $type <stdin &&
+ echo $a >expect &&
+ git symbolic-ref --no-recurse refs/heads/regularref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual &&
+ grep "$(git rev-parse $a) $(git rev-parse $a)" actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref fails with wrong old-oid" '
+ test_when_finished "git update-ref -d refs/heads/regularref" &&
+ git update-ref --no-deref refs/heads/regularref $a &&
+ format_command $type "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse refs/heads/target2)" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: is at $(git rev-parse $a) but expected $(git rev-parse refs/heads/target2)" err &&
+ echo $(git rev-parse $a) >expect &&
+ git rev-parse refs/heads/regularref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref fails with invalid old-oid" '
+ test_when_finished "git update-ref -d refs/heads/regularref" &&
+ git update-ref --no-deref refs/heads/regularref $a &&
+ format_command $type "symref-update refs/heads/regularref" "$a" "oid" "not-a-ref-oid" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: symref-update refs/heads/regularref: invalid oid: not-a-ref-oid" err &&
+ echo $(git rev-parse $a) >expect &&
+ git rev-parse refs/heads/regularref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update existing symref with zero old-oid" '
+ test_when_finished "git symbolic-ref -d --no-recurse refs/heads/symref" &&
+ git symbolic-ref refs/heads/symref refs/heads/target2 &&
+ format_command $type "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin &&
+ test_must_fail git update-ref --stdin $type <stdin 2>err &&
+ grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}: reference already exists" err &&
+ echo refs/heads/target2 >expect &&
+ git symbolic-ref refs/heads/symref >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref (with deref)" '
+ test_when_finished "git symbolic-ref -d refs/heads/symref" &&
+ test_when_finished "git update-ref -d --no-deref refs/heads/symref2" &&
+ git update-ref refs/heads/symref2 $a &&
+ git symbolic-ref --no-recurse refs/heads/symref refs/heads/symref2 &&
+ format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
+ git update-ref $type --stdin <stdin &&
+ echo $a >expect &&
+ git symbolic-ref --no-recurse refs/heads/symref2 >actual &&
+ test_cmp expect actual &&
+ echo refs/heads/symref2 >expect &&
+ git symbolic-ref --no-recurse refs/heads/symref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual &&
+ grep "$(git rev-parse $a) $(git rev-parse $a)" actual
+ '
+
+ test_expect_success "stdin $type symref-update regular ref to symref" '
+ test_when_finished "git symbolic-ref -d --no-recurse refs/heads/regularref" &&
+ git update-ref --no-deref refs/heads/regularref $a &&
+ format_command $type "symref-update refs/heads/regularref" "$a" >stdin &&
+ git update-ref $type --stdin <stdin &&
+ echo $a >expect &&
+ git symbolic-ref --no-recurse refs/heads/regularref >actual &&
+ test_cmp expect actual &&
+ test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual &&
+ grep "$(git rev-parse $a) $(git rev-parse $a)" actual
+ '
+
+done
+
test_done
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 33fb7a3..403f6b8 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -121,13 +121,13 @@
'
-test_expect_success 'show-ref --heads, --tags, --head, pattern' '
+test_expect_success 'show-ref --branches, --tags, --head, pattern' '
for branch in B main side
do
echo $(git rev-parse refs/heads/$branch) refs/heads/$branch || return 1
- done >expect.heads &&
- git show-ref --heads >actual &&
- test_cmp expect.heads actual &&
+ done >expect.branches &&
+ git show-ref --branches >actual &&
+ test_cmp expect.branches actual &&
for tag in A B C
do
@@ -136,15 +136,15 @@
git show-ref --tags >actual &&
test_cmp expect.tags actual &&
- cat expect.heads expect.tags >expect &&
- git show-ref --heads --tags >actual &&
+ cat expect.branches expect.tags >expect &&
+ git show-ref --branches --tags >actual &&
test_cmp expect actual &&
{
echo $(git rev-parse HEAD) HEAD &&
- cat expect.heads expect.tags
+ cat expect.branches expect.tags
} >expect &&
- git show-ref --heads --tags --head >actual &&
+ git show-ref --branches --tags --head >actual &&
test_cmp expect actual &&
{
@@ -165,6 +165,14 @@
test_cmp expect actual
'
+test_expect_success 'show-ref --heads is deprecated and hidden' '
+ test_expect_code 129 git show-ref -h >short-help &&
+ test_grep ! -e --heads short-help &&
+ git show-ref --heads >actual 2>warning &&
+ test_grep ! deprecated warning &&
+ test_cmp expect.branches actual
+'
+
test_expect_success 'show-ref --verify HEAD' '
echo $(git rev-parse HEAD) HEAD >expect &&
git show-ref --verify HEAD >actual &&
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index 98e9158..67ebd81 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -100,7 +100,7 @@
printf "%s\n" "delete $delname" "create $addname $D"
fi >commands &&
test_must_fail git update-ref --stdin <commands 2>output.err &&
- grep "fatal:\( cannot lock ref $SQ$addname$SQ:\)\? $SQ$delref$SQ exists; cannot create $SQ$addref$SQ" output.err &&
+ grep -E "fatal:( cannot lock ref $SQ$addname$SQ:)? $SQ$delref$SQ exists; cannot create $SQ$addref$SQ" output.err &&
printf "%s\n" "$C $delref" >expected-refs &&
git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs &&
test_cmp expected-refs actual-refs
diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh
index 2092488..5a812ca 100755
--- a/t/t1416-ref-transaction-hooks.sh
+++ b/t/t1416-ref-transaction-hooks.sh
@@ -134,4 +134,81 @@
test_cmp expect target-repo.git/actual
'
+test_expect_success 'hook captures git-symbolic-ref updates' '
+ test_when_finished "rm actual" &&
+
+ test_hook reference-transaction <<-\EOF &&
+ echo "$*" >>actual
+ while read -r line
+ do
+ printf "%s\n" "$line"
+ done >>actual
+ EOF
+
+ git symbolic-ref refs/heads/symref refs/heads/main &&
+
+ cat >expect <<-EOF &&
+ prepared
+ $ZERO_OID ref:refs/heads/main refs/heads/symref
+ committed
+ $ZERO_OID ref:refs/heads/main refs/heads/symref
+ EOF
+
+ test_cmp expect actual
+'
+
+test_expect_success 'hook gets all queued symref updates' '
+ test_when_finished "rm actual" &&
+
+ git update-ref refs/heads/branch $POST_OID &&
+ git symbolic-ref refs/heads/symref refs/heads/main &&
+ git symbolic-ref refs/heads/symrefd refs/heads/main &&
+ git symbolic-ref refs/heads/symrefu refs/heads/main &&
+
+ test_hook reference-transaction <<-\EOF &&
+ echo "$*" >>actual
+ while read -r line
+ do
+ printf "%s\n" "$line"
+ done >>actual
+ EOF
+
+ # In the files backend, "delete" also triggers an additional transaction
+ # update on the packed-refs backend, which constitutes additional reflog
+ # entries.
+ if test_have_prereq REFFILES
+ then
+ cat >expect <<-EOF
+ aborted
+ $ZERO_OID $ZERO_OID refs/heads/symrefd
+ EOF
+ else
+ >expect
+ fi &&
+
+ cat >>expect <<-EOF &&
+ prepared
+ ref:refs/heads/main $ZERO_OID refs/heads/symref
+ ref:refs/heads/main $ZERO_OID refs/heads/symrefd
+ $ZERO_OID ref:refs/heads/main refs/heads/symrefc
+ ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
+ committed
+ ref:refs/heads/main $ZERO_OID refs/heads/symref
+ ref:refs/heads/main $ZERO_OID refs/heads/symrefd
+ $ZERO_OID ref:refs/heads/main refs/heads/symrefc
+ ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
+ EOF
+
+ git update-ref --no-deref --stdin <<-EOF &&
+ start
+ symref-verify refs/heads/symref refs/heads/main
+ symref-delete refs/heads/symrefd refs/heads/main
+ symref-create refs/heads/symrefc refs/heads/main
+ symref-update refs/heads/symrefu refs/heads/branch ref refs/heads/main
+ prepare
+ commit
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh
new file mode 100755
index 0000000..f7c0783
--- /dev/null
+++ b/t/t1460-refs-migrate.sh
@@ -0,0 +1,243 @@
+#!/bin/sh
+
+test_description='migration of ref storage backends'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_migration () {
+ git -C "$1" for-each-ref --include-root-refs \
+ --format='%(refname) %(objectname) %(symref)' >expect &&
+ git -C "$1" refs migrate --ref-format="$2" &&
+ git -C "$1" for-each-ref --include-root-refs \
+ --format='%(refname) %(objectname) %(symref)' >actual &&
+ test_cmp expect actual &&
+
+ git -C "$1" rev-parse --show-ref-format >actual &&
+ echo "$2" >expect &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+ rm -rf .git &&
+ # The migration does not yet support reflogs.
+ git config --global core.logAllRefUpdates false
+'
+
+test_expect_success "superfluous arguments" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate foo 2>err &&
+ cat >expect <<-EOF &&
+ usage: too many arguments
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success "missing ref storage format" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate 2>err &&
+ cat >expect <<-EOF &&
+ usage: missing --ref-format=<format>
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success "unknown ref storage format" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=unknown 2>err &&
+ cat >expect <<-EOF &&
+ error: unknown ref storage format ${SQ}unknown${SQ}
+ EOF
+ test_cmp expect err
+'
+
+ref_formats="files reftable"
+for from_format in $ref_formats
+do
+ for to_format in $ref_formats
+ do
+ if test "$from_format" = "$to_format"
+ then
+ continue
+ fi
+
+ test_expect_success "$from_format: migration to same format fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$from_format 2>err &&
+ cat >expect <<-EOF &&
+ error: repository already uses ${SQ}$from_format${SQ} format
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: migration with reflog fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_config -C repo core.logAllRefUpdates true &&
+ test_commit -C repo logged &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$to_format 2>err &&
+ cat >expect <<-EOF &&
+ error: migrating reflogs is not supported yet
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: migration with worktree fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ git -C repo worktree add wt &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$to_format 2>err &&
+ cat >expect <<-EOF &&
+ error: migrating repositories with worktrees is not supported yet
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: unborn HEAD" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: single ref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: bare repository" '
+ test_when_finished "rm -rf repo repo.git" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git clone --ref-format=$from_format --mirror repo repo.git &&
+ test_migration repo.git "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: dangling symref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo symbolic-ref BROKEN_HEAD refs/heads/nonexistent &&
+ test_migration repo "$to_format" &&
+ echo refs/heads/nonexistent >expect &&
+ git -C repo symbolic-ref BROKEN_HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$from_format -> $to_format: broken ref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ test-tool -C repo ref-store main update-ref "" refs/heads/broken \
+ "$(test_oid 001)" "$ZERO_OID" REF_SKIP_CREATE_REFLOG,REF_SKIP_OID_VERIFICATION &&
+ test_migration repo "$to_format" &&
+ test_oid 001 >expect &&
+ git -C repo rev-parse refs/heads/broken >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$from_format -> $to_format: pseudo-refs" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo update-ref FOO_HEAD HEAD &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: special refs are left alone" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo rev-parse HEAD >repo/.git/MERGE_HEAD &&
+ git -C repo rev-parse MERGE_HEAD &&
+ test_migration repo "$to_format" &&
+ test_path_is_file repo/.git/MERGE_HEAD
+ '
+
+ test_expect_success "$from_format -> $to_format: a bunch of refs" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+
+ test_commit -C repo initial &&
+ cat >input <<-EOF &&
+ create FOO_HEAD HEAD
+ create refs/heads/branch-1 HEAD
+ create refs/heads/branch-2 HEAD
+ create refs/heads/branch-3 HEAD
+ create refs/heads/branch-4 HEAD
+ create refs/tags/tag-1 HEAD
+ create refs/tags/tag-2 HEAD
+ EOF
+ git -C repo update-ref --stdin <input &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: dry-run migration does not modify repository" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo refs migrate --dry-run \
+ --ref-format=$to_format >output &&
+ grep "Finished dry-run migration of refs" output &&
+ test_path_is_dir repo/.git/ref_migration.* &&
+ echo $from_format >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+ '
+ done
+done
+
+test_expect_success 'migrating from files format deletes backend files' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=files repo &&
+ test_commit -C repo first &&
+ git -C repo pack-refs --all &&
+ test_commit -C repo second &&
+ git -C repo update-ref ORIG_HEAD HEAD &&
+ git -C repo rev-parse HEAD >repo/.git/FETCH_HEAD &&
+
+ test_path_is_file repo/.git/HEAD &&
+ test_path_is_file repo/.git/ORIG_HEAD &&
+ test_path_is_file repo/.git/refs/heads/main &&
+ test_path_is_file repo/.git/packed-refs &&
+
+ test_migration repo reftable &&
+
+ echo "ref: refs/heads/.invalid" >expect &&
+ test_cmp expect repo/.git/HEAD &&
+ echo "this repository uses the reftable format" >expect &&
+ test_cmp expect repo/.git/refs/heads &&
+ test_path_is_file repo/.git/FETCH_HEAD &&
+ test_path_is_missing repo/.git/ORIG_HEAD &&
+ test_path_is_missing repo/.git/refs/heads/main &&
+ test_path_is_missing repo/.git/logs &&
+ test_path_is_missing repo/.git/packed-refs
+'
+
+test_expect_success 'migrating from reftable format deletes backend files' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=reftable repo &&
+ test_commit -C repo first &&
+
+ test_path_is_dir repo/.git/reftable &&
+ test_migration repo files &&
+
+ test_path_is_missing repo/.git/reftable &&
+ echo "ref: refs/heads/main" >expect &&
+ test_cmp expect repo/.git/HEAD &&
+ test_path_is_file repo/.git/refs/heads/main
+'
+
+test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index a669e59..30c3191 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -304,4 +304,10 @@
test_cmp expect actual
'
+test_expect_success '--short= truncates to the actual hash length' '
+ git rev-parse HEAD >expect &&
+ git rev-parse --short=100 HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh
new file mode 100755
index 0000000..990a036
--- /dev/null
+++ b/t/t1517-outside-repo.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+
+test_description='check random commands outside repo'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'set up a non-repo directory and test file' '
+ GIT_CEILING_DIRECTORIES=$(pwd) &&
+ export GIT_CEILING_DIRECTORIES &&
+ mkdir non-repo &&
+ (
+ cd non-repo &&
+ # confirm that git does not find a repo
+ test_must_fail git rev-parse --git-dir
+ ) &&
+ test_write_lines one two three four >nums &&
+ git add nums &&
+ cp nums nums.old &&
+ test_write_lines five >>nums &&
+ git diff >sample.patch
+'
+
+test_expect_success 'compute a patch-id outside repository (uses SHA-1)' '
+ nongit env GIT_DEFAULT_HASH=sha1 \
+ git patch-id <sample.patch >patch-id.expect &&
+ nongit \
+ git patch-id <sample.patch >patch-id.actual &&
+ test_cmp patch-id.expect patch-id.actual
+'
+
+test_expect_success 'hash-object outside repository (uses SHA-1)' '
+ nongit env GIT_DEFAULT_HASH=sha1 \
+ git hash-object --stdin <sample.patch >hash.expect &&
+ nongit \
+ git hash-object --stdin <sample.patch >hash.actual &&
+ test_cmp hash.expect hash.actual
+'
+
+test_expect_success 'apply a patch outside repository' '
+ (
+ cd non-repo &&
+ cp ../nums.old nums &&
+ git apply ../sample.patch
+ ) &&
+ test_cmp nums non-repo/nums
+'
+
+test_expect_success 'grep outside repository' '
+ git grep --cached two >expect &&
+ (
+ cd non-repo &&
+ cp ../nums.old nums &&
+ git grep --no-index two >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'imap-send outside repository' '
+ test_config_global imap.host imaps://localhost &&
+ test_config_global imap.folder Drafts &&
+
+ echo nothing to send >expect &&
+ test_must_fail git imap-send -v </dev/null 2>actual &&
+ test_cmp expect actual &&
+
+ (
+ cd non-repo &&
+ test_must_fail git imap-send -v </dev/null 2>../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-ref-format outside repository' '
+ git check-ref-format --branch refs/heads/xyzzy >expect &&
+ nongit git check-ref-format --branch refs/heads/xyzzy >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff outside repository' '
+ echo one >one &&
+ echo two >two &&
+ test_must_fail git diff --no-index one two >expect.raw &&
+ (
+ cd non-repo &&
+ cp ../one . &&
+ cp ../two . &&
+ test_must_fail git diff one two >../actual.raw
+ ) &&
+ # outside repository diff falls back to SHA-1 but
+ # GIT_DEFAULT_HASH may be set to sha256 on the in-repo side.
+ sed -e "/^index /d" expect.raw >expect &&
+ sed -e "/^index /d" actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stripspace outside repository' '
+ nongit git stripspace -s </dev/null
+'
+
+test_expect_success 'remote-http outside repository' '
+ test_must_fail git remote-http 2>actual &&
+ test_grep "^error: remote-curl" actual &&
+ (
+ cd non-repo &&
+ test_must_fail git remote-http 2>../actual
+ ) &&
+ test_grep "^error: remote-curl" actual
+'
+
+test_done
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index a7b7263..ac4a5b2 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -527,7 +527,7 @@
# ... and, for backwards compatibility, in the current GIT_DIR
# as well.
- mv -v ./reading-alternate-location/.git/sharedindex.* .git &&
+ mv ./reading-alternate-location/.git/sharedindex.* .git &&
GIT_INDEX_FILE=./reading-alternate-location/.git/index \
git ls-files --cached >actual &&
test_cmp expect actual
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index b2bdd1f..3c1d663 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -2,6 +2,7 @@
test_description='checkout can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index a3b1449..2caada3 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -4,6 +4,7 @@
Ensures that checkout on an unborn branch does what the user expects'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Is the current branch "refs/heads/$1"?
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
index c91c4db..77b2346 100755
--- a/t/t2060-switch.sh
+++ b/t/t2060-switch.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh
index 11018f3..1d7f605 100755
--- a/t/t2405-worktree-submodule.sh
+++ b/t/t2405-worktree-submodule.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
base_path=$(pwd -P)
diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh
index 61771ee..f04bdc8 100755
--- a/t/t3007-ls-files-recurse-submodules.sh
+++ b/t/t3007-ls-files-recurse-submodules.sh
@@ -6,6 +6,7 @@
submodules.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup directory structure and submodules' '
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 758963b..e627f08 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git branch display tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 7b05bf3..a767c35 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -545,6 +545,20 @@
'
done
+test_expect_success "--range-diff implies --cover-letter for multi-patch series" '
+ test_when_finished "rm -f v2-000?-*" &&
+ git format-patch -v2 --range-diff=topic main..unmodified &&
+ test_grep "^Range-diff against v1:$" v2-0000-cover-letter.patch
+'
+
+test_expect_success "explicit --no-cover-letter defeats implied --cover-letter" '
+ test_when_finished "rm -f v2-000?-*" &&
+ test_must_fail git format-patch --no-cover-letter \
+ -v2 --range-diff=topic main..unmodified &&
+ test_must_fail git -c format.coverLetter=no format-patch \
+ -v2 --range-diff=topic main..unmodified
+'
+
test_expect_success 'format-patch --range-diff as commentary' '
git format-patch --range-diff=HEAD~1 HEAD~1 >actual &&
test_when_finished "rm 0001-*" &&
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index fcc40d6..22452ff 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
index ba069dc..94ea88e 100755
--- a/t/t3426-rebase-submodule.sh
+++ b/t/t3426-rebase-submodule.sh
@@ -2,6 +2,7 @@
test_description='rebase can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index f22d1dd..9387a22 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
index 8bfe3ed..e178968 100755
--- a/t/t3513-revert-submodule.sh
+++ b/t/t3513-revert-submodule.sh
@@ -2,6 +2,7 @@
test_description='revert can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 98259e2..31ac31d 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -8,6 +8,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Setup some files to be removed, some with funny characters
diff --git a/t/t3602-rm-sparse-checkout.sh b/t/t3602-rm-sparse-checkout.sh
index 08580fd..fcdefba 100755
--- a/t/t3602-rm-sparse-checkout.sh
+++ b/t/t3602-rm-sparse-checkout.sh
@@ -2,6 +2,7 @@
test_description='git rm in sparse checked out working trees'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' "
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 04d8333..5d78868 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -8,6 +8,8 @@
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
+SP=" "
+
diff_cmp () {
for x
do
@@ -41,19 +43,17 @@
)
}
-test_expect_success 'warn about add.interactive.useBuiltin' '
- cat >expect <<-\EOF &&
- warning: the add.interactive.useBuiltin setting has been removed!
- See its entry in '\''git help config'\'' for details.
- No changes.
+test_expect_success 'unknown command' '
+ test_when_finished "git reset --hard; rm -f command" &&
+ echo W >command &&
+ git add -N command &&
+ git diff command >expect &&
+ cat >>expect <<-EOF &&
+ (1/1) Stage addition [y,n,q,a,d,e,p,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)
+ (1/1) Stage addition [y,n,q,a,d,e,p,?]?$SP
EOF
-
- for v in = =true =false
- do
- git -c "add.interactive.useBuiltin$v" add -p >out 2>actual &&
- test_must_be_empty out &&
- test_cmp expect actual || return 1
- done
+ git add -p -- command <command >actual 2>&1 &&
+ test_cmp expect actual
'
test_expect_success 'setup (initial)' '
@@ -145,6 +145,14 @@
grep "unchanged *+3/-0 file" output
'
+test_expect_success 'reject multi-key input' '
+ saved=$(git hash-object -w file) &&
+ test_when_finished "git cat-file blob $saved >file" &&
+ echo an extra line >>file &&
+ test_write_lines aa | git add -p >actual &&
+ test_grep "is expected, got ${SQ}aa${SQ}" actual
+'
+
test_expect_success 'setup expected' '
cat >expected <<-\EOF
EOF
@@ -232,7 +240,6 @@
'
test_expect_success 'setup patch' '
- SP=" " &&
NULL="" &&
cat >patch <<-EOF
@@ -1,4 +1,4 @@
@@ -335,13 +342,13 @@
test_expect_success 'correct message when there is nothing to do' '
git reset --hard &&
- git add -p 2>err &&
- test_grep "No changes" err &&
+ git add -p >out &&
+ test_grep "No changes" out &&
printf "\\0123" >binary &&
git add binary &&
printf "\\0abc" >binary &&
- git add -p 2>err &&
- test_grep "Only binary files changed" err
+ git add -p >out &&
+ test_grep "Only binary files changed" out
'
test_expect_success 'setup again' '
@@ -512,7 +519,7 @@
test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test
'
-test_expect_success 'goto hunk' '
+test_expect_success 'goto hunk 1 with "g 1"' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1: -1,2 +1,3 +15
@@ -528,7 +535,20 @@
test_cmp expect actual.trimmed
'
-test_expect_success 'navigate to hunk via regex' '
+test_expect_success 'goto hunk 1 with "g1"' '
+ test_when_finished "git reset" &&
+ tr _ " " >expect <<-EOF &&
+ _10
+ +15
+ _20
+ (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
+ EOF
+ test_write_lines s y g1 | git add -p >actual &&
+ tail -n 4 <actual >actual.trimmed &&
+ test_cmp expect actual.trimmed
+'
+
+test_expect_success 'navigate to hunk via regex /pattern' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
@@ -542,6 +562,19 @@
test_cmp expect actual.trimmed
'
+test_expect_success 'navigate to hunk via regex / pattern' '
+ test_when_finished "git reset" &&
+ tr _ " " >expect <<-EOF &&
+ _10
+ +15
+ _20
+ (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
+ EOF
+ test_write_lines s y / 1,2 | git add -p >actual &&
+ tail -n 4 <actual >actual.trimmed &&
+ test_cmp expect actual.trimmed
+'
+
test_expect_success 'split hunk "add -p (edit)"' '
# Split, say Edit and do nothing. Then:
#
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f27d09c..db7b403 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -5,6 +5,7 @@
test_description='commit and log output encodings'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
compare_with () {
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4b37f78..5f0b9af 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -8,6 +8,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_encoding () {
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
index 0f7348e..0f61f01 100755
--- a/t/t3906-stash-submodule.sh
+++ b/t/t3906-stash-submodule.sh
@@ -2,6 +2,7 @@
test_description='stash can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 49c042a..cd1931d 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -3,9 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='Test rename detection in diff engine.
+test_description='Test rename detection in diff engine.'
-'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 90fe6d0..884f83f 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -820,8 +820,8 @@
'
test_expect_success 'format-patch notes output control' '
+ test_when_finished "git notes remove HEAD || :" &&
git notes add -m "notes config message" HEAD &&
- test_when_finished git notes remove HEAD &&
git format-patch -1 --stdout >out &&
! grep "notes config message" out &&
@@ -848,10 +848,10 @@
'
test_expect_success 'format-patch with multiple notes refs' '
+ test_when_finished "git notes --ref note1 remove HEAD;
+ git notes --ref note2 remove HEAD || :" &&
git notes --ref note1 add -m "this is note 1" HEAD &&
- test_when_finished git notes --ref note1 remove HEAD &&
git notes --ref note2 add -m "this is note 2" HEAD &&
- test_when_finished git notes --ref note2 remove HEAD &&
git format-patch -1 --stdout >out &&
! grep "this is note 1" out &&
@@ -892,10 +892,10 @@
test_expect_success 'format-patch with multiple notes refs in config' '
test_when_finished "test_unconfig format.notes" &&
+ test_when_finished "git notes --ref note1 remove HEAD;
+ git notes --ref note2 remove HEAD || :" &&
git notes --ref note1 add -m "this is note 1" HEAD &&
- test_when_finished git notes --ref note1 remove HEAD &&
git notes --ref note2 add -m "this is note 2" HEAD &&
- test_when_finished git notes --ref note2 remove HEAD &&
git config format.notes note1 &&
git format-patch -1 --stdout >out &&
@@ -1368,12 +1368,38 @@
test_cmp expect actual
'
-test_expect_success '--rfc' '
+test_expect_success '--rfc and --no-rfc' '
cat >expect <<-\EOF &&
Subject: [RFC PATCH 1/1] header with . in it
EOF
git format-patch -n -1 --stdout --rfc >patch &&
grep "^Subject:" patch >actual &&
+ test_cmp expect actual &&
+ git format-patch -n -1 --stdout --rfc --no-rfc >patch &&
+ sed -e "s/RFC //" expect >expect-raw &&
+ grep "^Subject:" patch >actual &&
+ test_cmp expect-raw actual
+'
+
+test_expect_success '--rfc=WIP and --rfc=' '
+ cat >expect <<-\EOF &&
+ Subject: [WIP PATCH 1/1] header with . in it
+ EOF
+ git format-patch -n -1 --stdout --rfc=WIP >patch &&
+ grep "^Subject:" patch >actual &&
+ test_cmp expect actual &&
+ git format-patch -n -1 --stdout --rfc --rfc= >patch &&
+ sed -e "s/WIP //" expect >expect-raw &&
+ grep "^Subject:" patch >actual &&
+ test_cmp expect-raw actual
+'
+
+test_expect_success '--rfc=-(WIP) appends' '
+ cat >expect <<-\EOF &&
+ Subject: [PATCH (WIP) 1/1] header with . in it
+ EOF
+ git format-patch -n -1 --stdout --rfc="-(WIP)" >patch &&
+ grep "^Subject:" patch >actual &&
test_cmp expect actual
'
@@ -2456,16 +2482,55 @@
'
test_expect_success 'interdiff: solo-patch' '
- cat >expect <<-\EOF &&
- +fleep
-
- EOF
git format-patch --interdiff=boop~2 -1 boop &&
- test_grep "^Interdiff:$" 0001-fleep.patch &&
- sed "1,/^ @@ /d; /^$/q" 0001-fleep.patch >actual &&
+
+ # remove up to the last "patch" output line,
+ # and remove everything below the signature mark.
+ sed -e "1,/^+fleep\$/d" -e "/^-- /,\$d" 0001-fleep.patch >actual &&
+
+ # fabricate Interdiff output.
+ git diff boop~2 boop >inter &&
+ {
+ echo &&
+ echo "Interdiff:" &&
+ sed -e "s/^/ /" inter
+ } >expect &&
test_cmp expect actual
'
+test_expect_success 'range-diff: solo-patch' '
+ git format-patch --creation-factor=999 \
+ --range-diff=boop~2..boop~1 -1 boop &&
+
+ # remove up to the last "patch" output line,
+ # and remove everything below the signature mark.
+ sed -e "1,/^+fleep\$/d" -e "/^-- /,\$d" 0001-fleep.patch >actual &&
+
+ # fabricate range-diff output.
+ {
+ echo &&
+ echo "Range-diff:" &&
+ git range-diff --creation-factor=999 \
+ boop~2..boop~1 boop~1..boop
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'interdiff: multi-patch, implicit --cover-letter' '
+ test_when_finished "rm -f v23-0*.patch" &&
+ git format-patch --interdiff=boop~2 -2 -v23 &&
+ test_grep "^Interdiff against v22:$" v23-0000-cover-letter.patch &&
+ test_cmp expect actual
+'
+
+test_expect_success 'interdiff: explicit --no-cover-letter defeats implied --cover-letter' '
+ test_when_finished "rm -f v23-0*.patch" &&
+ test_must_fail git format-patch --no-cover-letter \
+ --interdiff=boop~2 -2 -v23 &&
+ test_must_fail git -c format.coverLetter=no format-patch \
+ --interdiff=boop~2 -2 -v23
+'
+
test_expect_success 'format-patch does not respect diff.noprefix' '
git -c diff.noprefix format-patch -1 --stdout >actual &&
grep "^--- a/blorp" actual
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index b443626..851cfe4 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -1184,6 +1184,15 @@
test_cmp expected actual
'
+test_expect_success '--color-moved with --no-ext-diff' '
+ test_config color.diff.oldMoved "yellow" &&
+ test_config color.diff.newMoved "blue" &&
+ args="--color --color-moved=zebra --no-renames HEAD" &&
+ git diff $args >expect &&
+ git -c diff.external=echo diff --no-ext-diff $args >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'detect malicious moved code, inside file' '
test_config color.diff.oldMoved "normal red" &&
test_config color.diff.newMoved "normal green" &&
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index fdd865f..3baa52a 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -172,6 +172,72 @@
grep Binary out
'
+check_external_diff () {
+ expect_code=$1
+ expect_out=$2
+ expect_err=$3
+ command_code=$4
+ trust_exit_code=$5
+ shift 5
+ options="$@"
+
+ command="echo output; exit $command_code;"
+ desc="external diff '$command' with trustExitCode=$trust_exit_code"
+ with_options="${options:+ with }$options"
+
+ test_expect_success "$desc via attribute$with_options" "
+ test_config diff.foo.command \"$command\" &&
+ test_config diff.foo.trustExitCode $trust_exit_code &&
+ echo \"file diff=foo\" >.gitattributes &&
+ test_expect_code $expect_code git diff $options >out 2>err &&
+ test_cmp $expect_out out &&
+ test_cmp $expect_err err
+ "
+
+ test_expect_success "$desc via diff.external$with_options" "
+ test_config diff.external \"$command\" &&
+ test_config diff.trustExitCode $trust_exit_code &&
+ >.gitattributes &&
+ test_expect_code $expect_code git diff $options >out 2>err &&
+ test_cmp $expect_out out &&
+ test_cmp $expect_err err
+ "
+
+ test_expect_success "$desc via GIT_EXTERNAL_DIFF$with_options" "
+ >.gitattributes &&
+ test_expect_code $expect_code env \
+ GIT_EXTERNAL_DIFF=\"$command\" \
+ GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE=$trust_exit_code \
+ git diff $options >out 2>err &&
+ test_cmp $expect_out out &&
+ test_cmp $expect_err err
+ "
+}
+
+test_expect_success 'setup output files' '
+ : >empty &&
+ echo output >output &&
+ echo "fatal: external diff died, stopping at file" >error
+'
+
+check_external_diff 0 output empty 0 off
+check_external_diff 128 output error 1 off
+check_external_diff 0 output empty 0 on
+check_external_diff 0 output empty 1 on
+check_external_diff 128 output error 2 on
+
+check_external_diff 1 output empty 0 off --exit-code
+check_external_diff 128 output error 1 off --exit-code
+check_external_diff 0 output empty 0 on --exit-code
+check_external_diff 1 output empty 1 on --exit-code
+check_external_diff 128 output error 2 on --exit-code
+
+check_external_diff 1 empty empty 0 off --quiet
+check_external_diff 1 empty empty 1 off --quiet # we don't even call the program
+check_external_diff 0 empty empty 0 on --quiet
+check_external_diff 1 empty empty 1 on --quiet
+check_external_diff 128 empty error 2 on --quiet
+
echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
test_expect_success 'force diff with "diff"' '
diff --git a/t/t4026-color.sh b/t/t4026-color.sh
index cc3f60d..b05f2a9 100755
--- a/t/t4026-color.sh
+++ b/t/t4026-color.sh
@@ -96,8 +96,8 @@
color "254 bold 255" "[1;38;5;254;48;5;255m"
'
-test_expect_success '24-bit colors' '
- color "#ff00ff black" "[38;2;255;0;255;40m"
+test_expect_success 'RGB colors' '
+ color "#ff00ff #0f0" "[38;2;255;0;255;48;2;0;255;0m"
'
test_expect_success '"default" foreground' '
@@ -112,7 +112,7 @@
color "default default no-reverse bold" "[1;27;39;49m"
'
-test_expect_success '"normal" yields no color at all"' '
+test_expect_success '"normal" yields no color at all' '
color "normal black" "[40m"
'
@@ -140,6 +140,26 @@
invalid_color "dimX"
'
+test_expect_success 'non-hex character in RGB color' '
+ invalid_color "#x23456" &&
+ invalid_color "#1x3456" &&
+ invalid_color "#12x456" &&
+ invalid_color "#123x56" &&
+ invalid_color "#1234x6" &&
+ invalid_color "#12345x" &&
+ invalid_color "#x23" &&
+ invalid_color "#1x3" &&
+ invalid_color "#12x"
+'
+
+test_expect_success 'wrong number of letters in RGB color' '
+ invalid_color "#1" &&
+ invalid_color "#23" &&
+ invalid_color "#789a" &&
+ invalid_color "#bcdef" &&
+ invalid_color "#1234567"
+'
+
test_expect_success 'unknown color slots are ignored (diff)' '
git config color.diff.nosuchslotwilleverbedefined white &&
git diff --color
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
index 0c1502d..8fc40e7 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -12,6 +12,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Tested non-UTF-8 encoding
diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh
index 2a2cf91..e486493 100755
--- a/t/t4043-diff-rename-binary.sh
+++ b/t/t4043-diff-rename-binary.sh
@@ -5,6 +5,7 @@
test_description='Move a binary file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh
index fb8c517..afda629 100755
--- a/t/t4046-diff-unmerged.sh
+++ b/t/t4046-diff-unmerged.sh
@@ -98,4 +98,12 @@
test_cmp diff-stat.expect diff-stat.actual
'
+test_expect_success 'diff --quiet' '
+ test_expect_code 1 git diff --cached --quiet
+'
+
+test_expect_success 'diff --quiet --ignore-all-space' '
+ test_expect_code 1 git diff --cached --quiet --ignore-all-space
+'
+
test_done
diff --git a/t/t4059-diff-submodule-not-initialized.sh b/t/t4059-diff-submodule-not-initialized.sh
index d489230..668f526 100755
--- a/t/t4059-diff-submodule-not-initialized.sh
+++ b/t/t4059-diff-submodule-not-initialized.sh
@@ -9,6 +9,7 @@
initialized previously but the checkout has since been removed.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Tested non-UTF-8 encoding
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index 97c6424..8ce6744 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -10,6 +10,7 @@
This test tries to verify the sanity of --submodule=diff option of git diff.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Tested non-UTF-8 encoding
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh
index 697e86c..f788428 100755
--- a/t/t4120-apply-popt.sh
+++ b/t/t4120-apply-popt.sh
@@ -5,6 +5,7 @@
test_description='git apply -p handling.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4137-apply-submodule.sh b/t/t4137-apply-submodule.sh
index 07d5262..ebd0d4a 100755
--- a/t/t4137-apply-submodule.sh
+++ b/t/t4137-apply-submodule.sh
@@ -2,6 +2,7 @@
test_description='git apply handling submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t4153-am-resume-override-opts.sh b/t/t4153-am-resume-override-opts.sh
index 4add7c7..a32cec4 100755
--- a/t/t4153-am-resume-override-opts.sh
+++ b/t/t4153-am-resume-override-opts.sh
@@ -3,7 +3,6 @@
test_description='git-am command-line options override saved options'
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-terminal.sh
format_patch () {
git format-patch --stdout -1 "$1" >"$1".eml
@@ -27,7 +26,12 @@
format_patch side2
'
-test_expect_success TTY '--3way overrides --no-3way' '
+test_expect_success '--retry fails without in-progress operation' '
+ test_must_fail git am --retry 2>err &&
+ test_grep "operation not in progress" err
+'
+
+test_expect_success '--3way overrides --no-3way' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout renamed-file &&
@@ -40,7 +44,7 @@
# Applying side1 with am --3way will succeed due to the threeway-merge.
# Applying side2 will fail as --3way does not apply to it.
- test_must_fail test_terminal git am --3way </dev/zero &&
+ test_must_fail git am --retry --3way &&
test_path_is_dir .git/rebase-apply &&
test side1 = "$(cat file2)"
'
@@ -84,7 +88,7 @@
test $(git cat-file commit HEAD | grep -c "Signed-off-by:") -eq 0
'
-test_expect_success TTY '--reject overrides --no-reject' '
+test_expect_success '--reject overrides --no-reject' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
@@ -94,7 +98,7 @@
test_path_is_dir .git/rebase-apply &&
test_path_is_missing file.rej &&
- test_must_fail test_terminal git am --reject </dev/zero &&
+ test_must_fail git am --retry --reject </dev/zero &&
test_path_is_dir .git/rebase-apply &&
test_path_is_file file.rej
'
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 60fe60d..51f7beb 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -1237,6 +1237,30 @@
test_cmp expect.whatchanged.full actual
'
+test_expect_success '--abbrev-commit with core.abbrev=false' '
+ git log --no-abbrev >expect &&
+ git -c core.abbrev=false log --abbrev-commit >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--abbrev-commit with --no-abbrev' '
+ git log --no-abbrev >expect &&
+ git log --abbrev-commit --no-abbrev >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--abbrev-commit with core.abbrev=9000' '
+ git log --no-abbrev >expect &&
+ git -c core.abbrev=9000 log --abbrev-commit >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--abbrev-commit with --abbrev=9000' '
+ git log --no-abbrev >expect &&
+ git log --abbrev-commit --abbrev=9000 >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'show added path under "--follow -M"' '
# This tests for a regression introduced in v1.7.2-rc0~103^2~2
test_create_repo regression &&
@@ -2022,7 +2046,7 @@
test_expect_success GPGSSH 'log --graph --show-signature ssh' '
test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
git log --graph --show-signature -n1 signed-ssh >actual &&
- grep "${GOOD_SIGNATURE_TRUSTED}" actual
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
'
test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure on expired signature key' '
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index a7fa94c..605faea 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -310,4 +310,38 @@
test_config patchid.stable true &&
calc_patch_id diffu1stable <diffu1
'
+
+test_expect_failure 'patch-id computes same ID with different object hashes' '
+ test_when_finished "rm -rf repo-sha1 repo-sha256" &&
+
+ cat >diff <<-\EOF &&
+ diff --git a/bar b/bar
+ index bdaf90f..31051f6 100644
+ --- a/bar
+ +++ b/bar
+ @@ -2 +2,2 @@
+ b
+ +c
+ EOF
+
+ git init --object-format=sha1 repo-sha1 &&
+ git -C repo-sha1 patch-id <diff >patch-id-sha1 &&
+ git init --object-format=sha256 repo-sha256 &&
+ git -C repo-sha256 patch-id <diff >patch-id-sha256 &&
+ test_cmp patch-id-sha1 patch-id-sha256
+'
+
+test_expect_success 'patch-id without repository' '
+ cat >diff <<-\EOF &&
+ diff --git a/bar b/bar
+ index bdaf90f..31051f6 100644
+ --- a/bar
+ +++ b/bar
+ @@ -2 +2,2 @@
+ b
+ +c
+ EOF
+ nongit git patch-id <diff
+'
+
test_done
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index 75216f1..7120030 100755
--- a/t/t4210-log-i18n.sh
+++ b/t/t4210-log-i18n.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test log with i18n features'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
# two forms of é
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index dd09134..10d2a6b 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -350,6 +350,29 @@
)
'
+test_expect_success 'preferred pack from existing MIDX without bitmaps' '
+ git init preferred-without-bitmaps &&
+ (
+ cd preferred-without-bitmaps &&
+
+ test_commit one &&
+ pack="$(git pack-objects --all $objdir/pack/pack </dev/null)" &&
+
+ git multi-pack-index write &&
+
+ # make another pack so that the subsequent MIDX write
+ # has something to do
+ test_commit two &&
+ git repack -d &&
+
+ # write a new MIDX without bitmaps reusing the singular
+ # pack from the existing MIDX as the preferred pack in
+ # the new MIDX
+ git multi-pack-index write --preferred-pack=pack-$pack.pack
+ )
+
+'
+
test_expect_success 'verify multi-pack-index success' '
git multi-pack-index verify --object-dir=$objdir
'
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
index 281266f..77e9154 100755
--- a/t/t5324-split-commit-graph.sh
+++ b/t/t5324-split-commit-graph.sh
@@ -13,7 +13,8 @@
git init &&
git config core.commitGraph true &&
git config gc.writeCommitGraph false &&
- infodir=".git/objects/info" &&
+ objdir=".git/objects" &&
+ infodir="$objdir/info" &&
graphdir="$infodir/commit-graphs" &&
test_oid_cache <<-EOM
shallow sha1:2132
@@ -718,4 +719,27 @@
)
'
+test_expect_success 'temporary graph layer is discarded upon failure' '
+ git init layer-discard &&
+ (
+ cd layer-discard &&
+
+ test_commit A &&
+ test_commit B &&
+
+ # Intentionally remove commit "A" from the object store
+ # so that the commit-graph machinery fails to parse the
+ # parents of "B".
+ #
+ # This takes place after the commit-graph machinery has
+ # initialized a new temporary file to store the contents
+ # of the new graph layer, so will allow us to ensure
+ # that the temporary file is discarded upon failure.
+ rm $objdir/$(test_oid_to_path $(git rev-parse HEAD^)) &&
+
+ test_must_fail git commit-graph write --reachable --split &&
+ test_dir_is_empty $graphdir
+ )
+'
+
test_done
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index cc7220b..916da38 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -551,4 +551,34 @@
'
done
+test_expect_success 'remove one packfile between MIDX bitmap writes' '
+ git init remove-pack-between-writes &&
+ (
+ cd remove-pack-between-writes &&
+
+ test_commit A &&
+ test_commit B &&
+ test_commit C &&
+
+ # Create packs with the prefix "pack-A", "pack-B",
+ # "pack-C" to impose a lexicographic order on these
+ # packs so the pack being removed is always from the
+ # middle.
+ packdir=.git/objects/pack &&
+ A="$(echo A | git pack-objects $packdir/pack-A --revs)" &&
+ B="$(echo B | git pack-objects $packdir/pack-B --revs)" &&
+ C="$(echo C | git pack-objects $packdir/pack-C --revs)" &&
+
+ git multi-pack-index write --bitmap &&
+
+ cat >in <<-EOF &&
+ pack-A-$A.idx
+ pack-C-$C.idx
+ EOF
+ git multi-pack-index write --bitmap --stdin-packs <in &&
+
+ git rev-list --test-bitmap HEAD
+ )
+'
+
test_done
diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh
index 3c20738..ed823f3 100755
--- a/t/t5332-multi-pack-reuse.sh
+++ b/t/t5332-multi-pack-reuse.sh
@@ -204,4 +204,30 @@
test_pack_objects_reused_all $(($objects_nr - 1)) $packs_nr
'
+test_expect_success 'non-omitted delta in MIDX preferred pack' '
+ test_config pack.allowPackReuse single &&
+
+ cat >p1.objects <<-EOF &&
+ $(git rev-parse $base)
+ ^$(git rev-parse $delta^)
+ EOF
+ cat >p2.objects <<-EOF &&
+ $(git rev-parse F)
+ EOF
+
+ p1="$(git pack-objects --revs $packdir/pack <p1.objects)" &&
+ p2="$(git pack-objects --revs $packdir/pack <p2.objects)" &&
+
+ cat >in <<-EOF &&
+ pack-$p1.idx
+ pack-$p2.idx
+ EOF
+ git multi-pack-index write --bitmap --stdin-packs \
+ --preferred-pack=pack-$p1.pack <in &&
+
+ git show-index <$packdir/pack-$p1.idx >expect &&
+
+ test_pack_objects_reused_all $(wc -l <expect) 1
+'
+
test_done
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
new file mode 100755
index 0000000..f052f39
--- /dev/null
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -0,0 +1,393 @@
+#!/bin/sh
+
+test_description='pseudo-merge bitmaps'
+
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
+
+. ./test-lib.sh
+
+test_pseudo_merges () {
+ test-tool bitmap dump-pseudo-merges
+}
+
+test_pseudo_merge_commits () {
+ test-tool bitmap dump-pseudo-merge-commits "$1"
+}
+
+test_pseudo_merges_satisfied () {
+ test_trace2_data bitmap pseudo_merges_satisfied "$1"
+}
+
+test_pseudo_merges_cascades () {
+ test_trace2_data bitmap pseudo_merges_cascades "$1"
+}
+
+test_pseudo_merges_reused () {
+ test_trace2_data pack-bitmap-write building_bitmaps_pseudo_merge_reused "$1"
+}
+
+tag_everything () {
+ git rev-list --all --no-object-names >in &&
+ perl -lne '
+ print "create refs/tags/" . $. . " " . $1 if /([0-9a-f]+)/
+ ' <in | git update-ref --stdin
+}
+
+test_expect_success 'setup' '
+ test_commit_bulk 512 &&
+ tag_everything
+'
+
+test_expect_success 'bitmap traversal without pseudo-merges' '
+ git repack -adb &&
+
+ git rev-list --count --all --objects >expect &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+
+ test_pseudo_merges_satisfied 0 <trace2.txt &&
+ test_pseudo_merges_cascades 0 <trace2.txt &&
+ test_pseudo_merges >merges &&
+ test_must_be_empty merges &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pseudo-merges accurately represent their objects' '
+ test_config bitmapPseudoMerge.test.pattern "refs/tags/" &&
+ test_config bitmapPseudoMerge.test.maxMerges 8 &&
+ test_config bitmapPseudoMerge.test.stableThreshold never &&
+
+ git repack -adb &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 8 merges &&
+
+ for i in $(test_seq 0 $(($(wc -l <merges)-1)))
+ do
+ test-tool bitmap dump-pseudo-merge-commits $i >commits &&
+
+ git rev-list --objects --no-object-names --stdin <commits >expect.raw &&
+ test-tool bitmap dump-pseudo-merge-objects $i >actual.raw &&
+
+ sort -u <expect.raw >expect &&
+ sort -u <actual.raw >actual &&
+
+ test_cmp expect actual || return 1
+ done
+'
+
+test_expect_success 'bitmap traversal with pseudo-merges' '
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+ git rev-list --count --all --objects >expect &&
+
+ test_pseudo_merges_satisfied 8 <trace2.txt &&
+ test_pseudo_merges_cascades 1 <trace2.txt &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stale bitmap traversal with pseudo-merges' '
+ test_commit other &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+ git rev-list --count --all --objects >expect &&
+
+ test_pseudo_merges_satisfied 8 <trace2.txt &&
+ test_pseudo_merges_cascades 1 <trace2.txt &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bitmapPseudoMerge.sampleRate adjusts commit selection rate' '
+ test_config bitmapPseudoMerge.test.pattern "refs/tags/" &&
+ test_config bitmapPseudoMerge.test.maxMerges 1 &&
+ test_config bitmapPseudoMerge.test.stableThreshold never &&
+
+ commits_nr=$(git rev-list --all --count) &&
+
+ for rate in 1.0 0.5 0.25
+ do
+ git -c bitmapPseudoMerge.test.sampleRate=$rate repack -adb &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 1 merges &&
+ test_pseudo_merge_commits 0 >commits &&
+
+ test-tool bitmap list-commits >bitmaps &&
+ bitmaps_nr="$(wc -l <bitmaps)" &&
+
+ perl -MPOSIX -e "print ceil(\$ARGV[0]*(\$ARGV[1]-\$ARGV[2]))" \
+ "$rate" "$commits_nr" "$bitmaps_nr" >expect &&
+
+ test $(cat expect) -eq $(wc -l <commits) || return 1
+ done
+'
+
+test_expect_success 'bitmapPseudoMerge.threshold excludes newer commits' '
+ git init pseudo-merge-threshold &&
+ (
+ cd pseudo-merge-threshold &&
+
+ new="1672549200" && # 2023-01-01
+ old="1641013200" && # 2022-01-01
+
+ GIT_COMMITTER_DATE="$new +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --message="new" --notick 128 &&
+
+ GIT_COMMITTER_DATE="$old +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --message="old" --notick 128 &&
+
+ tag_everything &&
+
+ git \
+ -c bitmapPseudoMerge.test.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.test.maxMerges=1 \
+ -c bitmapPseudoMerge.test.threshold=$(($new - 1)) \
+ -c bitmapPseudoMerge.test.stableThreshold=never \
+ repack -adb &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 1 merges &&
+
+ test_pseudo_merge_commits 0 >oids &&
+ git cat-file --batch <oids >commits &&
+
+ test $(wc -l <oids) = $(grep -c "^committer.*$old +0000$" commits)
+ )
+'
+
+test_expect_success 'bitmapPseudoMerge.stableThreshold creates stable groups' '
+ (
+ cd pseudo-merge-threshold &&
+
+ new="1672549200" && # 2023-01-01
+ mid="1654059600" && # 2022-06-01
+ old="1641013200" && # 2022-01-01
+
+ GIT_COMMITTER_DATE="$mid +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --message="mid" --notick 128 &&
+
+ git for-each-ref --format="delete %(refname)" refs/tags >in &&
+ git update-ref --stdin <in &&
+
+ tag_everything &&
+
+ git \
+ -c bitmapPseudoMerge.test.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.test.maxMerges=1 \
+ -c bitmapPseudoMerge.test.threshold=$(($new - 1)) \
+ -c bitmapPseudoMerge.test.stableThreshold=$(($mid - 1)) \
+ -c bitmapPseudoMerge.test.stableSize=10 \
+ repack -adb &&
+
+ test_pseudo_merges >merges &&
+ merges_nr="$(wc -l <merges)" &&
+
+ for i in $(test_seq $(($merges_nr - 1)))
+ do
+ test_pseudo_merge_commits 0 >oids &&
+ git cat-file --batch <oids >commits &&
+
+ expect="$(grep -c "^committer.*$old +0000$" commits)" &&
+ actual="$(wc -l <oids)" &&
+
+ test $expect = $actual || return 1
+ done &&
+
+ test_pseudo_merge_commits $(($merges_nr - 1)) >oids &&
+ git cat-file --batch <oids >commits &&
+ test $(wc -l <oids) = $(grep -c "^committer.*$mid +0000$" commits)
+ )
+'
+
+test_expect_success 'out of order thresholds are rejected' '
+ test_must_fail git \
+ -c bitmapPseudoMerge.test.pattern="refs/*" \
+ -c bitmapPseudoMerge.test.threshold=1.month.ago \
+ -c bitmapPseudoMerge.test.stableThreshold=1.week.ago \
+ repack -adb 2>err &&
+
+ cat >expect <<-EOF &&
+ fatal: pseudo-merge group ${SQ}test${SQ} has unstable threshold before stable one
+ EOF
+
+ test_cmp expect err
+'
+
+test_expect_success 'pseudo-merge pattern with capture groups' '
+ git init pseudo-merge-captures &&
+ (
+ cd pseudo-merge-captures &&
+
+ test_commit_bulk 128 &&
+ tag_everything &&
+
+ for r in $(test_seq 8)
+ do
+ test_commit_bulk 16 &&
+
+ git rev-list HEAD~16.. >in &&
+
+ perl -lne "print \"create refs/remotes/$r/tags/\$. \$_\"" <in |
+ git update-ref --stdin || return 1
+ done &&
+
+ git \
+ -c bitmapPseudoMerge.tags.pattern="refs/remotes/([0-9]+)/tags/" \
+ -c bitmapPseudoMerge.tags.maxMerges=1 \
+ repack -adb &&
+
+ git for-each-ref --format="%(objectname) %(refname)" >refs &&
+
+ test_pseudo_merges >merges &&
+ for m in $(test_seq 0 $(($(wc -l <merges) - 1)))
+ do
+ test_pseudo_merge_commits $m >oids &&
+ grep -f oids refs |
+ perl -lne "print \$1 if /refs\/remotes\/([0-9]+)/" |
+ sort -u || return 1
+ done >remotes &&
+
+ test $(wc -l <remotes) -eq $(sort -u <remotes | wc -l)
+ )
+'
+
+test_expect_success 'pseudo-merge overlap setup' '
+ git init pseudo-merge-overlap &&
+ (
+ cd pseudo-merge-overlap &&
+
+ test_commit_bulk 256 &&
+ tag_everything &&
+
+ git \
+ -c bitmapPseudoMerge.all.pattern="refs/" \
+ -c bitmapPseudoMerge.all.maxMerges=1 \
+ -c bitmapPseudoMerge.all.stableThreshold=never \
+ -c bitmapPseudoMerge.tags.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.tags.maxMerges=1 \
+ -c bitmapPseudoMerge.tags.stableThreshold=never \
+ repack -adb
+ )
+'
+
+test_expect_success 'pseudo-merge overlap generates overlapping groups' '
+ (
+ cd pseudo-merge-overlap &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 2 merges &&
+
+ test_pseudo_merge_commits 0 >commits-0.raw &&
+ test_pseudo_merge_commits 1 >commits-1.raw &&
+
+ sort commits-0.raw >commits-0 &&
+ sort commits-1.raw >commits-1 &&
+
+ comm -12 commits-0 commits-1 >overlap &&
+
+ test_line_count -gt 0 overlap
+ )
+'
+
+test_expect_success 'pseudo-merge overlap traversal' '
+ (
+ cd pseudo-merge-overlap &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+ git rev-list --count --all --objects >expect &&
+
+ test_pseudo_merges_satisfied 2 <trace2.txt &&
+ test_pseudo_merges_cascades 1 <trace2.txt &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'pseudo-merge overlap stale traversal' '
+ (
+ cd pseudo-merge-overlap &&
+
+ test_commit other &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt \
+ git rev-list --count --all --objects --use-bitmap-index >actual &&
+ git rev-list --count --all --objects >expect &&
+
+ test_pseudo_merges_satisfied 2 <trace2.txt &&
+ test_pseudo_merges_cascades 1 <trace2.txt &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'pseudo-merge reuse' '
+ git init pseudo-merge-reuse &&
+ (
+ cd pseudo-merge-reuse &&
+
+ stable="1641013200" && # 2022-01-01
+ unstable="1672549200" && # 2023-01-01
+
+ GIT_COMMITTER_DATE="$stable +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --notick 128 &&
+ GIT_COMMITTER_DATE="$unstable +0000" &&
+ export GIT_COMMITTER_DATE &&
+ test_commit_bulk --notick 128 &&
+
+ tag_everything &&
+
+ git \
+ -c bitmapPseudoMerge.test.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.test.maxMerges=1 \
+ -c bitmapPseudoMerge.test.threshold=now \
+ -c bitmapPseudoMerge.test.stableThreshold=$(($unstable - 1)) \
+ -c bitmapPseudoMerge.test.stableSize=512 \
+ repack -adb &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 2 merges &&
+
+ test_pseudo_merge_commits 0 >stable-oids.before &&
+ test_pseudo_merge_commits 1 >unstable-oids.before &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT=$PWD/trace2.txt git \
+ -c bitmapPseudoMerge.test.pattern="refs/tags/" \
+ -c bitmapPseudoMerge.test.maxMerges=2 \
+ -c bitmapPseudoMerge.test.threshold=now \
+ -c bitmapPseudoMerge.test.stableThreshold=$(($unstable - 1)) \
+ -c bitmapPseudoMerge.test.stableSize=512 \
+ repack -adb &&
+
+ test_pseudo_merges_reused 1 <trace2.txt &&
+
+ test_pseudo_merges >merges &&
+ test_line_count = 3 merges &&
+
+ test_pseudo_merge_commits 0 >stable-oids.after &&
+ for i in 1 2
+ do
+ test_pseudo_merge_commits $i || return 1
+ done >unstable-oids.after &&
+
+ sort -u <stable-oids.before >expect &&
+ sort -u <stable-oids.after >actual &&
+ test_cmp expect actual &&
+
+ sort -u <unstable-oids.before >expect &&
+ sort -u <unstable-oids.after >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 7789ff1..08424e8 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -1492,4 +1492,40 @@
)
'
+test_expect_success 'empty config clears remote.*.url list' '
+ test_when_finished "git config --remove-section remote.multi" &&
+ git config --add remote.multi.url wrong-one &&
+ git config --add remote.multi.url wrong-two &&
+ git -c remote.multi.url= \
+ -c remote.multi.url=right-one \
+ -c remote.multi.url=right-two \
+ remote show -n multi >actual.raw &&
+ grep URL actual.raw >actual &&
+ cat >expect <<-\EOF &&
+ Fetch URL: right-one
+ Push URL: right-one
+ Push URL: right-two
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'empty config clears remote.*.pushurl list' '
+ test_when_finished "git config --remove-section remote.multi" &&
+ git config --add remote.multi.url right &&
+ git config --add remote.multi.url will-be-ignored &&
+ git config --add remote.multi.pushurl wrong-push-one &&
+ git config --add remote.multi.pushurl wrong-push-two &&
+ git -c remote.multi.pushurl= \
+ -c remote.multi.pushurl=right-push-one \
+ -c remote.multi.pushurl=right-push-two \
+ remote show -n multi >actual.raw &&
+ grep URL actual.raw >actual &&
+ cat >expect <<-\EOF &&
+ Fetch URL: right
+ Push URL: right-push-one
+ Push URL: right-push-two
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 9441793..3b3991a 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -518,7 +518,7 @@
test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [1]' '
one_head=$(cd one && git rev-parse HEAD) &&
this_head=$(git rev-parse HEAD) &&
- git update-ref -d FETCH_HEAD &&
+ rm .git/FETCH_HEAD &&
git fetch one &&
test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
test $this_head = "$(git rev-parse --verify HEAD)"
@@ -530,7 +530,7 @@
one_ref=$(cd one && git symbolic-ref HEAD) &&
git config branch.main.remote blub &&
git config branch.main.merge "$one_ref" &&
- git update-ref -d FETCH_HEAD &&
+ rm .git/FETCH_HEAD &&
git fetch one &&
test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
test $this_head = "$(git rev-parse --verify HEAD)"
@@ -540,7 +540,7 @@
# the merge spec does not match the branch the remote HEAD points to
test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [3]' '
git config branch.main.merge "${one_ref}_not" &&
- git update-ref -d FETCH_HEAD &&
+ rm .git/FETCH_HEAD &&
git fetch one &&
test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
test $this_head = "$(git rev-parse --verify HEAD)"
@@ -1091,6 +1091,22 @@
test_cmp expect actual
'
+test_expect_success 'branchname D/F conflict rejected with targeted error message' '
+ git clone . df-conflict-error &&
+ git branch dir_conflict &&
+ (
+ cd df-conflict-error &&
+ git update-ref refs/remotes/origin/dir_conflict/file HEAD &&
+ test_must_fail git fetch 2>err &&
+ test_grep "error: some local refs could not be updated; try running" err &&
+ test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err &&
+ git pack-refs --all &&
+ test_must_fail git fetch 2>err-packed &&
+ test_grep "error: some local refs could not be updated; try running" err-packed &&
+ test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err-packed
+ )
+'
+
test_expect_success 'fetching a one-level ref works' '
test_commit extra &&
git reset --hard HEAD^ &&
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 5dbe107..42e77eb 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -47,6 +47,7 @@
git show-ref -d >refs &&
sed -e "s/ / /" refs >>expected.all &&
+ grep refs/heads/ expected.all >expected.branches &&
git remote add self "$(pwd)/.git" &&
git remote add self2 "."
'
@@ -71,6 +72,27 @@
test_cmp expected.all actual
'
+test_expect_success 'ls-remote --branches self' '
+ git ls-remote --branches self >actual &&
+ test_cmp expected.branches actual &&
+ git ls-remote -b self >actual &&
+ test_cmp expected.branches actual
+'
+
+test_expect_success 'ls-remote -h is deprecated w/o warning' '
+ git ls-remote -h self >actual 2>warning &&
+ test_cmp expected.branches actual &&
+ test_grep ! deprecated warning
+'
+
+test_expect_success 'ls-remote --heads is deprecated and hidden w/o warning' '
+ test_expect_code 129 git ls-remote -h >short-help &&
+ test_grep ! -e --head short-help &&
+ git ls-remote --heads self >actual 2>warning &&
+ test_cmp expected.branches actual &&
+ test_grep ! deprecated warning
+'
+
test_expect_success 'ls-remote --sort="version:refname" --tags self' '
generate_references \
refs/tags/mark \
@@ -275,7 +297,7 @@
test_cmp expect actual
'
-test_expect_success 'ls-remote with filtered symref (--heads)' '
+test_expect_success 'ls-remote with filtered symref (--branches)' '
git symbolic-ref refs/heads/foo refs/tags/mark &&
cat >expect.v2 <<-EOF &&
ref: refs/tags/mark refs/heads/foo
@@ -283,9 +305,9 @@
$rev refs/heads/main
EOF
grep -v "^ref: refs/tags/" <expect.v2 >expect.v0 &&
- git -c protocol.version=0 ls-remote --symref --heads . >actual.v0 &&
+ git -c protocol.version=0 ls-remote --symref --branches . >actual.v0 &&
test_cmp expect.v0 actual.v0 &&
- git -c protocol.version=2 ls-remote --symref --heads . >actual.v2 &&
+ git -c protocol.version=2 ls-remote --symref --branches . >actual.v2 &&
test_cmp expect.v2 actual.v2
'
@@ -335,9 +357,9 @@
test_expect_success 'ls-remote prefixes work with all protocol versions' '
git for-each-ref --format="%(objectname) %(refname)" \
refs/heads/ refs/tags/ >expect &&
- git -c protocol.version=0 ls-remote --heads --tags . >actual.v0 &&
+ git -c protocol.version=0 ls-remote --branches --tags . >actual.v0 &&
test_cmp expect actual.v0 &&
- git -c protocol.version=2 ls-remote --heads --tags . >actual.v2 &&
+ git -c protocol.version=2 ls-remote --branches --tags . >actual.v2 &&
test_cmp expect actual.v2
'
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 4c3b327..ea8e48f 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -25,6 +25,12 @@
git commit -m two
'
+test_expect_success 'packfile without repository does not crash' '
+ echo "fatal: not a git repository" >expect &&
+ test_must_fail nongit git http-fetch --packfile=abc 2>err &&
+ test_cmp expect err
+'
+
setup_post_update_server_info_hook () {
test_hook --setup -C "$1" post-update <<-\EOF &&
exec git update-server-info
@@ -55,6 +61,21 @@
test_cmp expect actual
'
+
+test_expect_success 'list detached HEAD from outside any repository' '
+ git clone --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" \
+ update-ref --no-deref HEAD refs/heads/main &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" update-server-info &&
+ cat >expect <<-EOF &&
+ $(git rev-parse main) HEAD
+ $(git rev-parse main) refs/heads/main
+ EOF
+ nongit git ls-remote "$HTTPD_URL/dumb/repo-detached.git" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'create password-protected repository' '
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
index ab8a721..4af796d 100755
--- a/t/t5563-simple-http-auth.sh
+++ b/t/t5563-simple-http-auth.sh
@@ -2,6 +2,7 @@
test_description='test http auth header and credential helper interop'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
@@ -21,9 +22,17 @@
CREDENTIAL_HELPER="$TRASH_DIRECTORY/bin/git-credential-test-helper" &&
write_script "$CREDENTIAL_HELPER" <<-\EOF
cmd=$1
- teefile=$cmd-query.cred
+ teefile=$cmd-query-temp.cred
catfile=$cmd-reply.cred
sed -n -e "/^$/q" -e "p" >>$teefile
+ state=$(sed -ne "s/^state\[\]=helper://p" "$teefile")
+ if test -z "$state"
+ then
+ mv "$teefile" "$cmd-query.cred"
+ else
+ mv "$teefile" "$cmd-query-$state.cred"
+ catfile="$cmd-reply-$state.cred"
+ fi
if test "$cmd" = "get"
then
cat $catfile
@@ -32,13 +41,15 @@
'
set_credential_reply () {
- cat >"$TRASH_DIRECTORY/$1-reply.cred"
+ local suffix="$(test -n "$2" && echo "-$2")"
+ cat >"$TRASH_DIRECTORY/$1-reply$suffix.cred"
}
expect_credential_query () {
- cat >"$TRASH_DIRECTORY/$1-expect.cred" &&
- test_cmp "$TRASH_DIRECTORY/$1-expect.cred" \
- "$TRASH_DIRECTORY/$1-query.cred"
+ local suffix="$(test -n "$2" && echo "-$2")"
+ cat >"$TRASH_DIRECTORY/$1-expect$suffix.cred" &&
+ test_cmp "$TRASH_DIRECTORY/$1-expect$suffix.cred" \
+ "$TRASH_DIRECTORY/$1-query$suffix.cred"
}
per_test_cleanup () {
@@ -63,17 +74,20 @@
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- WWW-Authenticate: Basic realm="example.com"
+ id=1 status=200
+ id=default response=WWW-Authenticate: Basic realm="example.com"
EOF
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=Basic realm="example.com"
@@ -87,6 +101,45 @@
EOF
'
+test_expect_success 'access using basic auth via authtype' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ authtype=Basic
+ credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default response=WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ GIT_CURL_VERBOSE=1 git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ capability[]=authtype
+ authtype=Basic
+ credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ protocol=http
+ host=$HTTPD_DEST
+ EOF
+'
+
test_expect_success 'access using basic auth invalid credentials' '
test_when_finished "per_test_cleanup" &&
@@ -97,17 +150,20 @@
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- WWW-Authenticate: Basic realm="example.com"
+ id=1 status=200
+ id=default response=WWW-Authenticate: Basic realm="example.com"
EOF
test_config_global credential.helper test-helper &&
test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=Basic realm="example.com"
@@ -132,19 +188,22 @@
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- WWW-Authenticate: FooBar param1="value1" param2="value2"
- WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
- WWW-Authenticate: Basic realm="example.com"
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
EOF
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -170,19 +229,22 @@
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- www-authenticate: foobar param1="value1" param2="value2"
- WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
- WwW-aUtHeNtIcAtE: baSiC realm="example.com"
+ id=1 status=200
+ id=default response=www-authenticate: foobar param1="value1" param2="value2"
+ id=default response=WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
+ id=default response=WwW-aUtHeNtIcAtE: baSiC realm="example.com"
EOF
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=foobar param1="value1" param2="value2"
@@ -208,24 +270,27 @@
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
- WWW-Authenticate: FooBar param1="value1"
- param2="value2"
- WWW-Authenticate: Bearer authorize_uri="id.example.com"
- p=1
- q=0
- WWW-Authenticate: Basic realm="example.com"
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1"
+ id=default response= param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com"
+ id=default response= p=1
+ id=default response= q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
EOF
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -251,26 +316,29 @@
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
- printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
- printf " \r\n" >>"$CHALLENGE" &&
- printf " param2=\"value2\"\r\n" >>"$CHALLENGE" &&
- printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
- printf " p=1\r\n" >>"$CHALLENGE" &&
- printf " \r\n" >>"$CHALLENGE" &&
- printf " q=0\r\n" >>"$CHALLENGE" &&
- printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
+ printf "id=1 status=200\n" >"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: FooBar param1=\"value1\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response= \r\n" >>"$CHALLENGE" &&
+ printf "id=default response= param2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response= p=1\r\n" >>"$CHALLENGE" &&
+ printf "id=default response= \r\n" >>"$CHALLENGE" &&
+ printf "id=default response= q=0\r\n" >>"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -296,22 +364,25 @@
# Basic base64(alice:secret-passwd)
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
- Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
EOF
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
- printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
- printf " \r\n" >>"$CHALLENGE" &&
- printf "\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
- printf "WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
+ printf "id=1 status=200\n" >"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: FooBar param1=\"value1\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response= \r\n" >>"$CHALLENGE" &&
+ printf "id=default response=\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "id=default response=WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
protocol=http
host=$HTTPD_DEST
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -326,4 +397,166 @@
EOF
'
+test_expect_success 'access using bearer auth' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ authtype=Bearer
+ credential=YS1naXQtdG9rZW4=
+ EOF
+
+ # Basic base64(a-git-token)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Bearer YS1naXQtdG9rZW4=
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=YS1naXQtdG9rZW4=
+ protocol=http
+ host=$HTTPD_DEST
+ EOF
+'
+
+test_expect_success 'access using bearer auth with invalid credentials' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ authtype=Bearer
+ credential=incorrect-token
+ EOF
+
+ # Basic base64(a-git-token)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Bearer YS1naXQtdG9rZW4=
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=200
+ id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=default response=WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query erase <<-EOF
+ capability[]=authtype
+ authtype=Bearer
+ credential=incorrect-token
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+'
+
+test_expect_success 'access using three-legged auth' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ authtype=Multistage
+ credential=YS1naXQtdG9rZW4=
+ state[]=helper:foobar
+ continue=1
+ EOF
+
+ set_credential_reply get foobar <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ authtype=Multistage
+ credential=YW5vdGhlci10b2tlbg==
+ state[]=helper:bazquux
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ id=1 creds=Multistage YS1naXQtdG9rZW4=
+ id=2 creds=Multistage YW5vdGhlci10b2tlbg==
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ id=1 status=401 response=WWW-Authenticate: Multistage challenge="456"
+ id=1 status=401 response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ id=2 status=200
+ id=default response=WWW-Authenticate: Multistage challenge="123"
+ id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Multistage challenge="123"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ EOF
+
+ expect_credential_query get foobar <<-EOF &&
+ capability[]=authtype
+ capability[]=state
+ authtype=Multistage
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Multistage challenge="456"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ state[]=helper:foobar
+ EOF
+
+ expect_credential_query store bazquux <<-EOF
+ capability[]=authtype
+ capability[]=state
+ authtype=Multistage
+ credential=YW5vdGhlci10b2tlbg==
+ protocol=http
+ host=$HTTPD_DEST
+ state[]=helper:bazquux
+ EOF
+'
+
test_done
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
index 9da5134..bb35b87 100755
--- a/t/t5564-http-proxy.sh
+++ b/t/t5564-http-proxy.sh
@@ -2,6 +2,7 @@
test_description="test fetching through http proxy"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh
index cded79c..724f610 100755
--- a/t/t5581-http-curl-verbose.sh
+++ b/t/t5581-http-curl-verbose.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index cc0b953..5d7ea14 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -46,6 +46,13 @@
test $(grep Clon output | wc -l) = 1
'
+test_expect_success 'output from clone with core.abbrev does not crash' '
+ rm -fr dst &&
+ echo "Cloning into ${SQ}dst${SQ}..." >expect &&
+ git -c core.abbrev=12 clone -n "file://$(pwd)/src" dst >actual 2>&1 &&
+ test_cmp expect actual
+'
+
test_expect_success 'clone does not keep pack' '
rm -fr dst &&
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index a305586..339d8c7 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -163,7 +163,7 @@
echo a >corrupt/.git/refs/heads/topic &&
test_must_fail git clone corrupt working 2>err &&
- grep "has a null OID" err
+ grep "has neither a valid OID nor a target" err
'
test_done
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index 4e0a77f..20f43f7 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -38,6 +38,29 @@
test_cmp server/file local/file
'
+test_expect_success 'clone with remote.*.vcs config' '
+ GIT_TRACE=$PWD/vcs-clone.trace \
+ git clone --no-local -c remote.origin.vcs=testgit "$PWD/server" vcs-clone &&
+ test_grep remote-testgit vcs-clone.trace
+'
+
+test_expect_success 'fetch with configured remote.*.vcs' '
+ git init vcs-fetch &&
+ git -C vcs-fetch config remote.origin.vcs testgit &&
+ git -C vcs-fetch config remote.origin.url "$PWD/server" &&
+ GIT_TRACE=$PWD/vcs-fetch.trace \
+ git -C vcs-fetch fetch origin &&
+ test_grep remote-testgit vcs-fetch.trace
+'
+
+test_expect_success 'vcs remote with no url' '
+ NOURL_UPSTREAM=$PWD/server &&
+ export NOURL_UPSTREAM &&
+ git init vcs-nourl &&
+ git -C vcs-nourl config remote.origin.vcs nourl &&
+ git -C vcs-nourl fetch origin
+'
+
test_expect_success 'create new commit on remote' '
(cd server &&
echo content >>file &&
diff --git a/t/t5801/git-remote-nourl b/t/t5801/git-remote-nourl
new file mode 100755
index 0000000..09be601
--- /dev/null
+++ b/t/t5801/git-remote-nourl
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec git-remote-testgit "$1" "$NOURL_UPSTREAM"
diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit
index c5b10f5..f8b4764 100755
--- a/t/t5801/git-remote-testgit
+++ b/t/t5801/git-remote-testgit
@@ -26,7 +26,8 @@
t_refspec=""
fi
-GIT_DIR="$url/.git"
+unset $(git rev-parse --local-env-vars)
+GIT_DIR=$(git -C "$url" rev-parse --absolute-git-dir)
export GIT_DIR
force=
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 573eb97..f1623b1 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -8,6 +8,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh
index 82013fc..3946e18 100755
--- a/t/t6041-bisect-submodule.sh
+++ b/t/t6041-bisect-submodule.sh
@@ -2,6 +2,7 @@
test_description='bisect can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 948f1bb..163c378 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -52,6 +52,23 @@
test_cmp expect actual
'
+test_expect_success '--include-root-refs pattern does not print special refs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ git rev-parse HEAD >.git/MERGE_HEAD &&
+ git for-each-ref --format="%(refname)" --include-root-refs >actual &&
+ cat >expect <<-EOF &&
+ HEAD
+ $(git symbolic-ref HEAD)
+ refs/tags/initial
+ EOF
+ test_cmp expect actual
+ )
+'
+
test_expect_success '--include-root-refs with other patterns' '
cat >expect <<-\EOF &&
HEAD
@@ -62,6 +79,23 @@
test_cmp expect actual
'
+test_expect_success '--include-root-refs omits dangling symrefs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ git symbolic-ref DANGLING_HEAD refs/heads/missing &&
+ cat >expect <<-EOF &&
+ HEAD
+ $(git symbolic-ref HEAD)
+ refs/tags/initial
+ EOF
+ git for-each-ref --format="%(refname)" --include-root-refs >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'filtering with --points-at' '
cat >expect <<-\EOF &&
refs/heads/main
diff --git a/t/t6400-merge-df.sh b/t/t6400-merge-df.sh
index 3de4ef6..27d6efd 100755
--- a/t/t6400-merge-df.sh
+++ b/t/t6400-merge-df.sh
@@ -7,6 +7,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'prepare repository' '
diff --git a/t/t6412-merge-large-rename.sh b/t/t6412-merge-large-rename.sh
index ca018d1..d0863a8 100755
--- a/t/t6412-merge-large-rename.sh
+++ b/t/t6412-merge-large-rename.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
count() {
diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index b059475..62f0180 100755
--- a/t/t6426-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -22,6 +22,7 @@
# underscore notation is to differentiate different
# files that might be renamed into each other's paths.)
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index 0f39ed0..cb1c4ce 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -2,6 +2,7 @@
test_description="remember regular & dir renames in sequence of merges"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
#
diff --git a/t/t6438-submodule-directory-file-conflicts.sh b/t/t6438-submodule-directory-file-conflicts.sh
index 8df67a0..3594190 100755
--- a/t/t6438-submodule-directory-file-conflicts.sh
+++ b/t/t6438-submodule-directory-file-conflicts.sh
@@ -2,6 +2,7 @@
test_description='merge can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 879a6dc..86258f9 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git mv in subdirs'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff-data.sh
diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh
index 26582ae..57969ce 100755
--- a/t/t7002-mv-sparse-checkout.sh
+++ b/t/t7002-mv-sparse-checkout.sh
@@ -2,6 +2,7 @@
test_description='git mv in sparse working trees'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
setup_sparse_checkout () {
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 696866d..fa6336e 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -668,6 +668,115 @@
test_cmp expect actual
'
+# trailers
+
+test_expect_success 'create tag with -m and --trailer' '
+ get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ create tag with trailers
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ git tag -m "create tag with trailers" \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-inline-message-and-trailers &&
+ get_tag_msg tag-with-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'list tag extracting trailers' '
+ cat >expect <<-\EOF &&
+ my-trailer: here
+ alt-trailer: there
+
+ EOF
+ git tag --list --format="%(trailers)" tag-with-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -F and --trailer' '
+ echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
+ get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ create tag from message file using --trailer
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ git tag -F messagefilewithnotrailers \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-file-message-and-trailers &&
+ get_tag_msg tag-with-file-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -m and --trailer and --edit' '
+ write_script fakeeditor <<-\EOF &&
+ sed -e "1s/^/EDITED: /g" <"$1" >"$1-"
+ mv "$1-" "$1"
+ EOF
+ get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ EDITED: create tag with trailers
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag --edit \
+ -m "create tag with trailers" \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-edited-inline-message-and-trailers &&
+ get_tag_msg tag-with-edited-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -F and --trailer and --edit' '
+ echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
+ get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ EDITED: create tag from message file using --trailer
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag --edit \
+ -F messagefilewithnotrailers \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-edited-file-message-and-trailers &&
+ get_tag_msg tag-with-edited-file-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create annotated tag and force editor when only --trailer is given' '
+ write_script fakeeditor <<-\EOF &&
+ echo "add a line" >"$1-"
+ cat <"$1" >>"$1-"
+ mv "$1-" "$1"
+ EOF
+ get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ add a line
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-trailers-and-no-message &&
+ get_tag_msg tag-with-trailers-and-no-message >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bad editor causes panic when only --trailer is given' '
+ test_must_fail env GIT_EDITOR=false git tag --trailer my-trailer=here tag-will-not-exist
+'
+
# listing messages for annotated non-signed tags:
test_expect_success \
@@ -810,6 +919,11 @@
refs/tags/tag-lines 0 1 !
refs/tags/tag-one-line 0 1 !
refs/tags/tag-right 0 0 !
+ refs/tags/tag-with-edited-file-message-and-trailers 0 1 !
+ refs/tags/tag-with-edited-inline-message-and-trailers 0 1 !
+ refs/tags/tag-with-file-message-and-trailers 0 1 !
+ refs/tags/tag-with-inline-message-and-trailers 0 1 !
+ refs/tags/tag-with-trailers-and-no-message 0 1 !
refs/tags/tag-zero-lines 0 1 !
EOF
git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 5fcf281..b982229 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -2,6 +2,7 @@
test_description='GIT_EDITOR, core.editor, and stuff'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
unset EDITOR VISUAL GIT_EDITOR
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 62d9f84..2add26d 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -10,6 +10,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
commit_msg () {
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
index a3e2413..b0d3d93 100755
--- a/t/t7112-reset-submodule.sh
+++ b/t/t7112-reset-submodule.sh
@@ -2,6 +2,7 @@
test_description='reset can handle submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
index 5e3051d..dbbb385 100755
--- a/t/t7417-submodule-path-url.sh
+++ b/t/t7417-submodule-path-url.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index ce64d8b..479c8fd 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -10,6 +10,7 @@
`git add` as done in t7401.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7423-submodule-symlinks.sh b/t/t7423-submodule-symlinks.sh
index 3d3c7af..f45d806 100755
--- a/t/t7423-submodule-symlinks.sh
+++ b/t/t7423-submodule-symlinks.sh
@@ -2,6 +2,7 @@
test_description='check that submodule operations do not follow symlinks'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'prepare' '
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index e5ff073..ef54cff 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -236,6 +236,16 @@
verify_parents $c1 $c2
'
+test_expect_success 'merge c1 with c2 when index.lock exists' '
+ test_when_finished rm .git/index.lock &&
+ git reset --hard c1 &&
+ >.git/index.lock &&
+ test_must_fail git merge c2 &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_path_is_missing .git/MERGE_MODE &&
+ test_path_is_missing .git/MERGE_MSG
+'
+
test_expect_success 'merge --squash c3 with c7' '
git reset --hard c3 &&
test_must_fail git merge --squash c7 &&
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 5a77100..58699f8 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -2526,7 +2526,7 @@
test_expect_success $PREREQ '--compose handles lowercase headers' '
write_script fake-editor <<-\EOF &&
- sed "s/^From:.*/from: edited-from@example.com/i" "$1" >"$1.tmp" &&
+ sed "s/^[Ff][Rr][Oo][Mm]:.*/from: edited-from@example.com/" "$1" >"$1.tmp" &&
mv "$1.tmp" "$1"
EOF
clean_fake_sendmail &&
diff --git a/t/t9004-example.sh b/t/t9004-example.sh
deleted file mode 100755
index 590aab0..0000000
--- a/t/t9004-example.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-test_description='check that example code compiles and runs'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-test_expect_success 'decorate' '
- test-tool example-decorate
-'
-
-test_done
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index d3261e3..a34fd46 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -38,7 +38,7 @@
# SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
# Look at what SVN wound up naming the branch and use that.
# Be sure to escape the @ if it shows up.
-non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p }')
+non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p; }')
test_expect_success 'test clone with funky branch names' '
git svn clone -s "$svnrepo/pr ject" project &&
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 185248a..01e1e8a 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,7 +4,6 @@
test_description='git svn honors i18n.commitEncoding in config'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
compare_git_head_with () {
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index b7f756b..22d80b0 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,7 +4,6 @@
test_description='git svn refuses to dcommit non-UTF8 messages'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
# ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index a44eabf..3d48421 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -4,6 +4,7 @@
#
test_description='Test export of commits to CVS'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if ! test_have_prereq PERL; then
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index a34805a..a67e6ab 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -12,6 +12,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
marked_as () {
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 5680849..41fcf36 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
if ! test_have_prereq NOT_ROOT; then
diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh
index 116cddb..e007669 100755
--- a/t/t9601-cvsimport-vendor-branch.sh
+++ b/t/t9601-cvsimport-vendor-branch.sh
@@ -35,6 +35,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
setup_cvs_test_repository t9601
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
index e5266c9..3768e3b 100755
--- a/t/t9602-cvsimport-branches-tags.sh
+++ b/t/t9602-cvsimport-branches-tags.sh
@@ -7,6 +7,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
setup_cvs_test_repository t9602
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
index 19f38f7..2a387fd 100755
--- a/t/t9603-cvsimport-patchsets.sh
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -12,6 +12,8 @@
# bug.
test_description='git cvsimport testing for correct patchset estimation'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
setup_cvs_test_repository t9603
diff --git a/t/t9604-cvsimport-timestamps.sh b/t/t9604-cvsimport-timestamps.sh
index 2d03259..9cf0685 100755
--- a/t/t9604-cvsimport-timestamps.sh
+++ b/t/t9604-cvsimport-timestamps.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git cvsimport timestamps'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-cvs.sh
test_lazy_prereq POSIX_TIMEZONE '
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 569cf23..932d5ad 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -73,7 +73,7 @@
print_comp ()
{
local IFS=$'\n'
- echo "${COMPREPLY[*]}" > out
+ printf '%s\n' "${COMPREPLY[*]}" > out
}
run_completion ()
@@ -2518,6 +2518,29 @@
EOF
'
+test_expect_success 'symbolic-ref completes builtin options' '
+ test_completion "git symbolic-ref --d" <<-\EOF
+ --delete Z
+ EOF
+'
+
+test_expect_success 'symbolic-ref completes short ref names' '
+ test_completion "git symbolic-ref foo m" <<-\EOF
+ main Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
+test_expect_success 'symbolic-ref completes full ref names' '
+ test_completion "git symbolic-ref foo refs/" <<-\EOF
+ refs/heads/main Z
+ refs/heads/mybranch Z
+ refs/tags/mytag Z
+ refs/tags/A Z
+ EOF
+'
+
test_expect_success PERL 'send-email' '
test_completion "git send-email --cov" <<-\EOF &&
--cover-from-description=Z
@@ -2719,30 +2742,58 @@
'
done
-test_expect_success 'git config - section' '
- test_completion "git config br" <<-\EOF
+test_expect_success 'git config subcommand' '
+ test_completion "git config " <<-\EOF
+ edit Z
+ get Z
+ list Z
+ remove-section Z
+ rename-section Z
+ set Z
+ unset Z
+ EOF
+'
+
+test_expect_success 'git config subcommand options' '
+ test_completion "git config get --show-" <<-\EOF
+ --show-names Z
+ --show-origin Z
+ --show-scope Z
+ EOF
+'
+
+test_expect_success 'git config get' '
+ test_when_finished "rm -f cfgfile" &&
+ git config set --file cfgfile foo.bar baz &&
+ test_completion "git config get --file cfgfile foo." <<-\EOF
+ foo.bar Z
+ EOF
+'
+
+test_expect_success 'git config set - section' '
+ test_completion "git config set br" <<-\EOF
branch.Z
browser.Z
EOF
'
-test_expect_success 'git config - section include, includeIf' '
- test_completion "git config inclu" <<-\EOF
+test_expect_success 'git config set - section include, includeIf' '
+ test_completion "git config set inclu" <<-\EOF
include.Z
includeIf.Z
EOF
'
-test_expect_success 'git config - variable name' '
- test_completion "git config log.d" <<-\EOF
+test_expect_success 'git config set - variable name' '
+ test_completion "git config set log.d" <<-\EOF
log.date Z
log.decorate Z
log.diffMerges Z
EOF
'
-test_expect_success 'git config - variable name include' '
- test_completion "git config include.p" <<-\EOF
+test_expect_success 'git config set - variable name include' '
+ test_completion "git config set include.p" <<-\EOF
include.path Z
EOF
'
@@ -2753,8 +2804,8 @@
git submodule add ./sub
'
-test_expect_success 'git config - variable name - submodule and __git_compute_first_level_config_vars_for_section' '
- test_completion "git config submodule." <<-\EOF
+test_expect_success 'git config set - variable name - submodule and __git_compute_first_level_config_vars_for_section' '
+ test_completion "git config set submodule." <<-\EOF
submodule.active Z
submodule.alternateErrorStrategy Z
submodule.alternateLocation Z
@@ -2765,8 +2816,8 @@
EOF
'
-test_expect_success 'git config - variable name - __git_compute_second_level_config_vars_for_section' '
- test_completion "git config submodule.sub." <<-\EOF
+test_expect_success 'git config set - variable name - __git_compute_second_level_config_vars_for_section' '
+ test_completion "git config set submodule.sub." <<-\EOF
submodule.sub.url Z
submodule.sub.update Z
submodule.sub.branch Z
@@ -2776,8 +2827,8 @@
EOF
'
-test_expect_success 'git config - value' '
- test_completion "git config color.pager " <<-\EOF
+test_expect_success 'git config set - value' '
+ test_completion "git config set color.pager " <<-\EOF
false Z
true Z
EOF
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 862d80c..1ea9f31 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -458,6 +458,7 @@
indir=.
ref=HEAD
n=1
+ notick=
message='commit %s'
filename='%s.t'
contents='content %s'
@@ -488,6 +489,9 @@
filename="${1#--*=}-%s.t"
contents="${1#--*=} %s"
;;
+ --notick)
+ notick=yes
+ ;;
-*)
BUG "invalid test_commit_bulk option: $1"
;;
@@ -507,7 +511,10 @@
while test "$total" -gt 0
do
- test_tick &&
+ if test -z "$notick"
+ then
+ test_tick
+ fi &&
echo "commit $ref"
printf 'author %s <%s> %s\n' \
"$GIT_AUTHOR_NAME" \
@@ -1096,6 +1103,11 @@
done
fi
+ if test "$1" = "nongit"
+ then
+ shift
+ fi
+
case "$1" in
git|__git*|scalar|test-tool|test_terminal)
return 0
diff --git a/t/test-terminal.perl b/t/test-terminal.perl
index 3810e9b..b8fd6a4 100755
--- a/t/test-terminal.perl
+++ b/t/test-terminal.perl
@@ -5,17 +5,15 @@
use IO::Pty;
use File::Copy;
-# Run @$argv in the background with stdio redirected to $in, $out and $err.
+# Run @$argv in the background with stdio redirected to $out and $err.
sub start_child {
- my ($argv, $in, $out, $err) = @_;
+ my ($argv, $out, $err) = @_;
my $pid = fork;
if (not defined $pid) {
die "fork failed: $!"
} elsif ($pid == 0) {
- open STDIN, "<&", $in;
open STDOUT, ">&", $out;
open STDERR, ">&", $err;
- close $in;
close $out;
exec(@$argv) or die "cannot exec '$argv->[0]': $!"
}
@@ -51,17 +49,6 @@
copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
}
-sub copy_stdin {
- my ($in) = @_;
- my $pid = fork;
- if (!$pid) {
- xsendfile($in, \*STDIN);
- exit 0;
- }
- close($in);
- return $pid;
-}
-
sub copy_stdio {
my ($out, $err) = @_;
my $pid = fork;
@@ -81,25 +68,15 @@
die "usage: test-terminal program args";
}
$ENV{TERM} = 'vt100';
-my $parent_in = new IO::Pty;
my $parent_out = new IO::Pty;
my $parent_err = new IO::Pty;
-$parent_in->set_raw();
$parent_out->set_raw();
$parent_err->set_raw();
-$parent_in->slave->set_raw();
$parent_out->slave->set_raw();
$parent_err->slave->set_raw();
-my $pid = start_child(\@ARGV, $parent_in->slave, $parent_out->slave, $parent_err->slave);
-close $parent_in->slave;
+my $pid = start_child(\@ARGV, $parent_out->slave, $parent_err->slave);
close $parent_out->slave;
close $parent_err->slave;
-my $in_pid = copy_stdin($parent_in);
copy_stdio($parent_out, $parent_err);
my $ret = finish_child($pid);
-# If the child process terminates before our copy_stdin() process is able to
-# write all of its data to $parent_in, the copy_stdin() process could stall.
-# Send SIGTERM to it to ensure it terminates.
-kill 'TERM', $in_pid;
-finish_child($in_pid);
exit($ret);
diff --git a/t/unit-tests/lib-oid.c b/t/unit-tests/lib-oid.c
new file mode 100644
index 0000000..37105f0
--- /dev/null
+++ b/t/unit-tests/lib-oid.c
@@ -0,0 +1,52 @@
+#include "test-lib.h"
+#include "lib-oid.h"
+#include "strbuf.h"
+#include "hex.h"
+
+static int init_hash_algo(void)
+{
+ static int algo = -1;
+
+ if (algo < 0) {
+ const char *algo_name = getenv("GIT_TEST_DEFAULT_HASH");
+ algo = algo_name ? hash_algo_by_name(algo_name) : GIT_HASH_SHA1;
+
+ if (!check(algo != GIT_HASH_UNKNOWN))
+ test_msg("BUG: invalid GIT_TEST_DEFAULT_HASH value ('%s')",
+ algo_name);
+ }
+ return algo;
+}
+
+static int get_oid_arbitrary_hex_algop(const char *hex, struct object_id *oid,
+ const struct git_hash_algo *algop)
+{
+ int ret;
+ size_t sz = strlen(hex);
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!check(sz <= algop->hexsz)) {
+ test_msg("BUG: hex string (%s) bigger than maximum allowed (%lu)",
+ hex, (unsigned long)algop->hexsz);
+ return -1;
+ }
+
+ strbuf_add(&buf, hex, sz);
+ strbuf_addchars(&buf, '0', algop->hexsz - sz);
+
+ ret = get_oid_hex_algop(buf.buf, oid, algop);
+ if (!check_int(ret, ==, 0))
+ test_msg("BUG: invalid hex input (%s) provided", hex);
+
+ strbuf_release(&buf);
+ return ret;
+}
+
+int get_oid_arbitrary_hex(const char *hex, struct object_id *oid)
+{
+ int hash_algo = init_hash_algo();
+
+ if (!check_int(hash_algo, !=, GIT_HASH_UNKNOWN))
+ return -1;
+ return get_oid_arbitrary_hex_algop(hex, oid, &hash_algos[hash_algo]);
+}
diff --git a/t/unit-tests/lib-oid.h b/t/unit-tests/lib-oid.h
new file mode 100644
index 0000000..8d2acca
--- /dev/null
+++ b/t/unit-tests/lib-oid.h
@@ -0,0 +1,17 @@
+#ifndef LIB_OID_H
+#define LIB_OID_H
+
+#include "hash.h"
+
+/*
+ * Convert arbitrary hex string to object_id.
+ * For example, passing "abc12" will generate
+ * "abc1200000000000000000000000000000000000" hex of length 40 for SHA-1 and
+ * create object_id with that.
+ * WARNING: passing a string of length more than the hexsz of respective hash
+ * algo is not allowed. The hash algo is decided based on GIT_TEST_DEFAULT_HASH
+ * environment variable.
+ */
+int get_oid_arbitrary_hex(const char *s, struct object_id *oid);
+
+#endif /* LIB_OID_H */
diff --git a/t/unit-tests/t-example-decorate.c b/t/unit-tests/t-example-decorate.c
new file mode 100644
index 0000000..a4a75db
--- /dev/null
+++ b/t/unit-tests/t-example-decorate.c
@@ -0,0 +1,82 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "test-lib.h"
+#include "object.h"
+#include "decorate.h"
+#include "repository.h"
+
+struct test_vars {
+ struct object *one, *two, *three;
+ struct decoration n;
+ int decoration_a, decoration_b;
+};
+
+static void t_add(struct test_vars *vars)
+{
+ void *ret = add_decoration(&vars->n, vars->one, &vars->decoration_a);
+
+ if (!check(ret == NULL))
+ test_msg("when adding a brand-new object, NULL should be returned");
+ ret = add_decoration(&vars->n, vars->two, NULL);
+ if (!check(ret == NULL))
+ test_msg("when adding a brand-new object, NULL should be returned");
+}
+
+static void t_readd(struct test_vars *vars)
+{
+ void *ret = add_decoration(&vars->n, vars->one, NULL);
+
+ if (!check(ret == &vars->decoration_a))
+ test_msg("when readding an already existing object, existing decoration should be returned");
+ ret = add_decoration(&vars->n, vars->two, &vars->decoration_b);
+ if (!check(ret == NULL))
+ test_msg("when readding an already existing object, existing decoration should be returned");
+}
+
+static void t_lookup(struct test_vars *vars)
+{
+ void *ret = lookup_decoration(&vars->n, vars->one);
+
+ if (!check(ret == NULL))
+ test_msg("lookup should return added declaration");
+ ret = lookup_decoration(&vars->n, vars->two);
+ if (!check(ret == &vars->decoration_b))
+ test_msg("lookup should return added declaration");
+ ret = lookup_decoration(&vars->n, vars->three);
+ if (!check(ret == NULL))
+ test_msg("lookup for unknown object should return NULL");
+}
+
+static void t_loop(struct test_vars *vars)
+{
+ int i, objects_noticed = 0;
+
+ for (i = 0; i < vars->n.size; i++) {
+ if (vars->n.entries[i].base)
+ objects_noticed++;
+ }
+ if (!check_int(objects_noticed, ==, 2))
+ test_msg("should have 2 objects");
+}
+
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
+{
+ struct object_id one_oid = { { 1 } }, two_oid = { { 2 } }, three_oid = { { 3 } };
+ struct test_vars vars = { 0 };
+
+ vars.one = lookup_unknown_object(the_repository, &one_oid);
+ vars.two = lookup_unknown_object(the_repository, &two_oid);
+ vars.three = lookup_unknown_object(the_repository, &three_oid);
+
+ TEST(t_add(&vars),
+ "Add 2 objects, one with a non-NULL decoration and one with a NULL decoration.");
+ TEST(t_readd(&vars),
+ "When re-adding an already existing object, the old decoration is returned.");
+ TEST(t_lookup(&vars),
+ "Lookup returns the added declarations, or NULL if the object was never added.");
+ TEST(t_loop(&vars), "The user can also loop through all entries.");
+
+ clear_decoration(&vars.n, NULL);
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-hash.c b/t/unit-tests/t-hash.c
new file mode 100644
index 0000000..e9a78bf
--- /dev/null
+++ b/t/unit-tests/t-hash.c
@@ -0,0 +1,84 @@
+#include "test-lib.h"
+#include "hex.h"
+#include "strbuf.h"
+
+static void check_hash_data(const void *data, size_t data_length,
+ const char *expected_hashes[])
+{
+ if (!check(data != NULL)) {
+ test_msg("BUG: NULL data pointer provided");
+ return;
+ }
+
+ for (size_t i = 1; i < ARRAY_SIZE(hash_algos); i++) {
+ git_hash_ctx ctx;
+ unsigned char hash[GIT_MAX_HEXSZ];
+ const struct git_hash_algo *algop = &hash_algos[i];
+
+ algop->init_fn(&ctx);
+ algop->update_fn(&ctx, data, data_length);
+ algop->final_fn(hash, &ctx);
+
+ if (!check_str(hash_to_hex_algop(hash, algop), expected_hashes[i - 1]))
+ test_msg("result does not match with the expected for %s\n", hash_algos[i].name);
+ }
+}
+
+/* Works with a NUL terminated string. Doesn't work if it should contain a NUL character. */
+#define TEST_HASH_STR(data, expected_sha1, expected_sha256) do { \
+ const char *expected_hashes[] = { expected_sha1, expected_sha256 }; \
+ TEST(check_hash_data(data, strlen(data), expected_hashes), \
+ "SHA1 and SHA256 (%s) works", #data); \
+ } while (0)
+
+/* Only works with a literal string, useful when it contains a NUL character. */
+#define TEST_HASH_LITERAL(literal, expected_sha1, expected_sha256) do { \
+ const char *expected_hashes[] = { expected_sha1, expected_sha256 }; \
+ TEST(check_hash_data(literal, (sizeof(literal) - 1), expected_hashes), \
+ "SHA1 and SHA256 (%s) works", #literal); \
+ } while (0)
+
+int cmd_main(int argc, const char **argv)
+{
+ struct strbuf aaaaaaaaaa_100000 = STRBUF_INIT;
+ struct strbuf alphabet_100000 = STRBUF_INIT;
+
+ strbuf_addstrings(&aaaaaaaaaa_100000, "aaaaaaaaaa", 100000);
+ strbuf_addstrings(&alphabet_100000, "abcdefghijklmnopqrstuvwxyz", 100000);
+
+ TEST_HASH_STR("",
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ TEST_HASH_STR("a",
+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb");
+ TEST_HASH_STR("abc",
+ "a9993e364706816aba3e25717850c26c9cd0d89d",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
+ TEST_HASH_STR("message digest",
+ "c12252ceda8be8994d5fa0290a47231c1d16aae3",
+ "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650");
+ TEST_HASH_STR("abcdefghijklmnopqrstuvwxyz",
+ "32d10c7b8cf96570ca04ce37f2a19d84240d3a89",
+ "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73");
+ TEST_HASH_STR(aaaaaaaaaa_100000.buf,
+ "34aa973cd4c4daa4f61eeb2bdbad27316534016f",
+ "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
+ TEST_HASH_STR(alphabet_100000.buf,
+ "e7da7c55b3484fdf52aebec9cbe7b85a98f02fd4",
+ "e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35");
+ TEST_HASH_LITERAL("blob 0\0",
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813");
+ TEST_HASH_LITERAL("blob 3\0abc",
+ "f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f",
+ "c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6");
+ TEST_HASH_LITERAL("tree 0\0",
+ "4b825dc642cb6eb9a060e54bf8d69288fbee4904",
+ "6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321");
+
+ strbuf_release(&aaaaaaaaaa_100000);
+ strbuf_release(&alphabet_100000);
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-oidtree.c b/t/unit-tests/t-oidtree.c
new file mode 100644
index 0000000..a38754b
--- /dev/null
+++ b/t/unit-tests/t-oidtree.c
@@ -0,0 +1,122 @@
+#include "test-lib.h"
+#include "lib-oid.h"
+#include "oidtree.h"
+#include "hash.h"
+#include "hex.h"
+#include "strvec.h"
+
+#define FILL_TREE(tree, ...) \
+ do { \
+ const char *hexes[] = { __VA_ARGS__ }; \
+ if (fill_tree_loc(tree, hexes, ARRAY_SIZE(hexes))) \
+ return; \
+ } while (0)
+
+static int fill_tree_loc(struct oidtree *ot, const char *hexes[], size_t n)
+{
+ for (size_t i = 0; i < n; i++) {
+ struct object_id oid;
+ if (!check_int(get_oid_arbitrary_hex(hexes[i], &oid), ==, 0))
+ return -1;
+ oidtree_insert(ot, &oid);
+ }
+ return 0;
+}
+
+static void check_contains(struct oidtree *ot, const char *hex, int expected)
+{
+ struct object_id oid;
+
+ if (!check_int(get_oid_arbitrary_hex(hex, &oid), ==, 0))
+ return;
+ if (!check_int(oidtree_contains(ot, &oid), ==, expected))
+ test_msg("oid: %s", oid_to_hex(&oid));
+}
+
+struct expected_hex_iter {
+ size_t i;
+ struct strvec expected_hexes;
+ const char *query;
+};
+
+static enum cb_next check_each_cb(const struct object_id *oid, void *data)
+{
+ struct expected_hex_iter *hex_iter = data;
+ struct object_id expected;
+
+ if (!check_int(hex_iter->i, <, hex_iter->expected_hexes.nr)) {
+ test_msg("error: extraneous callback for query: ('%s'), object_id: ('%s')",
+ hex_iter->query, oid_to_hex(oid));
+ return CB_BREAK;
+ }
+
+ if (!check_int(get_oid_arbitrary_hex(hex_iter->expected_hexes.v[hex_iter->i],
+ &expected), ==, 0))
+ ; /* the data is bogus and cannot be used */
+ else if (!check(oideq(oid, &expected)))
+ test_msg("expected: %s\n got: %s\n query: %s",
+ oid_to_hex(&expected), oid_to_hex(oid), hex_iter->query);
+
+ hex_iter->i += 1;
+ return CB_CONTINUE;
+}
+
+LAST_ARG_MUST_BE_NULL
+static void check_each(struct oidtree *ot, const char *query, ...)
+{
+ struct object_id oid;
+ struct expected_hex_iter hex_iter = { .expected_hexes = STRVEC_INIT,
+ .query = query };
+ const char *arg;
+ va_list hex_args;
+
+ va_start(hex_args, query);
+ while ((arg = va_arg(hex_args, const char *)))
+ strvec_push(&hex_iter.expected_hexes, arg);
+ va_end(hex_args);
+
+ if (!check_int(get_oid_arbitrary_hex(query, &oid), ==, 0))
+ return;
+ oidtree_each(ot, &oid, strlen(query), check_each_cb, &hex_iter);
+
+ if (!check_int(hex_iter.i, ==, hex_iter.expected_hexes.nr))
+ test_msg("error: could not find some 'object_id's for query ('%s')", query);
+ strvec_clear(&hex_iter.expected_hexes);
+}
+
+static void setup(void (*f)(struct oidtree *ot))
+{
+ struct oidtree ot;
+
+ oidtree_init(&ot);
+ f(&ot);
+ oidtree_clear(&ot);
+}
+
+static void t_contains(struct oidtree *ot)
+{
+ FILL_TREE(ot, "444", "1", "2", "3", "4", "5", "a", "b", "c", "d", "e");
+ check_contains(ot, "44", 0);
+ check_contains(ot, "441", 0);
+ check_contains(ot, "440", 0);
+ check_contains(ot, "444", 1);
+ check_contains(ot, "4440", 1);
+ check_contains(ot, "4444", 0);
+}
+
+static void t_each(struct oidtree *ot)
+{
+ FILL_TREE(ot, "f", "9", "8", "123", "321", "320", "a", "b", "c", "d", "e");
+ check_each(ot, "12300", "123", NULL);
+ check_each(ot, "3211", NULL); /* should not reach callback */
+ check_each(ot, "3210", "321", NULL);
+ check_each(ot, "32100", "321", NULL);
+ check_each(ot, "32", "320", "321", NULL);
+}
+
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
+{
+ TEST(setup(t_contains), "oidtree insert and contains works");
+ TEST(setup(t_each), "oidtree each works");
+ return test_done();
+}
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
new file mode 100644
index 0000000..4e80bdf
--- /dev/null
+++ b/t/unit-tests/t-reftable-basics.c
@@ -0,0 +1,160 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#include "test-lib.h"
+#include "reftable/basics.h"
+
+struct integer_needle_lesseq_args {
+ int needle;
+ int *haystack;
+};
+
+static int integer_needle_lesseq(size_t i, void *_args)
+{
+ struct integer_needle_lesseq_args *args = _args;
+ return args->needle <= args->haystack[i];
+}
+
+static void test_binsearch(void)
+{
+ int haystack[] = { 2, 4, 6, 8, 10 };
+ struct {
+ int needle;
+ size_t expected_idx;
+ } testcases[] = {
+ {-9000, 0},
+ {-1, 0},
+ {0, 0},
+ {2, 0},
+ {3, 1},
+ {4, 1},
+ {7, 3},
+ {9, 4},
+ {10, 4},
+ {11, 5},
+ {9000, 5},
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct integer_needle_lesseq_args args = {
+ .haystack = haystack,
+ .needle = testcases[i].needle,
+ };
+ size_t idx;
+
+ idx = binsearch(ARRAY_SIZE(haystack), &integer_needle_lesseq, &args);
+ check_int(idx, ==, testcases[i].expected_idx);
+ }
+}
+
+static void test_names_length(void)
+{
+ const char *a[] = { "a", "b", NULL };
+ check_int(names_length(a), ==, 2);
+}
+
+static void test_names_equal(void)
+{
+ const char *a[] = { "a", "b", "c", NULL };
+ const char *b[] = { "a", "b", "d", NULL };
+ const char *c[] = { "a", "b", NULL };
+
+ check(names_equal(a, a));
+ check(!names_equal(a, b));
+ check(!names_equal(a, c));
+}
+
+static void test_parse_names_normal(void)
+{
+ char in1[] = "line\n";
+ char in2[] = "a\nb\nc";
+ char **out = NULL;
+ parse_names(in1, strlen(in1), &out);
+ check_str(out[0], "line");
+ check(!out[1]);
+ free_names(out);
+
+ parse_names(in2, strlen(in2), &out);
+ check_str(out[0], "a");
+ check_str(out[1], "b");
+ check_str(out[2], "c");
+ check(!out[3]);
+ free_names(out);
+}
+
+static void test_parse_names_drop_empty(void)
+{
+ char in[] = "a\n\nb\n";
+ char **out = NULL;
+ parse_names(in, strlen(in), &out);
+ check_str(out[0], "a");
+ /* simply '\n' should be dropped as empty string */
+ check_str(out[1], "b");
+ check(!out[2]);
+ free_names(out);
+}
+
+static void test_common_prefix(void)
+{
+ struct strbuf a = STRBUF_INIT;
+ struct strbuf b = STRBUF_INIT;
+ struct {
+ const char *a, *b;
+ int want;
+ } cases[] = {
+ {"abcdef", "abc", 3},
+ { "abc", "ab", 2 },
+ { "", "abc", 0 },
+ { "abc", "abd", 2 },
+ { "abc", "pqr", 0 },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
+ strbuf_addstr(&a, cases[i].a);
+ strbuf_addstr(&b, cases[i].b);
+ check_int(common_prefix_size(&a, &b), ==, cases[i].want);
+ strbuf_reset(&a);
+ strbuf_reset(&b);
+ }
+ strbuf_release(&a);
+ strbuf_release(&b);
+}
+
+static void test_u24_roundtrip(void)
+{
+ uint32_t in = 0x112233;
+ uint8_t dest[3];
+ uint32_t out;
+ put_be24(dest, in);
+ out = get_be24(dest);
+ check_int(in, ==, out);
+}
+
+static void test_u16_roundtrip(void)
+{
+ uint32_t in = 0xfef1;
+ uint8_t dest[3];
+ uint32_t out;
+ put_be16(dest, in);
+ out = get_be16(dest);
+ check_int(in, ==, out);
+}
+
+int cmd_main(int argc, const char *argv[])
+{
+ TEST(test_common_prefix(), "common_prefix_size works");
+ TEST(test_parse_names_normal(), "parse_names works for basic input");
+ TEST(test_parse_names_drop_empty(), "parse_names drops empty string");
+ TEST(test_binsearch(), "binary search with binsearch works");
+ TEST(test_names_length(), "names_length retuns size of a NULL-terminated string array");
+ TEST(test_names_equal(), "names_equal compares NULL-terminated string arrays");
+ TEST(test_u24_roundtrip(), "put_be24 and get_be24 work");
+ TEST(test_u16_roundtrip(), "put_be16 and get_be16 work");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index de434a4..6027daf 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -2,7 +2,8 @@
#include "strbuf.h"
/* wrapper that supplies tests with an empty, initialized strbuf */
-static void setup(void (*f)(struct strbuf*, void*), void *data)
+static void setup(void (*f)(struct strbuf*, const void*),
+ const void *data)
{
struct strbuf buf = STRBUF_INIT;
@@ -13,7 +14,8 @@ static void setup(void (*f)(struct strbuf*, void*), void *data)
}
/* wrapper that supplies tests with a populated, initialized strbuf */
-static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+static void setup_populated(void (*f)(struct strbuf*, const void*),
+ const char *init_str, const void *data)
{
struct strbuf buf = STRBUF_INIT;
@@ -64,7 +66,7 @@ static void t_dynamic_init(void)
strbuf_release(&buf);
}
-static void t_addch(struct strbuf *buf, void *data)
+static void t_addch(struct strbuf *buf, const void *data)
{
const char *p_ch = data;
const char ch = *p_ch;
@@ -83,7 +85,7 @@ static void t_addch(struct strbuf *buf, void *data)
check_char(buf->buf[buf->len], ==, '\0');
}
-static void t_addstr(struct strbuf *buf, void *data)
+static void t_addstr(struct strbuf *buf, const void *data)
{
const char *text = data;
size_t len = strlen(text);
diff --git a/t/unit-tests/t-strcmp-offset.c b/t/unit-tests/t-strcmp-offset.c
new file mode 100644
index 0000000..fe4c270
--- /dev/null
+++ b/t/unit-tests/t-strcmp-offset.c
@@ -0,0 +1,35 @@
+#include "test-lib.h"
+#include "read-cache-ll.h"
+
+static void check_strcmp_offset(const char *string1, const char *string2,
+ int expect_result, uintmax_t expect_offset)
+{
+ size_t offset;
+ int result = strcmp_offset(string1, string2, &offset);
+
+ /*
+ * Because different CRTs behave differently, only rely on signs of the
+ * result values.
+ */
+ result = (result < 0 ? -1 :
+ result > 0 ? 1 :
+ 0);
+
+ check_int(result, ==, expect_result);
+ check_uint((uintmax_t)offset, ==, expect_offset);
+}
+
+#define TEST_STRCMP_OFFSET(string1, string2, expect_result, expect_offset) \
+ TEST(check_strcmp_offset(string1, string2, expect_result, \
+ expect_offset), \
+ "strcmp_offset(%s, %s) works", #string1, #string2)
+
+int cmd_main(int argc, const char **argv)
+{
+ TEST_STRCMP_OFFSET("abc", "abc", 0, 3);
+ TEST_STRCMP_OFFSET("abc", "def", -1, 0);
+ TEST_STRCMP_OFFSET("abc", "abz", -1, 2);
+ TEST_STRCMP_OFFSET("abc", "abcdef", -1, 3);
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-strvec.c b/t/unit-tests/t-strvec.c
new file mode 100644
index 0000000..d4615ab
--- /dev/null
+++ b/t/unit-tests/t-strvec.c
@@ -0,0 +1,272 @@
+#include "test-lib.h"
+#include "strbuf.h"
+#include "strvec.h"
+
+#define check_strvec(vec, ...) \
+ check_strvec_loc(TEST_LOCATION(), vec, __VA_ARGS__)
+LAST_ARG_MUST_BE_NULL
+static void check_strvec_loc(const char *loc, struct strvec *vec, ...)
+{
+ va_list ap;
+ size_t nr = 0;
+
+ va_start(ap, vec);
+ while (1) {
+ const char *str = va_arg(ap, const char *);
+ if (!str)
+ break;
+
+ if (!check_uint(vec->nr, >, nr) ||
+ !check_uint(vec->alloc, >, nr) ||
+ !check_str(vec->v[nr], str)) {
+ struct strbuf msg = STRBUF_INIT;
+ strbuf_addf(&msg, "strvec index %"PRIuMAX, (uintmax_t) nr);
+ test_assert(loc, msg.buf, 0);
+ strbuf_release(&msg);
+ va_end(ap);
+ return;
+ }
+
+ nr++;
+ }
+ va_end(ap);
+
+ check_uint(vec->nr, ==, nr);
+ check_uint(vec->alloc, >=, nr);
+ check_pointer_eq(vec->v[nr], NULL);
+}
+
+static void t_static_init(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ check_pointer_eq(vec.v, empty_strvec);
+ check_uint(vec.nr, ==, 0);
+ check_uint(vec.alloc, ==, 0);
+}
+
+static void t_dynamic_init(void)
+{
+ struct strvec vec;
+ strvec_init(&vec);
+ check_pointer_eq(vec.v, empty_strvec);
+ check_uint(vec.nr, ==, 0);
+ check_uint(vec.alloc, ==, 0);
+}
+
+static void t_clear(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_push(&vec, "foo");
+ strvec_clear(&vec);
+ check_pointer_eq(vec.v, empty_strvec);
+ check_uint(vec.nr, ==, 0);
+ check_uint(vec.alloc, ==, 0);
+}
+
+static void t_push(void)
+{
+ struct strvec vec = STRVEC_INIT;
+
+ strvec_push(&vec, "foo");
+ check_strvec(&vec, "foo", NULL);
+
+ strvec_push(&vec, "bar");
+ check_strvec(&vec, "foo", "bar", NULL);
+
+ strvec_clear(&vec);
+}
+
+static void t_pushf(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushf(&vec, "foo: %d", 1);
+ check_strvec(&vec, "foo: 1", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_pushl(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ check_strvec(&vec, "foo", "bar", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_pushv(void)
+{
+ const char *strings[] = {
+ "foo", "bar", "baz", NULL,
+ };
+ struct strvec vec = STRVEC_INIT;
+
+ strvec_pushv(&vec, strings);
+ check_strvec(&vec, "foo", "bar", "baz", NULL);
+
+ strvec_clear(&vec);
+}
+
+static void t_replace_at_head(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_replace(&vec, 0, "replaced");
+ check_strvec(&vec, "replaced", "bar", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_replace_at_tail(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_replace(&vec, 2, "replaced");
+ check_strvec(&vec, "foo", "bar", "replaced", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_replace_in_between(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_replace(&vec, 1, "replaced");
+ check_strvec(&vec, "foo", "replaced", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_replace_with_substring(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", NULL);
+ strvec_replace(&vec, 0, vec.v[0] + 1);
+ check_strvec(&vec, "oo", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_remove_at_head(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_remove(&vec, 0);
+ check_strvec(&vec, "bar", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_remove_at_tail(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_remove(&vec, 2);
+ check_strvec(&vec, "foo", "bar", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_remove_in_between(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_remove(&vec, 1);
+ check_strvec(&vec, "foo", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_pop_empty_array(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pop(&vec);
+ check_strvec(&vec, NULL);
+ strvec_clear(&vec);
+}
+
+static void t_pop_non_empty_array(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_pushl(&vec, "foo", "bar", "baz", NULL);
+ strvec_pop(&vec);
+ check_strvec(&vec, "foo", "bar", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_empty_string(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, "");
+ check_strvec(&vec, NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_single_item(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, "foo");
+ check_strvec(&vec, "foo", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_multiple_items(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, "foo bar baz");
+ check_strvec(&vec, "foo", "bar", "baz", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_whitespace_only(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, " \t\n");
+ check_strvec(&vec, NULL);
+ strvec_clear(&vec);
+}
+
+static void t_split_multiple_consecutive_whitespaces(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ strvec_split(&vec, "foo\n\t bar");
+ check_strvec(&vec, "foo", "bar", NULL);
+ strvec_clear(&vec);
+}
+
+static void t_detach(void)
+{
+ struct strvec vec = STRVEC_INIT;
+ const char **detached;
+
+ strvec_push(&vec, "foo");
+
+ detached = strvec_detach(&vec);
+ check_str(detached[0], "foo");
+ check_pointer_eq(detached[1], NULL);
+
+ check_pointer_eq(vec.v, empty_strvec);
+ check_uint(vec.nr, ==, 0);
+ check_uint(vec.alloc, ==, 0);
+
+ free((char *) detached[0]);
+ free(detached);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ TEST(t_static_init(), "static initialization");
+ TEST(t_dynamic_init(), "dynamic initialization");
+ TEST(t_clear(), "clear");
+ TEST(t_push(), "push");
+ TEST(t_pushf(), "pushf");
+ TEST(t_pushl(), "pushl");
+ TEST(t_pushv(), "pushv");
+ TEST(t_replace_at_head(), "replace at head");
+ TEST(t_replace_in_between(), "replace in between");
+ TEST(t_replace_at_tail(), "replace at tail");
+ TEST(t_replace_with_substring(), "replace with substring");
+ TEST(t_remove_at_head(), "remove at head");
+ TEST(t_remove_in_between(), "remove in between");
+ TEST(t_remove_at_tail(), "remove at tail");
+ TEST(t_pop_empty_array(), "pop with empty array");
+ TEST(t_pop_non_empty_array(), "pop with non-empty array");
+ TEST(t_split_empty_string(), "split empty string");
+ TEST(t_split_single_item(), "split single item");
+ TEST(t_split_multiple_items(), "split multiple items");
+ TEST(t_split_whitespace_only(), "split whitespace only");
+ TEST(t_split_multiple_consecutive_whitespaces(), "split multiple consecutive whitespaces");
+ TEST(t_detach(), "detach");
+ return test_done();
+}
diff --git a/t/unit-tests/t-trailer.c b/t/unit-tests/t-trailer.c
new file mode 100644
index 0000000..2ecca35
--- /dev/null
+++ b/t/unit-tests/t-trailer.c
@@ -0,0 +1,315 @@
+#include "test-lib.h"
+#include "trailer.h"
+
+struct contents {
+ const char *raw;
+ const char *key;
+ const char *val;
+};
+
+static void t_trailer_iterator(const char *msg, size_t num_expected,
+ struct contents *contents)
+{
+ struct trailer_iterator iter;
+ size_t i = 0;
+
+ trailer_iterator_init(&iter, msg);
+ while (trailer_iterator_advance(&iter)) {
+ if (num_expected) {
+ check_str(iter.raw, contents[i].raw);
+ check_str(iter.key.buf, contents[i].key);
+ check_str(iter.val.buf, contents[i].val);
+ }
+ i++;
+ }
+ trailer_iterator_release(&iter);
+
+ check_uint(i, ==, num_expected);
+}
+
+static void run_t_trailer_iterator(void)
+{
+
+ static struct test_cases {
+ const char *name;
+ const char *msg;
+ size_t num_expected;
+ struct contents contents[10];
+ } tc[] = {
+ {
+ "empty input",
+ "",
+ 0,
+ {{0}},
+ },
+ {
+ "no newline at beginning",
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 0,
+ {{0}},
+ },
+ {
+ "newline at beginning",
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 3,
+ {
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "without body text",
+ "subject: foo bar\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 3,
+ {
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with body text, without divider",
+ "my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n"
+ "Signed-off-by: x\n",
+ 4,
+ {
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with body text, without divider (second trailer block)",
+ "my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n"
+ "Signed-off-by: x\n"
+ "\n"
+ /*
+ * Because this is the last trailer block, it takes
+ * precedence over the first one encountered above.
+ */
+ "Helped-by: x\n"
+ "Signed-off-by: x\n",
+ 2,
+ {
+ {
+ .raw = "Helped-by: x\n",
+ .key = "Helped-by",
+ .val = "x",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with body text, with divider",
+ "my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "---\n"
+ "\n"
+ /*
+ * This trailer still counts because the iterator
+ * always ignores the divider.
+ */
+ "Signed-off-by: x\n",
+ 1,
+ {
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with non-trailer lines in trailer block",
+ "subject: foo bar\n"
+ "\n"
+ /*
+ * Even though this trailer block has a non-trailer line
+ * in it, it's still a valid trailer block because it's
+ * at least 25% trailers and is Git-generated (see
+ * git_generated_prefixes[] in trailer.c).
+ */
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "Signed-off-by: x\n",
+ /*
+ * Even though there is only really 1 real "trailer"
+ * (Signed-off-by), we still have 4 trailer objects
+ * because we still want to iterate through the entire
+ * block.
+ */
+ 4,
+ {
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ },
+ },
+ {
+ "with non-trailer lines (one too many) in trailer block",
+ "subject: foo bar\n"
+ "\n"
+ /*
+ * This block has only 20% trailers, so it's below the
+ * 25% threshold.
+ */
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "Signed-off-by: x\n",
+ 0,
+ {{0}},
+ },
+ {
+ "with non-trailer lines (only 1) in trailer block, but no Git-generated trailers",
+ "subject: foo bar\n"
+ "\n"
+ /*
+ * This block has only 1 non-trailer out of 10 (IOW, 90%
+ * trailers) but is not considered a trailer block
+ * because the 25% threshold only applies to cases where
+ * there was a Git-generated trailer.
+ */
+ "Reviewed-by: x\n"
+ "Reviewed-by: x\n"
+ "Reviewed-by: x\n"
+ "Helped-by: x\n"
+ "Helped-by: x\n"
+ "Helped-by: x\n"
+ "Acked-by: x\n"
+ "Acked-by: x\n"
+ "Acked-by: x\n"
+ "not a trailer line\n",
+ 0,
+ {{0}},
+ },
+ };
+
+ for (int i = 0; i < sizeof(tc) / sizeof(tc[0]); i++) {
+ TEST(t_trailer_iterator(tc[i].msg,
+ tc[i].num_expected,
+ tc[i].contents),
+ "%s", tc[i].name);
+ }
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ run_t_trailer_iterator();
+ return test_done();
+}
diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c
index 66d6980..3c513ce 100644
--- a/t/unit-tests/test-lib.c
+++ b/t/unit-tests/test-lib.c
@@ -318,6 +318,19 @@ int check_bool_loc(const char *loc, const char *check, int ok)
union test__tmp test__tmp[2];
+int check_pointer_eq_loc(const char *loc, const char *check, int ok,
+ const void *a, const void *b)
+{
+ int ret = test_assert(loc, check, ok);
+
+ if (!ret) {
+ test_msg(" left: %p", a);
+ test_msg(" right: %p", b);
+ }
+
+ return ret;
+}
+
int check_int_loc(const char *loc, const char *check, int ok,
intmax_t a, intmax_t b)
{
diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h
index a8f07ae..2de6d71 100644
--- a/t/unit-tests/test-lib.h
+++ b/t/unit-tests/test-lib.h
@@ -79,6 +79,18 @@ int check_bool_loc(const char *loc, const char *check, int ok);
* Compare two integers. Prints a message with the two values if the
* comparison fails. NB this is not thread safe.
*/
+#define check_pointer_eq(a, b) \
+ (test__tmp[0].p = (a), test__tmp[1].p = (b), \
+ check_pointer_eq_loc(TEST_LOCATION(), #a" == "#b, \
+ test__tmp[0].p == test__tmp[1].p, \
+ test__tmp[0].p, test__tmp[1].p))
+int check_pointer_eq_loc(const char *loc, const char *check, int ok,
+ const void *a, const void *b);
+
+/*
+ * Compare two integers. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
#define check_int(a, op, b) \
(test__tmp[0].i = (a), test__tmp[1].i = (b), \
check_int_loc(TEST_LOCATION(), #a" "#op" "#b, \
@@ -136,6 +148,7 @@ union test__tmp {
intmax_t i;
uintmax_t u;
char c;
+ const void *p;
};
extern union test__tmp test__tmp[2];
diff --git a/tag.c b/tag.c
index fc3834d..d24170e 100644
--- a/tag.c
+++ b/tag.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "tag.h"
@@ -91,10 +93,10 @@ struct object *deref_tag(struct repository *r, struct object *o, const char *war
return o;
}
-struct object *deref_tag_noverify(struct object *o)
+struct object *deref_tag_noverify(struct repository *r, struct object *o)
{
while (o && o->type == OBJ_TAG) {
- o = parse_object(the_repository, &o->oid);
+ o = parse_object(r, &o->oid);
if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
o = ((struct tag *)o)->tagged;
else
diff --git a/tag.h b/tag.h
index 3ce8e72..c49d7c1 100644
--- a/tag.h
+++ b/tag.h
@@ -16,7 +16,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
int parse_tag(struct tag *item);
void release_tag_memory(struct tag *t);
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-struct object *deref_tag_noverify(struct object *);
+struct object *deref_tag_noverify(struct repository *r, struct object *);
int gpg_verify_tag(const struct object_id *oid,
const char *name_to_report, unsigned flags);
struct object_id *get_tagged_oid(struct tag *tag);
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 3509258..a8e4553 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tmp-objdir.h"
#include "abspath.h"
diff --git a/trailer.c b/trailer.c
index c72ae68..72e5136 100644
--- a/trailer.c
+++ b/trailer.c
@@ -11,6 +11,27 @@
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
+struct trailer_info {
+ /*
+ * True if there is a blank line before the location pointed to by
+ * trailer_block_start.
+ */
+ int blank_line_before_trailer;
+
+ /*
+ * Offsets to the trailer block start and end positions in the input
+ * string. If no trailer block is found, these are both set to the
+ * "true" end of the input (find_end_of_log_message()).
+ */
+ size_t trailer_block_start, trailer_block_end;
+
+ /*
+ * Array of trailers found.
+ */
+ char **trailers;
+ size_t trailer_nr;
+};
+
struct conf_info {
char *name;
char *key;
@@ -42,7 +63,7 @@ struct arg_item {
static LIST_HEAD(conf_head);
-static char *separators = ":";
+static const char *separators = ":";
static int configured;
@@ -952,58 +973,16 @@ static void unfold_value(struct strbuf *val)
strbuf_release(&out);
}
-/*
- * Parse trailers in "str", populating the trailer info and "head"
- * linked list structure.
- */
-void parse_trailers(const struct process_trailer_options *opts,
- struct trailer_info *info,
- const char *str,
- struct list_head *head)
+static struct trailer_info *trailer_info_new(void)
{
- struct strbuf tok = STRBUF_INIT;
- struct strbuf val = STRBUF_INIT;
- size_t i;
-
- trailer_info_get(opts, str, info);
-
- for (i = 0; i < info->trailer_nr; i++) {
- int separator_pos;
- char *trailer = info->trailers[i];
- if (starts_with(trailer, comment_line_str))
- continue;
- separator_pos = find_separator(trailer, separators);
- 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 if (!opts->only_trailers) {
- strbuf_addstr(&val, trailer);
- strbuf_strip_suffix(&val, "\n");
- add_trailer_item(head,
- NULL,
- strbuf_detach(&val, NULL));
- }
- }
+ struct trailer_info *info = xcalloc(1, sizeof(*info));
+ return info;
}
-void free_trailers(struct list_head *trailers)
+static struct trailer_info *trailer_info_get(const struct process_trailer_options *opts,
+ const char *str)
{
- struct list_head *pos, *p;
- list_for_each_safe(pos, p, trailers) {
- list_del(pos);
- free_trailer_item(list_entry(pos, struct trailer_item, list));
- }
-}
-
-void trailer_info_get(const struct process_trailer_options *opts,
- const char *str,
- struct trailer_info *info)
-{
+ struct trailer_info *info = trailer_info_new();
size_t end_of_log_message = 0, trailer_block_start = 0;
struct strbuf **trailer_lines, **ptr;
char **trailer_strings = NULL;
@@ -1042,6 +1021,73 @@ void trailer_info_get(const struct process_trailer_options *opts,
info->trailer_block_end = end_of_log_message;
info->trailers = trailer_strings;
info->trailer_nr = nr;
+
+ return info;
+}
+
+/*
+ * Parse trailers in "str", populating the trailer info and "trailer_objects"
+ * linked list structure.
+ */
+struct trailer_info *parse_trailers(const struct process_trailer_options *opts,
+ const char *str,
+ struct list_head *trailer_objects)
+{
+ struct trailer_info *info;
+ struct strbuf tok = STRBUF_INIT;
+ struct strbuf val = STRBUF_INIT;
+ size_t i;
+
+ info = trailer_info_get(opts, str);
+
+ for (i = 0; i < info->trailer_nr; i++) {
+ int separator_pos;
+ char *trailer = info->trailers[i];
+ if (starts_with(trailer, comment_line_str))
+ continue;
+ separator_pos = find_separator(trailer, separators);
+ if (separator_pos >= 1) {
+ parse_trailer(&tok, &val, NULL, trailer,
+ separator_pos);
+ if (opts->unfold)
+ unfold_value(&val);
+ add_trailer_item(trailer_objects,
+ strbuf_detach(&tok, NULL),
+ strbuf_detach(&val, NULL));
+ } else if (!opts->only_trailers) {
+ strbuf_addstr(&val, trailer);
+ strbuf_strip_suffix(&val, "\n");
+ add_trailer_item(trailer_objects,
+ NULL,
+ strbuf_detach(&val, NULL));
+ }
+ }
+
+ return info;
+}
+
+void free_trailers(struct list_head *trailers)
+{
+ struct list_head *pos, *p;
+ list_for_each_safe(pos, p, trailers) {
+ list_del(pos);
+ free_trailer_item(list_entry(pos, struct trailer_item, list));
+ }
+}
+
+size_t trailer_block_start(struct trailer_info *info)
+{
+ return info->trailer_block_start;
+}
+
+size_t trailer_block_end(struct trailer_info *info)
+{
+ return info->trailer_block_end;
+}
+
+int blank_line_before_trailer_block(struct trailer_info *info)
+{
+ return info->blank_line_before_trailer;
}
void trailer_info_release(struct trailer_info *info)
@@ -1050,6 +1096,7 @@ void trailer_info_release(struct trailer_info *info)
for (i = 0; i < info->trailer_nr; i++)
free(info->trailers[i]);
free(info->trailers);
+ free(info);
}
void format_trailers(const struct process_trailer_options *opts,
@@ -1117,21 +1164,19 @@ void format_trailers_from_commit(const struct process_trailer_options *opts,
struct strbuf *out)
{
LIST_HEAD(trailer_objects);
- struct trailer_info info;
-
- parse_trailers(opts, &info, msg, &trailer_objects);
+ struct trailer_info *info = parse_trailers(opts, msg, &trailer_objects);
/* If we want the whole block untouched, we can take the fast path. */
if (!opts->only_trailers && !opts->unfold && !opts->filter &&
!opts->separator && !opts->key_only && !opts->value_only &&
!opts->key_value_separator) {
- strbuf_add(out, msg + info.trailer_block_start,
- info.trailer_block_end - info.trailer_block_start);
+ strbuf_add(out, msg + info->trailer_block_start,
+ info->trailer_block_end - info->trailer_block_start);
} else
format_trailers(opts, &trailer_objects, out);
free_trailers(&trailer_objects);
- trailer_info_release(&info);
+ trailer_info_release(info);
}
void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
@@ -1140,23 +1185,21 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
strbuf_init(&iter->key, 0);
strbuf_init(&iter->val, 0);
opts.no_divider = 1;
- trailer_info_get(&opts, msg, &iter->internal.info);
+ iter->internal.info = trailer_info_get(&opts, msg);
iter->internal.cur = 0;
}
int trailer_iterator_advance(struct trailer_iterator *iter)
{
- while (iter->internal.cur < iter->internal.info.trailer_nr) {
- char *trailer = iter->internal.info.trailers[iter->internal.cur++];
- int separator_pos = find_separator(trailer, separators);
+ if (iter->internal.cur < iter->internal.info->trailer_nr) {
+ char *line = iter->internal.info->trailers[iter->internal.cur++];
+ int separator_pos = find_separator(line, separators);
- if (separator_pos < 1)
- continue; /* not a real trailer */
-
+ iter->raw = line;
strbuf_reset(&iter->key);
strbuf_reset(&iter->val);
parse_trailer(&iter->key, &iter->val, NULL,
- trailer, separator_pos);
+ line, separator_pos);
/* Always unfold values during iteration. */
unfold_value(&iter->val);
return 1;
@@ -1166,7 +1209,19 @@ int trailer_iterator_advance(struct trailer_iterator *iter)
void trailer_iterator_release(struct trailer_iterator *iter)
{
- trailer_info_release(&iter->internal.info);
+ trailer_info_release(iter->internal.info);
strbuf_release(&iter->val);
strbuf_release(&iter->key);
}
+
+int amend_file_with_trailers(const char *path, const struct strvec *trailer_args)
+{
+ struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+ run_trailer.git_cmd = 1;
+ strvec_pushl(&run_trailer.args, "interpret-trailers",
+ "--in-place", "--no-divider",
+ path, NULL);
+ strvec_pushv(&run_trailer.args, trailer_args->v);
+ return run_command(&run_trailer);
+}
diff --git a/trailer.h b/trailer.h
index 9f42aa7..6eb53df 100644
--- a/trailer.h
+++ b/trailer.h
@@ -4,6 +4,9 @@
#include "list.h"
#include "strbuf.h"
+struct trailer_info;
+struct strvec;
+
enum trailer_where {
WHERE_DEFAULT,
WHERE_END,
@@ -29,27 +32,6 @@ 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
- * trailer_block_start.
- */
- int blank_line_before_trailer;
-
- /*
- * Offsets to the trailer block start and end positions in the input
- * string. If no trailer block is found, these are both set to the
- * "true" end of the input (find_end_of_log_message()).
- */
- size_t trailer_block_start, trailer_block_end;
-
- /*
- * Array of trailers found.
- */
- char **trailers;
- size_t trailer_nr;
-};
-
/*
* A list that represents newly-added trailers, such as those provided
* with the --trailer command line option of git-interpret-trailers.
@@ -89,15 +71,63 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head,
void process_trailers_lists(struct list_head *head,
struct list_head *arg_head);
-void parse_trailers(const struct process_trailer_options *,
- struct trailer_info *,
- const char *str,
- struct list_head *head);
+/*
+ * Given some input string "str", return a pointer to an opaque trailer_info
+ * structure. Also populate the trailer_objects list with parsed trailer
+ * objects. Internally this calls trailer_info_get() to get the opaque pointer,
+ * but does some extra work to populate the trailer_objects linked list.
+ *
+ * The opaque trailer_info pointer can be used to check the position of the
+ * trailer block as offsets relative to the beginning of "str" in
+ * trailer_block_start() and trailer_block_end().
+ * blank_line_before_trailer_block() returns 1 if there is a blank line just
+ * before the trailer block. All of these functions are useful for preserving
+ * the input before and after the trailer block, if we were to write out the
+ * original input (but with the trailer block itself modified); see
+ * builtin/interpret-trailers.c for an example.
+ *
+ * For iterating through the parsed trailer block (if you don't care about the
+ * position of the trailer block itself in the context of the larger string text
+ * from which it was parsed), please see trailer_iterator_init() which uses the
+ * trailer_info struct internally.
+ *
+ * Lastly, callers should call trailer_info_release() when they are done using
+ * the opaque pointer.
+ *
+ * NOTE: Callers should treat both trailer_info and trailer_objects as
+ * read-only items, because there is some overlap between the two (trailer_info
+ * has "char **trailers" string array, and trailer_objects will have the same
+ * data but as a linked list of trailer_item objects). This API does not perform
+ * any synchronization between the two. In the future we should be able to
+ * reduce the duplication and use just the linked list.
+ */
+struct trailer_info *parse_trailers(const struct process_trailer_options *,
+ const char *str,
+ struct list_head *trailer_objects);
-void trailer_info_get(const struct process_trailer_options *,
- const char *str,
- struct trailer_info *);
+/*
+ * Return the offset of the start of the trailer block. That is, 0 is the start
+ * of the input ("str" in parse_trailers()) and some other positive number
+ * indicates how many bytes we have to skip over before we get to the beginning
+ * of the trailer block.
+ */
+size_t trailer_block_start(struct trailer_info *);
+/*
+ * Return the end of the trailer block, again relative to the start of the
+ * input.
+ */
+size_t trailer_block_end(struct trailer_info *);
+
+/*
+ * Return 1 if the trailer block had an extra newline (blank line) just before
+ * it.
+ */
+int blank_line_before_trailer_block(struct trailer_info *);
+
+/*
+ * Free trailer_info struct.
+ */
void trailer_info_release(struct trailer_info *info);
void trailer_config_init(void);
@@ -125,12 +155,19 @@ void format_trailers_from_commit(const struct process_trailer_options *,
* trailer_iterator_release(&iter);
*/
struct trailer_iterator {
+ /*
+ * Raw line (e.g., "foo: bar baz") before being parsed as a trailer
+ * key/val pair as part of a trailer block (as the "key" and "val"
+ * fields below). If a line fails to parse as a trailer, then the "key"
+ * will be the entire line and "val" will be the empty string.
+ */
+ const char *raw;
struct strbuf key;
struct strbuf val;
/* private */
struct {
- struct trailer_info info;
+ struct trailer_info *info;
size_t cur;
} internal;
};
@@ -158,4 +195,11 @@ int trailer_iterator_advance(struct trailer_iterator *iter);
*/
void trailer_iterator_release(struct trailer_iterator *iter);
+/*
+ * Augment a file to add trailers to it by running git-interpret-trailers.
+ * This calls run_command() and its return value is the same (i.e. 0 for
+ * success, various non-zero for other errors). See run-command.h.
+ */
+int amend_file_with_trailers(const char *path, const struct strvec *trailer_args);
+
#endif /* TRAILER_H */
diff --git a/transport-helper.c b/transport-helper.c
index 8d284b2..09b3560 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "transport.h"
#include "quote.h"
@@ -22,7 +24,7 @@
static int debug;
struct helper_data {
- const char *name;
+ char *name;
struct child_process *helper;
FILE *out;
unsigned fetch : 1,
@@ -111,6 +113,7 @@ static void do_take_over(struct transport *transport)
data = (struct helper_data *)transport->data;
transport_take_over(transport, data->helper);
fclose(data->out);
+ free(data->name);
free(data);
}
@@ -253,6 +256,7 @@ static int disconnect_helper(struct transport *transport)
close(data->helper->out);
fclose(data->out);
res = finish_command(data->helper);
+ FREE_AND_NULL(data->name);
FREE_AND_NULL(data->helper);
}
return res;
@@ -551,7 +555,7 @@ static int fetch_with_import(struct transport *transport,
else
private = xstrdup(name);
if (private) {
- if (read_ref(private, &posn->old_oid) < 0)
+ if (refs_read_ref(get_main_ref_store(the_repository), private, &posn->old_oid) < 0)
die(_("could not read ref %s"), private);
free(private);
}
@@ -923,8 +927,10 @@ static int push_update_refs_status(struct helper_data *data,
private = apply_refspecs(&data->rs, ref->name);
if (!private)
continue;
- update_ref("update by helper", private, &(ref->new_oid),
- NULL, 0, 0);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "update by helper", private,
+ &(ref->new_oid),
+ NULL, 0, 0);
free(private);
} else {
for (report = ref->report; report; report = report->next) {
@@ -934,11 +940,12 @@ static int push_update_refs_status(struct helper_data *data,
: ref->name);
if (!private)
continue;
- update_ref("update by helper", private,
- report->new_oid
- ? report->new_oid
- : &(ref->new_oid),
- NULL, 0, 0);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "update by helper", private,
+ report->new_oid
+ ? report->new_oid
+ : &(ref->new_oid),
+ NULL, 0, 0);
free(private);
}
}
@@ -1105,9 +1112,11 @@ static int push_refs_with_export(struct transport *transport,
int flag;
/* Follow symbolic refs (mainly for HEAD). */
- name = resolve_ref_unsafe(ref->peer_ref->name,
- RESOLVE_REF_READING,
- &oid, &flag);
+ name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ ref->peer_ref->name,
+ RESOLVE_REF_READING,
+ &oid,
+ &flag);
if (!name || !(flag & REF_ISSYMREF))
name = ref->peer_ref->name;
@@ -1252,7 +1261,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
if (eon) {
if (has_attribute(eon + 1, "unchanged")) {
(*tail)->status |= REF_STATUS_UPTODATE;
- if (read_ref((*tail)->name, &(*tail)->old_oid) < 0)
+ if (refs_read_ref(get_main_ref_store(the_repository), (*tail)->name, &(*tail)->old_oid) < 0)
die(_("could not read ref %s"),
(*tail)->name);
}
@@ -1292,7 +1301,7 @@ static struct transport_vtable vtable = {
int transport_helper_init(struct transport *transport, const char *name)
{
struct helper_data *data = xcalloc(1, sizeof(*data));
- data->name = name;
+ data->name = xstrdup(name);
transport_check_allowed(name);
diff --git a/transport.c b/transport.c
index df518ea..139721a 100644
--- a/transport.c
+++ b/transport.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
@@ -100,8 +102,9 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
/* Follow symbolic refs (mainly for HEAD). */
localname = ref->peer_ref->name;
remotename = ref->name;
- tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
- NULL, &flag);
+ tmp = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ localname, RESOLVE_REF_READING,
+ NULL, &flag);
if (tmp && flag & REF_ISSYMREF &&
starts_with(tmp, "refs/heads/"))
localname = tmp;
@@ -543,10 +546,12 @@ static void update_one_tracking_ref(struct remote *remote, char *refname,
if (verbose)
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
if (deletion)
- delete_ref(NULL, rs.dst, NULL, 0);
+ refs_delete_ref(get_main_ref_store(the_repository),
+ NULL, rs.dst, NULL, 0);
else
- update_ref("update by push", rs.dst, new_oid,
- NULL, 0, 0);
+ refs_update_ref(get_main_ref_store(the_repository),
+ "update by push", rs.dst, new_oid,
+ NULL, 0, 0);
free(rs.dst);
}
}
@@ -814,7 +819,8 @@ void transport_print_push_status(const char *dest, struct ref *refs,
if (transport_color_config() < 0)
warning(_("could not parse transport.color.* config"));
- head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
+ head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+ RESOLVE_REF_READING, NULL, NULL);
if (verbose) {
for (ref = refs; ref; ref = ref->next)
@@ -1108,6 +1114,7 @@ static struct transport_vtable builtin_smart_vtable = {
struct transport *transport_get(struct remote *remote, const char *url)
{
const char *helper;
+ const char *p;
struct transport *ret = xcalloc(1, sizeof(*ret));
ret->progress = isatty(2);
@@ -1123,19 +1130,15 @@ struct transport *transport_get(struct remote *remote, const char *url)
ret->remote = remote;
helper = remote->foreign_vcs;
- if (!url && remote->url)
- url = remote->url[0];
+ if (!url)
+ url = remote->url.v[0];
ret->url = url;
- /* maybe it is a foreign URL? */
- if (url) {
- const char *p = url;
-
- while (is_urlschemechar(p == url, *p))
- p++;
- if (starts_with(p, "::"))
- helper = xstrndup(url, p - url);
- }
+ p = url;
+ while (is_urlschemechar(p == url, *p))
+ p++;
+ if (starts_with(p, "::"))
+ helper = xstrndup(url, p - url);
if (helper) {
transport_helper_init(ret, helper);
@@ -1172,6 +1175,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
int len = external_specification_len(url);
char *handler = xmemdupz(url, len);
transport_helper_init(ret, handler);
+ free(handler);
}
if (ret->smart_options) {
diff --git a/tree-diff.c b/tree-diff.c
index 4610777..9252481 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -8,6 +8,7 @@
#include "tree.h"
#include "tree-walk.h"
#include "environment.h"
+#include "repository.h"
/*
* Some mode bits are also used internally for computations.
diff --git a/tree-walk.c b/tree-walk.c
index 6565d9a..a033397 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tree-walk.h"
#include "dir.h"
@@ -38,8 +40,8 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
desc->entry.path = path;
desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
desc->entry.pathlen = len - 1;
- oidread_algop(&desc->entry.oid, (const unsigned char *)path + len,
- desc->algo);
+ oidread(&desc->entry.oid, (const unsigned char *)path + len,
+ desc->algo);
return 0;
}
diff --git a/tree-walk.h b/tree-walk.h
index 0b1067f..aaea689 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -1,7 +1,7 @@
#ifndef TREE_WALK_H
#define TREE_WALK_H
-#include "hash-ll.h"
+#include "hash.h"
struct index_state;
struct repository;
diff --git a/tree.c b/tree.c
index 7973d3f..ad86ad1 100644
--- a/tree.c
+++ b/tree.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "hex.h"
#include "tree.h"
diff --git a/unpack-trees.c b/unpack-trees.c
index c2b20b8..7dc884f 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "strvec.h"
@@ -2318,7 +2320,8 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
if (S_ISGITLINK(ce->ce_mode)) {
struct object_id oid;
- int sub_head = resolve_gitlink_ref(ce->name, "HEAD", &oid);
+ int sub_head = repo_resolve_gitlink_ref(the_repository, ce->name,
+ "HEAD", &oid);
/*
* If we are not going to update the submodule, then
* we don't care.
diff --git a/upload-pack.c b/upload-pack.c
index 902144b..0052c6a 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
@@ -94,7 +96,7 @@ struct upload_pack_data {
struct packet_writer writer;
- const char *pack_objects_hook;
+ char *pack_objects_hook;
unsigned stateless_rpc : 1; /* v0 only */
unsigned no_done : 1; /* v0 only */
@@ -618,7 +620,8 @@ static void for_each_namespaced_ref_1(each_ref_fn fn,
if (allow_hidden_refs(data->allow_uor))
excludes = hidden_refs_to_excludes(&data->hidden_refs);
- for_each_namespaced_ref(excludes, fn, data);
+ refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
+ excludes, fn, data);
}
@@ -873,7 +876,8 @@ static void deepen(struct upload_pack_data *data, int depth)
* Checking for reachable shallows requires that our refs be
* marked with OUR_REF.
*/
- head_ref_namespaced(check_ref, data);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ check_ref, data);
for_each_namespaced_ref_1(check_ref, data);
get_reachable_list(data, &reachable_shallows);
@@ -1267,7 +1271,7 @@ static void write_v0_ref(struct upload_pack_data *data,
packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
}
capabilities = NULL;
- if (!peel_iterated_oid(oid, &peeled))
+ if (!peel_iterated_oid(the_repository, oid, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return;
}
@@ -1288,7 +1292,8 @@ static int find_symref(const char *refname,
if ((flag & REF_ISSYMREF) == 0)
return 0;
- symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
+ symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, strip_namespace(refname));
@@ -1413,13 +1418,15 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
if (data.timeout)
data.daemon_mode = 1;
- head_ref_namespaced(find_symref, &data.symref);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ find_symref, &data.symref);
if (advertise_refs || !data.stateless_rpc) {
reset_timeout(data.timeout);
if (advertise_refs)
data.no_done = 1;
- head_ref_namespaced(send_ref, &data);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
const char *refname = "capabilities^{}";
@@ -1433,7 +1440,8 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
advertise_shallow_grafts(1);
packet_flush(1);
} else {
- head_ref_namespaced(check_ref, &data);
+ refs_head_ref_namespaced(get_main_ref_store(the_repository),
+ check_ref, &data);
for_each_namespaced_ref_1(check_ref, &data);
}
@@ -1511,7 +1519,7 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
if (ref_is_hidden(refname_nons, refname.buf, hidden_refs) ||
- read_ref(refname.buf, &oid)) {
+ refs_read_ref(get_main_ref_store(the_repository), refname.buf, &oid)) {
packet_writer_error(writer, "unknown ref %s", refname_nons);
die("unknown ref %s", refname_nons);
}
diff --git a/userdiff.c b/userdiff.c
index 82bc76b..c4ebb9f 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -333,7 +333,7 @@ PATTERNS("scheme",
"|([^][)(}{[ \t])+"),
PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
"\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
-{ "default", NULL, NULL, -1, { NULL, 0 } },
+{ .name = "default", .binary = -1 },
};
#undef PATTERNS
#undef IPATTERN
@@ -399,7 +399,7 @@ static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t
static int parse_funcname(struct userdiff_funcname *f, const char *k,
const char *v, int cflags)
{
- if (git_config_string(&f->pattern, k, v) < 0)
+ if (git_config_string((char **) &f->pattern, k, v) < 0)
return -1;
f->cflags = cflags;
return 0;
@@ -445,15 +445,19 @@ int userdiff_config(const char *k, const char *v)
if (!strcmp(type, "binary"))
return parse_tristate(&drv->binary, k, v);
if (!strcmp(type, "command"))
- return git_config_string(&drv->external, k, v);
+ return git_config_string((char **) &drv->external.cmd, k, v);
+ if (!strcmp(type, "trustexitcode")) {
+ drv->external.trust_exit_code = git_config_bool(k, v);
+ return 0;
+ }
if (!strcmp(type, "textconv"))
- return git_config_string(&drv->textconv, k, v);
+ return git_config_string((char **) &drv->textconv, k, v);
if (!strcmp(type, "cachetextconv"))
return parse_bool(&drv->textconv_want_cache, k, v);
if (!strcmp(type, "wordregex"))
- return git_config_string(&drv->word_regex, k, v);
+ return git_config_string((char **) &drv->word_regex, k, v);
if (!strcmp(type, "algorithm"))
- return git_config_string(&drv->algorithm, k, v);
+ return git_config_string((char **) &drv->algorithm, k, v);
return 0;
}
diff --git a/userdiff.h b/userdiff.h
index d726804..7565930 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -11,9 +11,14 @@ struct userdiff_funcname {
int cflags;
};
+struct external_diff {
+ char *cmd;
+ unsigned trust_exit_code:1;
+};
+
struct userdiff_driver {
const char *name;
- const char *external;
+ struct external_diff external;
const char *algorithm;
int binary;
struct userdiff_funcname funcname;
diff --git a/walker.c b/walker.c
index c0fd632..0fafdc9 100644
--- a/walker.c
+++ b/walker.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
@@ -286,7 +288,8 @@ int walker_fetch(struct walker *walker, int targets, char **target,
ALLOC_ARRAY(oids, targets);
if (write_ref) {
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ &err);
if (!transaction) {
error("%s", err.buf);
goto done;
@@ -294,7 +297,8 @@ int walker_fetch(struct walker *walker, int targets, char **target,
}
if (!walker->get_recover) {
- for_each_ref(mark_complete, NULL);
+ refs_for_each_ref(get_main_ref_store(the_repository),
+ mark_complete, NULL);
commit_list_sort_by_date(&complete);
}
@@ -324,7 +328,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
strbuf_reset(&refname);
strbuf_addf(&refname, "refs/%s", write_ref[i]);
if (ref_transaction_update(transaction, refname.buf,
- oids + i, NULL, 0,
+ oids + i, NULL, NULL, NULL, 0,
msg ? msg : "fetch (unknown)",
&err)) {
error("%s", err.buf);
diff --git a/worktree.c b/worktree.c
index cf5eea8..f3c4c8e 100644
--- a/worktree.c
+++ b/worktree.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "environment.h"
@@ -53,6 +55,15 @@ static void add_head_info(struct worktree *wt)
wt->is_detached = 1;
}
+static int is_current_worktree(struct worktree *wt)
+{
+ char *git_dir = absolute_pathdup(get_git_dir());
+ const char *wt_git_dir = get_worktree_git_dir(wt);
+ int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
+ free(git_dir);
+ return is_current;
+}
+
/**
* get the main worktree
*/
@@ -65,6 +76,7 @@ static struct worktree *get_main_worktree(int skip_reading_head)
strbuf_strip_suffix(&worktree_path, "/.git");
CALLOC_ARRAY(worktree, 1);
+ worktree->repo = the_repository;
worktree->path = strbuf_detach(&worktree_path, NULL);
/*
* NEEDSWORK: If this function is called from a secondary worktree and
@@ -75,6 +87,7 @@ static struct worktree *get_main_worktree(int skip_reading_head)
*/
worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository();
+ worktree->is_current = is_current_worktree(worktree);
if (!skip_reading_head)
add_head_info(worktree);
return worktree;
@@ -98,8 +111,10 @@ struct worktree *get_linked_worktree(const char *id,
strbuf_strip_suffix(&worktree_path, "/.git");
CALLOC_ARRAY(worktree, 1);
+ worktree->repo = the_repository;
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->id = xstrdup(id);
+ worktree->is_current = is_current_worktree(worktree);
if (!skip_reading_head)
add_head_info(worktree);
@@ -109,23 +124,6 @@ struct worktree *get_linked_worktree(const char *id,
return worktree;
}
-static void mark_current_worktree(struct worktree **worktrees)
-{
- char *git_dir = absolute_pathdup(get_git_dir());
- int i;
-
- for (i = 0; worktrees[i]; i++) {
- struct worktree *wt = worktrees[i];
- const char *wt_git_dir = get_worktree_git_dir(wt);
-
- if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
- wt->is_current = 1;
- break;
- }
- }
- free(git_dir);
-}
-
/*
* NEEDSWORK: This function exists so that we can look up metadata of a
* worktree without trying to access any of its internals like the refdb. It
@@ -162,7 +160,6 @@ static struct worktree **get_worktrees_internal(int skip_reading_head)
ALLOC_GROW(list, counter + 1, alloc);
list[counter] = NULL;
- mark_current_worktree(list);
return list;
}
diff --git a/worktree.h b/worktree.h
index 605ed6c..11279d0 100644
--- a/worktree.h
+++ b/worktree.h
@@ -6,6 +6,8 @@
struct strbuf;
struct worktree {
+ /* The repository this worktree belongs to. */
+ struct repository *repo;
char *path;
char *id;
char *head_ref; /* NULL if HEAD is broken or detached */
diff --git a/wt-status.c b/wt-status.c
index a12406e..b778eef 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "advice.h"
#include "wt-status.h"
@@ -146,7 +148,8 @@ void wt_status_prepare(struct repository *r, struct wt_status *s)
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
s->use_color = -1;
s->relative_paths = 1;
- s->branch = resolve_refdup("HEAD", 0, NULL, NULL);
+ s->branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+ "HEAD", 0, NULL, NULL);
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
@@ -641,7 +644,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
repo_init_revisions(s->repo, &rev, NULL);
memset(&opt, 0, sizeof(opt));
- opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
+ opt.def = s->is_initial ? empty_tree_oid_hex(the_repository->hash_algo) : s->reference;
setup_revisions(0, NULL, &rev, &opt);
rev.diffopt.flags.override_submodule_config = 1;
@@ -977,7 +980,8 @@ static int stash_count_refs(struct object_id *ooid UNUSED,
static int count_stash_entries(void)
{
int n = 0;
- for_each_reflog_ent("refs/stash", stash_count_refs, &n);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ "refs/stash", stash_count_refs, &n);
return n;
}
@@ -1135,7 +1139,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
rev.diffopt.ita_invisible_in_index = 1;
memset(&opt, 0, sizeof(opt));
- opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
+ opt.def = s->is_initial ? empty_tree_oid_hex(the_repository->hash_algo) : s->reference;
setup_revisions(0, NULL, &rev, &opt);
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
@@ -1305,10 +1309,10 @@ static int split_commit_in_progress(struct wt_status *s)
!s->branch || strcmp(s->branch, "HEAD"))
return 0;
- if (read_ref_full("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &head_oid, &head_flags) ||
- read_ref_full("ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &orig_head_oid, &orig_head_flags))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &head_oid, &head_flags) ||
+ refs_read_ref_full(get_main_ref_store(the_repository), "ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &orig_head_oid, &orig_head_flags))
return 0;
if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
return 0;
@@ -1680,7 +1684,7 @@ static void wt_status_get_detached_from(struct repository *r,
char *ref = NULL;
strbuf_init(&cb.buf, 0);
- if (for_each_reflog_ent_reverse("HEAD", grab_1st_switch, &cb) <= 0) {
+ if (refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository), "HEAD", grab_1st_switch, &cb) <= 0) {
strbuf_release(&cb.buf);
return;
}
@@ -2088,7 +2092,8 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
upstream_is_gone = 1;
}
- short_base = shorten_unambiguous_ref(base, 0);
+ short_base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ base, 0);
color_fprintf(s->fp, header_color, "...");
color_fprintf(s->fp, branch_color_remote, "%s", short_base);
free(short_base);
@@ -2221,7 +2226,8 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
&base, 0, s->ahead_behind_flags);
if (base) {
- base = shorten_unambiguous_ref(base, 0);
+ base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ base, 0);
fprintf(s->fp, "# branch.upstream %s%c", base, eol);
free((char *)base);
@@ -2405,7 +2411,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
int mode;
struct object_id oid;
} stages[3];
- char *key;
+ const char *key;
char submodule_token[5];
char unmerged_prefix = 'u';
char eol_char = s->null_termination ? '\0' : '\n';
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 16ed8ac..d5dc886 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "gettext.h"
#include "config.h"
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 3853716..1ed430b 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -1,7 +1,7 @@
#ifndef XDIFF_INTERFACE_H
#define XDIFF_INTERFACE_H
-#include "hash-ll.h"
+#include "hash.h"
#include "xdiff/xdiff.h"
/*