1. 09 Nov, 2021 1 commit
  2. 17 Oct, 2021 1 commit
    • str: introduce `git_str` for internal, `git_buf` is external · f0e693b1
      libgit2 has two distinct requirements that were previously solved by
      `git_buf`.  We require:
      
      1. A general purpose string class that provides a number of utility APIs
         for manipulating data (eg, concatenating, truncating, etc).
      2. A structure that we can use to return strings to callers that they
         can take ownership of.
      
      By using a single class (`git_buf`) for both of these purposes, we have
      confused the API to the point that refactorings are difficult and
      reasoning about correctness is also difficult.
      
      Move the utility class `git_buf` to be called `git_str`: this represents
      its general purpose, as an internal string buffer class.  The name also
      is an homage to Junio Hamano ("gitstr").
      
      The public API remains `git_buf`, and has a much smaller footprint.  It
      is generally only used as an "out" param with strict requirements that
      follow the documentation.  (Exceptions exist for some legacy APIs to
      avoid breaking callers unnecessarily.)
      
      Utility functions exist to convert a user-specified `git_buf` to a
      `git_str` so that we can call internal functions, then converting it
      back again.
      Edward Thomson committed
  3. 02 Oct, 2021 1 commit
  4. 01 Oct, 2021 1 commit
  5. 09 Sep, 2021 1 commit
  6. 23 Jun, 2021 1 commit
  7. 27 Nov, 2020 1 commit
  8. 01 Jun, 2020 1 commit
    • config: ignore unreadable configuration files · d1409f48
      Modified `config_file_open()` so it returns 0 if the config file is
      not readable, which happens on global config files under macOS
      sandboxing (note that for some reason `access(F_OK)` DOES work with
      sandboxing, but it is lying). Without this read check sandboxed
      applications on macOS can not open any repository, because
      `config_file_read()` will return GIT_ERROR when it cannot read the
      global /Users/username/.gitconfig file, and the upper layers will
      just completely abort on GIT_ERROR when attempting to load the
      global config file, so no repositories can be opened.
      Wil Shipley committed
  9. 05 Nov, 2019 4 commits
    • config_file: keep reference to config entries when creating iterator · 56b203a5
      When creating a configuration file iterator, then we first refresh the
      backend and then afterwards duplicate all refreshed configuration
      entries into the iterator in order to avoid seeing any concurrent
      modifications of the entries while iterating. The duplication of entries
      is not guarded, though, as we do not increase the refcount of the
      entries that we duplicate right now. This opens us up for a race, as
      another thread may concurrently refresh the repository configuration and
      thus swap out the current set of entries. As we didn't increase the
      refcount, this may lead to the entries being free'd while we iterate
      over them in the first thread.
      
      Fix the issue by properly handling the lifecycle of the backend's
      entries via `config_file_entries_take` and `git_config_entries_free`,
      respectively.
      Patrick Steinhardt committed
    • config_file: refactor taking entries ref to return an error code · 0927156a
      The function to take a reference to the config file's config entries
      currently returns the reference via return value. Due to this, it's
      harder than necessary to integrate into our typical coding style, as one
      needs to make sure that a proper error code is set before erroring out
      from the caller. This bites us in `config_file_delete`, where we call
      `goto out` directly when `config_file_entries_take` returns `NULL`, but
      we actually forget to set up the error code and thus return success.
      
      Fix the issue by refactoring the function to return an error code and
      pass the reference via an out-pointer.
      Patrick Steinhardt committed
    • config_file: rename function names · c2749849
      As with the predecessing commit, this commit renames backend functions
      of the configuration file backend. This helps to clearly separate
      functionality and also to be able to see from backtraces which backend
      is currently in use.
      Patrick Steinhardt committed
  10. 21 Sep, 2019 1 commit
  11. 01 Aug, 2019 1 commit
    • config: implement "onbranch" conditional · 722ba93f
      With Git v2.23.0, the conditional include mechanism gained another new
      conditional "onbranch". As the name says, it will cause a file to be
      included if the "onbranch" pattern matches the currently checked out
      branch.
      
      Implement this new condition and add a bunch of tests.
      Patrick Steinhardt committed
  12. 26 Jul, 2019 6 commits
    • config_backend: rename internal structures · 37ebe9ad
      The internal backend structures are kind-of legacy and do not really
      speak for themselves. Rename them accordingly to make them easier to
      understand.
      Patrick Steinhardt committed
    • config_file: separate out read-only backend · 2bff84ba
      To further distinguish the file writeable and readonly backends,
      separate the readonly backend into its own "config_snapshot.c"
      implementation. The snapshot backend can be generically used to snapshot
      any type of backend.
      Patrick Steinhardt committed
    • config_file: fix cast of readonly backend · f0b10066
      In `backend_readonly_free`, the passed in config backend is being cast
      to a `diskfile_backend` instead of to a `diskfile_readonly_backend`.
      While this works out just fine because we only access its header values,
      which were shared between both backends, it is undefined behaviour.
      
      Use the correct type to fix this.
      Patrick Steinhardt committed
    • config_file: remove shared `diskfile_header` struct · a3159df8
      The `diskfile_header` structure is shared between both
      `diskfile_backend` and `diskfile_readonly_backend`. The separation and
      resulting casting is confusing at times and a source for programming
      errors.
      
      Remove the shared structure and inline them directly.
      Patrick Steinhardt committed
    • config_file: duplicate accessors for readonly backend · 271e5fba
      While most functions of the readonly configuration backend are
      implemented separately from the writeable configuration backend, the two
      functions `config_iterator_new` and `config_get` are shared between
      both. This sharing makes it necessary to have some shared data
      structures, which is the `diskfile_header` structure. Unfortunately, this
      makes the backends harder to grasp than necessary due to all the casting
      between structs and also quite error prone.
      
      Reimplement those functions for the readonly backends. As readonly
      backends cannot be refreshed anyway, we can remove the calls to
      `config_refresh` in there.
      Patrick Steinhardt committed
    • config_file: reimplement `config_readonly_open` generically · 4e7ce1fb
      The `config_readonly_open` function currently receives as input a
      diskfile backend and will copy its entries to a new snapshot. This is
      rather intimate, as we need to assume that the source config backend is
      in fact a diskfile entry. We can do better than this though by using
      generic methods to copy contents of the provided backend, e.g. by using
      a config iterator. This also allows us to decouple the read-only backend
      from the read-write backend.
      Patrick Steinhardt committed
  13. 21 Jul, 2019 3 commits
  14. 11 Jul, 2019 11 commits
    • config_parse: provide parser init and dispose functions · dbeadf8a
      Right now, all configuration file backends are expected to
      directly mess with the configuration parser's internals in order
      to set it up. Let's avoid doing that by implementing both a
      `git_config_parser_init` and `git_config_parser_dispose` function
      to clearly define the interface between configuration backends
      and the parser.
      
      Ideally, we would make the `git_config_parser` structure
      definition private to its implementation. But as that would
      require an additional memory allocation that was not required
      before we just live with it being visible to others.
      Patrick Steinhardt committed
    • config_file: refactor error handling in `config_write` · 32157526
      Error handling in `config_write` is rather convoluted and does
      not match our current code style. Refactor it to make it easier
      to understand.
      Patrick Steinhardt committed
    • config_file: internalize `git_config_file` struct · 820fa1a3
      With the previous commits, we have finally separated the config
      parsing logic from the specific configuration file backend. Due
      to that, we can now move the `git_config_file` structure into the
      config file backend's implementation so that no other code may
      accidentally start using it again. Furthermore, we rename the
      structure to `diskfile` to make it obvious that it is internal,
      only, and to unify it with naming scheme of the other diskfile
      structures.
      Patrick Steinhardt committed
    • config_parse: remove use of `git_config_file` · 6e6da75f
      The config parser code needs to keep track of the current parsed
      file's name so that we are able to provide proper error messages
      to the user. Right now, we do that by storing a `git_config_file`
      in the parser structure, but as that is a specific backend and
      the parser aims to be generic, it is a layering violation.
      
      Switch over to use a simple string to fix that.
      Patrick Steinhardt committed
    • config_file: embed file in diskfile parse data · 54d350e0
      The config file code needs to keep track of the actual
      `git_config_file` structure, as it not only contains the path
      of the current configuration file, but it also keeps tracks of
      all includes of that file. Right now, we keep track of that
      structure via the `git_config_parser`, but as that's supposed to
      be a backend generic implementation of configuration parsing it's
      a layering violation to have it in there.
      
      Switch over the config file backend to use its own config file
      structure that's embedded in the backend parse data. This allows
      us to switch over the generic config parser to avoid using the
      `git_config_file` structure.
      Patrick Steinhardt committed
    • config_file: avoid re-reading files on write · 2ba7020f
      When we rewrite the configuration file due to any of its values
      being modified, we call `config_refresh` to update the in-memory
      representation of our config file backend. This is needlessly
      wasteful though, as `config_refresh` will always open the on-disk
      representation to reads the file contents while we already know
      the complete file contents at this point in time as we have just
      written it to disk.
      
      Implement a new function `config_refresh_from_buffer` that will
      refresh the backend's config entries from a buffer instead of
      from the config file itself. Note that this will thus _not_
      update the backend's timestamp, which will cause us to re-read
      the buffer when performing a read operation on it. But this is
      still an improvement as we now lazily re-read the contents, and
      most importantly we will avoid constantly re-reading the contents
      if we perform multiple write operations.
      
      The following strace demonstrates this if we're re-writing a key
      multiple times. It uses our config example with `config_set`
      changed to update the file 10 times with different keys:
      
      	$ strace lg2 config x.x z |& grep '^open.*config'
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      
      And now with the optimization of `config_refresh_from_buffer`:
      
      	$ strace lg2 config x.x z |& grep '^open.*config'
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
      
      As can be seen, this is quite a lot of `open` calls less.
      Patrick Steinhardt committed
    • config_file: split out function that sets config entries · a0dc3027
      Updating a config file backend's config entries is a bit more
      involved, as it requires clearing of the old config entries as
      well as handling locking correctly. As we will need this
      functionality in a future patch to refresh config entries from a
      buffer, let's extract this into its own function
      `config_set_entries`.
      Patrick Steinhardt committed
    • config_file: split out function that reads entries from a buffer · 985f5cdf
      The `config_read` function currently performs both reading the
      on-disk config file as well as parsing the retrieved buffer
      contents. To optimize how we refresh our config entries from an
      in-memory buffer, we need to be able to directly parse buffers,
      though, without involving any on-disk files at all.
      
      Extract a new function `config_read_buffer` that sets up the
      parsing logic and then parses config entries from a buffer, only.
      Have `config_read` use it to avoid duplicated logic.
      Patrick Steinhardt committed
    • config_file: move refresh into `write` function · 3e1c137a
      We are quite lazy in how we refresh our config file backend when
      updating any of its keys: instead of just updating our in-memory
      representation of the keys, we just discard the old set of keys
      and then re-read the config file contents from disk. This refresh
      currently happens separately at every callsite of `config_write`,
      but it is clear that we _always_ want to refresh if we have
      written the config file to disk. If we didn't, then we'd run
      around with an outdated config file backend that does not
      represent what we have on disk.
      
      By moving the refresh into `config_write`, we are also able to
      optimize the case where the config file is currently locked.
      Before, we would've tried to re-read the file even if we have
      only updated its cached contents without touching the on-disk
      file. Thus we'd have unnecessarily stat'd the file, even though
      we know that it shouldn't have been modified in the meantime due
      to its lock.
      Patrick Steinhardt committed
    • config_file: implement stat cache to avoid repeated rehashing · d7f58eab
      To decide whether a config file has changed, we always hash its
      complete contents. This is unnecessarily expensive, as
      well-behaved filesystems will always update stat information for
      files which have changed. So before computing the hash, we should
      first check whether the stat info has actually changed for either
      the configuration file or any of its includes. This avoids having
      to re-read the configuration file and its includes every time
      when we check whether it's been modified.
      
      Tracing the for-each-ref example previous to this commit, one can
      see that we repeatedly re-open both the repo configuration as
      well as the global configuration:
      
      	$ strace lg2 for-each-ref |& grep config
      	access("/home/pks/.gitconfig", F_OK)    = -1 ENOENT (No such file or directory)
      	access("/home/pks/.config/git/config", F_OK) = 0
      	access("/etc/gitconfig", F_OK)          = -1 ENOENT (No such file or directory)
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	access("/tmp/repo/.git/config", F_OK)   = 0
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/home/pks/.gitconfig", 0x7ffd15c05290) = -1 ENOENT (No such file or directory)
      	access("/home/pks/.gitconfig", F_OK)    = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	access("/home/pks/.config/git/config", F_OK) = 0
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/home/pks/.gitconfig", 0x7ffd15c051f0) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/home/pks/.gitconfig", 0x7ffd15c05090) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/home/pks/.gitconfig", 0x7ffd15c05090) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/home/pks/.gitconfig", 0x7ffd15c05090) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
      
      With the change, we only do stats for those files and open them a
      single time, only:
      
      	$ strace lg2 for-each-ref |& grep config
      	access("/home/pks/.gitconfig", F_OK)    = -1 ENOENT (No such file or directory)
      	access("/home/pks/.config/git/config", F_OK) = 0
      	access("/etc/gitconfig", F_OK)          = -1 ENOENT (No such file or directory)
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	access("/tmp/repo/.git/config", F_OK)   = 0
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/home/pks/.gitconfig", 0x7ffe70540d20) = -1 ENOENT (No such file or directory)
      	access("/home/pks/.gitconfig", F_OK)    = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	access("/home/pks/.config/git/config", F_OK) = 0
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	stat("/home/pks/.gitconfig", 0x7ffe70540ca0) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.gitconfig", 0x7ffe70540c80) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	stat("/home/pks/.gitconfig", 0x7ffe70540b40) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.gitconfig", 0x7ffe70540b20) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	stat("/home/pks/.gitconfig", 0x7ffe70540b40) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.gitconfig", 0x7ffe70540b20) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      	stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
      	stat("/home/pks/.gitconfig", 0x7ffe70540b40) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.gitconfig", 0x7ffe70540b20) = -1 ENOENT (No such file or directory)
      	stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
      
      The following benchmark has been performed with and without the
      stat cache in a best-of-ten run:
      
      ```
      
      int lg2_repro(git_repository *repo, int argc, char **argv)
      {
      	git_config *cfg;
      	int32_t dummy;
      	int i;
      
      	UNUSED(argc);
      	UNUSED(argv);
      
      	check_lg2(git_repository_config(&cfg, repo),
      			"Could not obtain config", NULL);
      
      	for (i = 1; i < 100000; ++i)
      		git_config_get_int32(&dummy, cfg, "foo.bar");
      
      	git_config_free(cfg);
      	return 0;
      }
      ```
      
      Without stat cache:
      
      	$ time lg2 repro
      	real    0m1.528s
      	user    0m0.568s
      	sys     0m0.944s
      
      With stat cache:
      
      	$ time lg2 repro
      	real    0m0.526s
      	user    0m0.268s
      	sys     0m0.258s
      
      This benchmark shows a nearly three-fold performance improvement.
      
      This change requires that we check our configuration stress tests
      as we're now in fact becoming more racy. If somebody is writing a
      configuration file at nearly the same time (there is a window of
      100ns on Windows-based systems), then it might be that we realize
      that this file has actually changed and thus may not re-read it.
      This will only happen if either an external process is rewriting
      the configuration file or if the same process has multiple
      `git_config` structures pointing to the same time, where one of
      both is being used to write and the other one is used to read
      values.
      Patrick Steinhardt committed
  15. 15 Jun, 2019 4 commits
    • config_file: use `wildmatch` to evaluate conditionals · 5811e3ba
      We currently use `p_fnmatch` to compute whether a given "gitdir:"
      or "gitdir/i:" conditional matches the current configuration file
      path. As git.git has moved to use `wildmatch` instead of
      `p_fnmatch` throughout its complete codebase, we evaluate
      conditionals inconsistently with git.git in some special cases.
      
      Convert `p_fnmatch` to use `wildmatch`. The `FNM_LEADINGDIR` flag
      cannot be translated to `wildmatch`, but in fact git.git doesn't
      use it here either. And in fact, dropping it while we go
      increases compatibility with git.git.
      Patrick Steinhardt committed
    • config_file: do not include trailing '/' for "gitdir" conditionals · cf1a114b
      When evaluating "gitdir:" and "gitdir/i:" conditionals, we
      currently compare the given pattern with the value of
      `git_repository_path`. Thing is though that `git_repository_path`
      returns the gitdir path with trailing '/', while we actually need
      to match against the gitdir without it.
      
      Fix this issue by stripping the trailing '/' previous to
      matching. Add various tests to ensure we get this right.
      Patrick Steinhardt committed
    • config_file: refactor `do_match_gitdir` to improve readability · 5d987f7d
      The function `do_match_gitdir` has some horribly named parameters
      and variables. Rename them to improve readability. Furthermore,
      fix a potentially undetected out-of-memory condition when
      appending "**" to the pattern.
      Patrick Steinhardt committed
    • posix: remove implicit include of "fnmatch.h" · 451df793
      We're about to phase out our bundled fnmatch implementation as
      git.git has moved to wildmatch long ago in 2014. To make it
      easier to spot which files are stilll using fnmatch, remove the
      implicit "fnmatch.h" include in "posix.h" and instead include it
      explicitly.
      Patrick Steinhardt committed
  16. 19 May, 2019 1 commit
  17. 30 Apr, 2019 1 commit