Merge branch 'rs/ref-read-cleanup'
Code cleanup.
* rs/ref-read-cleanup:
remote: pass NULL to read_ref_full() because object ID is not needed
refs: pass NULL to refs_read_ref_full() because object ID is not needed
diff --git a/.gitignore b/.gitignore
index 89b3b79..aebe7c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -158,6 +158,7 @@
/git-show-branch
/git-show-index
/git-show-ref
+/git-sparse-checkout
/git-stage
/git-stash
/git-status
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
index 4d24dae..aa828df 100644
--- a/Documentation/MyFirstObjectWalk.txt
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -17,7 +17,7 @@
- `Documentation/user-manual.txt` under "Hacking Git" contains some coverage of
the revision walker in its various incarnations.
-- `Documentation/technical/api-revision-walking.txt`
+- `revision.h`
- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]
gives a good overview of the types of objects in Git and what your object
walk is really describing.
@@ -119,9 +119,8 @@
`nr` represents the number of `rev_cmdline_entry` present in the array.
-`alloc` is used by the `ALLOC_GROW` macro. Check
-`Documentation/technical/api-allocation-growing.txt` - this variable is used to
-track the allocated size of the list.
+`alloc` is used by the `ALLOC_GROW` macro. Check `cache.h` - this variable is
+used to track the allocated size of the list.
Per entry, we find:
diff --git a/Documentation/RelNotes/2.25.0.txt b/Documentation/RelNotes/2.25.0.txt
index 19d1341..7163c33 100644
--- a/Documentation/RelNotes/2.25.0.txt
+++ b/Documentation/RelNotes/2.25.0.txt
@@ -132,6 +132,19 @@
encourage new callers to use the correct and more strict
validation.
+ * Unnecessary reading of state variables back from the disk during
+ sequencer operation has been reduced.
+
+ * The code has been made to avoid gmtime() and localtime() and prefer
+ their reentrant counterparts.
+
+ * "git add -i" that is getting rewritten in C has been extended to
+ cover subcommands other than the "patch".
+
+ * In a repository with many packfiles, the cost of the procedure that
+ avoids registering the same packfile twice was unnecessarily high
+ by using an inefficient search algorithm, which has been corrected.
+
Fixes since v2.24
-----------------
@@ -261,6 +274,18 @@
generation, instead of following the "if it takes more than two
seconds, show progress" pattern, which has been corrected.
+ * "git rebase" did not work well when format.useAutoBase
+ configuration variable is set, which has been corrected.
+
+ * The "diff" machinery learned not to lose added/removed blank lines
+ in the context when --ignore-blank-lines and --function-context are
+ used at the same time.
+ (merge 0bb313a552 rs/xdiff-ignore-ws-w-func-context later to maint).
+
+ * The test on "fast-import" used to get stuck when "fast-import" died
+ in the middle.
+ (merge 0d9b0d7885 sg/t9300-robustify later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge 80736d7c5e jc/am-show-current-patch-docfix later to maint).
(merge 8b656572ca sg/commit-graph-usage-fix later to maint).
@@ -288,3 +313,7 @@
(merge 528d9e6d01 jk/perf-wo-git-dot-pm later to maint).
(merge fc42f20e24 sg/test-squelch-noise-in-commit-bulk later to maint).
(merge c64368e3a2 bc/t9001-zsh-in-posix-emulation-mode later to maint).
+ (merge 11de8dd7ef dr/branch-usage-casefix later to maint).
+ (merge e05e8cf074 rs/archive-zip-code-cleanup later to maint).
+ (merge 147ee35558 rs/commit-export-env-simplify later to maint).
+ (merge 4507ecc771 rs/patch-id-use-oid-to-hex later to maint).
diff --git a/Documentation/RelNotes/2.7.3.txt b/Documentation/RelNotes/2.7.3.txt
index 6adf038..f618d71 100644
--- a/Documentation/RelNotes/2.7.3.txt
+++ b/Documentation/RelNotes/2.7.3.txt
@@ -20,7 +20,7 @@
tests.
* "git show 'HEAD:Foo[BAR]Baz'" did not interpret the argument as a
- rev, i.e. the object named by the the pathname with wildcard
+ rev, i.e. the object named by the pathname with wildcard
characters in a tree object.
* "git rev-parse --git-common-dir" used in the worktree feature
diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
index ad4fa4d..9e440b1 100644
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -599,8 +599,14 @@
multi-pack-index design document].
core.sparseCheckout::
- Enable "sparse checkout" feature. See section "Sparse checkout" in
- linkgit:git-read-tree[1] for more information.
+ Enable "sparse checkout" feature. See linkgit:git-sparse-checkout[1]
+ for more information.
+
+core.sparseCheckoutCone::
+ Enables the "cone mode" of the sparse checkout feature. When the
+ sparse-checkout file contains a limited set of patterns, then this
+ mode provides significant performance advantages. See
+ linkgit:git-sparse-checkout[1] for more information.
core.abbrev::
Set the length object names are abbreviated to. If
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index 513fcd8..45c7bd5 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -106,4 +106,20 @@
instead.
+
This configuration can be specified multiple times in order to allow
-multiple notes refs to be included.
+multiple notes refs to be included. In that case, it will behave
+similarly to multiple `--[no-]notes[=]` options passed in. That is, a
+value of `true` will show the default notes, a value of `<ref>` will
+also show notes from that notes ref and a value of `false` will negate
+previous configurations and not show notes.
++
+For example,
++
+------------
+[format]
+ notes = true
+ notes = foo
+ notes = false
+ notes = bar
+------------
++
+will only show notes from `refs/notes/bar`.
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 8b0e4c7..be5e3ac 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -11,7 +11,8 @@
'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
[--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
[--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
- [--chmod=(+|-)x] [--] [<pathspec>...]
+ [--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]]
+ [--] [<pathspec>...]
DESCRIPTION
-----------
@@ -187,6 +188,19 @@
bit is only changed in the index, the files on disk are left
unchanged.
+--pathspec-from-file=<file>::
+ Pathspec is passed in `<file>` instead of commandline args. If
+ `<file>` is exactly `-` then standard input is used. Pathspec
+ elements are separated by LF or CR/LF. Pathspec elements can be
+ quoted as explained for the configuration variable `core.quotePath`
+ (see linkgit:git-config[1]). See also `--pathspec-file-nul` and
+ global `--literal-pathspecs`.
+
+--pathspec-file-nul::
+ Only meaningful with `--pathspec-from-file`. Pathspec elements are
+ separated with NUL character and all other characters are taken
+ literally (including newlines and quotes).
+
\--::
This option can be used to separate command-line options from
the list of files, (useful when filenames might be mistaken
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index cf3cac0..c8fb995 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -12,14 +12,14 @@
'git checkout' [-q] [-f] [-m] --detach [<branch>]
'git checkout' [-q] [-f] [-m] [--detach] <commit>
'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
-'git checkout' [<tree-ish>] [--] <pathspec>...
-'git checkout' (-p|--patch) [<tree-ish>] [--] [<paths>...]
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]
+'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]
DESCRIPTION
-----------
Updates files in the working tree to match the version in the index
-or the specified tree. If no paths are given, 'git checkout' will
+or the specified tree. If no pathspec was given, 'git checkout' will
also update `HEAD` to set the specified branch as the current
branch.
@@ -79,13 +79,14 @@
+
Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
-'git checkout' [<tree-ish>] [--] <pathspec>...::
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...::
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]::
- Overwrite paths in the working tree by replacing with the
- contents in the index or in the `<tree-ish>` (most often a
- commit). When a `<tree-ish>` is given, the paths that
- match the `<pathspec>` are updated both in the index and in
- the working tree.
+ Overwrite the contents of the files that match the pathspec.
+ When the `<tree-ish>` (most often a commit) is not given,
+ overwrite working tree with the contents in the index.
+ When the `<tree-ish>` is given, overwrite both the index and
+ the working tree with the contents at the `<tree-ish>`.
+
The index may contain unmerged entries because of a previous failed merge.
By default, if you try to check out such an entry from the index, the
@@ -96,12 +97,10 @@
file can be discarded to re-create the original conflicted merge result.
'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]::
- This is similar to the "check out paths to the working tree
- from either the index or from a tree-ish" mode described
- above, but lets you use the interactive interface to show
- the "diff" output and choose which hunks to use in the
- result. See below for the description of `--patch` option.
-
+ This is similar to the previous mode, but lets you use the
+ interactive interface to show the "diff" output and choose which
+ hunks to use in the result. See below for the description of
+ `--patch` option.
OPTIONS
-------
@@ -309,6 +308,19 @@
working tree, but not in `<tree-ish>` are removed, to make them
match `<tree-ish>` exactly.
+--pathspec-from-file=<file>::
+ Pathspec is passed in `<file>` instead of commandline args. If
+ `<file>` is exactly `-` then standard input is used. Pathspec
+ elements are separated by LF or CR/LF. Pathspec elements can be
+ quoted as explained for the configuration variable `core.quotePath`
+ (see linkgit:git-config[1]). See also `--pathspec-file-nul` and
+ global `--literal-pathspecs`.
+
+--pathspec-file-nul::
+ Only meaningful with `--pathspec-from-file`. Pathspec elements are
+ separated with NUL character and all other characters are taken
+ literally (including newlines and quotes).
+
<branch>::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with "refs/heads/", is a valid ref), then that
@@ -339,7 +351,13 @@
Tree to checkout from (when paths are given). If not specified,
the index will be used.
+\--::
+ Do not interpret any more arguments as options.
+<pathspec>...::
+ Limits the paths affected by the operation.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
DETACHED HEAD
-------------
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 34011c2..0fe91d2 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -15,7 +15,7 @@
[--dissociate] [--separate-git-dir <git dir>]
[--depth <depth>] [--[no-]single-branch] [--no-tags]
[--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
- [--[no-]remote-submodules] [--jobs <n>] [--] <repository>
+ [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--] <repository>
[<directory>]
DESCRIPTION
@@ -156,6 +156,12 @@
used, neither remote-tracking branches nor the related
configuration variables are created.
+--sparse::
+ Initialize the sparse-checkout file so the working
+ directory starts with only the files in the root
+ of the repository. The sparse-checkout file can be
+ modified to grow the working directory as needed.
+
--mirror::
Set up a mirror of the source repository. This implies `--bare`.
Compared to `--bare`, `--mirror` not only maps local branches of the
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index b211440..6f0c7ca 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -19,8 +19,7 @@
usernames and passwords. The git-credential command exposes this
interface to scripts which may want to retrieve, store, or prompt for
credentials in the same manner as Git. The design of this scriptable
-interface models the internal C API; see
-link:technical/api-credentials.html[the Git credential API] for more
+interface models the internal C API; see credential.h for more
background on the concepts.
git-credential takes an "action" option on the command-line (one of
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 00bdf9b..0d4f895 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -333,11 +333,12 @@
Output an all-zero hash in each patch's From header instead
of the hash of the commit.
---base=<commit>::
+--[no-]base[=<commit>]::
Record the base tree information to identify the state the
patch series applies to. See the BASE TREE INFORMATION section
below for details. If <commit> is "auto", a base commit is
- automatically chosen.
+ automatically chosen. The `--no-base` option overrides a
+ `format.useAutoBase` configuration.
--root::
Treat the revision argument as a <revision range>, even if it
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index d271842..da33f84 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -436,7 +436,7 @@
SEE ALSO
--------
linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
-linkgit:gitignore[5]
+linkgit:gitignore[5]; linkgit:git-sparse-checkout[1];
GIT
---
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index 1ab2e40..5bf60d4 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -8,8 +8,9 @@
SYNOPSIS
--------
[verse]
-'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
-'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] [--] <pathspec>...
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] --pathspec-from-file=<file> [--pathspec-file-nul]
+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [--] [<pathspec>...]
DESCRIPTION
-----------
@@ -113,6 +114,27 @@
appear in the `--source` tree are removed, to make them match
`<tree>` exactly. The default is no-overlay mode.
+--pathspec-from-file=<file>::
+ Pathspec is passed in `<file>` instead of commandline args. If
+ `<file>` is exactly `-` then standard input is used. Pathspec
+ elements are separated by LF or CR/LF. Pathspec elements can be
+ quoted as explained for the configuration variable `core.quotePath`
+ (see linkgit:git-config[1]). See also `--pathspec-file-nul` and
+ global `--literal-pathspecs`.
+
+--pathspec-file-nul::
+ Only meaningful with `--pathspec-from-file`. Pathspec elements are
+ separated with NUL character and all other characters are taken
+ literally (including newlines and quotes).
+
+\--::
+ Do not interpret any more arguments as options.
+
+<pathspec>...::
+ Limits the paths affected by the operation.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
+
EXAMPLES
--------
diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
new file mode 100644
index 0000000..9c3c66c
--- /dev/null
+++ b/Documentation/git-sparse-checkout.txt
@@ -0,0 +1,166 @@
+git-sparse-checkout(1)
+======================
+
+NAME
+----
+git-sparse-checkout - Initialize and modify the sparse-checkout
+configuration, which reduces the checkout to a set of paths
+given by a list of atterns.
+
+
+SYNOPSIS
+--------
+[verse]
+'git sparse-checkout <subcommand> [options]'
+
+
+DESCRIPTION
+-----------
+
+Initialize and modify the sparse-checkout configuration, which reduces
+the checkout to a set of paths given by a list of patterns.
+
+THIS COMMAND IS EXPERIMENTAL. ITS BEHAVIOR, AND THE BEHAVIOR OF OTHER
+COMMANDS IN THE PRESENCE OF SPARSE-CHECKOUTS, WILL LIKELY CHANGE IN
+THE FUTURE.
+
+
+COMMANDS
+--------
+'list'::
+ Provide a list of the contents in the sparse-checkout file.
+
+'init'::
+ Enable the `core.sparseCheckout` setting. If the
+ sparse-checkout file does not exist, then populate it with
+ patterns that match every file in the root directory and
+ no other directories, then will remove all directories tracked
+ by Git. Add patterns to the sparse-checkout file to
+ repopulate the working directory.
++
+To avoid interfering with other worktrees, it first enables the
+`extensions.worktreeConfig` setting and makes sure to set the
+`core.sparseCheckout` setting in the worktree-specific config file.
+
+'set'::
+ Write a set of patterns to the sparse-checkout file, as given as
+ a list of arguments following the 'set' subcommand. Update the
+ working directory to match the new patterns. Enable the
+ core.sparseCheckout config setting if it is not already enabled.
++
+When the `--stdin` option is provided, the patterns are read from
+standard in as a newline-delimited list instead of from the arguments.
+
+'disable'::
+ Disable the `core.sparseCheckout` config setting, and restore the
+ working directory to include all files. Leaves the sparse-checkout
+ file intact so a later 'git sparse-checkout init' command may
+ return the working directory to the same state.
+
+SPARSE CHECKOUT
+---------------
+
+"Sparse checkout" allows populating the working directory sparsely.
+It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
+Git whether a file in the working directory is worth looking at. If
+the skip-worktree bit is set, then the file is ignored in the working
+directory. Git will not populate the contents of those files, which
+makes a sparse checkout helpful when working in a repository with many
+files, but only a few are important to the current user.
+
+The `$GIT_DIR/info/sparse-checkout` file is used to define the
+skip-worktree reference bitmap. When Git updates the working
+directory, it updates the skip-worktree bits in the index based
+on this file. The files matching the patterns in the file will
+appear in the working directory, and the rest will not.
+
+To enable the sparse-checkout feature, run `git sparse-checkout init` to
+initialize a simple sparse-checkout file and enable the `core.sparseCheckout`
+config setting. Then, run `git sparse-checkout set` to modify the patterns in
+the sparse-checkout file.
+
+To repopulate the working directory with all files, use the
+`git sparse-checkout disable` command.
+
+
+FULL PATTERN SET
+----------------
+
+By default, the sparse-checkout file uses the same syntax as `.gitignore`
+files.
+
+While `$GIT_DIR/info/sparse-checkout` is usually used to specify what
+files are included, you can also specify what files are _not_ included,
+using negative patterns. For example, to remove the file `unwanted`:
+
+----------------
+/*
+!unwanted
+----------------
+
+
+CONE PATTERN SET
+----------------
+
+The full pattern set allows for arbitrary pattern matches and complicated
+inclusion/exclusion rules. These can result in O(N*M) pattern matches when
+updating the index, where N is the number of patterns and M is the number
+of paths in the index. To combat this performance issue, a more restricted
+pattern set is allowed when `core.spareCheckoutCone` is enabled.
+
+The accepted patterns in the cone pattern set are:
+
+1. *Recursive:* All paths inside a directory are included.
+
+2. *Parent:* All files immediately inside a directory are included.
+
+In addition to the above two patterns, we also expect that all files in the
+root directory are included. If a recursive pattern is added, then all
+leading directories are added as parent patterns.
+
+By default, when running `git sparse-checkout init`, the root directory is
+added as a parent pattern. At this point, the sparse-checkout file contains
+the following patterns:
+
+----------------
+/*
+!/*/
+----------------
+
+This says "include everything in root, but nothing two levels below root."
+If we then add the folder `A/B/C` as a recursive pattern, the folders `A` and
+`A/B` are added as parent patterns. The resulting sparse-checkout file is
+now
+
+----------------
+/*
+!/*/
+/A/
+!/A/*/
+/A/B/
+!/A/B/*/
+/A/B/C/
+----------------
+
+Here, order matters, so the negative patterns are overridden by the positive
+patterns that appear lower in the file.
+
+If `core.sparseCheckoutCone=true`, then Git will parse the sparse-checkout file
+expecting patterns of these types. Git will warn if the patterns do not match.
+If the patterns do match the expected format, then Git will use faster hash-
+based algorithms to compute inclusion in the sparse-checkout.
+
+If `core.ignoreCase=true`, then the pattern-matching algorithm will use a
+case-insensitive check. This corrects for case mismatched filenames in the
+'git sparse-checkout set' command to reflect the expected cone in the working
+directory.
+
+SEE ALSO
+--------
+
+linkgit:git-read-tree[1]
+linkgit:gitignore[5]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
index adc7596..ea759fd 100644
--- a/Documentation/gitcredentials.txt
+++ b/Documentation/gitcredentials.txt
@@ -186,8 +186,7 @@
--------------
You can write your own custom helpers to interface with any system in
-which you keep credentials. See the documentation for Git's
-link:technical/api-credentials.html[credentials API] for details.
+which you keep credentials. See credential.h for details.
GIT
---
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index b5d1c05..67275fd 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -120,7 +120,7 @@
SEE ALSO
--------
-linkgit:git-submodule[1] linkgit:git-config[1]
+linkgit:git-submodule[1], linkgit:gitsubmodules[7], linkgit:git-config[1]
GIT
---
diff --git a/Documentation/technical/api-allocation-growing.txt b/Documentation/technical/api-allocation-growing.txt
deleted file mode 100644
index 5a59b54..0000000
--- a/Documentation/technical/api-allocation-growing.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-allocation growing API
-======================
-
-Dynamically growing an array using realloc() is error prone and boring.
-
-Define your array with:
-
-* a pointer (`item`) that points at the array, initialized to `NULL`
- (although please name the variable based on its contents, not on its
- type);
-
-* an integer variable (`alloc`) that keeps track of how big the current
- allocation is, initialized to `0`;
-
-* another integer variable (`nr`) to keep track of how many elements the
- array currently has, initialized to `0`.
-
-Then before adding `n`th element to the item, call `ALLOC_GROW(item, n,
-alloc)`. This ensures that the array can hold at least `n` elements by
-calling `realloc(3)` and adjusting `alloc` variable.
-
-------------
-sometype *item;
-size_t nr;
-size_t alloc
-
-for (i = 0; i < nr; i++)
- if (we like item[i] already)
- return;
-
-/* we did not like any existing one, so add one */
-ALLOC_GROW(item, nr + 1, alloc);
-item[nr++] = value you like;
-------------
-
-You are responsible for updating the `nr` variable.
-
-If you need to specify the number of elements to allocate explicitly
-then use the macro `REALLOC_ARRAY(item, alloc)` instead of `ALLOC_GROW`.
diff --git a/Documentation/technical/api-argv-array.txt b/Documentation/technical/api-argv-array.txt
deleted file mode 100644
index 870c8ed..0000000
--- a/Documentation/technical/api-argv-array.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-argv-array API
-==============
-
-The argv-array API allows one to dynamically build and store
-NULL-terminated lists. An argv-array maintains the invariant that the
-`argv` member always points to a non-NULL array, and that the array is
-always NULL-terminated at the element pointed to by `argv[argc]`. This
-makes the result suitable for passing to functions expecting to receive
-argv from main(), or the link:api-run-command.html[run-command API].
-
-The string-list API (documented in string-list.h) is similar, but cannot be
-used for these purposes; instead of storing a straight string pointer,
-it contains an item structure with a `util` field that is not compatible
-with the traditional argv interface.
-
-Each `argv_array` manages its own memory. Any strings pushed into the
-array are duplicated, and all memory is freed by argv_array_clear().
-
-Data Structures
----------------
-
-`struct argv_array`::
-
- A single array. This should be initialized by assignment from
- `ARGV_ARRAY_INIT`, or by calling `argv_array_init`. The `argv`
- member contains the actual array; the `argc` member contains the
- number of elements in the array, not including the terminating
- NULL.
-
-Functions
----------
-
-`argv_array_init`::
- Initialize an array. This is no different than assigning from
- `ARGV_ARRAY_INIT`.
-
-`argv_array_push`::
- Push a copy of a string onto the end of the array.
-
-`argv_array_pushl`::
- Push a list of strings onto the end of the array. The arguments
- should be a list of `const char *` strings, terminated by a NULL
- argument.
-
-`argv_array_pushf`::
- Format a string and push it onto the end of the array. This is a
- convenience wrapper combining `strbuf_addf` and `argv_array_push`.
-
-`argv_array_pushv`::
- Push a null-terminated array of strings onto the end of the array.
-
-`argv_array_pop`::
- Remove the final element from the array. If there are no
- elements in the array, do nothing.
-
-`argv_array_clear`::
- Free all memory associated with the array and return it to the
- initial, empty state.
-
-`argv_array_detach`::
- Disconnect the `argv` member from the `argv_array` struct and
- return it. The caller is responsible for freeing the memory used
- by the array, and by the strings it references. After detaching,
- the `argv_array` is in a reinitialized state and can be pushed
- into again.
diff --git a/Documentation/technical/api-credentials.txt b/Documentation/technical/api-credentials.txt
deleted file mode 100644
index 75368f2..0000000
--- a/Documentation/technical/api-credentials.txt
+++ /dev/null
@@ -1,271 +0,0 @@
-credentials API
-===============
-
-The credentials API provides an abstracted way of gathering username and
-password credentials from the user (even though credentials in the wider
-world can take many forms, in this document the word "credential" always
-refers to a username and password pair).
-
-This document describes two interfaces: the C API that the credential
-subsystem provides to the rest of Git, and the protocol that Git uses to
-communicate with system-specific "credential helpers". If you are
-writing Git code that wants to look up or prompt for credentials, see
-the section "C API" below. If you want to write your own helper, see
-the section on "Credential Helpers" below.
-
-Typical setup
--------------
-
-------------
-+-----------------------+
-| Git code (C) |--- to server requiring --->
-| | authentication
-|.......................|
-| C credential API |--- prompt ---> User
-+-----------------------+
- ^ |
- | pipe |
- | v
-+-----------------------+
-| Git credential helper |
-+-----------------------+
-------------
-
-The Git code (typically a remote-helper) will call the C API to obtain
-credential data like a login/password pair (credential_fill). The
-API will itself call a remote helper (e.g. "git credential-cache" or
-"git credential-store") that may retrieve credential data from a
-store. If the credential helper cannot find the information, the C API
-will prompt the user. Then, the caller of the API takes care of
-contacting the server, and does the actual authentication.
-
-C API
------
-
-The credential C API is meant to be called by Git code which needs to
-acquire or store a credential. It is centered around an object
-representing a single credential and provides three basic operations:
-fill (acquire credentials by calling helpers and/or prompting the user),
-approve (mark a credential as successfully used so that it can be stored
-for later use), and reject (mark a credential as unsuccessful so that it
-can be erased from any persistent storage).
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-`struct credential`::
-
- 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; see the section below
- for a description of each field.
-+
-The `helpers` member of the struct is a `string_list` of helpers. Each
-string specifies an external helper which will be run, in order, to
-either acquire or store credentials. See the section on credential
-helpers below. This list is filled-in by the API functions
-according to the corresponding configuration variables before
-consulting helpers, so there usually is no need for a caller to
-modify the helpers field at all.
-+
-This struct should always be initialized with `CREDENTIAL_INIT` or
-`credential_init`.
-
-
-Functions
-~~~~~~~~~
-
-`credential_init`::
-
- Initialize a credential structure, setting all fields to empty.
-
-`credential_clear`::
-
- Free any resources associated with the credential structure,
- returning it to a pristine initialized state.
-
-`credential_fill`::
-
- 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().
-
-`credential_reject`::
-
- Inform the credential subsystem that the provided credentials
- 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.
-
-`credential_approve`::
-
- Inform the credential subsystem that the provided credentials
- were successfully used for authentication. This will cause the
- credential subsystem to notify any helpers of the approval, so
- that they may store the result to be used again. Any errors
- from helpers are ignored.
-
-`credential_from_url`::
-
- Parse a URL into broken-down credential fields.
-
-Example
-~~~~~~~
-
-The example below shows how the functions of the credential API could be
-used to login to a fictitious "foo" service on a remote host:
-
------------------------------------------------------------------------
-int foo_login(struct foo_connection *f)
-{
- int status;
- /*
- * Create a credential with some context; we don't yet know the
- * username or password.
- */
-
- struct credential c = CREDENTIAL_INIT;
- c.protocol = xstrdup("foo");
- c.host = xstrdup(f->hostname);
-
- /*
- * Fill in the username and password fields by contacting
- * helpers and/or asking the user. The function will die if it
- * fails.
- */
- credential_fill(&c);
-
- /*
- * Otherwise, we have a username and password. Try to use it.
- */
- status = send_foo_login(f, c.username, c.password);
- switch (status) {
- case FOO_OK:
- /* It worked. Store the credential for later use. */
- credential_accept(&c);
- break;
- case FOO_BAD_LOGIN:
- /* Erase the credential from storage so we don't try it
- * again. */
- credential_reject(&c);
- break;
- default:
- /*
- * Some other error occurred. We don't know if the
- * credential is good or bad, so report nothing to the
- * credential subsystem.
- */
- }
-
- /* Free any associated resources. */
- credential_clear(&c);
-
- return status;
-}
------------------------------------------------------------------------
-
-
-Credential Helpers
-------------------
-
-Credential helpers are programs executed by Git to fetch or save
-credentials from and to long-term storage (where "long-term" is simply
-longer than a single Git process; e.g., credentials may be stored
-in-memory for a few minutes, or indefinitely on disk).
-
-Each helper is specified by a single string in the configuration
-variable `credential.helper` (and others, see linkgit:git-config[1]).
-The string is transformed by Git into a command to be executed using
-these rules:
-
- 1. If the helper string begins with "!", it is considered a shell
- snippet, and everything after the "!" becomes the command.
-
- 2. Otherwise, if the helper string begins with an absolute path, the
- verbatim helper string becomes the command.
-
- 3. Otherwise, the string "git credential-" is prepended to the helper
- string, and the result becomes the command.
-
-The resulting command then has an "operation" argument appended to it
-(see below for details), and the result is executed by the shell.
-
-Here are some example specifications:
-
-----------------------------------------------------
-# run "git credential-foo"
-foo
-
-# same as above, but pass an argument to the helper
-foo --bar=baz
-
-# the arguments are parsed by the shell, so use shell
-# quoting if necessary
-foo --bar="whitespace arg"
-
-# you can also use an absolute path, which will not use the git wrapper
-/path/to/my/helper --with-arguments
-
-# or you can specify your own shell snippet
-!f() { echo "password=`cat $HOME/.secret`"; }; f
-----------------------------------------------------
-
-Generally speaking, rule (3) above is the simplest for users to specify.
-Authors of credential helpers should make an effort to assist their
-users by naming their program "git-credential-$NAME", and putting it in
-the $PATH or $GIT_EXEC_PATH during installation, which will allow a user
-to enable it with `git config credential.helper $NAME`.
-
-When a helper is executed, it will have one "operation" argument
-appended to its command line, which is one of:
-
-`get`::
-
- Return a matching credential, if any exists.
-
-`store`::
-
- Store the credential, if applicable to the helper.
-
-`erase`::
-
- Remove a matching credential, if any, from the helper's storage.
-
-The details of the credential will be provided on the helper's stdin
-stream. The exact format is the same as the input/output format of the
-`git credential` plumbing command (see the section `INPUT/OUTPUT
-FORMAT` in linkgit:git-credential[1] for a detailed specification).
-
-For a `get` operation, the helper should produce a list of attributes
-on stdout in the same format. A helper is free to produce a subset, or
-even no values at all if it has nothing useful to provide. Any provided
-attributes will overwrite those already known about by Git. If a helper
-outputs a `quit` attribute with a value of `true` or `1`, no further
-helpers will be consulted, nor will the user be prompted (if no
-credential has been provided, the operation will then fail).
-
-For a `store` or `erase` operation, the helper's output is ignored.
-If it fails to perform the requested operation, it may complain to
-stderr to inform the user. If it does not support the requested
-operation (e.g., a read-only store), it should silently ignore the
-request.
-
-If a helper receives any other operation, it should silently ignore the
-request. This leaves room for future operations to be added (older
-helpers will just ignore the new requests).
-
-See also
---------
-
-linkgit:gitcredentials[7]
-
-linkgit:git-config[1] (See configuration variables `credential.*`)
diff --git a/Documentation/technical/api-diff.txt b/Documentation/technical/api-diff.txt
deleted file mode 100644
index 30fc0e9..0000000
--- a/Documentation/technical/api-diff.txt
+++ /dev/null
@@ -1,174 +0,0 @@
-diff API
-========
-
-The diff API is for programs that compare two sets of files (e.g. two
-trees, one tree and the index) and present the found difference in
-various ways. The calling program is responsible for feeding the API
-pairs of files, one from the "old" set and the corresponding one from
-"new" set, that are different. The library called through this API is
-called diffcore, and is responsible for two things.
-
-* finding total rewrites (`-B`), renames (`-M`) and copies (`-C`), and
- changes that touch a string (`-S`), as specified by the caller.
-
-* outputting the differences in various formats, as specified by the
- caller.
-
-Calling sequence
-----------------
-
-* Prepare `struct diff_options` to record the set of diff options, and
- then call `repo_diff_setup()` to initialize this structure. This
- sets up the vanilla default.
-
-* Fill in the options structure to specify desired output format, rename
- detection, etc. `diff_opt_parse()` can be used to parse options given
- from the command line in a way consistent with existing git-diff
- family of programs.
-
-* Call `diff_setup_done()`; this inspects the options set up so far for
- internal consistency and make necessary tweaking to it (e.g. if
- textual patch output was asked, recursive behaviour is turned on);
- the callback set_default in diff_options can be used to tweak this more.
-
-* As you find different pairs of files, call `diff_change()` to feed
- modified files, `diff_addremove()` to feed created or deleted files,
- or `diff_unmerge()` to feed a file whose state is 'unmerged' to the
- API. These are thin wrappers to a lower-level `diff_queue()` function
- that is flexible enough to record any of these kinds of changes.
-
-* Once you finish feeding the pairs of files, call `diffcore_std()`.
- This will tell the diffcore library to go ahead and do its work.
-
-* Calling `diff_flush()` will produce the output.
-
-
-Data structures
----------------
-
-* `struct diff_filespec`
-
-This is the internal representation for a single file (blob). It
-records the blob object name (if known -- for a work tree file it
-typically is a NUL SHA-1), filemode and pathname. This is what the
-`diff_addremove()`, `diff_change()` and `diff_unmerge()` synthesize and
-feed `diff_queue()` function with.
-
-* `struct diff_filepair`
-
-This records a pair of `struct diff_filespec`; the filespec for a file
-in the "old" set (i.e. preimage) is called `one`, and the filespec for a
-file in the "new" set (i.e. postimage) is called `two`. A change that
-represents file creation has NULL in `one`, and file deletion has NULL
-in `two`.
-
-A `filepair` starts pointing at `one` and `two` that are from the same
-filename, but `diffcore_std()` can break pairs and match component
-filespecs with other filespecs from a different filepair to form new
-filepair. This is called 'rename detection'.
-
-* `struct diff_queue`
-
-This is a collection of filepairs. Notable members are:
-
-`queue`::
-
- An array of pointers to `struct diff_filepair`. This
- dynamically grows as you add filepairs;
-
-`alloc`::
-
- The allocated size of the `queue` array;
-
-`nr`::
-
- The number of elements in the `queue` array.
-
-
-* `struct diff_options`
-
-This describes the set of options the calling program wants to affect
-the operation of diffcore library with.
-
-Notable members are:
-
-`output_format`::
- The output format used when `diff_flush()` is run.
-
-`context`::
- Number of context lines to generate in patch output.
-
-`break_opt`, `detect_rename`, `rename-score`, `rename_limit`::
- Affects the way detection logic for complete rewrites, renames
- and copies.
-
-`abbrev`::
- Number of hexdigits to abbreviate raw format output to.
-
-`pickaxe`::
- A constant string (can and typically does contain newlines to
- look for a block of text, not just a single line) to filter out
- the filepairs that do not change the number of strings contained
- in its preimage and postimage of the diff_queue.
-
-`flags`::
- This is mostly a collection of boolean options that affects the
- operation, but some do not have anything to do with the diffcore
- library.
-
-`touched_flags`::
- Records whether a flag has been changed due to user request
- (rather than just set/unset by default).
-
-`set_default`::
- Callback which allows tweaking the options in diff_setup_done().
-
-BINARY, TEXT;;
- Affects the way how a file that is seemingly binary is treated.
-
-FULL_INDEX;;
- Tells the patch output format not to use abbreviated object
- names on the "index" lines.
-
-FIND_COPIES_HARDER;;
- Tells the diffcore library that the caller is feeding unchanged
- filepairs to allow copies from unmodified files be detected.
-
-COLOR_DIFF;;
- Output should be colored.
-
-COLOR_DIFF_WORDS;;
- Output is a colored word-diff.
-
-NO_INDEX;;
- Tells diff-files that the input is not tracked files but files
- in random locations on the filesystem.
-
-ALLOW_EXTERNAL;;
- Tells output routine that it is Ok to call user specified patch
- output routine. Plumbing disables this to ensure stable output.
-
-QUIET;;
- Do not show any output.
-
-REVERSE_DIFF;;
- Tells the library that the calling program is feeding the
- filepairs reversed; `one` is two, and `two` is one.
-
-EXIT_WITH_STATUS;;
- For communication between the calling program and the options
- parser; tell the calling program to signal the presence of
- difference using program exit code.
-
-HAS_CHANGES;;
- Internal; used for optimization to see if there is any change.
-
-SILENT_ON_REMOVE;;
- Affects if diff-files shows removed files.
-
-RECURSIVE, TREE_IN_RECURSIVE;;
- Tells if tree traversal done by tree-diff should recursively
- descend into a tree object pair that are different in preimage
- and postimage set.
-
-(JC)
diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt
deleted file mode 100644
index 76b6e4f..0000000
--- a/Documentation/technical/api-directory-listing.txt
+++ /dev/null
@@ -1,130 +0,0 @@
-directory listing API
-=====================
-
-The directory listing API is used to enumerate paths in the work tree,
-optionally taking `.git/info/exclude` and `.gitignore` files per
-directory into account.
-
-Data structure
---------------
-
-`struct dir_struct` structure is used to pass directory traversal
-options to the library and to record the paths discovered. A single
-`struct dir_struct` is used regardless of whether or not the traversal
-recursively descends into subdirectories.
-
-The notable options are:
-
-`exclude_per_dir`::
-
- The name of the file to be read in each directory for excluded
- files (typically `.gitignore`).
-
-`flags`::
-
- A bit-field of options:
-
-`DIR_SHOW_IGNORED`:::
-
- Return just ignored files in `entries[]`, not untracked
- files. This flag is mutually exclusive with
- `DIR_SHOW_IGNORED_TOO`.
-
-`DIR_SHOW_IGNORED_TOO`:::
-
- Similar to `DIR_SHOW_IGNORED`, but return ignored files in
- `ignored[]` in addition to untracked files in
- `entries[]`. This flag is mutually exclusive with
- `DIR_SHOW_IGNORED`.
-
-`DIR_KEEP_UNTRACKED_CONTENTS`:::
-
- Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is set, the
- untracked contents of untracked directories are also returned in
- `entries[]`.
-
-`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::
-
- Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if
- this is set, returns ignored files and directories that match
- an exclude pattern. If a directory matches an exclude pattern,
- then the directory is returned and the contained paths are
- not. A directory that does not match an exclude pattern will
- not be returned even if all of its contents are ignored. In
- this case, the contents are returned as individual entries.
-+
-If this is set, files and directories that explicitly match an ignore
-pattern are reported. Implicitly ignored directories (directories that
-do not match an ignore pattern, but whose contents are all ignored)
-are not reported, instead all of the contents are reported.
-
-`DIR_COLLECT_IGNORED`:::
-
- Special mode for git-add. Return ignored files in `ignored[]` and
- untracked files in `entries[]`. Only returns ignored files that match
- pathspec exactly (no wildcards). Does not recurse into ignored
- directories.
-
-`DIR_SHOW_OTHER_DIRECTORIES`:::
-
- Include a directory that is not tracked.
-
-`DIR_HIDE_EMPTY_DIRECTORIES`:::
-
- Do not include a directory that is not tracked and is empty.
-
-`DIR_NO_GITLINKS`:::
-
- If set, recurse into a directory that looks like a Git
- directory. Otherwise it is shown as a directory.
-
-The result of the enumeration is left in these fields:
-
-`entries[]`::
-
- An array of `struct dir_entry`, each element of which describes
- a path.
-
-`nr`::
-
- The number of members in `entries[]` array.
-
-`alloc`::
-
- Internal use; keeps track of allocation of `entries[]` array.
-
-`ignored[]`::
-
- An array of `struct dir_entry`, used for ignored paths with the
- `DIR_SHOW_IGNORED_TOO` and `DIR_COLLECT_IGNORED` flags.
-
-`ignored_nr`::
-
- The number of members in `ignored[]` array.
-
-Calling sequence
-----------------
-
-Note: index may be looked at for .gitignore files that are CE_SKIP_WORKTREE
-marked. If you to exclude files, make sure you have loaded index first.
-
-* Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
- sizeof(dir))`.
-
-* To add single exclude pattern, call `add_pattern_list()` and then
- `add_pattern()`.
-
-* To add patterns from a file (e.g. `.git/info/exclude`), call
- `add_patterns_from_file()` , and/or set `dir.exclude_per_dir`. A
- short-hand function `setup_standard_excludes()` can be used to set
- up the standard set of exclude settings.
-
-* Set options described in the Data Structure section above.
-
-* Call `read_directory()`.
-
-* Use `dir.entries[]`.
-
-* Call `clear_directory()` when none of the contained elements are no longer in use.
-
-(JC)
diff --git a/Documentation/technical/api-gitattributes.txt b/Documentation/technical/api-gitattributes.txt
deleted file mode 100644
index 45f0df6..0000000
--- a/Documentation/technical/api-gitattributes.txt
+++ /dev/null
@@ -1,154 +0,0 @@
-gitattributes API
-=================
-
-gitattributes mechanism gives a uniform way to associate various
-attributes to set of paths.
-
-
-Data Structure
---------------
-
-`struct git_attr`::
-
- An attribute is an opaque object that is identified by its name.
- Pass the name to `git_attr()` function to obtain the object of
- this type. The internal representation of this structure is
- of no interest to the calling programs. The name of the
- attribute can be retrieved by calling `git_attr_name()`.
-
-`struct attr_check_item`::
-
- This structure represents one attribute and its value.
-
-`struct attr_check`::
-
- This structure represents a collection of `attr_check_item`.
- It is passed to `git_check_attr()` function, specifying the
- attributes to check, and receives their values.
-
-
-Attribute Values
-----------------
-
-An attribute for a path can be in one of four states: Set, Unset,
-Unspecified or set to a string, and `.value` member of `struct
-attr_check_item` records it. There are three macros to check these:
-
-`ATTR_TRUE()`::
-
- Returns true if the attribute is Set for the path.
-
-`ATTR_FALSE()`::
-
- Returns true if the attribute is Unset for the path.
-
-`ATTR_UNSET()`::
-
- Returns true if the attribute is Unspecified for the path.
-
-If none of the above returns true, `.value` member points at a string
-value of the attribute for the path.
-
-
-Querying Specific Attributes
-----------------------------
-
-* Prepare `struct attr_check` using attr_check_initl()
- function, enumerating the names of attributes whose values you are
- interested in, terminated with a NULL pointer. Alternatively, an
- empty `struct attr_check` can be prepared by calling
- `attr_check_alloc()` function and then attributes you want to
- ask about can be added to it with `attr_check_append()`
- function.
-
-* Call `git_check_attr()` to check the attributes for the path.
-
-* Inspect `attr_check` structure to see how each of the
- attribute in the array is defined for the path.
-
-
-Example
--------
-
-To see how attributes "crlf" and "ident" are set for different paths.
-
-. Prepare a `struct attr_check` with two elements (because
- we are checking two attributes):
-
-------------
-static struct attr_check *check;
-static void setup_check(void)
-{
- if (check)
- return; /* already done */
- check = attr_check_initl("crlf", "ident", NULL);
-}
-------------
-
-. Call `git_check_attr()` with the prepared `struct attr_check`:
-
-------------
- const char *path;
-
- setup_check();
- git_check_attr(path, check);
-------------
-
-. Act on `.value` member of the result, left in `check->items[]`:
-
-------------
- const char *value = check->items[0].value;
-
- if (ATTR_TRUE(value)) {
- The attribute is Set, by listing only the name of the
- attribute in the gitattributes file for the path.
- } else if (ATTR_FALSE(value)) {
- The attribute is Unset, by listing the name of the
- attribute prefixed with a dash - for the path.
- } else if (ATTR_UNSET(value)) {
- The attribute is neither set nor unset for the path.
- } else if (!strcmp(value, "input")) {
- If none of ATTR_TRUE(), ATTR_FALSE(), or ATTR_UNSET() is
- true, the value is a string set in the gitattributes
- file for the path by saying "attr=value".
- } else if (... other check using value as string ...) {
- ...
- }
-------------
-
-To see how attributes in argv[] are set for different paths, only
-the first step in the above would be different.
-
-------------
-static struct attr_check *check;
-static void setup_check(const char **argv)
-{
- check = attr_check_alloc();
- while (*argv) {
- struct git_attr *attr = git_attr(*argv);
- attr_check_append(check, attr);
- argv++;
- }
-}
-------------
-
-
-Querying All Attributes
------------------------
-
-To get the values of all attributes associated with a file:
-
-* Prepare an empty `attr_check` structure by calling
- `attr_check_alloc()`.
-
-* Call `git_all_attrs()`, which populates the `attr_check`
- with the attributes attached to the path.
-
-* Iterate over the `attr_check.items[]` array to examine
- the attribute names and values. The name of the attribute
- described by an `attr_check.items[]` object can be retrieved via
- `git_attr_name(check->items[i].attr)`. (Please note that no items
- will be returned for unset attributes, so `ATTR_UNSET()` will return
- false for all returned `attr_check.items[]` objects.)
-
-* Free the `attr_check` struct by calling `attr_check_free()`.
diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt
deleted file mode 100644
index d0d1707..0000000
--- a/Documentation/technical/api-history-graph.txt
+++ /dev/null
@@ -1,173 +0,0 @@
-history graph API
-=================
-
-The graph API is used to draw a text-based representation of the commit
-history. The API generates the graph in a line-by-line fashion.
-
-Functions
----------
-
-Core functions:
-
-* `graph_init()` creates a new `struct git_graph`
-
-* `graph_update()` moves the graph to a new commit.
-
-* `graph_next_line()` outputs the next line of the graph into a strbuf. It
- does not add a terminating newline.
-
-* `graph_padding_line()` outputs a line of vertical padding in the graph. It
- is similar to `graph_next_line()`, but is guaranteed to never print the line
- containing the current commit. Where `graph_next_line()` would print the
- commit line next, `graph_padding_line()` prints a line that simply extends
- all branch lines downwards one row, leaving their positions unchanged.
-
-* `graph_is_commit_finished()` determines if the graph has output all lines
- necessary for the current commit. If `graph_update()` is called before all
- lines for the current commit have been printed, the next call to
- `graph_next_line()` will output an ellipsis, to indicate that a portion of
- the graph was omitted.
-
-The following utility functions are wrappers around `graph_next_line()` and
-`graph_is_commit_finished()`. They always print the output to stdout.
-They can all be called with a NULL graph argument, in which case no graph
-output will be printed.
-
-* `graph_show_commit()` calls `graph_next_line()` and
- `graph_is_commit_finished()` until one of them return non-zero. This prints
- all graph lines up to, and including, the line containing this commit.
- Output is printed to stdout. The last line printed does not contain a
- terminating newline.
-
-* `graph_show_oneline()` calls `graph_next_line()` and prints the result to
- stdout. The line printed does not contain a terminating newline.
-
-* `graph_show_padding()` calls `graph_padding_line()` and prints the result to
- stdout. The line printed does not contain a terminating newline.
-
-* `graph_show_remainder()` calls `graph_next_line()` until
- `graph_is_commit_finished()` returns non-zero. Output is printed to stdout.
- The last line printed does not contain a terminating newline. Returns 1 if
- output was printed, and 0 if no output was necessary.
-
-* `graph_show_strbuf()` prints the specified strbuf to stdout, prefixing all
- lines but the first with a graph line. The caller is responsible for
- ensuring graph output for the first line has already been printed to stdout.
- (This can be done with `graph_show_commit()` or `graph_show_oneline()`.) If
- a NULL graph is supplied, the strbuf is printed as-is.
-
-* `graph_show_commit_msg()` is similar to `graph_show_strbuf()`, but it also
- prints the remainder of the graph, if more lines are needed after the strbuf
- ends. It is better than directly calling `graph_show_strbuf()` followed by
- `graph_show_remainder()` since it properly handles buffers that do not end in
- a terminating newline. The output printed by `graph_show_commit_msg()` will
- end in a newline if and only if the strbuf ends in a newline.
-
-Data structure
---------------
-`struct git_graph` is an opaque data type used to store the current graph
-state.
-
-Calling sequence
-----------------
-
-* Create a `struct git_graph` by calling `graph_init()`. When using the
- revision walking API, this is done automatically by `setup_revisions()` if
- the '--graph' option is supplied.
-
-* Use the revision walking API to walk through a group of contiguous commits.
- The `get_revision()` function automatically calls `graph_update()` each time
- it is invoked.
-
-* For each commit, call `graph_next_line()` repeatedly, until
- `graph_is_commit_finished()` returns non-zero. Each call to
- `graph_next_line()` will output a single line of the graph. The resulting
- lines will not contain any newlines. `graph_next_line()` returns 1 if the
- resulting line contains the current commit, or 0 if this is merely a line
- needed to adjust the graph before or after the current commit. This return
- value can be used to determine where to print the commit summary information
- alongside the graph output.
-
-Limitations
------------
-
-* `graph_update()` must be called with commits in topological order. It should
- not be called on a commit if it has already been invoked with an ancestor of
- that commit, or the graph output will be incorrect.
-
-* `graph_update()` must be called on a contiguous group of commits. If
- `graph_update()` is called on a particular commit, it should later be called
- on all parents of that commit. Parents must not be skipped, or the graph
- output will appear incorrect.
-+
-`graph_update()` may be used on a pruned set of commits only if the parent list
-has been rewritten so as to include only ancestors from the pruned set.
-
-* The graph API does not currently support reverse commit ordering. In
- order to implement reverse ordering, the graphing API needs an
- (efficient) mechanism to find the children of a commit.
-
-Sample usage
-------------
-
-------------
-struct commit *commit;
-struct git_graph *graph = graph_init(opts);
-
-while ((commit = get_revision(opts)) != NULL) {
- while (!graph_is_commit_finished(graph))
- {
- struct strbuf sb;
- int is_commit_line;
-
- strbuf_init(&sb, 0);
- is_commit_line = graph_next_line(graph, &sb);
- fputs(sb.buf, stdout);
-
- if (is_commit_line)
- log_tree_commit(opts, commit);
- else
- putchar(opts->diffopt.line_termination);
- }
-}
-------------
-
-Sample output
--------------
-
-The following is an example of the output from the graph API. This output does
-not include any commit summary information--callers are responsible for
-outputting that information, if desired.
-
-------------
-*
-*
-*
-|\
-* |
-| | *
-| \ \
-| \ \
-*-. \ \
-|\ \ \ \
-| | * | |
-| | | | | *
-| | | | | *
-| | | | | *
-| | | | | |\
-| | | | | | *
-| * | | | | |
-| | | | | * \
-| | | | | |\ |
-| | | | * | | |
-| | | | * | | |
-* | | | | | | |
-| |/ / / / / /
-|/| / / / / /
-* | | | | | |
-|/ / / / / /
-* | | | | |
-| | | | | *
-| | | | |/
-| | | | *
-------------
diff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt
index 9dc1bed..487d4d8 100644
--- a/Documentation/technical/api-merge.txt
+++ b/Documentation/technical/api-merge.txt
@@ -28,77 +28,9 @@
* `struct ll_merge_options`
-This describes the set of options the calling program wants to affect
-the operation of a low-level (single file) merge. Some options:
-
-`virtual_ancestor`::
- Behave as though this were part of a merge between common
- ancestors in a recursive merge.
- If a helper program is specified by the
- `[merge "<driver>"] recursive` configuration, it will
- be used (see linkgit:gitattributes[5]).
-
-`variant`::
- Resolve local conflicts automatically in favor
- of one side or the other (as in 'git merge-file'
- `--ours`/`--theirs`/`--union`). Can be `0`,
- `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
- `XDL_MERGE_FAVOR_UNION`.
-
-`renormalize`::
- Resmudge and clean the "base", "theirs" and "ours" files
- before merging. Use this when the merge is likely to have
- overlapped with a change in smudge/clean or end-of-line
- normalization rules.
+Check ll-merge.h for details.
Low-level (single file) merge
-----------------------------
-`ll_merge`::
-
- Perform a three-way single-file merge in core. This is
- a thin wrapper around `xdl_merge` that takes the path and
- any merge backend specified in `.gitattributes` or
- `.git/info/attributes` into account. Returns 0 for a
- clean merge.
-
-Calling sequence:
-
-* Prepare a `struct ll_merge_options` to record options.
- If you have no special requests, skip this and pass `NULL`
- as the `opts` parameter to use the default options.
-
-* Allocate an mmbuffer_t variable for the result.
-
-* Allocate and fill variables with the file's original content
- and two modified versions (using `read_mmfile`, for example).
-
-* Call `ll_merge()`.
-
-* Read the merged content from `result_buf.ptr` and `result_buf.size`.
-
-* Release buffers when finished. A simple
- `free(ancestor.ptr); free(ours.ptr); free(theirs.ptr);
- free(result_buf.ptr);` will do.
-
-If the modifications do not merge cleanly, `ll_merge` will return a
-nonzero value and `result_buf` will generally include a description of
-the conflict bracketed by markers such as the traditional `<<<<<<<`
-and `>>>>>>>`.
-
-The `ancestor_label`, `our_label`, and `their_label` parameters are
-used to label the different sides of a conflict if the merge driver
-supports this.
-
-Everything else
----------------
-
-Talk about <merge-recursive.h> and merge_file():
-
- - merge_trees() to merge with rename detection
- - merge_recursive() for ancestor consolidation
- - try_merge_command() for other strategies
- - conflict format
- - merge options
-
-(Daniel, Miklos, Stephan, JC)
+Check ll-merge.h for details.
diff --git a/Documentation/technical/api-oid-array.txt b/Documentation/technical/api-oid-array.txt
deleted file mode 100644
index c97428c..0000000
--- a/Documentation/technical/api-oid-array.txt
+++ /dev/null
@@ -1,90 +0,0 @@
-oid-array API
-==============
-
-The oid-array API provides storage and manipulation of sets of object
-identifiers. The emphasis is on storage and processing efficiency,
-making them suitable for large lists. Note that the ordering of items is
-not preserved over some operations.
-
-Data Structures
----------------
-
-`struct oid_array`::
-
- A single array of object IDs. This should be initialized by
- assignment from `OID_ARRAY_INIT`. The `oid` member contains
- the actual data. The `nr` member contains the number of items in
- the set. The `alloc` and `sorted` members are used internally,
- and should not be needed by API callers.
-
-Functions
----------
-
-`oid_array_append`::
- Add an item to the set. The object ID will be placed at the end of
- the array (but note that some operations below may lose this
- ordering).
-
-`oid_array_lookup`::
- Perform a binary search of the array for a specific object ID.
- If found, returns the offset (in number of elements) of the
- object ID. If not found, returns a negative integer. If the array
- is not sorted, this function has the side effect of sorting it.
-
-`oid_array_clear`::
- Free all memory associated with the array and return it to the
- initial, empty state.
-
-`oid_array_for_each`::
- Iterate over each element of the list, executing the callback
- function for each one. Does not sort the list, so any custom
- hash order is retained. If the callback returns a non-zero
- value, the iteration ends immediately and the callback's
- return is propagated; otherwise, 0 is returned.
-
-`oid_array_for_each_unique`::
- Iterate over each unique element of the list in sorted order,
- but otherwise behave like `oid_array_for_each`. If the array
- is not sorted, this function has the side effect of sorting
- it.
-
-`oid_array_filter`::
- Apply the callback function `want` to each entry in the array,
- retaining only the entries for which the function returns true.
- Preserve the order of the entries that are retained.
-
-Examples
---------
-
------------------------------------------
-int print_callback(const struct object_id *oid,
- void *data)
-{
- printf("%s\n", oid_to_hex(oid));
- return 0; /* always continue */
-}
-
-void some_func(void)
-{
- struct sha1_array hashes = OID_ARRAY_INIT;
- struct object_id oid;
-
- /* Read objects into our set */
- while (read_object_from_stdin(oid.hash))
- oid_array_append(&hashes, &oid);
-
- /* Check if some objects are in our set */
- while (read_object_from_stdin(oid.hash)) {
- if (oid_array_lookup(&hashes, &oid) >= 0)
- printf("it's in there!\n");
-
- /*
- * Print the unique set of objects. We could also have
- * avoided adding duplicate objects in the first place,
- * but we would end up re-sorting the array repeatedly.
- * Instead, this will sort once and then skip duplicates
- * in linear time.
- */
- oid_array_for_each_unique(&hashes, print_callback, NULL);
-}
------------------------------------------
diff --git a/Documentation/technical/api-ref-iteration.txt b/Documentation/technical/api-ref-iteration.txt
deleted file mode 100644
index ad9d019..0000000
--- a/Documentation/technical/api-ref-iteration.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-ref iteration API
-=================
-
-
-Iteration of refs is done by using an iterate function which will call a
-callback function for every ref. The callback function has this
-signature:
-
- int handle_one_ref(const char *refname, const struct object_id *oid,
- int flags, void *cb_data);
-
-There are different kinds of iterate functions which all take a
-callback of this type. The callback is then called for each found ref
-until the callback returns nonzero. The returned value is then also
-returned by the iterate function.
-
-Iteration functions
--------------------
-
-* `head_ref()` just iterates the head ref.
-
-* `for_each_ref()` iterates all refs.
-
-* `for_each_ref_in()` iterates all refs which have a defined prefix and
- strips that prefix from the passed variable refname.
-
-* `for_each_tag_ref()`, `for_each_branch_ref()`, `for_each_remote_ref()`,
- `for_each_replace_ref()` iterate refs from the respective area.
-
-* `for_each_glob_ref()` iterates all refs that match the specified glob
- pattern.
-
-* `for_each_glob_ref_in()` the previous and `for_each_ref_in()` combined.
-
-* Use `refs_` API for accessing submodules. The submodule ref store could
- be obtained with `get_submodule_ref_store()`.
-
-* `for_each_rawref()` can be used to learn about broken ref and symref.
-
-* `for_each_reflog()` iterates each reflog file.
-
-Submodules
-----------
-
-If you want to iterate the refs of a submodule you first need to add the
-submodules object database. You can do this by a code-snippet like
-this:
-
- const char *path = "path/to/submodule"
- if (add_submodule_odb(path))
- die("Error submodule '%s' not populated.", path);
-
-`add_submodule_odb()` will return zero on success. If you
-do not do this you will get an error for each ref that it does not point
-to a valid object.
-
-Note: As a side-effect of this you cannot safely assume that all
-objects you lookup are available in superproject. All submodule objects
-will be available the same way as the superprojects objects.
-
-Example:
---------
-
-----
-static int handle_remote_ref(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
-{
- struct strbuf *output = cb_data;
- strbuf_addf(output, "%s\n", refname);
- return 0;
-}
-
-...
-
- struct strbuf output = STRBUF_INIT;
- for_each_remote_ref(handle_remote_ref, &output);
- printf("%s", output.buf);
-----
diff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt
deleted file mode 100644
index f10941b..0000000
--- a/Documentation/technical/api-remote.txt
+++ /dev/null
@@ -1,127 +0,0 @@
-Remotes configuration API
-=========================
-
-The API in remote.h gives access to the configuration related to
-remotes. It handles all three configuration mechanisms historically
-and currently used by Git, and presents the information in a uniform
-fashion. Note that the code also handles plain URLs without any
-configuration, giving them just the default information.
-
-struct remote
--------------
-
-`name`::
-
- The user's nickname for the remote
-
-`url`::
-
- An array of all of the url_nr URLs configured for the remote
-
-`pushurl`::
-
- An array of all of the pushurl_nr push URLs configured for the remote
-
-`push`::
-
- An array of refspecs configured for pushing, with
- push_refspec being the literal strings, and push_refspec_nr
- being the quantity.
-
-`fetch`::
-
- An array of refspecs configured for fetching, with
- fetch_refspec being the literal strings, and fetch_refspec_nr
- being the quantity.
-
-`fetch_tags`::
-
- The setting for whether to fetch tags (as a separate rule from
- the configured refspecs); -1 means never to fetch tags, 0
- means to auto-follow tags based on the default heuristic, 1
- means to always auto-follow tags, and 2 means to fetch all
- tags.
-
-`receivepack`, `uploadpack`::
-
- The configured helper programs to run on the remote side, for
- Git-native protocols.
-
-`http_proxy`::
-
- The proxy to use for curl (http, https, ftp, etc.) URLs.
-
-`http_proxy_authmethod`::
-
- The method used for authenticating against `http_proxy`.
-
-struct remotes can be found by name with remote_get(), and iterated
-through with for_each_remote(). remote_get(NULL) will return the
-default remote, given the current branch and configuration.
-
-struct refspec
---------------
-
-A struct refspec holds the parsed interpretation of a refspec. If it
-will force updates (starts with a '+'), force is true. If it is a
-pattern (sides end with '*') pattern is true. src and dest are the
-two sides (including '*' characters if present); if there is only one
-side, it is src, and dst is NULL; if sides exist but are empty (i.e.,
-the refspec either starts or ends with ':'), the corresponding side is
-"".
-
-An array of strings can be parsed into an array of struct refspecs
-using parse_fetch_refspec() or parse_push_refspec().
-
-remote_find_tracking(), given a remote and a struct refspec with
-either src or dst filled out, will fill out the other such that the
-result is in the "fetch" specification for the remote (note that this
-evaluates patterns and returns a single result).
-
-struct branch
--------------
-
-Note that this may end up moving to branch.h
-
-struct branch holds the configuration for a branch. It can be looked
-up with branch_get(name) for "refs/heads/{name}", or with
-branch_get(NULL) for HEAD.
-
-It contains:
-
-`name`::
-
- The short name of the branch.
-
-`refname`::
-
- The full path for the branch ref.
-
-`remote_name`::
-
- The name of the remote listed in the configuration.
-
-`merge_name`::
-
- An array of the "merge" lines in the configuration.
-
-`merge`::
-
- An array of the struct refspecs used for the merge lines. That
- is, merge[i]->dst is a local tracking ref which should be
- merged into this branch by default.
-
-`merge_nr`::
-
- The number of merge configurations
-
-branch_has_merge_config() returns true if the given branch has merge
-configuration given.
-
-Other stuff
------------
-
-There is other stuff in remote.h that is related, in general, to the
-process of interacting with remotes.
-
-(Daniel Barkalow)
diff --git a/Documentation/technical/api-revision-walking.txt b/Documentation/technical/api-revision-walking.txt
deleted file mode 100644
index 03f9ea6..0000000
--- a/Documentation/technical/api-revision-walking.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-revision walking API
-====================
-
-The revision walking API offers functions to build a list of revisions
-and then iterate over that list.
-
-Calling sequence
-----------------
-
-The walking API has a given calling sequence: first you need to
-initialize a rev_info structure, then add revisions to control what kind
-of revision list do you want to get, finally you can iterate over the
-revision list.
-
-Functions
----------
-
-`repo_init_revisions`::
-
- Initialize a rev_info structure with default values. The third
- parameter may be NULL or can be prefix path, and then the `.prefix`
- variable will be set to it. This is typically the first function you
- want to call when you want to deal with a revision list. After calling
- this function, you are free to customize options, like set
- `.ignore_merges` to 0 if you don't want to ignore merges, and so on. See
- `revision.h` for a complete list of available options.
-
-`add_pending_object`::
-
- This function can be used if you want to add commit objects as revision
- information. You can use the `UNINTERESTING` object flag to indicate if
- you want to include or exclude the given commit (and commits reachable
- from the given commit) from the revision list.
-+
-NOTE: If you have the commits as a string list then you probably want to
-use setup_revisions(), instead of parsing each string and using this
-function.
-
-`setup_revisions`::
-
- Parse revision information, filling in the `rev_info` structure, and
- removing the used arguments from the argument list. Returns the number
- of arguments left that weren't recognized, which are also moved to the
- head of the argument list. The last parameter is used in case no
- parameter given by the first two arguments.
-
-`prepare_revision_walk`::
-
- Prepares the rev_info structure for a walk. You should check if it
- returns any error (non-zero return code) and if it does not, you can
- start using get_revision() to do the iteration.
-
-`get_revision`::
-
- Takes a pointer to a `rev_info` structure and iterates over it,
- returning a `struct commit *` each time you call it. The end of the
- revision list is indicated by returning a NULL pointer.
-
-`reset_revision_walk`::
-
- Reset the flags used by the revision walking api. You can use
- this to do multiple sequential revision walks.
-
-Data structures
----------------
-
-Talk about <revision.h>, things like:
-
-* two diff_options, one for path limiting, another for output;
-* remaining functions;
-
-(Linus, JC, Dscho)
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
deleted file mode 100644
index 8bf3e37..0000000
--- a/Documentation/technical/api-run-command.txt
+++ /dev/null
@@ -1,264 +0,0 @@
-run-command API
-===============
-
-The run-command API offers a versatile tool to run sub-processes with
-redirected input and output as well as with a modified environment
-and an alternate current directory.
-
-A similar API offers the capability to run a function asynchronously,
-which is primarily used to capture the output that the function
-produces in the caller in order to process it.
-
-
-Functions
----------
-
-`child_process_init`::
-
- Initialize a struct child_process variable.
-
-`start_command`::
-
- Start a sub-process. Takes a pointer to a `struct child_process`
- that specifies the details and returns pipe FDs (if requested).
- See below for details.
-
-`finish_command`::
-
- Wait for the completion of a sub-process that was started with
- start_command().
-
-`run_command`::
-
- A convenience function that encapsulates a sequence of
- start_command() followed by finish_command(). Takes a pointer
- to a `struct child_process` that specifies the details.
-
-`run_command_v_opt`, `run_command_v_opt_cd_env`::
-
- Convenience functions that encapsulate a sequence of
- start_command() followed by finish_command(). The argument argv
- specifies the program and its arguments. The argument opt is zero
- or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,
- `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`
- that correspond to the members .no_stdin, .git_cmd,
- .stdout_to_stderr, .silent_exec_failure of `struct child_process`.
- The argument dir corresponds the member .dir. The argument env
- corresponds to the member .env.
-
-`child_process_clear`::
-
- Release the memory associated with the struct child_process.
- Most users of the run-command API don't need to call this
- function explicitly because `start_command` invokes it on
- failure and `finish_command` calls it automatically already.
-
-The functions above do the following:
-
-. If a system call failed, errno is set and -1 is returned. A diagnostic
- is printed.
-
-. If the program was not found, then -1 is returned and errno is set to
- ENOENT; a diagnostic is printed only if .silent_exec_failure is 0.
-
-. Otherwise, the program is run. If it terminates regularly, its exit
- code is returned. No diagnostic is printed, even if the exit code is
- non-zero.
-
-. If the program terminated due to a signal, then the return value is the
- signal number + 128, ie. the same value that a POSIX shell's $? would
- report. A diagnostic is printed.
-
-
-`start_async`::
-
- Run a function asynchronously. Takes a pointer to a `struct
- async` that specifies the details and returns a set of pipe FDs
- for communication with the function. See below for details.
-
-`finish_async`::
-
- Wait for the completion of an asynchronous function that was
- started with start_async().
-
-`run_hook`::
-
- Run a hook.
- The first argument is a pathname to an index file, or NULL
- if the hook uses the default index file or no index is needed.
- The second argument is the name of the hook.
- The further arguments correspond to the hook arguments.
- The last argument has to be NULL to terminate the arguments list.
- If the hook does not exist or is not executable, the return
- value will be zero.
- If it is executable, the hook will be executed and the exit
- status of the hook is returned.
- On execution, .stdout_to_stderr and .no_stdin will be set.
- (See below.)
-
-
-Data structures
----------------
-
-* `struct child_process`
-
-This describes the arguments, redirections, and environment of a
-command to run in a sub-process.
-
-The caller:
-
-1. allocates and clears (using child_process_init() or
- CHILD_PROCESS_INIT) a struct child_process variable;
-2. initializes the members;
-3. calls start_command();
-4. processes the data;
-5. closes file descriptors (if necessary; see below);
-6. calls finish_command().
-
-The .argv member is set up as an array of string pointers (NULL
-terminated), of which .argv[0] is the program name to run (usually
-without a path). If the command to run is a git command, set argv[0] to
-the command name without the 'git-' prefix and set .git_cmd = 1.
-
-Note that the ownership of the memory pointed to by .argv stays with the
-caller, but it should survive until `finish_command` completes. If the
-.argv member is NULL, `start_command` will point it at the .args
-`argv_array` (so you may use one or the other, but you must use exactly
-one). The memory in .args will be cleaned up automatically during
-`finish_command` (or during `start_command` when it is unsuccessful).
-
-The members .in, .out, .err are used to redirect stdin, stdout,
-stderr as follows:
-
-. Specify 0 to request no special redirection. No new file descriptor
- is allocated. The child process simply inherits the channel from the
- parent.
-
-. Specify -1 to have a pipe allocated; start_command() replaces -1
- by the pipe FD in the following way:
-
- .in: Returns the writable pipe end into which the caller writes;
- the readable end of the pipe becomes the child's stdin.
-
- .out, .err: Returns the readable pipe end from which the caller
- reads; the writable end of the pipe end becomes child's
- stdout/stderr.
-
- The caller of start_command() must close the so returned FDs
- after it has completed reading from/writing to it!
-
-. Specify a file descriptor > 0 to be used by the child:
-
- .in: The FD must be readable; it becomes child's stdin.
- .out: The FD must be writable; it becomes child's stdout.
- .err: The FD must be writable; it becomes child's stderr.
-
- The specified FD is closed by start_command(), even if it fails to
- run the sub-process!
-
-. Special forms of redirection are available by setting these members
- to 1:
-
- .no_stdin, .no_stdout, .no_stderr: The respective channel is
- redirected to /dev/null.
-
- .stdout_to_stderr: stdout of the child is redirected to its
- stderr. This happens after stderr is itself redirected.
- So stdout will follow stderr to wherever it is
- redirected.
-
-To modify the environment of the sub-process, specify an array of
-string pointers (NULL terminated) in .env:
-
-. If the string is of the form "VAR=value", i.e. it contains '='
- the variable is added to the child process's environment.
-
-. If the string does not contain '=', it names an environment
- variable that will be removed from the child process's environment.
-
-If the .env member is NULL, `start_command` will point it at the
-.env_array `argv_array` (so you may use one or the other, but not both).
-The memory in .env_array will be cleaned up automatically during
-`finish_command` (or during `start_command` when it is unsuccessful).
-
-To specify a new initial working directory for the sub-process,
-specify it in the .dir member.
-
-If the program cannot be found, the functions return -1 and set
-errno to ENOENT. Normally, an error message is printed, but if
-.silent_exec_failure is set to 1, no message is printed for this
-special error condition.
-
-
-* `struct async`
-
-This describes a function to run asynchronously, whose purpose is
-to produce output that the caller reads.
-
-The caller:
-
-1. allocates and clears (memset(&asy, 0, sizeof(asy));) a
- struct async variable;
-2. initializes .proc and .data;
-3. calls start_async();
-4. processes communicates with proc through .in and .out;
-5. closes .in and .out;
-6. calls finish_async().
-
-The members .in, .out are used to provide a set of fd's for
-communication between the caller and the callee as follows:
-
-. Specify 0 to have no file descriptor passed. The callee will
- receive -1 in the corresponding argument.
-
-. Specify < 0 to have a pipe allocated; start_async() replaces
- with the pipe FD in the following way:
-
- .in: Returns the writable pipe end into which the caller
- writes; the readable end of the pipe becomes the function's
- in argument.
-
- .out: Returns the readable pipe end from which the caller
- reads; the writable end of the pipe becomes the function's
- out argument.
-
- The caller of start_async() must close the returned FDs after it
- has completed reading from/writing from them.
-
-. Specify a file descriptor > 0 to be used by the function:
-
- .in: The FD must be readable; it becomes the function's in.
- .out: The FD must be writable; it becomes the function's out.
-
- The specified FD is closed by start_async(), even if it fails to
- run the function.
-
-The function pointer in .proc has the following signature:
-
- int proc(int in, int out, void *data);
-
-. in, out specifies a set of file descriptors to which the function
- must read/write the data that it needs/produces. The function
- *must* close these descriptors before it returns. A descriptor
- may be -1 if the caller did not configure a descriptor for that
- direction.
-
-. data is the value that the caller has specified in the .data member
- of struct async.
-
-. The return value of the function is 0 on success and non-zero
- on failure. If the function indicates failure, finish_async() will
- report failure as well.
-
-
-There are serious restrictions on what the asynchronous function can do
-because this facility is implemented by a thread in the same address
-space on most platforms (when pthreads is available), but by a pipe to
-a forked process otherwise:
-
-. It cannot change the program's state (global variables, environment,
- etc.) in a way that the caller notices; in other words, .in and .out
- are the only communication channels to the caller.
-
-. It must not change the program's state that the caller of the
- facility also uses.
diff --git a/Documentation/technical/api-setup.txt b/Documentation/technical/api-setup.txt
deleted file mode 100644
index eb1fa98..0000000
--- a/Documentation/technical/api-setup.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-setup API
-=========
-
-Talk about
-
-* setup_git_directory()
-* setup_git_directory_gently()
-* is_inside_git_dir()
-* is_inside_work_tree()
-* setup_work_tree()
-
-(Dscho)
-
-Pathspec
---------
-
-See glossary-context.txt for the syntax of pathspec. In memory, a
-pathspec set is represented by "struct pathspec" and is prepared by
-parse_pathspec(). This function takes several arguments:
-
-- magic_mask specifies what features that are NOT supported by the
- following code. If a user attempts to use such a feature,
- parse_pathspec() can reject it early.
-
-- flags specifies other things that the caller wants parse_pathspec to
- perform.
-
-- prefix and args come from cmd_* functions
-
-parse_pathspec() helps catch unsupported features and reject them
-politely. At a lower level, different pathspec-related functions may
-not support the same set of features. Such pathspec-sensitive
-functions are guarded with GUARD_PATHSPEC(), which will die in an
-unfriendly way when an unsupported feature is requested.
-
-The command designers are supposed to make sure that GUARD_PATHSPEC()
-never dies. They have to make sure all unsupported features are caught
-by parse_pathspec(), not by GUARD_PATHSPEC. grepping GUARD_PATHSPEC()
-should give the designers all pathspec-sensitive codepaths and what
-features they support.
-
-A similar process is applied when a new pathspec magic is added. The
-designer lifts the GUARD_PATHSPEC restriction in the functions that
-support the new magic. At the same time (s)he has to make sure this
-new feature will be caught at parse_pathspec() in commands that cannot
-handle the new magic in some cases. grepping parse_pathspec() should
-help.
diff --git a/Documentation/technical/api-sigchain.txt b/Documentation/technical/api-sigchain.txt
deleted file mode 100644
index 9e1189e..0000000
--- a/Documentation/technical/api-sigchain.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-sigchain API
-============
-
-Code often wants to set a signal handler to clean up temporary files or
-other work-in-progress when we die unexpectedly. For multiple pieces of
-code to do this without conflicting, each piece of code must remember
-the old value of the handler and restore it either when:
-
- 1. The work-in-progress is finished, and the handler is no longer
- necessary. The handler should revert to the original behavior
- (either another handler, SIG_DFL, or SIG_IGN).
-
- 2. The signal is received. We should then do our cleanup, then chain
- to the next handler (or die if it is SIG_DFL).
-
-Sigchain is a tiny library for keeping a stack of handlers. Your handler
-and installation code should look something like:
-
-------------------------------------------
- void clean_foo_on_signal(int sig)
- {
- clean_foo();
- sigchain_pop(sig);
- raise(sig);
- }
-
- void other_func()
- {
- sigchain_push_common(clean_foo_on_signal);
- mess_up_foo();
- clean_foo();
- }
-------------------------------------------
-
-Handlers are given the typedef of sigchain_fun. This is the same type
-that is given to signal() or sigaction(). It is perfectly reasonable to
-push SIG_DFL or SIG_IGN onto the stack.
-
-You can sigchain_push and sigchain_pop individual signals. For
-convenience, sigchain_push_common will push the handler onto the stack
-for many common signals.
diff --git a/Documentation/technical/api-submodule-config.txt b/Documentation/technical/api-submodule-config.txt
deleted file mode 100644
index c409559..0000000
--- a/Documentation/technical/api-submodule-config.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-submodule config cache API
-==========================
-
-The submodule config cache API allows to read submodule
-configurations/information from specified revisions. Internally
-information is lazily read into a cache that is used to avoid
-unnecessary parsing of the same .gitmodules files. Lookups can be done by
-submodule path or name.
-
-Usage
------
-
-To initialize the cache with configurations from the worktree the caller
-typically first calls `gitmodules_config()` to read values from the
-worktree .gitmodules and then to overlay the local git config values
-`parse_submodule_config_option()` from the config parsing
-infrastructure.
-
-The caller can look up information about submodules by using the
-`submodule_from_path()` or `submodule_from_name()` functions. They return
-a `struct submodule` which contains the values. The API automatically
-initializes and allocates the needed infrastructure on-demand. If the
-caller does only want to lookup values from revisions the initialization
-can be skipped.
-
-If the internal cache might grow too big or when the caller is done with
-the API, all internally cached values can be freed with submodule_free().
-
-Data Structures
----------------
-
-`struct submodule`::
-
- This structure is used to return the information about one
- submodule for a certain revision. It is returned by the lookup
- functions.
-
-Functions
----------
-
-`void submodule_free(struct repository *r)`::
-
- Use these to free the internally cached values.
-
-`int parse_submodule_config_option(const char *var, const char *value)`::
-
- Can be passed to the config parsing infrastructure to parse
- local (worktree) submodule configurations.
-
-`const struct submodule *submodule_from_path(const unsigned char *treeish_name, const char *path)`::
-
- Given a tree-ish in the superproject and a path, return the
- submodule that is bound at the path in the named tree.
-
-`const struct submodule *submodule_from_name(const unsigned char *treeish_name, const char *name)`::
-
- The same as above but lookup by name.
-
-Whenever a submodule configuration is parsed in `parse_submodule_config_option`
-via e.g. `gitmodules_config()`, it will overwrite the null_sha1 entry.
-So in the normal case, when HEAD:.gitmodules is parsed first and then overlaid
-with the repository configuration, the null_sha1 entry contains the local
-configuration of a submodule (e.g. consolidated values from local git
-configuration and the .gitmodules file in the worktree).
-
-For an example usage see test-submodule-config.c.
diff --git a/Documentation/technical/api-trace.txt b/Documentation/technical/api-trace.txt
deleted file mode 100644
index fadb597..0000000
--- a/Documentation/technical/api-trace.txt
+++ /dev/null
@@ -1,140 +0,0 @@
-trace API
-=========
-
-The trace API can be used to print debug messages to stderr or a file. Trace
-code is inactive unless explicitly enabled by setting `GIT_TRACE*` environment
-variables.
-
-The trace implementation automatically adds `timestamp file:line ... \n` to
-all trace messages. E.g.:
-
-------------
-23:59:59.123456 git.c:312 trace: built-in: git 'foo'
-00:00:00.000001 builtin/foo.c:99 foo: some message
-------------
-
-Data Structures
----------------
-
-`struct trace_key`::
-
- Defines a trace key (or category). The default (for API functions that
- don't take a key) is `GIT_TRACE`.
-+
-E.g. to define a trace key controlled by environment variable `GIT_TRACE_FOO`:
-+
-------------
-static struct trace_key trace_foo = TRACE_KEY_INIT(FOO);
-
-static void trace_print_foo(const char *message)
-{
- trace_printf_key(&trace_foo, "%s", message);
-}
-------------
-+
-Note: don't use `const` as the trace implementation stores internal state in
-the `trace_key` structure.
-
-Functions
----------
-
-`int trace_want(struct trace_key *key)`::
-
- Checks whether the trace key is enabled. Used to prevent expensive
- string formatting before calling one of the printing APIs.
-
-`void trace_disable(struct trace_key *key)`::
-
- Disables tracing for the specified key, even if the environment
- variable was set.
-
-`void trace_printf(const char *format, ...)`::
-`void trace_printf_key(struct trace_key *key, const char *format, ...)`::
-
- Prints a formatted message, similar to printf.
-
-`void trace_argv_printf(const char **argv, const char *format, ...)``::
-
- Prints a formatted message, followed by a quoted list of arguments.
-
-`void trace_strbuf(struct trace_key *key, const struct strbuf *data)`::
-
- Prints the strbuf, without additional formatting (i.e. doesn't
- choke on `%` or even `\0`).
-
-`uint64_t getnanotime(void)`::
-
- Returns nanoseconds since the epoch (01/01/1970), typically used
- for performance measurements.
-+
-Currently there are high precision timer implementations for Linux (using
-`clock_gettime(CLOCK_MONOTONIC)`) and Windows (`QueryPerformanceCounter`).
-Other platforms use `gettimeofday` as time source.
-
-`void trace_performance(uint64_t nanos, const char *format, ...)`::
-`void trace_performance_since(uint64_t start, const char *format, ...)`::
-
- Prints the elapsed time (in nanoseconds), or elapsed time since
- `start`, followed by a formatted message. Enabled via environment
- variable `GIT_TRACE_PERFORMANCE`. Used for manual profiling, e.g.:
-+
-------------
-uint64_t start = getnanotime();
-/* code section to measure */
-trace_performance_since(start, "foobar");
-------------
-+
-------------
-uint64_t t = 0;
-for (;;) {
- /* ignore */
- t -= getnanotime();
- /* code section to measure */
- t += getnanotime();
- /* ignore */
-}
-trace_performance(t, "frotz");
-------------
-
-Bugs & Caveats
---------------
-
-GIT_TRACE_* environment variables can be used to tell Git to show
-trace output to its standard error stream. Git can often spawn a pager
-internally to run its subcommand and send its standard output and
-standard error to it.
-
-Because GIT_TRACE_PERFORMANCE trace is generated only at the very end
-of the program with atexit(), which happens after the pager exits, it
-would not work well if you send its log to the standard error output
-and let Git spawn the pager at the same time.
-
-As a work around, you can for example use '--no-pager', or set
-GIT_TRACE_PERFORMANCE to another file descriptor which is redirected
-to stderr, or set GIT_TRACE_PERFORMANCE to a file specified by its
-absolute path.
-
-For example instead of the following command which by default may not
-print any performance information:
-
-------------
-GIT_TRACE_PERFORMANCE=2 git log -1
-------------
-
-you may want to use:
-
-------------
-GIT_TRACE_PERFORMANCE=2 git --no-pager log -1
-------------
-
-or:
-
-------------
-GIT_TRACE_PERFORMANCE=3 3>&2 git log -1
-------------
-
-or:
-
-------------
-GIT_TRACE_PERFORMANCE=/path/to/log/file git log -1
-------------
diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt
index 17490b5..4f07cea 100644
--- a/Documentation/technical/api-trace2.txt
+++ b/Documentation/technical/api-trace2.txt
@@ -188,261 +188,36 @@
=== Basic Command Messages
These are concerned with the lifetime of the overall git process.
-
-`void trace2_initialize_clock()`::
-
- Initialize the Trace2 start clock and nothing else. This should
- be called at the very top of main() to capture the process start
- time and reduce startup order dependencies.
-
-`void trace2_initialize()`::
-
- Determines if any Trace2 Targets should be enabled and
- initializes the Trace2 facility. This includes setting up the
- Trace2 thread local storage (TLS).
-+
-This function emits a "version" message containing the version of git
-and the Trace2 protocol.
-+
-This function should be called from `main()` as early as possible in
-the life of the process after essential process initialization.
-
-`int trace2_is_enabled()`::
-
- Returns 1 if Trace2 is enabled (at least one target is
- active).
-
-`void trace2_cmd_start(int argc, const char **argv)`::
-
- Emits a "start" message containing the process command line
- arguments.
-
-`int trace2_cmd_exit(int exit_code)`::
-
- Emits an "exit" message containing the process exit-code and
- elapsed time.
-+
-Returns the exit-code.
-
-`void trace2_cmd_error(const char *fmt, va_list ap)`::
-
- Emits an "error" message containing a formatted error message.
-
-`void trace2_cmd_path(const char *pathname)`::
-
- Emits a "cmd_path" message with the full pathname of the
- current process.
+e.g: `void trace2_initialize_clock()`, `void trace2_initialize()`,
+`int trace2_is_enabled()`, `void trace2_cmd_start(int argc, const char **argv)`.
=== Command Detail Messages
These are concerned with describing the specific Git command
after the command line, config, and environment are inspected.
-
-`void trace2_cmd_name(const char *name)`::
-
- Emits a "cmd_name" message with the canonical name of the
- command, for example "status" or "checkout".
-
-`void trace2_cmd_mode(const char *mode)`::
-
- Emits a "cmd_mode" message with a qualifier name to further
- describe the current git command.
-+
-This message is intended to be used with git commands having multiple
-major modes. For example, a "checkout" command can checkout a new
-branch or it can checkout a single file, so the checkout code could
-emit a cmd_mode message of "branch" or "file".
-
-`void trace2_cmd_alias(const char *alias, const char **argv_expansion)`::
-
- Emits an "alias" message containing the alias used and the
- argument expansion.
-
-`void trace2_def_param(const char *parameter, const char *value)`::
-
- Emits a "def_param" message containing a key/value pair.
-+
-This message is intended to report some global aspect of the current
-command, such as a configuration setting or command line switch that
-significantly affects program performance or behavior, such as
-`core.abbrev`, `status.showUntrackedFiles`, or `--no-ahead-behind`.
-
-`void trace2_cmd_list_config()`::
-
- Emits a "def_param" messages for "important" configuration
- settings.
-+
-The environment variable `GIT_TRACE2_CONFIG_PARAMS` or the `trace2.configParams`
-config value can be set to a
-list of patterns of important configuration settings, for example:
-`core.*,remote.*.url`. This function will iterate over all config
-settings and emit a "def_param" message for each match.
-
-`void trace2_cmd_set_config(const char *key, const char *value)`::
-
- Emits a "def_param" message for a new or updated key/value
- pair IF `key` is considered important.
-+
-This is used to hook into `git_config_set()` and catch any
-configuration changes and update a value previously reported by
-`trace2_cmd_list_config()`.
-
-`void trace2_def_repo(struct repository *repo)`::
-
- Registers a repository with the Trace2 layer. Assigns a
- unique "repo-id" to `repo->trace2_repo_id`.
-+
-Emits a "worktree" messages containing the repo-id and the worktree
-pathname.
-+
-Region and data messages (described later) may refer to this repo-id.
-+
-The main/top-level repository will have repo-id value 1 (aka "r1").
-+
-The repo-id field is in anticipation of future in-proc submodule
-repositories.
+e.g: `void trace2_cmd_name(const char *name)`,
+`void trace2_cmd_mode(const char *mode)`.
=== Child Process Messages
These are concerned with the various spawned child processes,
including shell scripts, git commands, editors, pagers, and hooks.
-`void trace2_child_start(struct child_process *cmd)`::
-
- Emits a "child_start" message containing the "child-id",
- "child-argv", and "child-classification".
-+
-Before calling this, set `cmd->trace2_child_class` to a name
-describing the type of child process, for example "editor".
-+
-This function assigns a unique "child-id" to `cmd->trace2_child_id`.
-This field is used later during the "child_exit" message to associate
-it with the "child_start" message.
-+
-This function should be called before spawning the child process.
-
-`void trace2_child_exit(struct child_proess *cmd, int child_exit_code)`::
-
- Emits a "child_exit" message containing the "child-id",
- the child's elapsed time and exit-code.
-+
-The reported elapsed time includes the process creation overhead and
-time spend waiting for it to exit, so it may be slightly longer than
-the time reported by the child itself.
-+
-This function should be called after reaping the child process.
-
-`int trace2_exec(const char *exe, const char **argv)`::
-
- Emits a "exec" message containing the "exec-id" and the
- argv of the new process.
-+
-This function should be called before calling one of the `exec()`
-variants, such as `execvp()`.
-+
-This function returns a unique "exec-id". This value is used later
-if the exec() fails and a "exec-result" message is necessary.
-
-`void trace2_exec_result(int exec_id, int error_code)`::
-
- Emits a "exec_result" message containing the "exec-id"
- and the error code.
-+
-On Unix-based systems, `exec()` does not return if successful.
-This message is used to indicate that the `exec()` failed and
-that the current program is continuing.
+e.g: `void trace2_child_start(struct child_process *cmd)`.
=== Git Thread Messages
These messages are concerned with Git thread usage.
-`void trace2_thread_start(const char *thread_name)`::
-
- Emits a "thread_start" message.
-+
-The `thread_name` field should be a descriptive name, such as the
-unique name of the thread-proc. A unique "thread-id" will be added
-to the name to uniquely identify thread instances.
-+
-Region and data messages (described later) may refer to this thread
-name.
-+
-This function must be called by the thread-proc of the new thread
-(so that TLS data is properly initialized) and not by the caller
-of `pthread_create()`.
-
-`void trace2_thread_exit()`::
-
- Emits a "thread_exit" message containing the thread name
- and the thread elapsed time.
-+
-This function must be called by the thread-proc before it returns
-(so that the correct TLS data is used and cleaned up). It should
-not be called by the caller of `pthread_join()`.
+e.g: `void trace2_thread_start(const char *thread_name)`.
=== Region and Data Messages
These are concerned with recording performance data
-over regions or spans of code.
+over regions or spans of code. e.g:
+`void trace2_region_enter(const char *category, const char *label, const struct repository *repo)`.
-`void trace2_region_enter(const char *category, const char *label, const struct repository *repo)`::
-
-`void trace2_region_enter_printf(const char *category, const char *label, const struct repository *repo, const char *fmt, ...)`::
-
-`void trace2_region_enter_printf_va(const char *category, const char *label, const struct repository *repo, const char *fmt, va_list ap)`::
-
- Emits a thread-relative "region_enter" message with optional
- printf string.
-+
-This function pushes a new region nesting stack level on the current
-thread and starts a clock for the new stack frame.
-+
-The `category` field is an arbitrary category name used to classify
-regions by feature area, such as "status" or "index". At this time
-it is only just printed along with the rest of the message. It may
-be used in the future to filter messages.
-+
-The `label` field is an arbitrary label used to describe the activity
-being started, such as "read_recursive" or "do_read_index".
-+
-The `repo` field, if set, will be used to get the "repo-id", so that
-recursive operations can be attributed to the correct repository.
-
-`void trace2_region_leave(const char *category, const char *label, const struct repository *repo)`::
-
-`void trace2_region_leave_printf(const char *category, const char *label, const struct repository *repo, const char *fmt, ...)`::
-
-`void trace2_region_leave_printf_va(const char *category, const char *label, const struct repository *repo, const char *fmt, va_list ap)`::
-
- Emits a thread-relative "region_leave" message with optional
- printf string.
-+
-This function pops the region nesting stack on the current thread
-and reports the elapsed time of the stack frame.
-+
-The `category`, `label`, and `repo` fields are the same as above.
-The `category` and `label` do not need to match the corresponding
-"region_enter" message, but it makes the data stream easier to
-understand.
-
-`void trace2_data_string(const char *category, const struct repository *repo, const char *key, const char * value)`::
-
-`void trace2_data_intmax(const char *category, const struct repository *repo, const char *key, intmax value)`::
-
-`void trace2_data_json(const char *category, const struct repository *repo, const char *key, const struct json_writer *jw)`::
-
- Emits a region- and thread-relative "data" or "data_json" message.
-+
-This is a key/value pair message containing information about the
-current thread, region stack, and repository. This could be used
-to print the number of files in a directory during a multi-threaded
-recursive tree walk.
-
-`void trace2_printf(const char *fmt, ...)`::
-
-`void trace2_printf_va(const char *fmt, va_list ap)`::
-
- Emits a region- and thread-relative "printf" message.
+Refer to trace2.h for details about all trace2 functions.
== Trace2 Target Formats
diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt
deleted file mode 100644
index 7962e32..0000000
--- a/Documentation/technical/api-tree-walking.txt
+++ /dev/null
@@ -1,149 +0,0 @@
-tree walking API
-================
-
-The tree walking API is used to traverse and inspect trees.
-
-Data Structures
----------------
-
-`struct name_entry`::
-
- An entry in a tree. Each entry has a sha1 identifier, pathname, and
- mode.
-
-`struct tree_desc`::
-
- A semi-opaque data structure used to maintain the current state of the
- walk.
-+
-* `buffer` is a pointer into the memory representation of the tree. It always
-points at the current entry being visited.
-
-* `size` counts the number of bytes left in the `buffer`.
-
-* `entry` points to the current entry being visited.
-
-`struct traverse_info`::
-
- A structure used to maintain the state of a traversal.
-+
-* `prev` points to the traverse_info which was used to descend into the
-current tree. If this is the top-level tree `prev` will point to
-a dummy traverse_info.
-
-* `name` is the entry for the current tree (if the tree is a subtree).
-
-* `pathlen` is the length of the full path for the current tree.
-
-* `conflicts` can be used by callbacks to maintain directory-file conflicts.
-
-* `fn` is a callback called for each entry in the tree. See Traversing for more
-information.
-
-* `data` can be anything the `fn` callback would want to use.
-
-* `show_all_errors` tells whether to stop at the first error or not.
-
-Initializing
-------------
-
-`init_tree_desc`::
-
- Initialize a `tree_desc` and decode its first entry. The buffer and
- size parameters are assumed to be the same as the buffer and size
- members of `struct tree`.
-
-`fill_tree_descriptor`::
-
- Initialize a `tree_desc` and decode its first entry given the
- object ID of a tree. Returns the `buffer` member if the latter
- is a valid tree identifier and NULL otherwise.
-
-`setup_traverse_info`::
-
- Initialize a `traverse_info` given the pathname of the tree to start
- traversing from.
-
-Walking
--------
-
-`tree_entry`::
-
- Visit the next entry in a tree. Returns 1 when there are more entries
- left to visit and 0 when all entries have been visited. This is
- commonly used in the test of a while loop.
-
-`tree_entry_len`::
-
- Calculate the length of a tree entry's pathname. This utilizes the
- memory structure of a tree entry to avoid the overhead of using a
- generic strlen().
-
-`update_tree_entry`::
-
- Walk to the next entry in a tree. This is commonly used in conjunction
- with `tree_entry_extract` to inspect the current entry.
-
-`tree_entry_extract`::
-
- Decode the entry currently being visited (the one pointed to by
- `tree_desc's` `entry` member) and return the sha1 of the entry. The
- `pathp` and `modep` arguments are set to the entry's pathname and mode
- respectively.
-
-`get_tree_entry`::
-
- Find an entry in a tree given a pathname and the sha1 of a tree to
- search. Returns 0 if the entry is found and -1 otherwise. The third
- and fourth parameters are set to the entry's sha1 and mode
- respectively.
-
-Traversing
-----------
-
-`traverse_trees`::
-
- Traverse `n` number of trees in parallel. The `fn` callback member of
- `traverse_info` is called once for each tree entry.
-
-`traverse_callback_t`::
- The arguments passed to the traverse callback are as follows:
-+
-* `n` counts the number of trees being traversed.
-
-* `mask` has its nth bit set if something exists in the nth entry.
-
-* `dirmask` has its nth bit set if the nth tree's entry is a directory.
-
-* `entry` is an array of size `n` where the nth entry is from the nth tree.
-
-* `info` maintains the state of the traversal.
-
-+
-Returning a negative value will terminate the traversal. Otherwise the
-return value is treated as an update mask. If the nth bit is set the nth tree
-will be updated and if the bit is not set the nth tree entry will be the
-same in the next callback invocation.
-
-`make_traverse_path`::
-
- Generate the full pathname of a tree entry based from the root of the
- traversal. For example, if the traversal has recursed into another
- tree named "bar" the pathname of an entry "baz" in the "bar"
- tree would be "bar/baz".
-
-`traverse_path_len`::
-
- Calculate the length of a pathname returned by `make_traverse_path`.
- This utilizes the memory structure of a tree entry to avoid the
- overhead of using a generic strlen().
-
-`strbuf_make_traverse_path`::
-
- Convenience wrapper to `make_traverse_path` into a strbuf.
-
-Authors
--------
-
-Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds
-<torvalds@linux-foundation.org>
diff --git a/Makefile b/Makefile
index b7d7374..8cd6e85 100644
--- a/Makefile
+++ b/Makefile
@@ -481,7 +481,7 @@
#
# When DEVELOPER is set, DEVOPTS can be used to control compiler
# options. This variable contains keywords separated by
-# whitespace. The following keywords are are recognized:
+# whitespace. The following keywords are recognized:
#
# no-error:
#
@@ -1127,6 +1127,7 @@
BUILTIN_OBJS += builtin/show-branch.o
BUILTIN_OBJS += builtin/show-index.o
BUILTIN_OBJS += builtin/show-ref.o
+BUILTIN_OBJS += builtin/sparse-checkout.o
BUILTIN_OBJS += builtin/stash.o
BUILTIN_OBJS += builtin/stripspace.o
BUILTIN_OBJS += builtin/submodule--helper.o
diff --git a/add-interactive.c b/add-interactive.c
index d6cb98c..f395d54 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -6,6 +6,9 @@
#include "revision.h"
#include "refs.h"
#include "string-list.h"
+#include "lockfile.h"
+#include "dir.h"
+#include "run-command.h"
struct add_i_state {
struct repository *r;
@@ -72,15 +75,17 @@
struct prefix_item_list {
struct string_list items;
struct string_list sorted;
+ int *selected; /* for multi-selections */
size_t min_length, max_length;
};
#define PREFIX_ITEM_LIST_INIT \
- { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, 1, 4 }
+ { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, NULL, 1, 4 }
static void prefix_item_list_clear(struct prefix_item_list *list)
{
string_list_clear(&list->items, 1);
string_list_clear(&list->sorted, 0);
+ FREE_AND_NULL(list->selected);
}
static void extend_prefix_length(struct string_list_item *p,
@@ -182,11 +187,12 @@
struct list_options {
int columns;
const char *header;
- void (*print_item)(int i, struct string_list_item *item, void *print_item_data);
+ void (*print_item)(int i, int selected, struct string_list_item *item,
+ void *print_item_data);
void *print_item_data;
};
-static void list(struct add_i_state *s, struct string_list *list,
+static void list(struct add_i_state *s, struct string_list *list, int *selected,
struct list_options *opts)
{
int i, last_lf = 0;
@@ -199,7 +205,8 @@
"%s", opts->header);
for (i = 0; i < list->nr; i++) {
- opts->print_item(i, list->items + i, opts->print_item_data);
+ opts->print_item(i, selected ? selected[i] : 0, list->items + i,
+ opts->print_item_data);
if ((opts->columns) && ((i + 1) % (opts->columns))) {
putchar('\t');
@@ -218,6 +225,10 @@
struct list_options list_opts;
const char *prompt;
+ enum {
+ SINGLETON = (1<<0),
+ IMMEDIATE = (1<<1),
+ } flags;
void (*print_help)(struct add_i_state *s);
};
@@ -225,7 +236,8 @@
#define LIST_AND_CHOOSE_QUIT (-2)
/*
- * Returns the selected index.
+ * Returns the selected index in singleton mode, the number of selected items
+ * otherwise.
*
* If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF,
* `LIST_AND_CHOOSE_QUIT` is returned.
@@ -234,8 +246,19 @@
struct prefix_item_list *items,
struct list_and_choose_options *opts)
{
+ int singleton = opts->flags & SINGLETON;
+ int immediate = opts->flags & IMMEDIATE;
+
struct strbuf input = STRBUF_INIT;
- ssize_t res = LIST_AND_CHOOSE_ERROR;
+ ssize_t res = singleton ? LIST_AND_CHOOSE_ERROR : 0;
+
+ if (!singleton) {
+ free(items->selected);
+ CALLOC_ARRAY(items->selected, items->items.nr);
+ }
+
+ if (singleton && !immediate)
+ BUG("singleton requires immediate");
find_unique_prefixes(items);
@@ -244,15 +267,16 @@
strbuf_reset(&input);
- list(s, &items->items, &opts->list_opts);
+ list(s, &items->items, items->selected, &opts->list_opts);
color_fprintf(stdout, s->prompt_color, "%s", opts->prompt);
- fputs("> ", stdout);
+ fputs(singleton ? "> " : ">> ", stdout);
fflush(stdout);
if (strbuf_getline(&input, stdin) == EOF) {
putchar('\n');
- res = LIST_AND_CHOOSE_QUIT;
+ if (immediate)
+ res = LIST_AND_CHOOSE_QUIT;
break;
}
strbuf_trim(&input);
@@ -268,7 +292,9 @@
p = input.buf;
for (;;) {
size_t sep = strcspn(p, " \t\r\n,");
- ssize_t index = -1;
+ int choose = 1;
+ /* `from` is inclusive, `to` is exclusive */
+ ssize_t from = -1, to = -1;
if (!sep) {
if (!*p)
@@ -277,30 +303,70 @@
continue;
}
- if (isdigit(*p)) {
+ /* Input that begins with '-'; de-select */
+ if (*p == '-') {
+ choose = 0;
+ p++;
+ sep--;
+ }
+
+ if (sep == 1 && *p == '*') {
+ from = 0;
+ to = items->items.nr;
+ } else if (isdigit(*p)) {
char *endp;
- index = strtoul(p, &endp, 10) - 1;
- if (endp != p + sep)
- index = -1;
+ /*
+ * A range can be specified like 5-7 or 5-.
+ *
+ * Note: `from` is 0-based while the user input
+ * is 1-based, hence we have to decrement by
+ * one. We do not have to decrement `to` even
+ * if it is 0-based because it is an exclusive
+ * boundary.
+ */
+ from = strtoul(p, &endp, 10) - 1;
+ if (endp == p + sep)
+ to = from + 1;
+ else if (*endp == '-') {
+ to = strtoul(++endp, &endp, 10);
+ /* extra characters after the range? */
+ if (endp != p + sep)
+ from = -1;
+ }
}
if (p[sep])
p[sep++] = '\0';
- if (index < 0)
- index = find_unique(p, items);
+ if (from < 0) {
+ from = find_unique(p, items);
+ if (from >= 0)
+ to = from + 1;
+ }
- if (index < 0 || index >= items->items.nr)
+ if (from < 0 || from >= items->items.nr ||
+ (singleton && from + 1 != to)) {
color_fprintf_ln(stdout, s->error_color,
_("Huh (%s)?"), p);
- else {
- res = index;
+ break;
+ } else if (singleton) {
+ res = from;
break;
}
+ if (to > items->items.nr)
+ to = items->items.nr;
+
+ for (; from < to; from++)
+ if (items->selected[from] != choose) {
+ items->selected[from] = choose;
+ res += choose ? +1 : -1;
+ }
+
p += sep;
}
- if (res != LIST_AND_CHOOSE_ERROR)
+ if ((immediate && res != LIST_AND_CHOOSE_ERROR) ||
+ !strcmp(input.buf, "*"))
break;
}
@@ -310,10 +376,11 @@
struct adddel {
uintmax_t add, del;
- unsigned seen:1, binary:1;
+ unsigned seen:1, unmerged:1, binary:1;
};
struct file_item {
+ size_t prefix_length;
struct adddel index, worktree;
};
@@ -344,10 +411,12 @@
}
struct collection_status {
- enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } phase;
+ enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } mode;
const char *reference;
+ unsigned skip_unseen:1;
+ size_t unmerged_count, binary_count;
struct string_list *files;
struct hashmap file_map;
};
@@ -370,11 +439,14 @@
int hash = strhash(name);
struct pathname_entry *entry;
struct file_item *file_item;
- struct adddel *adddel;
+ struct adddel *adddel, *other_adddel;
entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
struct pathname_entry, ent);
if (!entry) {
+ if (s->skip_unseen)
+ continue;
+
add_file_item(s->files, name);
entry = xcalloc(sizeof(*entry), 1);
@@ -385,37 +457,64 @@
}
file_item = entry->item;
- adddel = s->phase == FROM_INDEX ?
+ adddel = s->mode == FROM_INDEX ?
&file_item->index : &file_item->worktree;
+ other_adddel = s->mode == FROM_INDEX ?
+ &file_item->worktree : &file_item->index;
adddel->seen = 1;
adddel->add = stat.files[i]->added;
adddel->del = stat.files[i]->deleted;
- if (stat.files[i]->is_binary)
+ if (stat.files[i]->is_binary) {
+ if (!other_adddel->binary)
+ s->binary_count++;
adddel->binary = 1;
+ }
+ if (stat.files[i]->is_unmerged) {
+ if (!other_adddel->unmerged)
+ s->unmerged_count++;
+ adddel->unmerged = 1;
+ }
}
free_diffstat_info(&stat);
}
-static int get_modified_files(struct repository *r, struct string_list *files,
- const struct pathspec *ps)
+enum modified_files_filter {
+ NO_FILTER = 0,
+ WORKTREE_ONLY = 1,
+ INDEX_ONLY = 2,
+};
+
+static int get_modified_files(struct repository *r,
+ enum modified_files_filter filter,
+ struct prefix_item_list *files,
+ const struct pathspec *ps,
+ size_t *unmerged_count,
+ size_t *binary_count)
{
struct object_id head_oid;
int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
&head_oid, NULL);
- struct collection_status s = { FROM_WORKTREE };
+ struct collection_status s = { 0 };
+ int i;
if (discard_index(r->index) < 0 ||
repo_read_index_preload(r, ps, 0) < 0)
return error(_("could not read index"));
- string_list_clear(files, 1);
- s.files = files;
+ prefix_item_list_clear(files);
+ s.files = &files->items;
hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0);
- for (s.phase = FROM_WORKTREE; s.phase <= FROM_INDEX; s.phase++) {
+ for (i = 0; i < 2; i++) {
struct rev_info rev;
struct setup_revision_opt opt = { 0 };
+ if (filter == INDEX_ONLY)
+ s.mode = (i == 0) ? FROM_INDEX : FROM_WORKTREE;
+ else
+ s.mode = (i == 0) ? FROM_WORKTREE : FROM_INDEX;
+ s.skip_unseen = filter && i;
+
opt.def = is_initial ?
empty_tree_oid_hex() : oid_to_hex(&head_oid);
@@ -429,17 +528,24 @@
if (ps)
copy_pathspec(&rev.prune_data, ps);
- if (s.phase == FROM_INDEX)
+ if (s.mode == FROM_INDEX)
run_diff_index(&rev, 1);
else {
rev.diffopt.flags.ignore_dirty_submodules = 1;
run_diff_files(&rev, 0);
}
+
+ if (ps)
+ clear_pathspec(&rev.prune_data);
}
hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
+ if (unmerged_count)
+ *unmerged_count = s.unmerged_count;
+ if (binary_count)
+ *binary_count = s.binary_count;
/* While the diffs are ordered already, we ran *two* diffs... */
- string_list_sort(files);
+ string_list_sort(&files->items);
return 0;
}
@@ -474,43 +580,401 @@
}
struct print_file_item_data {
- const char *modified_fmt;
- struct strbuf buf, index, worktree;
+ const char *modified_fmt, *color, *reset;
+ struct strbuf buf, name, index, worktree;
+ unsigned only_names:1;
};
-static void print_file_item(int i, struct string_list_item *item,
+static void print_file_item(int i, int selected, struct string_list_item *item,
void *print_file_item_data)
{
struct file_item *c = item->util;
struct print_file_item_data *d = print_file_item_data;
+ const char *highlighted = NULL;
strbuf_reset(&d->index);
strbuf_reset(&d->worktree);
strbuf_reset(&d->buf);
+ /* Format the item with the prefix highlighted. */
+ if (c->prefix_length > 0 &&
+ is_valid_prefix(item->string, c->prefix_length)) {
+ strbuf_reset(&d->name);
+ strbuf_addf(&d->name, "%s%.*s%s%s", d->color,
+ (int)c->prefix_length, item->string, d->reset,
+ item->string + c->prefix_length);
+ highlighted = d->name.buf;
+ }
+
+ if (d->only_names) {
+ printf("%c%2d: %s", selected ? '*' : ' ', i + 1,
+ highlighted ? highlighted : item->string);
+ return;
+ }
+
render_adddel(&d->worktree, &c->worktree, _("nothing"));
render_adddel(&d->index, &c->index, _("unchanged"));
- strbuf_addf(&d->buf, d->modified_fmt,
- d->index.buf, d->worktree.buf, item->string);
- printf(" %2d: %s", i + 1, d->buf.buf);
+ strbuf_addf(&d->buf, d->modified_fmt, d->index.buf, d->worktree.buf,
+ highlighted ? highlighted : item->string);
+
+ printf("%c%2d: %s", selected ? '*' : ' ', i + 1, d->buf.buf);
}
static int run_status(struct add_i_state *s, const struct pathspec *ps,
- struct string_list *files, struct list_options *opts)
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
{
- if (get_modified_files(s->r, files, ps) < 0)
+ if (get_modified_files(s->r, NO_FILTER, files, ps, NULL, NULL) < 0)
return -1;
- list(s, files, opts);
+ list(s, &files->items, NULL, &opts->list_opts);
putchar('\n');
return 0;
}
+static int run_update(struct add_i_state *s, const struct pathspec *ps,
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
+{
+ int res = 0, fd;
+ size_t count, i;
+ struct lock_file index_lock;
+
+ if (get_modified_files(s->r, WORKTREE_ONLY, files, ps, NULL, NULL) < 0)
+ return -1;
+
+ if (!files->items.nr) {
+ putchar('\n');
+ return 0;
+ }
+
+ opts->prompt = N_("Update");
+ count = list_and_choose(s, files, opts);
+ if (count <= 0) {
+ putchar('\n');
+ return 0;
+ }
+
+ fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
+ if (fd < 0) {
+ putchar('\n');
+ return -1;
+ }
+
+ for (i = 0; i < files->items.nr; i++) {
+ const char *name = files->items.items[i].string;
+ if (files->selected[i] &&
+ add_file_to_index(s->r->index, name, 0) < 0) {
+ res = error(_("could not stage '%s'"), name);
+ break;
+ }
+ }
+
+ if (!res && write_locked_index(s->r->index, &index_lock, COMMIT_LOCK) < 0)
+ res = error(_("could not write index"));
+
+ if (!res)
+ printf(Q_("updated %d path\n",
+ "updated %d paths\n", count), (int)count);
+
+ putchar('\n');
+ return res;
+}
+
+static void revert_from_diff(struct diff_queue_struct *q,
+ struct diff_options *opt, void *data)
+{
+ int i, add_flags = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filespec *one = q->queue[i]->one;
+ struct cache_entry *ce;
+
+ if (!(one->mode && !is_null_oid(&one->oid))) {
+ remove_file_from_index(opt->repo->index, one->path);
+ printf(_("note: %s is untracked now.\n"), one->path);
+ } else {
+ ce = make_cache_entry(opt->repo->index, one->mode,
+ &one->oid, one->path, 0, 0);
+ if (!ce)
+ die(_("make_cache_entry failed for path '%s'"),
+ one->path);
+ add_index_entry(opt->repo->index, ce, add_flags);
+ }
+ }
+}
+
+static int run_revert(struct add_i_state *s, const struct pathspec *ps,
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
+{
+ int res = 0, fd;
+ size_t count, i, j;
+
+ struct object_id oid;
+ int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
+ NULL);
+ struct lock_file index_lock;
+ const char **paths;
+ struct tree *tree;
+ struct diff_options diffopt = { NULL };
+
+ if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
+ return -1;
+
+ if (!files->items.nr) {
+ putchar('\n');
+ return 0;
+ }
+
+ opts->prompt = N_("Revert");
+ count = list_and_choose(s, files, opts);
+ if (count <= 0)
+ goto finish_revert;
+
+ fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
+ if (fd < 0) {
+ res = -1;
+ goto finish_revert;
+ }
+
+ if (is_initial)
+ oidcpy(&oid, s->r->hash_algo->empty_tree);
+ else {
+ tree = parse_tree_indirect(&oid);
+ if (!tree) {
+ res = error(_("Could not parse HEAD^{tree}"));
+ goto finish_revert;
+ }
+ oidcpy(&oid, &tree->object.oid);
+ }
+
+ ALLOC_ARRAY(paths, count + 1);
+ for (i = j = 0; i < files->items.nr; i++)
+ if (files->selected[i])
+ paths[j++] = files->items.items[i].string;
+ paths[j] = NULL;
+
+ parse_pathspec(&diffopt.pathspec, 0,
+ PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH,
+ NULL, paths);
+
+ diffopt.output_format = DIFF_FORMAT_CALLBACK;
+ diffopt.format_callback = revert_from_diff;
+ diffopt.flags.override_submodule_config = 1;
+ diffopt.repo = s->r;
+
+ if (do_diff_cache(&oid, &diffopt))
+ res = -1;
+ else {
+ diffcore_std(&diffopt);
+ diff_flush(&diffopt);
+ }
+ free(paths);
+ clear_pathspec(&diffopt.pathspec);
+
+ if (!res && write_locked_index(s->r->index, &index_lock,
+ COMMIT_LOCK) < 0)
+ res = -1;
+ else
+ res = repo_refresh_and_write_index(s->r, REFRESH_QUIET, 0, 1,
+ NULL, NULL, NULL);
+
+ if (!res)
+ printf(Q_("reverted %d path\n",
+ "reverted %d paths\n", count), (int)count);
+
+finish_revert:
+ putchar('\n');
+ return res;
+}
+
+static int get_untracked_files(struct repository *r,
+ struct prefix_item_list *files,
+ const struct pathspec *ps)
+{
+ struct dir_struct dir = { 0 };
+ size_t i;
+ struct strbuf buf = STRBUF_INIT;
+
+ if (repo_read_index(r) < 0)
+ return error(_("could not read index"));
+
+ prefix_item_list_clear(files);
+ setup_standard_excludes(&dir);
+ add_pattern_list(&dir, EXC_CMDL, "--exclude option");
+ fill_directory(&dir, r->index, ps);
+
+ for (i = 0; i < dir.nr; i++) {
+ struct dir_entry *ent = dir.entries[i];
+
+ if (index_name_is_other(r->index, ent->name, ent->len)) {
+ strbuf_reset(&buf);
+ strbuf_add(&buf, ent->name, ent->len);
+ add_file_item(&files->items, buf.buf);
+ }
+ }
+
+ strbuf_release(&buf);
+ return 0;
+}
+
+static int run_add_untracked(struct add_i_state *s, const struct pathspec *ps,
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
+{
+ struct print_file_item_data *d = opts->list_opts.print_item_data;
+ int res = 0, fd;
+ size_t count, i;
+ struct lock_file index_lock;
+
+ if (get_untracked_files(s->r, files, ps) < 0)
+ return -1;
+
+ if (!files->items.nr) {
+ printf(_("No untracked files.\n"));
+ goto finish_add_untracked;
+ }
+
+ opts->prompt = N_("Add untracked");
+ d->only_names = 1;
+ count = list_and_choose(s, files, opts);
+ d->only_names = 0;
+ if (count <= 0)
+ goto finish_add_untracked;
+
+ fd = repo_hold_locked_index(s->r, &index_lock, LOCK_REPORT_ON_ERROR);
+ if (fd < 0) {
+ res = -1;
+ goto finish_add_untracked;
+ }
+
+ for (i = 0; i < files->items.nr; i++) {
+ const char *name = files->items.items[i].string;
+ if (files->selected[i] &&
+ add_file_to_index(s->r->index, name, 0) < 0) {
+ res = error(_("could not stage '%s'"), name);
+ break;
+ }
+ }
+
+ if (!res &&
+ write_locked_index(s->r->index, &index_lock, COMMIT_LOCK) < 0)
+ res = error(_("could not write index"));
+
+ if (!res)
+ printf(Q_("added %d path\n",
+ "added %d paths\n", count), (int)count);
+
+finish_add_untracked:
+ putchar('\n');
+ return res;
+}
+
+static int run_patch(struct add_i_state *s, const struct pathspec *ps,
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
+{
+ int res = 0;
+ ssize_t count, i, j;
+ size_t unmerged_count = 0, binary_count = 0;
+
+ if (get_modified_files(s->r, WORKTREE_ONLY, files, ps,
+ &unmerged_count, &binary_count) < 0)
+ return -1;
+
+ if (unmerged_count || binary_count) {
+ for (i = j = 0; i < files->items.nr; i++) {
+ struct file_item *item = files->items.items[i].util;
+
+ if (item->index.binary || item->worktree.binary) {
+ free(item);
+ free(files->items.items[i].string);
+ } else if (item->index.unmerged ||
+ item->worktree.unmerged) {
+ color_fprintf_ln(stderr, s->error_color,
+ _("ignoring unmerged: %s"),
+ files->items.items[i].string);
+ free(item);
+ free(files->items.items[i].string);
+ } else
+ files->items.items[j++] = files->items.items[i];
+ }
+ files->items.nr = j;
+ }
+
+ if (!files->items.nr) {
+ if (binary_count)
+ fprintf(stderr, _("Only binary files changed.\n"));
+ else
+ fprintf(stderr, _("No changes.\n"));
+ return 0;
+ }
+
+ opts->prompt = N_("Patch update");
+ count = list_and_choose(s, files, opts);
+ if (count >= 0) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+
+ argv_array_pushl(&args, "git", "add--interactive", "--patch",
+ "--", NULL);
+ for (i = 0; i < files->items.nr; i++)
+ if (files->selected[i])
+ argv_array_push(&args,
+ files->items.items[i].string);
+ res = run_command_v_opt(args.argv, 0);
+ argv_array_clear(&args);
+ }
+
+ return res;
+}
+
+static int run_diff(struct add_i_state *s, const struct pathspec *ps,
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts)
+{
+ int res = 0;
+ ssize_t count, i;
+
+ struct object_id oid;
+ int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
+ NULL);
+ if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
+ return -1;
+
+ if (!files->items.nr) {
+ putchar('\n');
+ return 0;
+ }
+
+ opts->prompt = N_("Review diff");
+ opts->flags = IMMEDIATE;
+ count = list_and_choose(s, files, opts);
+ opts->flags = 0;
+ if (count >= 0) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+
+ argv_array_pushl(&args, "git", "diff", "-p", "--cached",
+ oid_to_hex(!is_initial ? &oid :
+ s->r->hash_algo->empty_tree),
+ "--", NULL);
+ for (i = 0; i < files->items.nr; i++)
+ if (files->selected[i])
+ argv_array_push(&args,
+ files->items.items[i].string);
+ res = run_command_v_opt(args.argv, 0);
+ argv_array_clear(&args);
+ }
+
+ putchar('\n');
+ return res;
+}
+
static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
- struct string_list *unused_files,
- struct list_options *unused_opts)
+ struct prefix_item_list *unused_files,
+ struct list_and_choose_options *unused_opts)
{
color_fprintf_ln(stdout, s->help_color, "status - %s",
_("show paths with changes"));
@@ -528,9 +992,29 @@
return 0;
}
+static void choose_prompt_help(struct add_i_state *s)
+{
+ color_fprintf_ln(stdout, s->help_color, "%s",
+ _("Prompt help:"));
+ color_fprintf_ln(stdout, s->help_color, "1 - %s",
+ _("select a single item"));
+ color_fprintf_ln(stdout, s->help_color, "3-5 - %s",
+ _("select a range of items"));
+ color_fprintf_ln(stdout, s->help_color, "2-3,6-9 - %s",
+ _("select multiple ranges"));
+ color_fprintf_ln(stdout, s->help_color, "foo - %s",
+ _("select item based on unique prefix"));
+ color_fprintf_ln(stdout, s->help_color, "-... - %s",
+ _("unselect specified items"));
+ color_fprintf_ln(stdout, s->help_color, "* - %s",
+ _("choose all items"));
+ color_fprintf_ln(stdout, s->help_color, " - %s",
+ _("(empty) finish selecting"));
+}
+
typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
- struct string_list *files,
- struct list_options *opts);
+ struct prefix_item_list *files,
+ struct list_and_choose_options *opts);
struct command_item {
size_t prefix_length;
@@ -541,7 +1025,8 @@
const char *color, *reset;
};
-static void print_command_item(int i, struct string_list_item *item,
+static void print_command_item(int i, int selected,
+ struct string_list_item *item,
void *print_command_item_data)
{
struct print_command_item_data *d = print_command_item_data;
@@ -574,25 +1059,33 @@
struct print_command_item_data data = { "[", "]" };
struct list_and_choose_options main_loop_opts = {
{ 4, N_("*** Commands ***"), print_command_item, &data },
- N_("What now"), command_prompt_help
+ N_("What now"), SINGLETON | IMMEDIATE, command_prompt_help
};
struct {
const char *string;
command_t command;
} command_list[] = {
{ "status", run_status },
+ { "update", run_update },
+ { "revert", run_revert },
+ { "add untracked", run_add_untracked },
+ { "patch", run_patch },
+ { "diff", run_diff },
+ { "quit", NULL },
{ "help", run_help },
};
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
struct print_file_item_data print_file_item_data = {
- "%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
+ "%12s %12s %s", NULL, NULL,
+ STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
};
- struct list_options opts = {
- 0, NULL, print_file_item, &print_file_item_data
+ struct list_and_choose_options opts = {
+ { 0, NULL, print_file_item, &print_file_item_data },
+ NULL, 0, choose_prompt_help
};
struct strbuf header = STRBUF_INIT;
- struct string_list files = STRING_LIST_INIT_DUP;
+ struct prefix_item_list files = PREFIX_ITEM_LIST_INIT;
ssize_t i;
int res = 0;
@@ -613,11 +1106,13 @@
data.color = s.prompt_color;
data.reset = s.reset_color;
}
+ print_file_item_data.color = data.color;
+ print_file_item_data.reset = data.reset;
strbuf_addstr(&header, " ");
strbuf_addf(&header, print_file_item_data.modified_fmt,
_("staged"), _("unstaged"), _("path"));
- opts.header = header.buf;
+ opts.list_opts.header = header.buf;
if (discard_index(r->index) < 0 ||
repo_read_index(r) < 0 ||
@@ -628,21 +1123,27 @@
res = run_status(&s, ps, &files, &opts);
for (;;) {
+ struct command_item *util;
+
i = list_and_choose(&s, &commands, &main_loop_opts);
- if (i == LIST_AND_CHOOSE_QUIT) {
+ if (i < 0 || i >= commands.items.nr)
+ util = NULL;
+ else
+ util = commands.items.items[i].util;
+
+ if (i == LIST_AND_CHOOSE_QUIT || (util && !util->command)) {
printf(_("Bye.\n"));
res = 0;
break;
}
- if (i != LIST_AND_CHOOSE_ERROR) {
- struct command_item *util =
- commands.items.items[i].util;
+
+ if (util)
res = util->command(&s, ps, &files, &opts);
- }
}
- string_list_clear(&files, 1);
+ prefix_item_list_clear(&files);
strbuf_release(&print_file_item_data.buf);
+ strbuf_release(&print_file_item_data.name);
strbuf_release(&print_file_item_data.index);
strbuf_release(&print_file_item_data.worktree);
strbuf_release(&header);
diff --git a/apply.c b/apply.c
index b5485ef..fab4432 100644
--- a/apply.c
+++ b/apply.c
@@ -2662,6 +2662,16 @@
int backwards_lno, forwards_lno, current_lno;
/*
+ * When running with --allow-overlap, it is possible that a hunk is
+ * seen that pretends to start at the beginning (but no longer does),
+ * and that *still* needs to match the end. So trust `match_end` more
+ * than `match_beginning`.
+ */
+ if (state->allow_overlap && match_beginning && match_end &&
+ img->nr - preimage->nr != 0)
+ match_beginning = 0;
+
+ /*
* If match_beginning or match_end is specified, there is no
* point starting from a wrong line that will never match and
* wander around and wait for a match at the specified end.
diff --git a/archive-zip.c b/archive-zip.c
index 4d66b5b..11f5b19 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -24,6 +24,11 @@
#define ZIP_STREAM (1 << 3)
#define ZIP_UTF8 (1 << 11)
+enum zip_method {
+ ZIP_METHOD_STORE = 0,
+ ZIP_METHOD_DEFLATE = 8
+};
+
struct zip_local_header {
unsigned char magic[4];
unsigned char version[2];
@@ -291,7 +296,7 @@
unsigned long attr2;
unsigned long compressed_size;
unsigned long crc;
- int method;
+ enum zip_method method;
unsigned char *out;
void *deflated = NULL;
void *buffer;
@@ -320,7 +325,7 @@
}
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
- method = 0;
+ method = ZIP_METHOD_STORE;
attr2 = 16;
out = NULL;
size = 0;
@@ -330,13 +335,13 @@
enum object_type type = oid_object_info(args->repo, oid,
&size);
- method = 0;
+ method = ZIP_METHOD_STORE;
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
(mode & 0111) ? ((mode) << 16) : 0;
if (S_ISLNK(mode) || (mode & 0111))
creator_version = 0x0317;
if (S_ISREG(mode) && args->compression_level != 0 && size > 0)
- method = 8;
+ method = ZIP_METHOD_DEFLATE;
if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert &&
size > big_file_threshold) {
@@ -358,7 +363,7 @@
buffer, size);
out = buffer;
}
- compressed_size = (method == 0) ? size : 0;
+ compressed_size = (method == ZIP_METHOD_STORE) ? size : 0;
} else {
return error(_("unsupported file mode: 0%o (SHA1: %s)"), mode,
oid_to_hex(oid));
@@ -367,13 +372,13 @@
if (creator_version > max_creator_version)
max_creator_version = creator_version;
- if (buffer && method == 8) {
+ if (buffer && method == ZIP_METHOD_DEFLATE) {
out = deflated = zlib_deflate_raw(buffer, size,
args->compression_level,
&compressed_size);
if (!out || compressed_size >= size) {
out = buffer;
- method = 0;
+ method = ZIP_METHOD_STORE;
compressed_size = size;
}
}
@@ -420,7 +425,7 @@
zip_offset += ZIP64_EXTRA_SIZE;
}
- if (stream && method == 0) {
+ if (stream && method == ZIP_METHOD_STORE) {
unsigned char buf[STREAM_BUFFER_SIZE];
ssize_t readlen;
@@ -443,7 +448,7 @@
zip_offset += compressed_size;
write_zip_data_desc(size, compressed_size, crc);
- } else if (stream && method == 8) {
+ } else if (stream && method == ZIP_METHOD_DEFLATE) {
unsigned char buf[STREAM_BUFFER_SIZE];
ssize_t readlen;
git_zstream zstream;
@@ -603,18 +608,18 @@
static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
{
time_t time;
- struct tm *t;
+ struct tm tm;
if (date_overflows(*timestamp))
die(_("timestamp too large for this system: %"PRItime),
*timestamp);
time = (time_t)*timestamp;
- t = localtime(&time);
+ localtime_r(&time, &tm);
*timestamp = time;
- *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
- (t->tm_year + 1900 - 1980) * 512;
- *dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
+ *dos_date = tm.tm_mday + (tm.tm_mon + 1) * 32 +
+ (tm.tm_year + 1900 - 1980) * 512;
+ *dos_time = tm.tm_sec / 2 + tm.tm_min * 32 + tm.tm_hour * 2048;
}
static int archive_zip_config(const char *var, const char *value, void *data)
diff --git a/argv-array.h b/argv-array.h
index a39ba43..a7d3b10 100644
--- a/argv-array.h
+++ b/argv-array.h
@@ -1,8 +1,32 @@
#ifndef ARGV_ARRAY_H
#define ARGV_ARRAY_H
+/**
+ * The argv-array API allows one to dynamically build and store
+ * NULL-terminated lists. An argv-array maintains the invariant that the
+ * `argv` member always points to a non-NULL array, and that the array is
+ * always NULL-terminated at the element pointed to by `argv[argc]`. This
+ * makes the result suitable for passing to functions expecting to receive
+ * argv from main().
+ *
+ * The string-list API (documented in string-list.h) is similar, but cannot be
+ * used for these purposes; instead of storing a straight string pointer,
+ * it contains an item structure with a `util` field that is not compatible
+ * with the traditional argv interface.
+ *
+ * Each `argv_array` manages its own memory. Any strings pushed into the
+ * array are duplicated, and all memory is freed by argv_array_clear().
+ */
+
extern const char *empty_argv[];
+/**
+ * A single array. This should be initialized by assignment from
+ * `ARGV_ARRAY_INIT`, or by calling `argv_array_init`. The `argv`
+ * member contains the actual array; the `argc` member contains the
+ * number of elements in the array, not including the terminating
+ * NULL.
+ */
struct argv_array {
const char **argv;
int argc;
@@ -11,17 +35,55 @@
#define ARGV_ARRAY_INIT { empty_argv, 0, 0 }
+/**
+ * Initialize an array. This is no different than assigning from
+ * `ARGV_ARRAY_INIT`.
+ */
void argv_array_init(struct argv_array *);
+
+/* Push a copy of a string onto the end of the array. */
const char *argv_array_push(struct argv_array *, const char *);
+
+/**
+ * Format a string and push it onto the end of the array. This is a
+ * convenience wrapper combining `strbuf_addf` and `argv_array_push`.
+ */
__attribute__((format (printf,2,3)))
const char *argv_array_pushf(struct argv_array *, const char *fmt, ...);
+
+/**
+ * Push a list of strings onto the end of the array. The arguments
+ * should be a list of `const char *` strings, terminated by a NULL
+ * argument.
+ */
LAST_ARG_MUST_BE_NULL
void argv_array_pushl(struct argv_array *, ...);
+
+/* Push a null-terminated array of strings onto the end of the array. */
void argv_array_pushv(struct argv_array *, const char **);
+
+/**
+ * Remove the final element from the array. If there are no
+ * elements in the array, do nothing.
+ */
void argv_array_pop(struct argv_array *);
+
/* Splits by whitespace; does not handle quoted arguments! */
void argv_array_split(struct argv_array *, const char *);
+
+/**
+ * Free all memory associated with the array and return it to the
+ * initial, empty state.
+ */
void argv_array_clear(struct argv_array *);
+
+/**
+ * Disconnect the `argv` member from the `argv_array` struct and
+ * return it. The caller is responsible for freeing the memory used
+ * by the array, and by the strings it references. After detaching,
+ * the `argv_array` is in a reinitialized state and can be pushed
+ * into again.
+ */
const char **argv_array_detach(struct argv_array *);
#endif /* ARGV_ARRAY_H */
diff --git a/attr.c b/attr.c
index 11f19b5..a826b2e 100644
--- a/attr.c
+++ b/attr.c
@@ -1,7 +1,6 @@
/*
* Handle git attributes. See gitattributes(5) for a description of
- * the file syntax, and Documentation/technical/api-gitattributes.txt
- * for a description of the API.
+ * the file syntax, and attr.h for a description of the API.
*
* One basic design decision here is that we are not going to support
* an insanely large number of attributes.
diff --git a/attr.h b/attr.h
index b0378bf..404548f 100644
--- a/attr.h
+++ b/attr.h
@@ -1,9 +1,121 @@
#ifndef ATTR_H
#define ATTR_H
+/**
+ * gitattributes mechanism gives a uniform way to associate various attributes
+ * to set of paths.
+ *
+ *
+ * Querying Specific Attributes
+ * ----------------------------
+ *
+ * - Prepare `struct attr_check` using attr_check_initl() function, enumerating
+ * the names of attributes whose values you are interested in, terminated with
+ * a NULL pointer. Alternatively, an empty `struct attr_check` can be
+ * prepared by calling `attr_check_alloc()` function and then attributes you
+ * want to ask about can be added to it with `attr_check_append()` function.
+ *
+ * - Call `git_check_attr()` to check the attributes for the path.
+ *
+ * - Inspect `attr_check` structure to see how each of the attribute in the
+ * array is defined for the path.
+ *
+ *
+ * Example
+ * -------
+ *
+ * To see how attributes "crlf" and "ident" are set for different paths.
+ *
+ * - Prepare a `struct attr_check` with two elements (because we are checking
+ * two attributes):
+ *
+ * ------------
+ * static struct attr_check *check;
+ * static void setup_check(void)
+ * {
+ * if (check)
+ * return; // already done
+ * check = attr_check_initl("crlf", "ident", NULL);
+ * }
+ * ------------
+ *
+ * - Call `git_check_attr()` with the prepared `struct attr_check`:
+ *
+ * ------------
+ * const char *path;
+ *
+ * setup_check();
+ * git_check_attr(path, check);
+ * ------------
+ *
+ * - Act on `.value` member of the result, left in `check->items[]`:
+ *
+ * ------------
+ * const char *value = check->items[0].value;
+ *
+ * if (ATTR_TRUE(value)) {
+ * The attribute is Set, by listing only the name of the
+ * attribute in the gitattributes file for the path.
+ * } else if (ATTR_FALSE(value)) {
+ * The attribute is Unset, by listing the name of the
+ * attribute prefixed with a dash - for the path.
+ * } else if (ATTR_UNSET(value)) {
+ * The attribute is neither set nor unset for the path.
+ * } else if (!strcmp(value, "input")) {
+ * If none of ATTR_TRUE(), ATTR_FALSE(), or ATTR_UNSET() is
+ * true, the value is a string set in the gitattributes
+ * file for the path by saying "attr=value".
+ * } else if (... other check using value as string ...) {
+ * ...
+ * }
+ * ------------
+ *
+ * To see how attributes in argv[] are set for different paths, only
+ * the first step in the above would be different.
+ *
+ * ------------
+ * static struct attr_check *check;
+ * static void setup_check(const char **argv)
+ * {
+ * check = attr_check_alloc();
+ * while (*argv) {
+ * struct git_attr *attr = git_attr(*argv);
+ * attr_check_append(check, attr);
+ * argv++;
+ * }
+ * }
+ * ------------
+ *
+ *
+ * Querying All Attributes
+ * -----------------------
+ *
+ * To get the values of all attributes associated with a file:
+ *
+ * - Prepare an empty `attr_check` structure by calling `attr_check_alloc()`.
+ *
+ * - Call `git_all_attrs()`, which populates the `attr_check` with the
+ * attributes attached to the path.
+ *
+ * - Iterate over the `attr_check.items[]` array to examine the attribute
+ * names and values. The name of the attribute described by an
+ * `attr_check.items[]` object can be retrieved via
+ * `git_attr_name(check->items[i].attr)`. (Please note that no items will be
+ * returned for unset attributes, so `ATTR_UNSET()` will return false for all
+ * returned `attr_check.items[]` objects.)
+ *
+ * - Free the `attr_check` struct by calling `attr_check_free()`.
+ */
+
struct index_state;
-/* An attribute is a pointer to this opaque structure */
+/**
+ * An attribute is an opaque object that is identified by its name. Pass the
+ * name to `git_attr()` function to obtain the object of this type.
+ * The internal representation of this structure is of no interest to the
+ * calling programs. The name of the attribute can be retrieved by calling
+ * `git_attr_name()`.
+ */
struct git_attr;
/* opaque structures used internally for attribute collection */
@@ -21,21 +133,36 @@
extern const char git_attr__true[];
extern const char git_attr__false[];
-/* For public to check git_attr_check results */
+/**
+ * Attribute Values
+ * ----------------
+ *
+ * An attribute for a path can be in one of four states: Set, Unset, Unspecified
+ * or set to a string, and `.value` member of `struct attr_check_item` records
+ * it. The three macros check these, if none of them returns true, `.value`
+ * member points at a string value of the attribute for the path.
+ */
+
+/* Returns true if the attribute is Set for the path. */
#define ATTR_TRUE(v) ((v) == git_attr__true)
+
+/* Returns true if the attribute is Unset for the path. */
#define ATTR_FALSE(v) ((v) == git_attr__false)
+
+/* Returns true if the attribute is Unspecified for the path. */
#define ATTR_UNSET(v) ((v) == NULL)
-/*
- * Send one or more git_attr_check to git_check_attrs(), and
- * each 'value' member tells what its value is.
- * Unset one is returned as NULL.
- */
+/* This structure represents one attribute and its value. */
struct attr_check_item {
const struct git_attr *attr;
const char *value;
};
+/**
+ * This structure represents a collection of `attr_check_item`. It is passed to
+ * `git_check_attr()` function, specifying the attributes to check, and
+ * receives their values.
+ */
struct attr_check {
int nr;
int alloc;
diff --git a/builtin.h b/builtin.h
index 5cf5df6..2b25a80 100644
--- a/builtin.h
+++ b/builtin.h
@@ -225,6 +225,7 @@
int cmd_show(int argc, const char **argv, const char *prefix);
int cmd_show_branch(int argc, const char **argv, const char *prefix);
int cmd_show_index(int argc, const char **argv, const char *prefix);
+int cmd_sparse_checkout(int argc, const char **argv, const char *prefix);
int cmd_status(int argc, const char **argv, const char *prefix);
int cmd_stash(int argc, const char **argv, const char *prefix);
int cmd_stripspace(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index d4686d5..7c21ad4 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -29,6 +29,8 @@
static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
static int add_renormalize;
+static int pathspec_file_nul;
+static const char *pathspec_from_file;
struct update_callback_data {
int flags;
@@ -320,6 +322,8 @@
N_("override the executable bit of the listed files")),
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
N_("warn when adding an embedded repository")),
+ OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+ OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END(),
};
@@ -414,11 +418,17 @@
builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
if (patch_interactive)
add_interactive = 1;
- if (add_interactive)
+ if (add_interactive) {
+ if (pathspec_from_file)
+ die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
+ }
- if (edit_interactive)
+ if (edit_interactive) {
+ if (pathspec_from_file)
+ die(_("--pathspec-from-file is incompatible with --edit"));
return(edit_patch(argc, argv, prefix));
+ }
argc--;
argv++;
@@ -430,10 +440,6 @@
if (addremove && take_worktree_changes)
die(_("-A and -u are mutually incompatible"));
- if (!take_worktree_changes && addremove_explicit < 0 && argc)
- /* Turn "git add pathspec..." to "git add -A pathspec..." */
- addremove = 1;
-
if (!show_only && ignore_missing)
die(_("Option --ignore-missing can only be used together with --dry-run"));
@@ -446,19 +452,6 @@
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
- flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
- (show_only ? ADD_CACHE_PRETEND : 0) |
- (intent_to_add ? ADD_CACHE_INTENT : 0) |
- (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
- (!(addremove || take_worktree_changes)
- ? ADD_CACHE_IGNORE_REMOVAL : 0));
-
- if (require_pathspec && argc == 0) {
- fprintf(stderr, _("Nothing specified, nothing added.\n"));
- fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
- return 0;
- }
-
/*
* Check the "pathspec '%s' did not match any files" block
* below before enabling new magic.
@@ -468,6 +461,35 @@
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, argv);
+ if (pathspec_from_file) {
+ if (pathspec.nr)
+ die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+ parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
+ PATHSPEC_PREFER_FULL |
+ PATHSPEC_SYMLINK_LEADING_PATH,
+ prefix, pathspec_from_file, pathspec_file_nul);
+ } else if (pathspec_file_nul) {
+ die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ }
+
+ if (require_pathspec && pathspec.nr == 0) {
+ fprintf(stderr, _("Nothing specified, nothing added.\n"));
+ fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
+ return 0;
+ }
+
+ if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr)
+ /* Turn "git add pathspec..." to "git add -A pathspec..." */
+ addremove = 1;
+
+ flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
+ (show_only ? ADD_CACHE_PRETEND : 0) |
+ (intent_to_add ? ADD_CACHE_INTENT : 0) |
+ (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
+ (!(addremove || take_worktree_changes)
+ ? ADD_CACHE_IGNORE_REMOVAL : 0));
+
if (read_cache_preload(&pathspec) < 0)
die(_("index file corrupt"));
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 1fbe156..3055b2b 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -169,11 +169,12 @@
argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL);
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ error(_("could not check out original"
+ " HEAD '%s'. Try 'git bisect"
+ " reset <commit>'."), branch.buf);
strbuf_release(&branch);
argv_array_clear(&argv);
- return error(_("could not check out original"
- " HEAD '%s'. Try 'git bisect"
- " reset <commit>'."), branch.buf);
+ return -1;
}
argv_array_clear(&argv);
}
diff --git a/builtin/branch.c b/builtin/branch.c
index 2ef2146..d8297f8 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -624,7 +624,7 @@
OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"),
BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN),
OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
- OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("Unset the upstream info")),
+ OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")),
OPT__COLOR(&branch_use_color, N_("use colored output")),
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
FILTER_REFS_REMOTES),
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3634a3d..b52c490 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -70,6 +70,8 @@
int checkout_worktree;
const char *ignore_unmerged_opt;
int ignore_unmerged;
+ int pathspec_file_nul;
+ const char *pathspec_from_file;
const char *new_branch;
const char *new_branch_force;
@@ -1480,6 +1482,8 @@
OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
N_("do not limit pathspecs to sparse entries only")),
+ OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file),
+ OPT_PATHSPEC_FILE_NUL(&opts->pathspec_file_nul),
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
@@ -1618,10 +1622,6 @@
die(_("reference is not a tree: %s"), opts->from_treeish);
}
- if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
- !opts->patch_mode) /* patch mode is special */
- die(_("you must specify path(s) to restore"));
-
if (argc) {
parse_pathspec(&opts->pathspec, 0,
opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
@@ -1641,10 +1641,33 @@
if (opts->force_detach)
die(_("git checkout: --detach does not take a path argument '%s'"),
argv[0]);
+ }
+ if (opts->pathspec_from_file) {
+ if (opts->pathspec.nr)
+ die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+ if (opts->force_detach)
+ die(_("--pathspec-from-file is incompatible with --detach"));
+
+ if (opts->patch_mode)
+ die(_("--pathspec-from-file is incompatible with --patch"));
+
+ parse_pathspec_file(&opts->pathspec, 0,
+ 0,
+ prefix, opts->pathspec_from_file, opts->pathspec_file_nul);
+ } else if (opts->pathspec_file_nul) {
+ die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ }
+
+ if (opts->pathspec.nr) {
if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
"checking out of the index."));
+ } else {
+ if (opts->accept_pathspec && !opts->empty_pathspec_ok &&
+ !opts->patch_mode) /* patch mode is special */
+ die(_("you must specify path(s) to restore"));
}
if (opts->new_branch) {
diff --git a/builtin/clone.c b/builtin/clone.c
index 6dee265..0fc89ae 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -59,6 +59,7 @@
static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
+static int option_sparse_checkout;
static enum transport_family family;
static struct string_list option_config = STRING_LIST_INIT_NODUP;
static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
@@ -146,6 +147,8 @@
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
N_("any cloned submodules will use their remote-tracking branch")),
+ OPT_BOOL(0, "sparse", &option_sparse_checkout,
+ N_("initialize sparse-checkout file to include only files at root")),
OPT_END()
};
@@ -733,6 +736,27 @@
}
}
+static int git_sparse_checkout_init(const char *repo)
+{
+ struct argv_array argv = ARGV_ARRAY_INIT;
+ int result = 0;
+ argv_array_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
+
+ /*
+ * We must apply the setting in the current process
+ * for the later checkout to use the sparse-checkout file.
+ */
+ core_apply_sparse_checkout = 1;
+
+ if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ error(_("failed to initialize sparse-checkout"));
+ result = 1;
+ }
+
+ argv_array_clear(&argv);
+ return result;
+}
+
static int checkout(int submodule_progress)
{
struct object_id oid;
@@ -1104,6 +1128,9 @@
if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
+ if (option_sparse_checkout && git_sparse_checkout_init(repo))
+ return 1;
+
remote = remote_get(option_origin);
strbuf_addf(&default_refspec, "+%s*:%s*", src_ref_prefix,
diff --git a/builtin/commit.c b/builtin/commit.c
index 2db2ad0..aa13323 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -347,6 +347,9 @@
if (interactive)
die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+ if (all)
+ die(_("--pathspec-from-file with -a does not make sense"));
+
if (pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
@@ -554,7 +557,7 @@
struct strbuf buf = STRBUF_INIT;
if (hack)
strbuf_addch(&buf, hack);
- strbuf_addf(&buf, "%.*s", (int)(e - s), s);
+ strbuf_add(&buf, s, e - s);
setenv(var, buf.buf, 1);
strbuf_release(&buf);
}
diff --git a/builtin/log.c b/builtin/log.c
index e192f21..83a4a61 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -208,7 +208,7 @@
if (!rev->show_notes_given && (!rev->pretty_given || w.notes))
rev->show_notes = 1;
if (rev->show_notes)
- init_display_notes(&rev->notes_opt);
+ load_display_notes(&rev->notes_opt);
if ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||
rev->diffopt.filter || rev->diffopt.flags.follow_renames)
@@ -795,6 +795,8 @@
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;
static enum cover_from_description parse_cover_from_description(const char *arg)
{
@@ -814,8 +816,6 @@
static int git_format_config(const char *var, const char *value, void *cb)
{
- struct rev_info *rev = cb;
-
if (!strcmp(var, "format.headers")) {
if (!value)
die(_("format.headers without value"));
@@ -902,19 +902,13 @@
return 0;
}
if (!strcmp(var, "format.notes")) {
- struct strbuf buf = STRBUF_INIT;
int b = git_parse_maybe_bool(value);
- if (!b)
- return 0;
- rev->show_notes = 1;
- if (b < 0) {
- strbuf_addstr(&buf, value);
- expand_notes_ref(&buf);
- string_list_append(&rev->notes_opt.extra_notes_refs,
- strbuf_detach(&buf, NULL));
- } else {
- rev->notes_opt.use_default_notes = 1;
- }
+ if (b < 0)
+ enable_ref_display_notes(¬es_opt, &show_notes, value);
+ else if (b)
+ enable_default_display_notes(¬es_opt, &show_notes);
+ else
+ disable_display_notes(¬es_opt, &show_notes);
return 0;
}
if (!strcmp(var, "format.coverfromdescription")) {
@@ -1372,7 +1366,7 @@
string_list_clear(&extra_to, 0);
string_list_clear(&extra_cc, 0);
} else {
- add_header(arg);
+ add_header(arg);
}
return 0;
}
@@ -1428,7 +1422,7 @@
base = lookup_commit_reference_by_name(base_commit);
if (!base)
die(_("unknown commit %s"), base_commit);
- } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+ } else if ((base_commit && !strcmp(base_commit, "auto"))) {
struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL);
if (upstream) {
@@ -1719,8 +1713,11 @@
extra_to.strdup_strings = 1;
extra_cc.strdup_strings = 1;
init_log_defaults();
+ init_display_notes(¬es_opt);
+ git_config(git_format_config, NULL);
repo_init_revisions(the_repository, &rev, prefix);
- git_config(git_format_config, &rev);
+ rev.show_notes = show_notes;
+ memcpy(&rev.notes_opt, ¬es_opt, sizeof(notes_opt));
rev.commit_format = CMIT_FMT_EMAIL;
rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
@@ -1732,6 +1729,9 @@
s_r_opt.def = "HEAD";
s_r_opt.revarg_opt = REVARG_COMMITTISH;
+ if (base_auto)
+ base_commit = "auto";
+
if (default_attach) {
rev.mime_boundary = default_attach;
rev.no_inline = 1;
@@ -1836,7 +1836,7 @@
rev.diffopt.flags.binary = 1;
if (rev.show_notes)
- init_display_notes(&rev.notes_opt);
+ load_display_notes(&rev.notes_opt);
if (!output_directory && !use_stdout)
output_directory = config_output_directory;
@@ -1995,7 +1995,7 @@
}
memset(&bases, 0, sizeof(bases));
- if (base_commit || base_auto) {
+ if (base_commit) {
struct commit *base = get_base_commit(base_commit, list, nr);
reset_revision_walk();
clear_object_flags(UNINTERESTING);
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index e55a4f0..6b9e8c8 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -6,6 +6,7 @@
#include "tag.h"
#include "refs.h"
#include "parse-options.h"
+#include "prio-queue.h"
#include "sha1-lookup.h"
#include "commit-slab.h"
@@ -79,30 +80,16 @@
return 0;
}
-static void name_rev(struct commit *commit,
- const char *tip_name, timestamp_t taggerdate,
- int generation, int distance, int from_tag,
- int deref)
+static struct rev_name *create_or_update_name(struct commit *commit,
+ const char *tip_name,
+ timestamp_t taggerdate,
+ int generation, int distance,
+ int from_tag)
{
struct rev_name *name = get_commit_rev_name(commit);
- struct commit_list *parents;
- int parent_number = 1;
- char *to_free = NULL;
-
- parse_commit(commit);
-
- if (commit->date < cutoff)
- return;
-
- if (deref) {
- tip_name = to_free = xstrfmt("%s^0", tip_name);
-
- if (generation)
- die("generation: %d, but deref?", generation);
- }
if (name == NULL) {
- name = xmalloc(sizeof(rev_name));
+ name = xmalloc(sizeof(*name));
set_commit_rev_name(commit, name);
goto copy_data;
} else if (is_better_name(name, taggerdate, distance, from_tag)) {
@@ -112,35 +99,97 @@
name->generation = generation;
name->distance = distance;
name->from_tag = from_tag;
- } else {
+
+ return name;
+ } else
+ return NULL;
+}
+
+static void name_rev(struct commit *start_commit,
+ const char *tip_name, timestamp_t taggerdate,
+ int from_tag, int deref)
+{
+ struct prio_queue queue;
+ struct commit *commit;
+ struct commit **parents_to_queue = NULL;
+ size_t parents_to_queue_nr, parents_to_queue_alloc = 0;
+ char *to_free = NULL;
+
+ parse_commit(start_commit);
+ if (start_commit->date < cutoff)
+ return;
+
+ if (deref)
+ tip_name = to_free = xstrfmt("%s^0", tip_name);
+
+ if (!create_or_update_name(start_commit, tip_name, taggerdate, 0, 0,
+ from_tag)) {
free(to_free);
return;
}
- for (parents = commit->parents;
- parents;
- parents = parents->next, parent_number++) {
- if (parent_number > 1) {
- size_t len;
- char *new_name;
+ memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */
+ prio_queue_put(&queue, start_commit);
- strip_suffix(tip_name, "^0", &len);
- if (generation > 0)
- new_name = xstrfmt("%.*s~%d^%d", (int)len, tip_name,
- generation, parent_number);
- else
- new_name = xstrfmt("%.*s^%d", (int)len, tip_name,
- parent_number);
+ while ((commit = prio_queue_get(&queue))) {
+ struct rev_name *name = get_commit_rev_name(commit);
+ struct commit_list *parents;
+ int parent_number = 1;
- name_rev(parents->item, new_name, taggerdate, 0,
- distance + MERGE_TRAVERSAL_WEIGHT,
- from_tag, 0);
- } else {
- name_rev(parents->item, tip_name, taggerdate,
- generation + 1, distance + 1,
- from_tag, 0);
+ parents_to_queue_nr = 0;
+
+ for (parents = commit->parents;
+ parents;
+ parents = parents->next, parent_number++) {
+ struct commit *parent = parents->item;
+ const char *new_name;
+ int generation, distance;
+
+ parse_commit(parent);
+ if (parent->date < cutoff)
+ continue;
+
+ if (parent_number > 1) {
+ size_t len;
+
+ strip_suffix(name->tip_name, "^0", &len);
+ if (name->generation > 0)
+ new_name = xstrfmt("%.*s~%d^%d",
+ (int)len,
+ name->tip_name,
+ name->generation,
+ parent_number);
+ else
+ new_name = xstrfmt("%.*s^%d", (int)len,
+ name->tip_name,
+ parent_number);
+ generation = 0;
+ distance = name->distance + MERGE_TRAVERSAL_WEIGHT;
+ } else {
+ new_name = name->tip_name;
+ generation = name->generation + 1;
+ distance = name->distance + 1;
+ }
+
+ if (create_or_update_name(parent, new_name, taggerdate,
+ generation, distance,
+ from_tag)) {
+ ALLOC_GROW(parents_to_queue,
+ parents_to_queue_nr + 1,
+ parents_to_queue_alloc);
+ parents_to_queue[parents_to_queue_nr] = parent;
+ parents_to_queue_nr++;
+ }
}
+
+ /* The first parent must come out first from the prio_queue */
+ while (parents_to_queue_nr)
+ prio_queue_put(&queue,
+ parents_to_queue[--parents_to_queue_nr]);
}
+
+ clear_prio_queue(&queue);
+ free(parents_to_queue);
}
static int subpath_matches(const char *path, const char *filter)
@@ -272,10 +321,9 @@
int from_tag = starts_with(path, "refs/tags/");
if (taggerdate == TIME_MAX)
- taggerdate = ((struct commit *)o)->date;
+ taggerdate = commit->date;
path = name_ref_abbrev(path, can_abbreviate_output);
- name_rev(commit, xstrdup(path), taggerdate, 0, 0,
- from_tag, deref);
+ name_rev(commit, xstrdup(path), taggerdate, from_tag, deref);
}
return 0;
}
@@ -321,11 +369,10 @@
if (!n->generation)
return n->tip_name;
else {
- int len = strlen(n->tip_name);
- if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
- len -= 2;
strbuf_reset(buf);
- strbuf_addf(buf, "%.*s~%d", len, n->tip_name, n->generation);
+ strbuf_addstr(buf, n->tip_name);
+ strbuf_strip_suffix(buf, "^0");
+ strbuf_addf(buf, "~%d", n->generation);
return buf->buf;
}
}
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 3059e52..822ffff 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -5,13 +5,8 @@
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
{
- char name[GIT_MAX_HEXSZ + 1];
-
- if (!patchlen)
- return;
-
- memcpy(name, oid_to_hex(id), the_hash_algo->hexsz + 1);
- printf("%s %s\n", oid_to_hex(result), name);
+ if (patchlen)
+ printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
}
static int remove_space(char *line)
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index 98acf35..d8a4670 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -84,6 +84,7 @@
res = show_range_diff(range1.buf, range2.buf, creation_factor,
simple_color < 1, &diffopt, &other_arg);
+ argv_array_clear(&other_arg);
strbuf_release(&range1);
strbuf_release(&range2);
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index ca5e655..af7424b 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -185,7 +185,7 @@
if (opts.reset || opts.merge || opts.prefix) {
if (read_cache_unmerged() && (opts.prefix || opts.merge))
- die("You need to resolve your current index first");
+ die(_("You need to resolve your current index first"));
stage = opts.merge = 1;
}
resolve_undo_clear();
diff --git a/builtin/rebase.c b/builtin/rebase.c
index a11e15b..ddf33bc 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -130,6 +130,12 @@
parse_strategy_opts(&replay, strategy_buf.buf);
strbuf_release(&strategy_buf);
+
+ if (opts->squash_onto) {
+ oidcpy(&replay.squash_onto, opts->squash_onto);
+ replay.have_squash_onto = 1;
+ }
+
return replay;
}
@@ -1033,7 +1039,8 @@
argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
"--full-index", "--cherry-pick", "--right-only",
"--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
- "--no-cover-letter", "--pretty=mboxrd", "--topo-order", NULL);
+ "--no-cover-letter", "--pretty=mboxrd", "--topo-order",
+ "--no-base", NULL);
if (opts->git_format_patch_opt.len)
argv_array_split(&format_patch.args,
opts->git_format_patch_opt.buf);
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
new file mode 100644
index 0000000..5d62f7a
--- /dev/null
+++ b/builtin/sparse-checkout.c
@@ -0,0 +1,495 @@
+#include "builtin.h"
+#include "config.h"
+#include "dir.h"
+#include "parse-options.h"
+#include "pathspec.h"
+#include "repository.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "string-list.h"
+#include "cache.h"
+#include "cache-tree.h"
+#include "lockfile.h"
+#include "resolve-undo.h"
+#include "unpack-trees.h"
+#include "wt-status.h"
+
+static const char *empty_base = "";
+
+static char const * const builtin_sparse_checkout_usage[] = {
+ N_("git sparse-checkout (init|list|set|disable) <options>"),
+ NULL
+};
+
+static char *get_sparse_checkout_filename(void)
+{
+ return git_pathdup("info/sparse-checkout");
+}
+
+static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)
+{
+ int i;
+
+ for (i = 0; i < pl->nr; i++) {
+ struct path_pattern *p = pl->patterns[i];
+
+ if (p->flags & PATTERN_FLAG_NEGATIVE)
+ fprintf(fp, "!");
+
+ fprintf(fp, "%s", p->pattern);
+
+ if (p->flags & PATTERN_FLAG_MUSTBEDIR)
+ fprintf(fp, "/");
+
+ fprintf(fp, "\n");
+ }
+}
+
+static int sparse_checkout_list(int argc, const char **argv)
+{
+ struct pattern_list pl;
+ char *sparse_filename;
+ int res;
+
+ memset(&pl, 0, sizeof(pl));
+
+ sparse_filename = get_sparse_checkout_filename();
+ res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
+ free(sparse_filename);
+
+ if (res < 0) {
+ warning(_("this worktree is not sparse (sparse-checkout file may not exist)"));
+ return 0;
+ }
+
+ write_patterns_to_file(stdout, &pl);
+ clear_pattern_list(&pl);
+
+ return 0;
+}
+
+static int update_working_directory(struct pattern_list *pl)
+{
+ int result = 0;
+ struct unpack_trees_options o;
+ struct lock_file lock_file = LOCK_INIT;
+ struct object_id oid;
+ struct tree *tree;
+ struct tree_desc t;
+ struct repository *r = the_repository;
+
+ if (repo_read_index_unmerged(r))
+ die(_("you need to resolve your current index first"));
+
+ if (get_oid("HEAD", &oid))
+ return 0;
+
+ tree = parse_tree_indirect(&oid);
+ parse_tree(tree);
+ init_tree_desc(&t, tree->buffer, tree->size);
+
+ memset(&o, 0, sizeof(o));
+ o.verbose_update = isatty(2);
+ o.merge = 1;
+ o.update = 1;
+ o.fn = oneway_merge;
+ o.head_idx = -1;
+ o.src_index = r->index;
+ o.dst_index = r->index;
+ o.skip_sparse_checkout = 0;
+ o.pl = pl;
+ o.keep_pattern_list = !!pl;
+
+ resolve_undo_clear_index(r->index);
+ setup_work_tree();
+
+ cache_tree_free(&r->index->cache_tree);
+
+ repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
+
+ core_apply_sparse_checkout = 1;
+ result = unpack_trees(1, &t, &o);
+
+ if (!result) {
+ prime_cache_tree(r, r->index, tree);
+ write_locked_index(r->index, &lock_file, COMMIT_LOCK);
+ } else
+ rollback_lock_file(&lock_file);
+
+ return result;
+}
+
+static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
+{
+ int i;
+ struct pattern_entry *pe;
+ struct hashmap_iter iter;
+ struct string_list sl = STRING_LIST_INIT_DUP;
+ struct strbuf parent_pattern = STRBUF_INIT;
+
+ hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent) {
+ if (hashmap_get_entry(&pl->recursive_hashmap, pe, ent, NULL))
+ continue;
+
+ if (!hashmap_contains_parent(&pl->recursive_hashmap,
+ pe->pattern,
+ &parent_pattern))
+ string_list_insert(&sl, pe->pattern);
+ }
+
+ string_list_sort(&sl);
+ string_list_remove_duplicates(&sl, 0);
+
+ fprintf(fp, "/*\n!/*/\n");
+
+ for (i = 0; i < sl.nr; i++) {
+ char *pattern = sl.items[i].string;
+
+ if (strlen(pattern))
+ fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
+ }
+
+ string_list_clear(&sl, 0);
+
+ hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent) {
+ if (!hashmap_contains_parent(&pl->recursive_hashmap,
+ pe->pattern,
+ &parent_pattern))
+ string_list_insert(&sl, pe->pattern);
+ }
+
+ strbuf_release(&parent_pattern);
+
+ string_list_sort(&sl);
+ string_list_remove_duplicates(&sl, 0);
+
+ for (i = 0; i < sl.nr; i++) {
+ char *pattern = sl.items[i].string;
+ fprintf(fp, "%s/\n", pattern);
+ }
+}
+
+static int write_patterns_and_update(struct pattern_list *pl)
+{
+ char *sparse_filename;
+ FILE *fp;
+ int fd;
+ struct lock_file lk = LOCK_INIT;
+ int result;
+
+ sparse_filename = get_sparse_checkout_filename();
+ fd = hold_lock_file_for_update(&lk, sparse_filename,
+ LOCK_DIE_ON_ERROR);
+
+ result = update_working_directory(pl);
+ if (result) {
+ rollback_lock_file(&lk);
+ free(sparse_filename);
+ clear_pattern_list(pl);
+ update_working_directory(NULL);
+ return result;
+ }
+
+ fp = xfdopen(fd, "w");
+
+ if (core_sparse_checkout_cone)
+ write_cone_to_file(fp, pl);
+ else
+ write_patterns_to_file(fp, pl);
+
+ fflush(fp);
+ commit_lock_file(&lk);
+
+ free(sparse_filename);
+ clear_pattern_list(pl);
+
+ return 0;
+}
+
+enum sparse_checkout_mode {
+ MODE_NO_PATTERNS = 0,
+ MODE_ALL_PATTERNS = 1,
+ MODE_CONE_PATTERNS = 2,
+};
+
+static int set_config(enum sparse_checkout_mode mode)
+{
+ const char *config_path;
+
+ if (git_config_set_gently("extensions.worktreeConfig", "true")) {
+ error(_("failed to set extensions.worktreeConfig setting"));
+ return 1;
+ }
+
+ config_path = git_path("config.worktree");
+ git_config_set_in_file_gently(config_path,
+ "core.sparseCheckout",
+ mode ? "true" : NULL);
+
+ git_config_set_in_file_gently(config_path,
+ "core.sparseCheckoutCone",
+ mode == MODE_CONE_PATTERNS ? "true" : NULL);
+
+ return 0;
+}
+
+static char const * const builtin_sparse_checkout_init_usage[] = {
+ N_("git sparse-checkout init [--cone]"),
+ NULL
+};
+
+static struct sparse_checkout_init_opts {
+ int cone_mode;
+} init_opts;
+
+static int sparse_checkout_init(int argc, const char **argv)
+{
+ struct pattern_list pl;
+ char *sparse_filename;
+ int res;
+ struct object_id oid;
+ int mode;
+ struct strbuf pattern = STRBUF_INIT;
+
+ static struct option builtin_sparse_checkout_init_options[] = {
+ OPT_BOOL(0, "cone", &init_opts.cone_mode,
+ N_("initialize the sparse-checkout in cone mode")),
+ OPT_END(),
+ };
+
+ repo_read_index(the_repository);
+ require_clean_work_tree(the_repository,
+ N_("initialize sparse-checkout"), NULL, 1, 0);
+
+ argc = parse_options(argc, argv, NULL,
+ builtin_sparse_checkout_init_options,
+ builtin_sparse_checkout_init_usage, 0);
+
+ if (init_opts.cone_mode) {
+ mode = MODE_CONE_PATTERNS;
+ core_sparse_checkout_cone = 1;
+ } else
+ mode = MODE_ALL_PATTERNS;
+
+ if (set_config(mode))
+ return 1;
+
+ memset(&pl, 0, sizeof(pl));
+
+ sparse_filename = get_sparse_checkout_filename();
+ res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
+
+ /* If we already have a sparse-checkout file, use it. */
+ if (res >= 0) {
+ free(sparse_filename);
+ core_apply_sparse_checkout = 1;
+ return update_working_directory(NULL);
+ }
+
+ if (get_oid("HEAD", &oid)) {
+ FILE *fp;
+
+ /* assume we are in a fresh repo, but update the sparse-checkout file */
+ fp = xfopen(sparse_filename, "w");
+ if (!fp)
+ die(_("failed to open '%s'"), sparse_filename);
+
+ free(sparse_filename);
+ fprintf(fp, "/*\n!/*/\n");
+ fclose(fp);
+ 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);
+
+ return write_patterns_and_update(&pl);
+}
+
+static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
+{
+ struct pattern_entry *e = xmalloc(sizeof(*e));
+ e->patternlen = path->len;
+ e->pattern = strbuf_detach(path, NULL);
+ hashmap_entry_init(&e->ent,
+ ignore_case ?
+ strihash(e->pattern) :
+ strhash(e->pattern));
+
+ hashmap_add(&pl->recursive_hashmap, &e->ent);
+
+ while (e->patternlen) {
+ char *slash = strrchr(e->pattern, '/');
+ char *oldpattern = e->pattern;
+ size_t newlen;
+
+ if (slash == e->pattern)
+ break;
+
+ newlen = slash - e->pattern;
+ e = xmalloc(sizeof(struct pattern_entry));
+ e->patternlen = newlen;
+ e->pattern = xstrndup(oldpattern, newlen);
+ hashmap_entry_init(&e->ent,
+ ignore_case ?
+ strihash(e->pattern) :
+ strhash(e->pattern));
+
+ if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
+ hashmap_add(&pl->parent_hashmap, &e->ent);
+ }
+}
+
+static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
+{
+ strbuf_trim(line);
+
+ strbuf_trim_trailing_dir_sep(line);
+
+ if (!line->len)
+ return;
+
+ if (line->buf[0] != '/')
+ strbuf_insert(line, 0, "/", 1);
+
+ insert_recursive_pattern(pl, line);
+}
+
+static char const * const builtin_sparse_checkout_set_usage[] = {
+ N_("git sparse-checkout set (--stdin | <patterns>)"),
+ NULL
+};
+
+static struct sparse_checkout_set_opts {
+ int use_stdin;
+} set_opts;
+
+static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ struct pattern_list pl;
+ int result;
+ int changed_config = 0;
+
+ static struct option builtin_sparse_checkout_set_options[] = {
+ OPT_BOOL(0, "stdin", &set_opts.use_stdin,
+ N_("read patterns from standard in")),
+ OPT_END(),
+ };
+
+ repo_read_index(the_repository);
+ require_clean_work_tree(the_repository,
+ N_("set sparse-checkout patterns"), NULL, 1, 0);
+
+ memset(&pl, 0, sizeof(pl));
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_set_options,
+ builtin_sparse_checkout_set_usage,
+ PARSE_OPT_KEEP_UNKNOWN);
+
+ if (core_sparse_checkout_cone) {
+ struct strbuf line = STRBUF_INIT;
+
+ hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
+ hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
+ pl.use_cone_patterns = 1;
+
+ if (set_opts.use_stdin) {
+ while (!strbuf_getline(&line, stdin))
+ strbuf_to_cone_pattern(&line, &pl);
+ } else {
+ for (i = 0; i < argc; i++) {
+ strbuf_setlen(&line, 0);
+ strbuf_addstr(&line, argv[i]);
+ strbuf_to_cone_pattern(&line, &pl);
+ }
+ }
+ } else {
+ if (set_opts.use_stdin) {
+ struct strbuf line = STRBUF_INIT;
+
+ while (!strbuf_getline(&line, stdin)) {
+ size_t len;
+ char *buf = strbuf_detach(&line, &len);
+ add_pattern(buf, empty_base, 0, &pl, 0);
+ }
+ } else {
+ for (i = 0; i < argc; i++)
+ add_pattern(argv[i], empty_base, 0, &pl, 0);
+ }
+ }
+
+ if (!core_apply_sparse_checkout) {
+ set_config(MODE_ALL_PATTERNS);
+ core_apply_sparse_checkout = 1;
+ changed_config = 1;
+ }
+
+ result = write_patterns_and_update(&pl);
+
+ if (result && changed_config)
+ set_config(MODE_NO_PATTERNS);
+
+ clear_pattern_list(&pl);
+ return result;
+}
+
+static int sparse_checkout_disable(int argc, const char **argv)
+{
+ struct pattern_list pl;
+ struct strbuf match_all = STRBUF_INIT;
+
+ repo_read_index(the_repository);
+ require_clean_work_tree(the_repository,
+ N_("disable sparse-checkout"), NULL, 1, 0);
+
+ memset(&pl, 0, sizeof(pl));
+ hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
+ hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
+ 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);
+
+ if (update_working_directory(&pl))
+ die(_("error while refreshing working directory"));
+
+ clear_pattern_list(&pl);
+ return set_config(MODE_NO_PATTERNS);
+}
+
+int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
+{
+ static struct option builtin_sparse_checkout_options[] = {
+ OPT_END(),
+ };
+
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage_with_options(builtin_sparse_checkout_usage,
+ builtin_sparse_checkout_options);
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_options,
+ builtin_sparse_checkout_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ git_config(git_default_config, NULL);
+
+ if (argc > 0) {
+ if (!strcmp(argv[0], "list"))
+ return sparse_checkout_list(argc, argv);
+ if (!strcmp(argv[0], "init"))
+ return sparse_checkout_init(argc, argv);
+ if (!strcmp(argv[0], "set"))
+ return sparse_checkout_set(argc, argv, prefix);
+ if (!strcmp(argv[0], "disable"))
+ return sparse_checkout_disable(argc, argv);
+ }
+
+ usage_with_options(builtin_sparse_checkout_usage,
+ builtin_sparse_checkout_options);
+}
diff --git a/cache.h b/cache.h
index 64669a4..1554488 100644
--- a/cache.h
+++ b/cache.h
@@ -304,6 +304,7 @@
struct split_index;
struct untracked_cache;
+struct progress;
struct index_state {
struct cache_entry **cache;
@@ -326,6 +327,7 @@
uint64_t fsmonitor_last_update;
struct ewah_bitmap *fsmonitor_dirty;
struct mem_pool *ce_mem_pool;
+ struct progress *progress;
};
/* Name hashing */
@@ -632,10 +634,43 @@
#define alloc_nr(x) (((x)+16)*3/2)
-/*
- * Realloc the buffer pointed at by variable 'x' so that it can hold
- * at least 'nr' entries; the number of entries currently allocated
- * is 'alloc', using the standard growing factor alloc_nr() macro.
+/**
+ * Dynamically growing an array using realloc() is error prone and boring.
+ *
+ * Define your array with:
+ *
+ * - a pointer (`item`) that points at the array, initialized to `NULL`
+ * (although please name the variable based on its contents, not on its
+ * type);
+ *
+ * - an integer variable (`alloc`) that keeps track of how big the current
+ * allocation is, initialized to `0`;
+ *
+ * - another integer variable (`nr`) to keep track of how many elements the
+ * array currently has, initialized to `0`.
+ *
+ * Then before adding `n`th element to the item, call `ALLOC_GROW(item, n,
+ * alloc)`. This ensures that the array can hold at least `n` elements by
+ * calling `realloc(3)` and adjusting `alloc` variable.
+ *
+ * ------------
+ * sometype *item;
+ * size_t nr;
+ * size_t alloc
+ *
+ * for (i = 0; i < nr; i++)
+ * if (we like item[i] already)
+ * return;
+ *
+ * // we did not like any existing one, so add one
+ * ALLOC_GROW(item, nr + 1, alloc);
+ * item[nr++] = value you like;
+ * ------------
+ *
+ * You are responsible for updating the `nr` variable.
+ *
+ * If you need to specify the number of elements to allocate explicitly
+ * then use the macro `REALLOC_ARRAY(item, alloc)` instead of `ALLOC_GROW`.
*
* Consider using ALLOC_GROW_BY instead of ALLOC_GROW as it has some
* added niceties.
@@ -918,12 +953,14 @@
extern int fsync_object_files;
extern int core_preload_index;
-extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
extern int protect_hfs;
extern int protect_ntfs;
extern const char *core_fsmonitor;
+int core_apply_sparse_checkout;
+int core_sparse_checkout_cone;
+
/*
* Include broken refs in all ref iterations, which will
* generally choke dangerous operations rather than letting
diff --git a/command-list.txt b/command-list.txt
index 72e435c..2087894 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -166,6 +166,7 @@
git-show-ref plumbinginterrogators
git-sh-i18n purehelpers
git-sh-setup purehelpers
+git-sparse-checkout mainporcelain worktree
git-stash mainporcelain
git-stage complete
git-status mainporcelain info
diff --git a/compat/mingw.c b/compat/mingw.c
index c2a4835..76ac871 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1018,16 +1018,16 @@
struct tm *gmtime_r(const time_t *timep, struct tm *result)
{
- /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
- memcpy(result, gmtime(timep), sizeof(struct tm));
- return result;
+ if (gmtime_s(result, timep) == 0)
+ return result;
+ return NULL;
}
struct tm *localtime_r(const time_t *timep, struct tm *result)
{
- /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
- memcpy(result, localtime(timep), sizeof(struct tm));
- return result;
+ if (localtime_s(result, timep) == 0)
+ return result;
+ return NULL;
}
char *mingw_getcwd(char *pointer, int len)
diff --git a/config.c b/config.c
index e7052b3..d75f88c 100644
--- a/config.c
+++ b/config.c
@@ -1364,6 +1364,11 @@
return 0;
}
+ if (!strcmp(var, "core.sparsecheckoutcone")) {
+ core_sparse_checkout_cone = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "core.precomposeunicode")) {
precomposed_unicode = git_config_bool(var, value);
return 0;
diff --git a/credential.h b/credential.h
index 6b0cd16..5772d50 100644
--- a/credential.h
+++ b/credential.h
@@ -3,8 +3,208 @@
#include "string-list.h"
+/**
+ * The credentials API provides an abstracted way of gathering username and
+ * password credentials from the user.
+ *
+ * Typical setup
+ * -------------
+ *
+ * ------------
+ * +-----------------------+
+ * | Git code (C) |--- to server requiring --->
+ * | | authentication
+ * |.......................|
+ * | C credential API |--- prompt ---> User
+ * +-----------------------+
+ * ^ |
+ * | pipe |
+ * | v
+ * +-----------------------+
+ * | Git credential helper |
+ * +-----------------------+
+ * ------------
+ *
+ * The Git code (typically a remote-helper) will call the C API to obtain
+ * credential data like a login/password pair (credential_fill). The
+ * API will itself call a remote helper (e.g. "git credential-cache" or
+ * "git credential-store") that may retrieve credential data from a
+ * store. If the credential helper cannot find the information, the C API
+ * will prompt the user. Then, the caller of the API takes care of
+ * contacting the server, and does the actual authentication.
+ *
+ * C API
+ * -----
+ *
+ * The credential C API is meant to be called by Git code which needs to
+ * acquire or store a credential. It is centered around an object
+ * representing a single credential and provides three basic operations:
+ * fill (acquire credentials by calling helpers and/or prompting the user),
+ * approve (mark a credential as successfully used so that it can be stored
+ * for later use), and reject (mark a credential as unsuccessful so that it
+ * can be erased from any persistent storage).
+ *
+ * Example
+ * ~~~~~~~
+ *
+ * The example below shows how the functions of the credential API could be
+ * used to login to a fictitious "foo" service on a remote host:
+ *
+ * -----------------------------------------------------------------------
+ * int foo_login(struct foo_connection *f)
+ * {
+ * int status;
+ * // Create a credential with some context; we don't yet know the
+ * // username or password.
+ *
+ * struct credential c = CREDENTIAL_INIT;
+ * c.protocol = xstrdup("foo");
+ * c.host = xstrdup(f->hostname);
+ *
+ * // Fill in the username and password fields by contacting
+ * // helpers and/or asking the user. The function will die if it
+ * // fails.
+ * credential_fill(&c);
+ *
+ * // Otherwise, we have a username and password. Try to use it.
+ *
+ * status = send_foo_login(f, c.username, c.password);
+ * switch (status) {
+ * case FOO_OK:
+ * // It worked. Store the credential for later use.
+ * credential_accept(&c);
+ * break;
+ * case FOO_BAD_LOGIN:
+ * // Erase the credential from storage so we don't try it again.
+ * credential_reject(&c);
+ * break;
+ * default:
+ * // Some other error occurred. We don't know if the
+ * // credential is good or bad, so report nothing to the
+ * // credential subsystem.
+ * }
+ *
+ * // Free any associated resources.
+ * credential_clear(&c);
+ *
+ * return status;
+ * }
+ * -----------------------------------------------------------------------
+ *
+ * Credential Helpers
+ * ------------------
+ *
+ * Credential helpers are programs executed by Git to fetch or save
+ * credentials from and to long-term storage (where "long-term" is simply
+ * longer than a single Git process; e.g., credentials may be stored
+ * in-memory for a few minutes, or indefinitely on disk).
+ *
+ * Each helper is specified by a single string in the configuration
+ * variable `credential.helper` (and others, see Documentation/git-config.txt).
+ * The string is transformed by Git into a command to be executed using
+ * these rules:
+ *
+ * 1. If the helper string begins with "!", it is considered a shell
+ * snippet, and everything after the "!" becomes the command.
+ *
+ * 2. Otherwise, if the helper string begins with an absolute path, the
+ * verbatim helper string becomes the command.
+ *
+ * 3. Otherwise, the string "git credential-" is prepended to the helper
+ * string, and the result becomes the command.
+ *
+ * The resulting command then has an "operation" argument appended to it
+ * (see below for details), and the result is executed by the shell.
+ *
+ * Here are some example specifications:
+ *
+ * ----------------------------------------------------
+ * # run "git credential-foo"
+ * foo
+ *
+ * # same as above, but pass an argument to the helper
+ * foo --bar=baz
+ *
+ * # the arguments are parsed by the shell, so use shell
+ * # quoting if necessary
+ * foo --bar="whitespace arg"
+ *
+ * # you can also use an absolute path, which will not use the git wrapper
+ * /path/to/my/helper --with-arguments
+ *
+ * # or you can specify your own shell snippet
+ * !f() { echo "password=`cat $HOME/.secret`"; }; f
+ * ----------------------------------------------------
+ *
+ * Generally speaking, rule (3) above is the simplest for users to specify.
+ * Authors of credential helpers should make an effort to assist their
+ * users by naming their program "git-credential-$NAME", and putting it in
+ * the $PATH or $GIT_EXEC_PATH during installation, which will allow a user
+ * to enable it with `git config credential.helper $NAME`.
+ *
+ * When a helper is executed, it will have one "operation" argument
+ * appended to its command line, which is one of:
+ *
+ * `get`::
+ *
+ * Return a matching credential, if any exists.
+ *
+ * `store`::
+ *
+ * Store the credential, if applicable to the helper.
+ *
+ * `erase`::
+ *
+ * Remove a matching credential, if any, from the helper's storage.
+ *
+ * The details of the credential will be provided on the helper's stdin
+ * stream. The exact format is the same as the input/output format of the
+ * `git credential` plumbing command (see the section `INPUT/OUTPUT
+ * FORMAT` in Documentation/git-credential.txt for a detailed specification).
+ *
+ * For a `get` operation, the helper should produce a list of attributes
+ * on stdout in the same format. A helper is free to produce a subset, or
+ * even no values at all if it has nothing useful to provide. Any provided
+ * attributes will overwrite those already known about by Git. If a helper
+ * outputs a `quit` attribute with a value of `true` or `1`, no further
+ * helpers will be consulted, nor will the user be prompted (if no
+ * credential has been provided, the operation will then fail).
+ *
+ * For a `store` or `erase` operation, the helper's output is ignored.
+ * If it fails to perform the requested operation, it may complain to
+ * stderr to inform the user. If it does not support the requested
+ * operation (e.g., a read-only store), it should silently ignore the
+ * request.
+ *
+ * If a helper receives any other operation, it should silently ignore the
+ * request. This leaves room for future operations to be added (older
+ * helpers will just ignore the new requests).
+ *
+ */
+
+
+/**
+ * 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 should always be initialized with `CREDENTIAL_INIT` or
+ * `credential_init`.
+ */
struct credential {
+
+ /**
+ * A `string_list` of helpers. Each string specifies an external
+ * helper which will be run, in order, to either acquire or store
+ * credentials. This list is filled-in by the API functions
+ * according to the corresponding configuration variables before
+ * consulting helpers, so there usually is no need for a caller to
+ * modify the helpers field at all.
+ */
struct string_list helpers;
+
unsigned approved:1,
configured:1,
quit:1,
@@ -19,16 +219,52 @@
#define CREDENTIAL_INIT { STRING_LIST_INIT_DUP }
+/* Initialize a credential structure, setting all fields to empty. */
void credential_init(struct credential *);
+
+/**
+ * Free any resources associated with the credential structure, returning
+ * it to a pristine initialized state.
+ */
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().
+ */
void credential_fill(struct credential *);
+
+/**
+ * Inform the credential subsystem that the provided credentials
+ * were successfully used for authentication. This will cause the
+ * credential subsystem to notify any helpers of the approval, so
+ * that they may store the result to be used again. Any errors
+ * from helpers are ignored.
+ */
void credential_approve(struct credential *);
+
+/**
+ * Inform the credential subsystem that the provided credentials
+ * 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.
+ */
void credential_reject(struct credential *);
int credential_read(struct credential *, FILE *);
void credential_write(const struct credential *, FILE *);
+
+/* Parse a URL into broken-down credential fields. */
void credential_from_url(struct credential *, const char *url);
+
int credential_match(const struct credential *have,
const struct credential *want);
diff --git a/date.c b/date.c
index 041db7d..b0d9a84 100644
--- a/date.c
+++ b/date.c
@@ -64,16 +64,16 @@
* thing, which means that tz -0100 is passed in as the integer -100,
* even though it means "sixty minutes off"
*/
-static struct tm *time_to_tm(timestamp_t time, int tz)
+static struct tm *time_to_tm(timestamp_t time, int tz, struct tm *tm)
{
time_t t = gm_time_t(time, tz);
- return gmtime(&t);
+ return gmtime_r(&t, tm);
}
-static struct tm *time_to_tm_local(timestamp_t time)
+static struct tm *time_to_tm_local(timestamp_t time, struct tm *tm)
{
time_t t = time;
- return localtime(&t);
+ return localtime_r(&t, tm);
}
/*
@@ -283,6 +283,7 @@
const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
{
struct tm *tm;
+ struct tm tmbuf = { 0 };
struct tm human_tm = { 0 };
int human_tz = -1;
static struct strbuf timebuf = STRBUF_INIT;
@@ -318,11 +319,11 @@
}
if (mode->local)
- tm = time_to_tm_local(time);
+ tm = time_to_tm_local(time, &tmbuf);
else
- tm = time_to_tm(time, tz);
+ tm = time_to_tm(time, tz, &tmbuf);
if (!tm) {
- tm = time_to_tm(0, 0);
+ tm = time_to_tm(0, 0, &tmbuf);
tz = 0;
}
@@ -959,10 +960,11 @@
{
time_t now;
int offset;
+ struct tm tm = { 0 };
time(&now);
- offset = tm_to_time_t(localtime(&now)) - now;
+ offset = tm_to_time_t(localtime_r(&now, &tm)) - now;
offset /= 60;
date_string(now, offset, out);
diff --git a/diff.h b/diff.h
index d986ddc..6febe7e 100644
--- a/diff.h
+++ b/diff.h
@@ -9,6 +9,49 @@
#include "object.h"
#include "oidset.h"
+/**
+ * The diff API is for programs that compare two sets of files (e.g. two trees,
+ * one tree and the index) and present the found difference in various ways.
+ * The calling program is responsible for feeding the API pairs of files, one
+ * from the "old" set and the corresponding one from "new" set, that are
+ * different.
+ * The library called through this API is called diffcore, and is responsible
+ * for two things.
+ *
+ * - finding total rewrites (`-B`), renames (`-M`) and copies (`-C`), and
+ * changes that touch a string (`-S`), as specified by the caller.
+ *
+ * - outputting the differences in various formats, as specified by the caller.
+ *
+ * Calling sequence
+ * ----------------
+ *
+ * - Prepare `struct diff_options` to record the set of diff options, and then
+ * call `repo_diff_setup()` to initialize this structure. This sets up the
+ * vanilla default.
+ *
+ * - Fill in the options structure to specify desired output format, rename
+ * detection, etc. `diff_opt_parse()` can be used to parse options given
+ * from the command line in a way consistent with existing git-diff family
+ * of programs.
+ *
+ * - Call `diff_setup_done()`; this inspects the options set up so far for
+ * internal consistency and make necessary tweaking to it (e.g. if textual
+ * patch output was asked, recursive behaviour is turned on); the callback
+ * set_default in diff_options can be used to tweak this more.
+ *
+ * - As you find different pairs of files, call `diff_change()` to feed
+ * modified files, `diff_addremove()` to feed created or deleted files, or
+ * `diff_unmerge()` to feed a file whose state is 'unmerged' to the API.
+ * These are thin wrappers to a lower-level `diff_queue()` function that is
+ * flexible enough to record any of these kinds of changes.
+ *
+ * - Once you finish feeding the pairs of files, call `diffcore_std()`.
+ * This will tell the diffcore library to go ahead and do its work.
+ *
+ * - Calling `diff_flush()` will produce the output.
+ */
+
struct combine_diff_path;
struct commit;
struct diff_filespec;
@@ -65,21 +108,66 @@
#define DIFF_FLAGS_INIT { 0 }
struct diff_flags {
+
+ /**
+ * Tells if tree traversal done by tree-diff should recursively descend
+ * into a tree object pair that are different in preimage and postimage set.
+ */
unsigned recursive;
unsigned tree_in_recursive;
+
+ /* Affects the way how a file that is seemingly binary is treated. */
unsigned binary;
unsigned text;
+
+ /**
+ * Tells the patch output format not to use abbreviated object names on the
+ * "index" lines.
+ */
unsigned full_index;
+
+ /* Affects if diff-files shows removed files. */
unsigned silent_on_remove;
+
+ /**
+ * Tells the diffcore library that the caller is feeding unchanged
+ * filepairs to allow copies from unmodified files be detected.
+ */
unsigned find_copies_harder;
+
unsigned follow_renames;
unsigned rename_empty;
+
+ /* Internal; used for optimization to see if there is any change. */
unsigned has_changes;
+
unsigned quick;
+
+ /**
+ * Tells diff-files that the input is not tracked files but files in random
+ * locations on the filesystem.
+ */
unsigned no_index;
+
+ /**
+ * Tells output routine that it is Ok to call user specified patch output
+ * routine. Plumbing disables this to ensure stable output.
+ */
unsigned allow_external;
+
+ /**
+ * For communication between the calling program and the options parser;
+ * tell the calling program to signal the presence of difference using
+ * program exit code.
+ */
unsigned exit_with_status;
+
+ /**
+ * Tells the library that the calling program is feeding the filepairs
+ * reversed; `one` is two, and `two` is one.
+ */
unsigned reverse_diff;
+
unsigned check_failed;
unsigned relative_name;
unsigned ignore_submodules;
@@ -131,36 +219,72 @@
DIFF_SUBMODULE_INLINE_DIFF
};
+/**
+ * the set of options the calling program wants to affect the operation of
+ * diffcore library with.
+ */
struct diff_options {
const char *orderfile;
+
+ /**
+ * A constant string (can and typically does contain newlines to look for
+ * a block of text, not just a single line) to filter out the filepairs
+ * that do not change the number of strings contained in its preimage and
+ * postimage of the diff_queue.
+ */
const char *pickaxe;
+
const char *single_follow;
const char *a_prefix, *b_prefix;
const char *line_prefix;
size_t line_prefix_length;
+
+ /**
+ * collection of boolean options that affects the operation, but some do
+ * not have anything to do with the diffcore library.
+ */
struct diff_flags flags;
/* diff-filter bits */
unsigned int filter;
int use_color;
+
+ /* Number of context lines to generate in patch output. */
int context;
+
int interhunkcontext;
+
+ /* Affects the way detection logic for complete rewrites, renames and
+ * copies.
+ */
int break_opt;
int detect_rename;
+
int irreversible_delete;
int skip_stat_unmatch;
int line_termination;
+
+ /* The output format used when `diff_flush()` is run. */
int output_format;
+
unsigned pickaxe_opts;
+
+ /* Affects the way detection logic for complete rewrites, renames and
+ * copies.
+ */
int rename_score;
int rename_limit;
+
int needed_rename_limit;
int degraded_cc_to_c;
int show_rename_progress;
int dirstat_permille;
int setup;
+
+ /* Number of hexdigits to abbreviate raw format output to. */
int abbrev;
+
int ita_invisible_in_index;
/* white-space error highlighting */
#define WSEH_NEW (1<<12)
@@ -192,6 +316,7 @@
/* to support internal diff recursion by --follow hack*/
int found_follow;
+ /* Callback which allows tweaking the options in diff_setup_done(). */
void (*set_default)(struct diff_options *);
FILE *file;
@@ -286,6 +411,7 @@
DIFF_FILE_OLD_BOLD = 21,
DIFF_FILE_NEW_BOLD = 22,
};
+
const char *diff_get_color(int diff_use_color, enum color_diff ix);
#define diff_get_color_opt(o, ix) \
diff_get_color((o)->use_color, ix)
diff --git a/diffcore.h b/diffcore.h
index b651061..7c07347 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -28,6 +28,12 @@
#define MINIMUM_BREAK_SIZE 400 /* do not break a file smaller than this */
+/**
+ * the internal representation for a single file (blob). It records the blob
+ * object name (if known -- for a work tree file it typically is a NUL SHA-1),
+ * filemode and pathname. This is what the `diff_addremove()`, `diff_change()`
+ * and `diff_unmerge()` synthesize and feed `diff_queue()` function with.
+ */
struct diff_filespec {
struct object_id oid;
char *path;
@@ -66,6 +72,17 @@
void diff_free_filespec_blob(struct diff_filespec *);
int diff_filespec_is_binary(struct repository *, struct diff_filespec *);
+/**
+ * This records a pair of `struct diff_filespec`; the filespec for a file in
+ * the "old" set (i.e. preimage) is called `one`, and the filespec for a file
+ * in the "new" set (i.e. postimage) is called `two`. A change that represents
+ * file creation has NULL in `one`, and file deletion has NULL in `two`.
+ *
+ * A `filepair` starts pointing at `one` and `two` that are from the same
+ * filename, but `diffcore_std()` can break pairs and match component filespecs
+ * with other filespecs from a different filepair to form new filepair. This is
+ * called 'rename detection'.
+ */
struct diff_filepair {
struct diff_filespec *one;
struct diff_filespec *two;
@@ -77,6 +94,7 @@
unsigned done_skip_stat_unmatch : 1;
unsigned skip_stat_unmatch_result : 1;
};
+
#define DIFF_PAIR_UNMERGED(p) ((p)->is_unmerged)
#define DIFF_PAIR_RENAME(p) ((p)->renamed_pair)
@@ -94,11 +112,25 @@
int diff_unmodified_pair(struct diff_filepair *);
+/**
+ * This is a collection of filepairs. Notable members are:
+ *
+ * - `queue`:
+ * An array of pointers to `struct diff_filepair`. This dynamically grows as
+ * you add filepairs;
+ *
+ * - `alloc`:
+ * The allocated size of the `queue` array;
+ *
+ * - `nr`:
+ * The number of elements in the `queue` array.
+ */
struct diff_queue_struct {
struct diff_filepair **queue;
int alloc;
int nr;
};
+
#define DIFF_QUEUE_CLEAR(q) \
do { \
(q)->queue = NULL; \
diff --git a/dir.c b/dir.c
index 9047373..a7c7227 100644
--- a/dir.c
+++ b/dir.c
@@ -2,8 +2,6 @@
* This handles recursive filename detection with exclude
* files, index knowledge etc..
*
- * See Documentation/technical/api-directory-listing.txt
- *
* Copyright (C) Linus Torvalds, 2005-2006
* Junio Hamano, 2005-2006
*/
@@ -611,6 +609,159 @@
*patternlen = len;
}
+int pl_hashmap_cmp(const void *unused_cmp_data,
+ const struct hashmap_entry *a,
+ const struct hashmap_entry *b,
+ const void *key)
+{
+ const struct pattern_entry *ee1 =
+ container_of(a, struct pattern_entry, ent);
+ const struct pattern_entry *ee2 =
+ container_of(b, struct pattern_entry, ent);
+
+ size_t min_len = ee1->patternlen <= ee2->patternlen
+ ? ee1->patternlen
+ : ee2->patternlen;
+
+ if (ignore_case)
+ return strncasecmp(ee1->pattern, ee2->pattern, min_len);
+ return strncmp(ee1->pattern, ee2->pattern, min_len);
+}
+
+static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
+{
+ struct pattern_entry *translated;
+ char *truncated;
+ char *data = NULL;
+
+ if (!pl->use_cone_patterns)
+ return;
+
+ if (given->flags & PATTERN_FLAG_NEGATIVE &&
+ given->flags & PATTERN_FLAG_MUSTBEDIR &&
+ !strcmp(given->pattern, "/*")) {
+ pl->full_cone = 0;
+ return;
+ }
+
+ if (!given->flags && !strcmp(given->pattern, "/*")) {
+ pl->full_cone = 1;
+ return;
+ }
+
+ if (given->patternlen > 2 &&
+ !strcmp(given->pattern + given->patternlen - 2, "/*")) {
+ if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
+ /* Not a cone pattern. */
+ pl->use_cone_patterns = 0;
+ warning(_("unrecognized pattern: '%s'"), given->pattern);
+ goto clear_hashmaps;
+ }
+
+ truncated = xstrdup(given->pattern);
+ truncated[given->patternlen - 2] = 0;
+
+ translated = xmalloc(sizeof(struct pattern_entry));
+ translated->pattern = truncated;
+ translated->patternlen = given->patternlen - 2;
+ hashmap_entry_init(&translated->ent,
+ ignore_case ?
+ strihash(translated->pattern) :
+ strhash(translated->pattern));
+
+ if (!hashmap_get_entry(&pl->recursive_hashmap,
+ translated, ent, NULL)) {
+ /* We did not see the "parent" included */
+ warning(_("unrecognized negative pattern: '%s'"),
+ given->pattern);
+ free(truncated);
+ free(translated);
+ goto clear_hashmaps;
+ }
+
+ hashmap_add(&pl->parent_hashmap, &translated->ent);
+ hashmap_remove(&pl->recursive_hashmap, &translated->ent, &data);
+ free(data);
+ return;
+ }
+
+ if (given->flags & PATTERN_FLAG_NEGATIVE) {
+ warning(_("unrecognized negative pattern: '%s'"),
+ given->pattern);
+ goto clear_hashmaps;
+ }
+
+ translated = xmalloc(sizeof(struct pattern_entry));
+
+ translated->pattern = xstrdup(given->pattern);
+ translated->patternlen = given->patternlen;
+ hashmap_entry_init(&translated->ent,
+ ignore_case ?
+ strihash(translated->pattern) :
+ strhash(translated->pattern));
+
+ hashmap_add(&pl->recursive_hashmap, &translated->ent);
+
+ if (hashmap_get_entry(&pl->parent_hashmap, translated, ent, NULL)) {
+ /* we already included this at the parent level */
+ warning(_("your sparse-checkout file may have issues: pattern '%s' is repeated"),
+ given->pattern);
+ hashmap_remove(&pl->parent_hashmap, &translated->ent, &data);
+ free(data);
+ free(translated);
+ }
+
+ return;
+
+clear_hashmaps:
+ warning(_("disabling cone pattern matching"));
+ hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent);
+ hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent);
+ pl->use_cone_patterns = 0;
+}
+
+static int hashmap_contains_path(struct hashmap *map,
+ struct strbuf *pattern)
+{
+ struct pattern_entry p;
+
+ /* Check straight mapping */
+ p.pattern = pattern->buf;
+ p.patternlen = pattern->len;
+ hashmap_entry_init(&p.ent,
+ ignore_case ?
+ strihash(p.pattern) :
+ strhash(p.pattern));
+ return !!hashmap_get_entry(map, &p, ent, NULL);
+}
+
+int hashmap_contains_parent(struct hashmap *map,
+ const char *path,
+ struct strbuf *buffer)
+{
+ char *slash_pos;
+
+ strbuf_setlen(buffer, 0);
+
+ if (path[0] != '/')
+ strbuf_addch(buffer, '/');
+
+ strbuf_addstr(buffer, path);
+
+ slash_pos = strrchr(buffer->buf, '/');
+
+ while (slash_pos > buffer->buf) {
+ strbuf_setlen(buffer, slash_pos - buffer->buf);
+
+ if (hashmap_contains_path(map, buffer))
+ return 1;
+
+ slash_pos = strrchr(buffer->buf, '/');
+ }
+
+ return 0;
+}
+
void add_pattern(const char *string, const char *base,
int baselen, struct pattern_list *pl, int srcpos)
{
@@ -635,6 +786,8 @@
ALLOC_GROW(pl->patterns, pl->nr + 1, pl->alloc);
pl->patterns[pl->nr++] = pattern;
pattern->pl = pl;
+
+ add_pattern_to_hashsets(pl, pattern);
}
static int read_skip_worktree_file_from_index(const struct index_state *istate,
@@ -860,6 +1013,9 @@
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))
@@ -1096,16 +1252,58 @@
struct index_state *istate)
{
struct path_pattern *pattern;
- pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
- dtype, pl, istate);
- if (pattern) {
- if (pattern->flags & PATTERN_FLAG_NEGATIVE)
- return NOT_MATCHED;
- else
- return MATCHED;
+ struct strbuf parent_pathname = STRBUF_INIT;
+ int result = NOT_MATCHED;
+ const char *slash_pos;
+
+ if (!pl->use_cone_patterns) {
+ pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
+ dtype, pl, istate);
+ if (pattern) {
+ if (pattern->flags & PATTERN_FLAG_NEGATIVE)
+ return NOT_MATCHED;
+ else
+ return MATCHED;
+ }
+
+ return UNDECIDED;
}
- return UNDECIDED;
+ if (pl->full_cone)
+ return MATCHED;
+
+ strbuf_addch(&parent_pathname, '/');
+ strbuf_add(&parent_pathname, pathname, pathlen);
+
+ if (hashmap_contains_path(&pl->recursive_hashmap,
+ &parent_pathname)) {
+ result = MATCHED_RECURSIVE;
+ goto done;
+ }
+
+ slash_pos = strrchr(parent_pathname.buf, '/');
+
+ if (slash_pos == parent_pathname.buf) {
+ /* include every file in root */
+ result = MATCHED;
+ goto done;
+ }
+
+ strbuf_setlen(&parent_pathname, slash_pos - parent_pathname.buf);
+
+ if (hashmap_contains_path(&pl->parent_hashmap, &parent_pathname)) {
+ result = MATCHED;
+ goto done;
+ }
+
+ if (hashmap_contains_parent(&pl->recursive_hashmap,
+ pathname,
+ &parent_pathname))
+ result = MATCHED_RECURSIVE;
+
+done:
+ strbuf_release(&parent_pathname);
+ return result;
}
static struct path_pattern *last_matching_pattern_from_lists(
diff --git a/dir.h b/dir.h
index 2fbdef0..5855c06 100644
--- a/dir.h
+++ b/dir.h
@@ -1,11 +1,45 @@
#ifndef DIR_H
#define DIR_H
-/* See Documentation/technical/api-directory-listing.txt */
-
#include "cache.h"
+#include "hashmap.h"
#include "strbuf.h"
+/**
+ * The directory listing API is used to enumerate paths in the work tree,
+ * optionally taking `.git/info/exclude` and `.gitignore` files per directory
+ * into account.
+ */
+
+/**
+ * Calling sequence
+ * ----------------
+ *
+ * Note: The index may be checked for .gitignore files that are
+ * CE_SKIP_WORKTREE marked. If you want to exclude files, make sure you have
+ * loaded the index first.
+ *
+ * - Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
+ * sizeof(dir))`.
+ *
+ * - To add single exclude pattern, call `add_pattern_list()` and then
+ * `add_pattern()`.
+ *
+ * - To add patterns from a file (e.g. `.git/info/exclude`), call
+ * `add_patterns_from_file()` , and/or set `dir.exclude_per_dir`. A
+ * short-hand function `setup_standard_excludes()` can be used to set
+ * up the standard set of exclude settings.
+ *
+ * - Set options described in the Data Structure section above.
+ *
+ * - Call `read_directory()`.
+ *
+ * - Use `dir.entries[]`.
+ *
+ * - Call `clear_directory()` when none of the contained elements are no longer in use.
+ *
+ */
+
struct dir_entry {
unsigned int len;
char name[FLEX_ARRAY]; /* more */
@@ -37,6 +71,13 @@
int srcpos;
};
+/* used for hashmaps for cone patterns */
+struct pattern_entry {
+ struct hashmap_entry ent;
+ char *pattern;
+ size_t patternlen;
+};
+
/*
* Each excludes file will be parsed into a fresh exclude_list which
* is appended to the relevant exclude_list_group (either EXC_DIRS or
@@ -55,6 +96,26 @@
const char *src;
struct path_pattern **patterns;
+
+ /*
+ * While scanning the excludes, we attempt to match the patterns
+ * with a more restricted set that allows us to use hashsets for
+ * matching logic, which is faster than the linear lookup in the
+ * excludes array above. If non-zero, that check succeeded.
+ */
+ unsigned use_cone_patterns;
+ unsigned full_cone;
+
+ /*
+ * Stores paths where everything starting with those paths
+ * is included.
+ */
+ struct hashmap recursive_hashmap;
+
+ /*
+ * Used to check single-level parents of blobs.
+ */
+ struct hashmap parent_hashmap;
};
/*
@@ -144,25 +205,101 @@
unsigned int use_fsmonitor : 1;
};
+/**
+ * structure is used to pass directory traversal options to the library and to
+ * record the paths discovered. A single `struct dir_struct` is used regardless
+ * of whether or not the traversal recursively descends into subdirectories.
+ */
struct dir_struct {
- int nr, alloc;
- int ignored_nr, ignored_alloc;
+
+ /* The number of members in `entries[]` array. */
+ int nr;
+
+ /* Internal use; keeps track of allocation of `entries[]` array.*/
+ int alloc;
+
+ /* The number of members in `ignored[]` array. */
+ int ignored_nr;
+
+ int ignored_alloc;
+
+ /* bit-field of options */
enum {
+
+ /**
+ * Return just ignored files in `entries[]`, not untracked files.
+ * This flag is mutually exclusive with `DIR_SHOW_IGNORED_TOO`.
+ */
DIR_SHOW_IGNORED = 1<<0,
+
+ /* Include a directory that is not tracked. */
DIR_SHOW_OTHER_DIRECTORIES = 1<<1,
+
+ /* Do not include a directory that is not tracked and is empty. */
DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
+
+ /**
+ * If set, recurse into a directory that looks like a Git directory.
+ * Otherwise it is shown as a directory.
+ */
DIR_NO_GITLINKS = 1<<3,
+
+ /**
+ * Special mode for git-add. Return ignored files in `ignored[]` and
+ * untracked files in `entries[]`. Only returns ignored files that match
+ * pathspec exactly (no wildcards). Does not recurse into ignored
+ * directories.
+ */
DIR_COLLECT_IGNORED = 1<<4,
+
+ /**
+ * Similar to `DIR_SHOW_IGNORED`, but return ignored files in
+ * `ignored[]` in addition to untracked files in `entries[]`.
+ * This flag is mutually exclusive with `DIR_SHOW_IGNORED`.
+ */
DIR_SHOW_IGNORED_TOO = 1<<5,
+
DIR_COLLECT_KILLED_ONLY = 1<<6,
+
+ /**
+ * Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is
+ * set, the untracked contents of untracked directories are also
+ * returned in `entries[]`.
+ */
DIR_KEEP_UNTRACKED_CONTENTS = 1<<7,
+
+ /**
+ * Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is
+ * set, returns ignored files and directories that match an exclude
+ * pattern. If a directory matches an exclude pattern, then the
+ * directory is returned and the contained paths are not. A directory
+ * that does not match an exclude pattern will not be returned even if
+ * all of its contents are ignored. In this case, the contents are
+ * returned as individual entries.
+ *
+ * If this is set, files and directories that explicitly match an ignore
+ * pattern are reported. Implicitly ignored directories (directories that
+ * do not match an ignore pattern, but whose contents are all ignored)
+ * are not reported, instead all of the contents are reported.
+ */
DIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8,
+
DIR_SKIP_NESTED_GIT = 1<<9
} flags;
+
+ /* An array of `struct dir_entry`, each element of which describes a path. */
struct dir_entry **entries;
+
+ /**
+ * used for ignored paths with the `DIR_SHOW_IGNORED_TOO` and
+ * `DIR_COLLECT_IGNORED` flags.
+ */
struct dir_entry **ignored;
- /* Exclude info */
+ /**
+ * The name of the file to be read in each directory for excluded files
+ * (typically `.gitignore`).
+ */
const char *exclude_per_dir;
/*
@@ -236,6 +373,7 @@
UNDECIDED = -1,
NOT_MATCHED = 0,
MATCHED = 1,
+ MATCHED_RECURSIVE = 2,
};
/*
@@ -271,6 +409,13 @@
struct index_state *istate,
const char *name, int *dtype);
+int pl_hashmap_cmp(const void *unused_cmp_data,
+ const struct hashmap_entry *a,
+ const struct hashmap_entry *b,
+ const void *key);
+int hashmap_contains_parent(struct hashmap *map,
+ const char *path,
+ struct strbuf *buffer);
struct pattern_list *add_pattern_list(struct dir_struct *dir,
int group_type, const char *src);
int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen,
diff --git a/environment.c b/environment.c
index 6f0be4b..e72a02d 100644
--- a/environment.c
+++ b/environment.c
@@ -67,6 +67,7 @@
char *notes_ref_name;
int grafts_replace_parents = 1;
int core_apply_sparse_checkout;
+int core_sparse_checkout_cone;
int merge_log_config = -1;
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
unsigned long pack_size_limit_cfg;
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 52659bb..10fd30a 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -177,7 +177,9 @@
} else {
my $fh = undef;
open($fh, '-|', @_) or die;
- return <$fh>;
+ my @out = <$fh>;
+ close $fh || die "Cannot close @_ ($!)";
+ return @out;
}
}
@@ -224,7 +226,7 @@
sub get_empty_tree {
return $empty_tree if defined $empty_tree;
- $empty_tree = run_cmd_pipe(qw(git hash-object -t tree /dev/null));
+ ($empty_tree) = run_cmd_pipe(qw(git hash-object -t tree /dev/null));
chomp $empty_tree;
return $empty_tree;
}
@@ -1127,7 +1129,7 @@
EOF2
close $fh;
- chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
+ chomp(my ($editor) = run_cmd_pipe(qw(git var GIT_EDITOR)));
system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
if ($? != 0) {
diff --git a/git-p4.py b/git-p4.py
index 60c73b6..0b3a07c 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -1257,9 +1257,15 @@
pointerFile = re.sub(r'Git LFS pointer for.*\n\n', '', pointerFile)
oid = re.search(r'^oid \w+:(\w+)', pointerFile, re.MULTILINE).group(1)
+ # if someone use external lfs.storage ( not in local repo git )
+ lfs_path = gitConfig('lfs.storage')
+ if not lfs_path:
+ lfs_path = 'lfs'
+ if not os.path.isabs(lfs_path):
+ lfs_path = os.path.join(os.getcwd(), '.git', lfs_path)
localLargeFile = os.path.join(
- os.getcwd(),
- '.git', 'lfs', 'objects', oid[:2], oid[2:4],
+ lfs_path,
+ 'objects', oid[:2], oid[2:4],
oid,
)
# LFS Spec states that pointer files should not have the executable bit set.
diff --git a/git.c b/git.c
index ce6ab0e..7be7ad3 100644
--- a/git.c
+++ b/git.c
@@ -572,6 +572,7 @@
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show-index", cmd_show_index },
{ "show-ref", cmd_show_ref, RUN_SETUP },
+ { "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
/*
* NEEDSWORK: Until the builtin stash is thoroughly robust and no
diff --git a/graph.c b/graph.c
index e3fd0ea..66ae18a 100644
--- a/graph.c
+++ b/graph.c
@@ -34,6 +34,7 @@
* handle directly. It is assumed that this is the same file handle as the
* file specified by the graph diff options. This is necessary so that
* graph_show_strbuf can be called even with a NULL graph.
+ * If a NULL graph is supplied, the strbuf is printed as-is.
*/
static void graph_show_strbuf(struct git_graph *graph,
FILE *file,
@@ -218,7 +219,7 @@
int merge_layout;
/*
* The number of columns added to the graph by the current commit. For
- * 2-way and octopus merges, this is is usually one less than the
+ * 2-way and octopus merges, this is usually one less than the
* number of parents:
*
* | | | | | \
diff --git a/graph.h b/graph.h
index af62339..8313e29 100644
--- a/graph.h
+++ b/graph.h
@@ -2,6 +2,103 @@
#define GRAPH_H
#include "diff.h"
+/**
+ * The graph API is used to draw a text-based representation of the commit
+ * history. The API generates the graph in a line-by-line fashion.
+ *
+ * Calling sequence
+ * ----------------
+ *
+ * - Create a `struct git_graph` by calling `graph_init()`. When using the
+ * revision walking API, this is done automatically by `setup_revisions()` if
+ * the '--graph' option is supplied.
+ *
+ * - Use the revision walking API to walk through a group of contiguous commits.
+ * The `get_revision()` function automatically calls `graph_update()` each time
+ * it is invoked.
+ *
+ * - For each commit, call `graph_next_line()` repeatedly, until
+ * `graph_is_commit_finished()` returns non-zero. Each call to
+ * `graph_next_line()` will output a single line of the graph. The resulting
+ * lines will not contain any newlines. `graph_next_line()` returns 1 if the
+ * resulting line contains the current commit, or 0 if this is merely a line
+ * needed to adjust the graph before or after the current commit. This return
+ * value can be used to determine where to print the commit summary information
+ * alongside the graph output.
+ *
+ * Limitations
+ * -----------
+ * - Check the graph_update() function for its limitations.
+ *
+ * - The graph API does not currently support reverse commit ordering. In
+ * order to implement reverse ordering, the graphing API needs an
+ * (efficient) mechanism to find the children of a commit.
+ *
+ * Sample usage
+ * ------------
+ *
+ * ------------
+ * struct commit *commit;
+ * struct git_graph *graph = graph_init(opts);
+ *
+ * while ((commit = get_revision(opts)) != NULL) {
+ * while (!graph_is_commit_finished(graph))
+ * {
+ * struct strbuf sb;
+ * int is_commit_line;
+ *
+ * strbuf_init(&sb, 0);
+ * is_commit_line = graph_next_line(graph, &sb);
+ * fputs(sb.buf, stdout);
+ *
+ * if (is_commit_line)
+ * log_tree_commit(opts, commit);
+ * else
+ * putchar(opts->diffopt.line_termination);
+ * }
+ * }
+ * ------------
+ * Sample output
+ * -------------
+ *
+ * The following is an example of the output from the graph API. This output does
+ * not include any commit summary information--callers are responsible for
+ * outputting that information, if desired.
+ * ------------
+ * *
+ * *
+ * *
+ * |\
+ * * |
+ * | | *
+ * | \ \
+ * | \ \
+ * *-. \ \
+ * |\ \ \ \
+ * | | * | |
+ * | | | | | *
+ * | | | | | *
+ * | | | | | *
+ * | | | | | |\
+ * | | | | | | *
+ * | * | | | | |
+ * | | | | | * \
+ * | | | | | |\ |
+ * | | | | * | | |
+ * | | | | * | | |
+ * * | | | | | | |
+ * | |/ / / / / /
+ * |/| / / / / /
+ * * | | | | | |
+ * |/ / / / / /
+ * * | | | | |
+ * | | | | | *
+ * | | | | |/
+ * | | | | *
+ * ------------
+ *
+ */
+
/* A graph is a pointer to this opaque structure */
struct git_graph;
@@ -50,6 +147,21 @@
* If graph_update() is called before graph_is_commit_finished() returns 1,
* the next call to graph_next_line() will output an ellipsis ("...")
* to indicate that a portion of the graph is missing.
+ *
+ * Limitations:
+ * -----------
+ *
+ * - `graph_update()` must be called with commits in topological order. It should
+ * not be called on a commit if it has already been invoked with an ancestor of
+ * that commit, or the graph output will be incorrect.
+ *
+ * - `graph_update()` must be called on a contiguous group of commits. If
+ * `graph_update()` is called on a particular commit, it should later be called
+ * on all parents of that commit. Parents must not be skipped, or the graph
+ * output will appear incorrect.
+ *
+ * - `graph_update()` may be used on a pruned set of commits only if the parent list
+ * has been rewritten so as to include only ancestors from the pruned set.
*/
void graph_update(struct git_graph *graph, struct commit *commit);
@@ -62,6 +174,10 @@
* for this commit. If 0 is returned, graph_next_line() may still be
* called without calling graph_update(), and it will merely output
* appropriate "vertical padding" in the graph.
+ *
+ * If `graph_update()` is called before all lines for the current commit have
+ * been printed, the next call to `graph_next_line()` will output an ellipsis,
+ * to indicate that a portion of the graph was omitted.
*/
int graph_is_commit_finished(struct git_graph const *graph);
@@ -112,6 +228,7 @@
/*
* If the graph is non-NULL, print the rest of the history graph for this
* commit to stdout. Does not print a terminating newline on the last line.
+ * Returns 1 if output was printed, and 0 if no output was necessary.
*/
int graph_show_remainder(struct git_graph *graph);
@@ -121,6 +238,10 @@
* This is similar to graph_show_strbuf(), but it always prints the
* remainder of the graph.
*
+ * It is better than directly calling `graph_show_strbuf()` followed by
+ * `graph_show_remainder()` since it properly handles buffers that do not end in
+ * a terminating newline.
+ *
* If the strbuf ends with a newline, the output printed by
* graph_show_commit_msg() will end with a newline. If the strbuf is
* missing a terminating newline (including if it is empty), the output
diff --git a/ll-merge.h b/ll-merge.h
index e78973d..aceb1b2 100644
--- a/ll-merge.h
+++ b/ll-merge.h
@@ -7,16 +7,87 @@
#include "xdiff/xdiff.h"
+/**
+ *
+ * Calling sequence:
+ * ----------------
+ *
+ * - Prepare a `struct ll_merge_options` to record options.
+ * If you have no special requests, skip this and pass `NULL`
+ * as the `opts` parameter to use the default options.
+ *
+ * - Allocate an mmbuffer_t variable for the result.
+ *
+ * - Allocate and fill variables with the file's original content
+ * and two modified versions (using `read_mmfile`, for example).
+ *
+ * - Call `ll_merge()`.
+ *
+ * - Read the merged content from `result_buf.ptr` and `result_buf.size`.
+ *
+ * - Release buffers when finished. A simple
+ * `free(ancestor.ptr); free(ours.ptr); free(theirs.ptr);
+ * free(result_buf.ptr);` will do.
+ *
+ * If the modifications do not merge cleanly, `ll_merge` will return a
+ * nonzero value and `result_buf` will generally include a description of
+ * the conflict bracketed by markers such as the traditional `<<<<<<<`
+ * and `>>>>>>>`.
+ *
+ * The `ancestor_label`, `our_label`, and `their_label` parameters are
+ * used to label the different sides of a conflict if the merge driver
+ * supports this.
+ */
+
+
struct index_state;
+/**
+ * This describes the set of options the calling program wants to affect
+ * the operation of a low-level (single file) merge.
+ */
struct ll_merge_options {
+
+ /**
+ * Behave as though this were part of a merge between common ancestors in
+ * a recursive merge (merges of binary files may need to be handled
+ * differently in such cases, for example). If a helper program is
+ * specified by the `[merge "<driver>"] recursive` configuration, it will
+ * be used.
+ */
unsigned virtual_ancestor : 1;
- unsigned variant : 2; /* favor ours, favor theirs, or union merge */
+
+ /**
+ * Resolve local conflicts automatically in favor of one side or the other
+ * (as in 'git merge-file' `--ours`/`--theirs`/`--union`). Can be `0`,
+ * `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`,
+ * or `XDL_MERGE_FAVOR_UNION`.
+ */
+ unsigned variant : 2;
+
+ /**
+ * Resmudge and clean the "base", "theirs" and "ours" files before merging.
+ * Use this when the merge is likely to have overlapped with a change in
+ * smudge/clean or end-of-line normalization rules.
+ */
unsigned renormalize : 1;
+
+ /**
+ * Increase the length of conflict markers so that nested conflicts
+ * can be differentiated.
+ */
unsigned extra_marker_size;
+
+ /* Extra xpparam_t flags as defined in xdiff/xdiff.h. */
long xdl_opts;
};
+/**
+ * Perform a three-way single-file merge in core. This is a thin wrapper
+ * around `xdl_merge` that takes the path and any merge backend specified in
+ * `.gitattributes` or `.git/info/attributes` into account.
+ * Returns 0 for a clean merge.
+ */
int ll_merge(mmbuffer_t *result_buf,
const char *path,
mmfile_t *ancestor, const char *ancestor_label,
diff --git a/notes.c b/notes.c
index 03e7d0c..0c79964 100644
--- a/notes.c
+++ b/notes.c
@@ -1043,6 +1043,39 @@
void init_display_notes(struct display_notes_opt *opt)
{
+ memset(opt, 0, sizeof(*opt));
+ opt->use_default_notes = -1;
+}
+
+void enable_default_display_notes(struct display_notes_opt *opt, int *show_notes)
+{
+ opt->use_default_notes = 1;
+ *show_notes = 1;
+}
+
+void enable_ref_display_notes(struct display_notes_opt *opt, int *show_notes,
+ const char *ref) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addstr(&buf, ref);
+ expand_notes_ref(&buf);
+ string_list_append(&opt->extra_notes_refs,
+ strbuf_detach(&buf, NULL));
+ *show_notes = 1;
+}
+
+void disable_display_notes(struct display_notes_opt *opt, int *show_notes)
+{
+ opt->use_default_notes = -1;
+ /* we have been strdup'ing ourselves, so trick
+ * string_list into free()ing strings */
+ opt->extra_notes_refs.strdup_strings = 1;
+ string_list_clear(&opt->extra_notes_refs, 0);
+ opt->extra_notes_refs.strdup_strings = 0;
+ *show_notes = 0;
+}
+
+void load_display_notes(struct display_notes_opt *opt)
+{
char *display_ref_env;
int load_config_refs = 0;
display_notes_refs.strdup_strings = 1;
diff --git a/notes.h b/notes.h
index 76337f2..c1682c3 100644
--- a/notes.h
+++ b/notes.h
@@ -261,6 +261,26 @@
};
/*
+ * Initialize a display_notes_opt to its default value.
+ */
+void init_display_notes(struct display_notes_opt *opt);
+
+/*
+ * This family of functions enables or disables the display of notes. In
+ * particular, 'enable_default_display_notes' will display the default notes,
+ * 'enable_ref_display_notes' will display the notes ref 'ref' and
+ * 'disable_display_notes' will disable notes, including those added by previous
+ * invocations of the 'enable_*_display_notes' functions.
+ *
+ * 'show_notes' is a pointer to a boolean which will be set to 1 if notes are
+ * displayed, else 0. It must not be NULL.
+ */
+void enable_default_display_notes(struct display_notes_opt *opt, int *show_notes);
+void enable_ref_display_notes(struct display_notes_opt *opt, int *show_notes,
+ const char *ref);
+void disable_display_notes(struct display_notes_opt *opt, int *show_notes);
+
+/*
* Load the notes machinery for displaying several notes trees.
*
* If 'opt' is not NULL, then it specifies additional settings for the
@@ -272,16 +292,16 @@
* - extra_notes_refs may contain a list of globs (in the same style
* as notes.displayRef) where notes should be loaded from.
*/
-void init_display_notes(struct display_notes_opt *opt);
+void load_display_notes(struct display_notes_opt *opt);
/*
* Append notes for the given 'object_sha1' from all trees set up by
- * init_display_notes() to 'sb'.
+ * load_display_notes() to 'sb'.
*
* If 'raw' is false the note will be indented by 4 places and
* a 'Notes (refname):' header added.
*
- * You *must* call init_display_notes() before using this function.
+ * You *must* call load_display_notes() before using this function.
*/
void format_display_notes(const struct object_id *object_oid,
struct strbuf *sb, const char *output_encoding, int raw);
diff --git a/object-store.h b/object-store.h
index 7f7b3cd..55ee639 100644
--- a/object-store.h
+++ b/object-store.h
@@ -60,6 +60,7 @@
void odb_clear_loose_cache(struct object_directory *odb);
struct packed_git {
+ struct hashmap_entry packmap_ent;
struct packed_git *next;
struct list_head mru;
struct pack_window *windows;
@@ -88,6 +89,20 @@
struct multi_pack_index;
+static inline int pack_map_entry_cmp(const void *unused_cmp_data,
+ const struct hashmap_entry *entry,
+ const struct hashmap_entry *entry2,
+ const void *keydata)
+{
+ const char *key = keydata;
+ const struct packed_git *pg1, *pg2;
+
+ pg1 = container_of(entry, const struct packed_git, packmap_ent);
+ pg2 = container_of(entry2, const struct packed_git, packmap_ent);
+
+ return strcmp(pg1->pack_name, key ? key : pg2->pack_name);
+}
+
struct raw_object_store {
/*
* Set of all object directories; the main directory is first (and
@@ -132,6 +147,12 @@
struct list_head packed_git_mru;
/*
+ * A map of packfiles to packed_git structs for tracking which
+ * packs have been loaded already.
+ */
+ struct hashmap pack_map;
+
+ /*
* A fast, rough count of the number of objects in the repository.
* These two fields are not meant for direct access. Use
* approximate_object_count() instead.
diff --git a/object.c b/object.c
index 3b8b8c5..142ef69 100644
--- a/object.c
+++ b/object.c
@@ -479,6 +479,7 @@
memset(o, 0, sizeof(*o));
INIT_LIST_HEAD(&o->packed_git_mru);
+ hashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);
return o;
}
@@ -518,6 +519,8 @@
INIT_LIST_HEAD(&o->packed_git_mru);
close_object_store(o);
o->packed_git = NULL;
+
+ hashmap_free(&o->pack_map);
}
void parsed_object_pool_clear(struct parsed_object_pool *o)
diff --git a/packfile.c b/packfile.c
index 355066d..f0dc63e 100644
--- a/packfile.c
+++ b/packfile.c
@@ -757,6 +757,9 @@
pack->next = r->objects->packed_git;
r->objects->packed_git = pack;
+
+ hashmap_entry_init(&pack->packmap_ent, strhash(pack->pack_name));
+ hashmap_add(&r->objects->pack_map, &pack->packmap_ent);
}
void (*report_garbage)(unsigned seen_bits, const char *path);
@@ -856,20 +859,18 @@
if (strip_suffix_mem(full_name, &base_len, ".idx") &&
!(data->m && midx_contains_pack(data->m, file_name))) {
- /* Don't reopen a pack we already have. */
- for (p = data->r->objects->packed_git; p; p = p->next) {
- size_t len;
- if (strip_suffix(p->pack_name, ".pack", &len) &&
- len == base_len &&
- !memcmp(p->pack_name, full_name, len))
- break;
- }
+ struct hashmap_entry hent;
+ char *pack_name = xstrfmt("%.*s.pack", (int)base_len, full_name);
+ unsigned int hash = strhash(pack_name);
+ hashmap_entry_init(&hent, hash);
- if (!p) {
+ /* Don't reopen a pack we already have. */
+ if (!hashmap_get(&data->r->objects->pack_map, &hent, pack_name)) {
p = add_packed_git(full_name, full_name_len, data->local);
if (p)
install_packed_git(data->r, p);
}
+ free(pack_name);
}
if (!report_garbage)
diff --git a/parse-options.h b/parse-options.h
index c6cc01e..fdc0c1c 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -1,6 +1,10 @@
#ifndef PARSE_OPTIONS_H
#define PARSE_OPTIONS_H
+/**
+ * Refer to Documentation/technical/api-parse-options.txt for the API doc.
+ */
+
enum parse_opt_type {
/* special types */
OPTION_END,
diff --git a/pathspec.h b/pathspec.h
index a27dc81..454ce36 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -22,6 +22,11 @@
#define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */
+/**
+ * See glossary-context.txt for the syntax of pathspec.
+ * In memory, a pathspec set is represented by "struct pathspec" and is
+ * prepared by parse_pathspec().
+ */
struct pathspec {
int nr;
unsigned int has_wildcard:1;
@@ -73,12 +78,39 @@
*/
#define PATHSPEC_LITERAL_PATH (1<<6)
-/*
+/**
* Given command line arguments and a prefix, convert the input to
* pathspec. die() if any magic in magic_mask is used.
*
* Any arguments used are copied. It is safe for the caller to modify
* or free 'prefix' and 'args' after calling this function.
+ *
+ * - magic_mask specifies what features that are NOT supported by the following
+ * code. If a user attempts to use such a feature, parse_pathspec() can reject
+ * it early.
+ *
+ * - flags specifies other things that the caller wants parse_pathspec to
+ * perform.
+ *
+ * - prefix and args come from cmd_* functions
+ *
+ * parse_pathspec() helps catch unsupported features and reject them politely.
+ * At a lower level, different pathspec-related functions may not support the
+ * same set of features. Such pathspec-sensitive functions are guarded with
+ * GUARD_PATHSPEC(), which will die in an unfriendly way when an unsupported
+ * feature is requested.
+ *
+ * The command designers are supposed to make sure that GUARD_PATHSPEC() never
+ * dies. They have to make sure all unsupported features are caught by
+ * parse_pathspec(), not by GUARD_PATHSPEC. grepping GUARD_PATHSPEC() should
+ * give the designers all pathspec-sensitive codepaths and what features they
+ * support.
+ *
+ * A similar process is applied when a new pathspec magic is added. The designer
+ * lifts the GUARD_PATHSPEC restriction in the functions that support the new
+ * magic. At the same time (s)he has to make sure this new feature will be
+ * caught at parse_pathspec() in commands that cannot handle the new magic in
+ * some cases. grepping parse_pathspec() should help.
*/
void parse_pathspec(struct pathspec *pathspec,
unsigned magic_mask,
@@ -95,6 +127,7 @@
const char *prefix,
const char *file,
int nul_term_line);
+
void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
void clear_pathspec(struct pathspec *);
diff --git a/range-diff.c b/range-diff.c
index f56b401..f745567 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -41,7 +41,7 @@
* as struct object_id (will need to be free()d).
*/
static int read_patches(const char *range, struct string_list *list,
- struct argv_array *other_arg)
+ const struct argv_array *other_arg)
{
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;
@@ -506,8 +506,8 @@
int show_range_diff(const char *range1, const char *range2,
int creation_factor, int dual_color,
- struct diff_options *diffopt,
- struct argv_array *other_arg)
+ const struct diff_options *diffopt,
+ const struct argv_array *other_arg)
{
int res = 0;
diff --git a/range-diff.h b/range-diff.h
index c57ec7d..e11976d 100644
--- a/range-diff.h
+++ b/range-diff.h
@@ -13,7 +13,7 @@
*/
int show_range_diff(const char *range1, const char *range2,
int creation_factor, int dual_color,
- struct diff_options *diffopt,
- struct argv_array *other_arg);
+ const struct diff_options *diffopt,
+ const struct argv_array *other_arg);
#endif
diff --git a/refs.h b/refs.h
index 730d05a..545029c 100644
--- a/refs.h
+++ b/refs.h
@@ -310,19 +310,35 @@
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_fullref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data,
unsigned int broken);
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
unsigned int broken);
+
+/**
+ * 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 for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data);
@@ -791,6 +807,41 @@
int ref_storage_backend_exists(const char *name);
struct ref_store *get_main_ref_store(struct repository *r);
+
+/**
+ * Submodules
+ * ----------
+ *
+ * If you want to iterate the refs of a submodule you first need to add the
+ * submodules object database. You can do this by a code-snippet like
+ * this:
+ *
+ * const char *path = "path/to/submodule"
+ * if (add_submodule_odb(path))
+ * die("Error submodule '%s' not populated.", path);
+ *
+ * `add_submodule_odb()` will return zero on success. If you
+ * do not do this you will get an error for each ref that it does not point
+ * to a valid object.
+ *
+ * Note: As a side-effect of this you cannot safely assume that all
+ * objects you lookup are available in superproject. All submodule objects
+ * will be available the same way as the superprojects objects.
+ *
+ * Example:
+ * --------
+ *
+ * ----
+ * static int handle_remote_ref(const char *refname,
+ * const unsigned char *sha1, int flags, void *cb_data)
+ * {
+ * struct strbuf *output = cb_data;
+ * strbuf_addf(output, "%s\n", refname);
+ * return 0;
+ * }
+ *
+ */
+
/*
* Return the ref_store instance for the specified submodule. For the
* main repository, use submodule==NULL; such a call cannot fail. For
diff --git a/refspec.h b/refspec.h
index 9b6e64a..3f2bd4a 100644
--- a/refspec.h
+++ b/refspec.h
@@ -20,6 +20,22 @@
#define REFSPEC_INIT_FETCH { .fetch = REFSPEC_FETCH }
#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH }
+/**
+ * A struct refspec holds the parsed interpretation of a refspec. If it will
+ * force updates (starts with a '+'), force is true. If it is a pattern
+ * (sides end with '*') pattern is true. src and dest are the two sides
+ * (including '*' characters if present); if there is only one side, it is src,
+ * and dst is NULL; if sides exist but are empty (i.e., the refspec either
+ * starts or ends with ':'), the corresponding side is "".
+ *
+ * An array of strings can be parsed into an array of struct refspecs using
+ * parse_fetch_refspec() or parse_push_refspec().
+ *
+ * remote_find_tracking(), given a remote and a struct refspec with either src
+ * or dst filled out, will fill out the other such that the result is in the
+ * "fetch" specification for the remote (note that this evaluates patterns and
+ * returns a single result).
+ */
struct refspec {
struct refspec_item *items;
int alloc;
diff --git a/remote.h b/remote.h
index 0e1d2b2..b134cc2 100644
--- a/remote.h
+++ b/remote.h
@@ -6,6 +6,14 @@
#include "hashmap.h"
#include "refspec.h"
+/**
+ * The API gives access to the configuration related to remotes. It handles
+ * all three configuration mechanisms historically and currently used by Git,
+ * and presents the information in a uniform fashion. Note that the code also
+ * handles plain URLs without any configuration, giving them just the default
+ * information.
+ */
+
enum {
REMOTE_UNCONFIGURED = 0,
REMOTE_CONFIG,
@@ -16,16 +24,22 @@
struct remote {
struct hashmap_entry ent;
+ /* The user's nickname for the remote */
const char *name;
+
int origin, configured_in_repo;
const 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;
+ /* An array of all of the pushurl_nr push URLs configured for the remote */
const char **pushurl;
+
int pushurl_nr;
int pushurl_alloc;
@@ -34,32 +48,47 @@
struct refspec fetch;
/*
+ * The setting for whether to fetch tags (as a separate rule from the
+ * configured refspecs);
* -1 to never fetch tags
* 0 to auto-follow tags on heuristic (default)
* 1 to always auto-follow tags
* 2 to always fetch tags
*/
int fetch_tags;
+
int skip_default_update;
int mirror;
int prune;
int prune_tags;
+ /**
+ * The configured helper programs to run on the remote side, for
+ * Git-native protocols.
+ */
const char *receivepack;
const char *uploadpack;
- /*
- * for curl remotes only
- */
+ /* The proxy to use for curl (http, https, ftp, etc.) URLs. */
char *http_proxy;
+
+ /* The method used for authenticating against `http_proxy`. */
char *http_proxy_authmethod;
};
+/**
+ * struct remotes can be found by name with remote_get().
+ * remote_get(NULL) will return the default remote, given the current branch
+ * and configuration.
+ */
struct remote *remote_get(const char *name);
+
struct remote *pushremote_get(const char *name);
int remote_is_configured(struct remote *remote, int in_repo);
typedef int each_remote_fn(struct remote *remote, void *priv);
+
+/* iterate through struct remotes */
int for_each_remote(each_remote_fn fn, void *priv);
int remote_has_url(struct remote *remote, const char *url);
@@ -194,16 +223,36 @@
*/
int remote_find_tracking(struct remote *remote, struct refspec_item *refspec);
+/**
+ * struct branch holds the configuration for a branch. It can be looked up with
+ * branch_get(name) for "refs/heads/{name}", or with branch_get(NULL) for HEAD.
+ */
struct branch {
+
+ /* The short name of the branch. */
const char *name;
+
+ /* The full path for the branch ref. */
const char *refname;
+ /* The name of the remote listed in the configuration. */
const char *remote_name;
+
const char *pushremote_name;
+ /* An array of the "merge" lines in the configuration. */
const char **merge_name;
+
+ /**
+ * An array of the struct refspecs used for the merge lines. That is,
+ * merge[i]->dst is a local tracking ref which should be merged into this
+ * branch by default.
+ */
struct refspec_item **merge;
+
+ /* The number of merge configurations */
int merge_nr;
+
int merge_alloc;
const char *push_tracking_ref;
@@ -215,7 +264,9 @@
const char *remote_ref_for_branch(struct branch *branch, int for_push,
int *explicit);
+/* returns true if the given branch has merge configuration given. */
int branch_has_merge_config(struct branch *branch);
+
int branch_merge_matches(struct branch *, int n, const char *);
/**
diff --git a/revision.c b/revision.c
index 39a25e7..8136929 100644
--- a/revision.c
+++ b/revision.c
@@ -1668,7 +1668,7 @@
revs->diffopt.prefix_length = strlen(prefix);
}
- revs->notes_opt.use_default_notes = -1;
+ init_display_notes(&revs->notes_opt);
}
static void add_pending_commit_list(struct rev_info *revs,
@@ -2203,9 +2203,8 @@
die("'%s': not a non-negative integer", arg);
revs->expand_tabs_in_log = val;
} else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
- revs->show_notes = 1;
+ enable_default_display_notes(&revs->notes_opt, &revs->show_notes);
revs->show_notes_given = 1;
- revs->notes_opt.use_default_notes = 1;
} else if (!strcmp(arg, "--show-signature")) {
revs->show_signature = 1;
} else if (!strcmp(arg, "--no-show-signature")) {
@@ -2220,25 +2219,14 @@
revs->track_first_time = 1;
} else if (skip_prefix(arg, "--show-notes=", &optarg) ||
skip_prefix(arg, "--notes=", &optarg)) {
- struct strbuf buf = STRBUF_INIT;
- revs->show_notes = 1;
- revs->show_notes_given = 1;
if (starts_with(arg, "--show-notes=") &&
revs->notes_opt.use_default_notes < 0)
revs->notes_opt.use_default_notes = 1;
- strbuf_addstr(&buf, optarg);
- expand_notes_ref(&buf);
- string_list_append(&revs->notes_opt.extra_notes_refs,
- strbuf_detach(&buf, NULL));
- } else if (!strcmp(arg, "--no-notes")) {
- revs->show_notes = 0;
+ enable_ref_display_notes(&revs->notes_opt, &revs->show_notes, optarg);
revs->show_notes_given = 1;
- revs->notes_opt.use_default_notes = -1;
- /* we have been strdup'ing ourselves, so trick
- * string_list into free()ing strings */
- revs->notes_opt.extra_notes_refs.strdup_strings = 1;
- string_list_clear(&revs->notes_opt.extra_notes_refs, 0);
- revs->notes_opt.extra_notes_refs.strdup_strings = 0;
+ } else if (!strcmp(arg, "--no-notes")) {
+ disable_display_notes(&revs->notes_opt, &revs->show_notes);
+ revs->show_notes_given = 1;
} else if (!strcmp(arg, "--standard-notes")) {
revs->show_notes_given = 1;
revs->notes_opt.use_default_notes = 1;
diff --git a/revision.h b/revision.h
index addd694..475f048 100644
--- a/revision.h
+++ b/revision.h
@@ -9,6 +9,19 @@
#include "diff.h"
#include "commit-slab-decl.h"
+/**
+ * The revision walking API offers functions to build a list of revisions
+ * and then iterate over that list.
+ *
+ * Calling sequence
+ * ----------------
+ *
+ * The walking API has a given calling sequence: first you need to initialize
+ * a rev_info structure, then add revisions to control what kind of revision
+ * list do you want to get, finally you can iterate over the revision list.
+ *
+ */
+
/* Remember to update object flag allocation in object.h */
#define SEEN (1u<<0)
#define UNINTERESTING (1u<<1)
@@ -177,10 +190,10 @@
always_show_header:1;
/* Format info */
+ int show_notes;
unsigned int shown_one:1,
shown_dashes:1,
show_merge:1,
- show_notes:1,
show_notes_given:1,
show_signature:1,
pretty_given:1,
@@ -306,11 +319,29 @@
#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
#endif
+
+/**
+ * Initialize a rev_info structure with default values. The third parameter may
+ * be NULL or can be prefix path, and then the `.prefix` variable will be set
+ * to it. This is typically the first function you want to call when you want
+ * to deal with a revision list. After calling this function, you are free to
+ * customize options, like set `.ignore_merges` to 0 if you don't want to
+ * ignore merges, and so on.
+ */
void repo_init_revisions(struct repository *r,
struct rev_info *revs,
const char *prefix);
+
+/**
+ * Parse revision information, filling in the `rev_info` structure, and
+ * removing the used arguments from the argument list. Returns the number
+ * of arguments left that weren't recognized, which are also moved to the
+ * head of the argument list. The last parameter is used in case no
+ * parameter given by the first two arguments.
+ */
int setup_revisions(int argc, const char **argv, struct rev_info *revs,
struct setup_revision_opt *);
+
void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
const struct option *options,
const char * const usagestr[]);
@@ -319,9 +350,26 @@
int handle_revision_arg(const char *arg, struct rev_info *revs,
int flags, unsigned revarg_opt);
+/**
+ * Reset the flags used by the revision walking api. You can use this to do
+ * multiple sequential revision walks.
+ */
void reset_revision_walk(void);
+
+/**
+ * Prepares the rev_info structure for a walk. You should check if it returns
+ * any error (non-zero return code) and if it does not, you can start using
+ * get_revision() to do the iteration.
+ */
int prepare_revision_walk(struct rev_info *revs);
+
+/**
+ * Takes a pointer to a `rev_info` structure and iterates over it, returning a
+ * `struct commit *` each time you call it. The end of the revision list is
+ * indicated by returning a NULL pointer.
+ */
struct commit *get_revision(struct rev_info *revs);
+
const char *get_revision_mark(const struct rev_info *revs,
const struct commit *commit);
void put_revision_mark(const struct rev_info *revs,
@@ -333,8 +381,19 @@
void show_object_with_name(FILE *, struct object *, const char *);
+/**
+ * This function can be used if you want to add commit objects as revision
+ * information. You can use the `UNINTERESTING` object flag to indicate if
+ * you want to include or exclude the given commit (and commits reachable
+ * from the given commit) from the revision list.
+ *
+ * NOTE: If you have the commits as a string list then you probably want to
+ * use setup_revisions(), instead of parsing each string and using this
+ * function.
+ */
void add_pending_object(struct rev_info *revs,
struct object *obj, const char *name);
+
void add_pending_oid(struct rev_info *revs,
const char *name, const struct object_id *oid,
unsigned int flags);
diff --git a/run-command.h b/run-command.h
index f769e03..592d9dc 100644
--- a/run-command.h
+++ b/run-command.h
@@ -5,8 +5,60 @@
#include "argv-array.h"
+/**
+ * The run-command API offers a versatile tool to run sub-processes with
+ * redirected input and output as well as with a modified environment
+ * and an alternate current directory.
+ *
+ * A similar API offers the capability to run a function asynchronously,
+ * which is primarily used to capture the output that the function
+ * produces in the caller in order to process it.
+ */
+
+
+/**
+ * This describes the arguments, redirections, and environment of a
+ * command to run in a sub-process.
+ *
+ * The caller:
+ *
+ * 1. allocates and clears (using child_process_init() or
+ * CHILD_PROCESS_INIT) a struct child_process variable;
+ * 2. initializes the members;
+ * 3. calls start_command();
+ * 4. processes the data;
+ * 5. closes file descriptors (if necessary; see below);
+ * 6. calls finish_command().
+ *
+ * Special forms of redirection are available by setting these members
+ * to 1:
+ *
+ * .no_stdin, .no_stdout, .no_stderr: The respective channel is
+ * redirected to /dev/null.
+ *
+ * .stdout_to_stderr: stdout of the child is redirected to its
+ * stderr. This happens after stderr is itself redirected.
+ * So stdout will follow stderr to wherever it is
+ * redirected.
+ */
struct child_process {
+
+ /**
+ * The .argv member is set up as an array of string pointers (NULL
+ * terminated), of which .argv[0] is the program name to run (usually
+ * without a path). If the command to run is a git command, set argv[0] to
+ * the command name without the 'git-' prefix and set .git_cmd = 1.
+ *
+ * Note that the ownership of the memory pointed to by .argv stays with the
+ * caller, but it should survive until `finish_command` completes. If the
+ * .argv member is NULL, `start_command` will point it at the .args
+ * `argv_array` (so you may use one or the other, but you must use exactly
+ * one). The memory in .args will be cleaned up automatically during
+ * `finish_command` (or during `start_command` when it is unsuccessful).
+ *
+ */
const char **argv;
+
struct argv_array args;
struct argv_array env_array;
pid_t pid;
@@ -18,8 +70,8 @@
/*
* Using .in, .out, .err:
- * - Specify 0 for no redirections (child inherits stdin, stdout,
- * stderr from parent).
+ * - Specify 0 for no redirections. No new file descriptor is allocated.
+ * (child inherits stdin, stdout, stderr from parent).
* - Specify -1 to have a pipe allocated as follows:
* .in: returns the writable pipe end; parent writes to it,
* the readable pipe end becomes child's stdin
@@ -37,13 +89,43 @@
int in;
int out;
int err;
+
+ /**
+ * To specify a new initial working directory for the sub-process,
+ * specify it in the .dir member.
+ */
const char *dir;
+
+ /**
+ * To modify the environment of the sub-process, specify an array of
+ * string pointers (NULL terminated) in .env:
+ *
+ * - If the string is of the form "VAR=value", i.e. it contains '='
+ * the variable is added to the child process's environment.
+ *
+ * - If the string does not contain '=', it names an environment
+ * variable that will be removed from the child process's environment.
+ *
+ * If the .env member is NULL, `start_command` will point it at the
+ * .env_array `argv_array` (so you may use one or the other, but not both).
+ * The memory in .env_array will be cleaned up automatically during
+ * `finish_command` (or during `start_command` when it is unsuccessful).
+ */
const char *const *env;
+
unsigned no_stdin:1;
unsigned no_stdout:1;
unsigned no_stderr:1;
- unsigned git_cmd:1; /* if this is to be git sub-command */
+ unsigned git_cmd:1; /* if this is to be git sub-command */
+
+ /**
+ * If the program cannot be found, the functions return -1 and set
+ * errno to ENOENT. Normally, an error message is printed, but if
+ * .silent_exec_failure is set to 1, no message is printed for this
+ * special error condition.
+ */
unsigned silent_exec_failure:1;
+
unsigned stdout_to_stderr:1;
unsigned use_shell:1;
unsigned clean_on_exit:1;
@@ -53,13 +135,63 @@
};
#define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }
+
+/**
+ * The functions: child_process_init, start_command, finish_command,
+ * run_command, run_command_v_opt, run_command_v_opt_cd_env, child_process_clear
+ * do the following:
+ *
+ * - If a system call failed, errno is set and -1 is returned. A diagnostic
+ * is printed.
+ *
+ * - If the program was not found, then -1 is returned and errno is set to
+ * ENOENT; a diagnostic is printed only if .silent_exec_failure is 0.
+ *
+ * - Otherwise, the program is run. If it terminates regularly, its exit
+ * code is returned. No diagnostic is printed, even if the exit code is
+ * non-zero.
+ *
+ * - If the program terminated due to a signal, then the return value is the
+ * signal number + 128, ie. the same value that a POSIX shell's $? would
+ * report. A diagnostic is printed.
+ *
+ */
+
+/**
+ * Initialize a struct child_process variable.
+ */
void child_process_init(struct child_process *);
+
+/**
+ * Release the memory associated with the struct child_process.
+ * Most users of the run-command API don't need to call this
+ * function explicitly because `start_command` invokes it on
+ * failure and `finish_command` calls it automatically already.
+ */
void child_process_clear(struct child_process *);
+
int is_executable(const char *name);
+/**
+ * Start a sub-process. Takes a pointer to a `struct child_process`
+ * that specifies the details and returns pipe FDs (if requested).
+ * See below for details.
+ */
int start_command(struct child_process *);
+
+/**
+ * Wait for the completion of a sub-process that was started with
+ * start_command().
+ */
int finish_command(struct child_process *);
+
int finish_command_in_signal(struct child_process *);
+
+/**
+ * A convenience function that encapsulates a sequence of
+ * start_command() followed by finish_command(). Takes a pointer
+ * to a `struct child_process` that specifies the details.
+ */
int run_command(struct child_process *);
/*
@@ -68,6 +200,20 @@
* overwritten by further calls to find_hook and run_hook_*.
*/
const char *find_hook(const char *name);
+
+/**
+ * Run a hook.
+ * The first argument is a pathname to an index file, or NULL
+ * if the hook uses the default index file or no index is needed.
+ * The second argument is the name of the hook.
+ * The further arguments correspond to the hook arguments.
+ * The last argument has to be NULL to terminate the arguments list.
+ * If the hook does not exist or is not executable, the return
+ * value will be zero.
+ * If it is executable, the hook will be executed and the exit
+ * status of the hook is returned.
+ * On execution, .stdout_to_stderr and .no_stdin will be set.
+ */
LAST_ARG_MUST_BE_NULL
int run_hook_le(const char *const *env, const char *name, ...);
int run_hook_ve(const char *const *env, const char *name, va_list args);
@@ -78,6 +224,18 @@
#define RUN_SILENT_EXEC_FAILURE 8
#define RUN_USING_SHELL 16
#define RUN_CLEAN_ON_EXIT 32
+
+/**
+ * Convenience functions that encapsulate a sequence of
+ * start_command() followed by finish_command(). The argument argv
+ * specifies the program and its arguments. The argument opt is zero
+ * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,
+ * `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`
+ * that correspond to the members .no_stdin, .git_cmd,
+ * .stdout_to_stderr, .silent_exec_failure of `struct child_process`.
+ * The argument dir corresponds the member .dir. The argument env
+ * corresponds to the member .env.
+ */
int run_command_v_opt(const char **argv, int opt);
int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class);
/*
@@ -125,15 +283,84 @@
* It is expected that no synchronization and mutual exclusion between
* the caller and the feed function is necessary so that the function
* can run in a thread without interfering with the caller.
+ *
+ * The caller:
+ *
+ * 1. allocates and clears (memset(&asy, 0, sizeof(asy));) a
+ * struct async variable;
+ * 2. initializes .proc and .data;
+ * 3. calls start_async();
+ * 4. processes communicates with proc through .in and .out;
+ * 5. closes .in and .out;
+ * 6. calls finish_async().
+ *
+ * There are serious restrictions on what the asynchronous function can do
+ * because this facility is implemented by a thread in the same address
+ * space on most platforms (when pthreads is available), but by a pipe to
+ * a forked process otherwise:
+ *
+ * - It cannot change the program's state (global variables, environment,
+ * etc.) in a way that the caller notices; in other words, .in and .out
+ * are the only communication channels to the caller.
+ *
+ * - It must not change the program's state that the caller of the
+ * facility also uses.
+ *
*/
struct async {
- /*
- * proc reads from in; closes it before return
- * proc writes to out; closes it before return
- * returns 0 on success, non-zero on failure
+
+ /**
+ * The function pointer in .proc has the following signature:
+ *
+ * int proc(int in, int out, void *data);
+ *
+ * - in, out specifies a set of file descriptors to which the function
+ * must read/write the data that it needs/produces. The function
+ * *must* close these descriptors before it returns. A descriptor
+ * may be -1 if the caller did not configure a descriptor for that
+ * direction.
+ *
+ * - data is the value that the caller has specified in the .data member
+ * of struct async.
+ *
+ * - The return value of the function is 0 on success and non-zero
+ * on failure. If the function indicates failure, finish_async() will
+ * report failure as well.
+ *
*/
int (*proc)(int in, int out, void *data);
+
void *data;
+
+ /**
+ * The members .in, .out are used to provide a set of fd's for
+ * communication between the caller and the callee as follows:
+ *
+ * - Specify 0 to have no file descriptor passed. The callee will
+ * receive -1 in the corresponding argument.
+ *
+ * - Specify < 0 to have a pipe allocated; start_async() replaces
+ * with the pipe FD in the following way:
+ *
+ * .in: Returns the writable pipe end into which the caller
+ * writes; the readable end of the pipe becomes the function's
+ * in argument.
+ *
+ * .out: Returns the readable pipe end from which the caller
+ * reads; the writable end of the pipe becomes the function's
+ * out argument.
+ *
+ * The caller of start_async() must close the returned FDs after it
+ * has completed reading from/writing from them.
+ *
+ * - Specify a file descriptor > 0 to be used by the function:
+ *
+ * .in: The FD must be readable; it becomes the function's in.
+ * .out: The FD must be writable; it becomes the function's out.
+ *
+ * The specified FD is closed by start_async(), even if it fails to
+ * run the function.
+ */
int in; /* caller writes here and closes it */
int out; /* caller reads from here and closes it */
#ifdef NO_PTHREADS
@@ -146,8 +373,19 @@
int isolate_sigpipe;
};
+/**
+ * Run a function asynchronously. Takes a pointer to a `struct
+ * async` that specifies the details and returns a set of pipe FDs
+ * for communication with the function. See below for details.
+ */
int start_async(struct async *async);
+
+/**
+ * Wait for the completion of an asynchronous function that was
+ * started with start_async().
+ */
int finish_async(struct async *async);
+
int in_async(void);
int async_with_fork(void);
void check_pipe(int err);
diff --git a/sequencer.c b/sequencer.c
index 1bee26e..763ccbb 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2130,6 +2130,7 @@
static struct todo_item *append_new_todo(struct todo_list *todo_list)
{
ALLOC_GROW(todo_list->items, todo_list->nr + 1, todo_list->alloc);
+ todo_list->total_nr++;
return todo_list->items + todo_list->nr++;
}
@@ -2401,6 +2402,16 @@
sequencer_remove_state(&opts);
}
+static void todo_list_write_total_nr(struct todo_list *todo_list)
+{
+ FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
+
+ if (f) {
+ fprintf(f, "%d\n", todo_list->total_nr);
+ fclose(f);
+ }
+}
+
static int read_populate_todo(struct repository *r,
struct todo_list *todo_list,
struct replay_opts *opts)
@@ -2446,7 +2457,6 @@
if (is_rebase_i(opts)) {
struct todo_list done = TODO_LIST_INIT;
- FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
!todo_list_parse_insn_buffer(r, done.buf.buf, &done))
@@ -2458,10 +2468,7 @@
+ count_commands(todo_list);
todo_list_release(&done);
- if (f) {
- fprintf(f, "%d\n", todo_list->total_nr);
- fclose(f);
- }
+ todo_list_write_total_nr(todo_list);
}
return 0;
@@ -5176,6 +5183,7 @@
MOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);
todo_list->nr -= i;
todo_list->current = 0;
+ todo_list->done_nr += i;
if (is_fixup(peek_command(todo_list, 0)))
record_in_rewritten(base_oid, peek_command(todo_list, 0));
@@ -5255,15 +5263,21 @@
return error_errno(_("could not write '%s'"), todo_file);
}
- todo_list_release(&new_todo);
+ res = -1;
if (checkout_onto(r, opts, onto_name, &oid, orig_head))
- return -1;
+ goto cleanup;
if (require_clean_work_tree(r, "rebase", "", 1, 1))
- return -1;
+ goto cleanup;
- return sequencer_continue(r, opts);
+ todo_list_write_total_nr(&new_todo);
+ res = pick_commits(r, &new_todo, opts);
+
+cleanup:
+ todo_list_release(&new_todo);
+
+ return res;
}
struct subject2item_entry {
diff --git a/sha1-array.c b/sha1-array.c
index d922e94..3eeadfe 100644
--- a/sha1-array.c
+++ b/sha1-array.c
@@ -48,7 +48,7 @@
{
int i;
- /* No oid_array_sort() here! See the api-oid-array.txt docs! */
+ /* No oid_array_sort() here! See sha1-array.h */
for (i = 0; i < array->nr; i++) {
int ret = fn(array->oid + i, data);
diff --git a/sha1-array.h b/sha1-array.h
index 55d016c..dc1bca9 100644
--- a/sha1-array.h
+++ b/sha1-array.h
@@ -1,6 +1,52 @@
#ifndef SHA1_ARRAY_H
#define SHA1_ARRAY_H
+/**
+ * The API provides storage and manipulation of sets of object identifiers.
+ * The emphasis is on storage and processing efficiency, making them suitable
+ * for large lists. Note that the ordering of items is not preserved over some
+ * operations.
+ *
+ * Examples
+ * --------
+ * -----------------------------------------
+ * int print_callback(const struct object_id *oid,
+ * void *data)
+ * {
+ * printf("%s\n", oid_to_hex(oid));
+ * return 0; // always continue
+ * }
+ *
+ * void some_func(void)
+ * {
+ * struct sha1_array hashes = OID_ARRAY_INIT;
+ * struct object_id oid;
+ *
+ * // Read objects into our set
+ * while (read_object_from_stdin(oid.hash))
+ * oid_array_append(&hashes, &oid);
+ *
+ * // Check if some objects are in our set
+ * while (read_object_from_stdin(oid.hash)) {
+ * if (oid_array_lookup(&hashes, &oid) >= 0)
+ * printf("it's in there!\n");
+ *
+ * // Print the unique set of objects. We could also have
+ * // avoided adding duplicate objects in the first place,
+ * // but we would end up re-sorting the array repeatedly.
+ * // Instead, this will sort once and then skip duplicates
+ * // in linear time.
+ *
+ * oid_array_for_each_unique(&hashes, print_callback, NULL);
+ * }
+ */
+
+/**
+ * A single array of object IDs. This should be initialized by assignment from
+ * `OID_ARRAY_INIT`. The `oid` member contains the actual data. The `nr` member
+ * contains the number of items in the set. The `alloc` and `sorted` members
+ * are used internally, and should not be needed by API callers.
+ */
struct oid_array {
struct object_id *oid;
int nr;
@@ -10,18 +56,52 @@
#define OID_ARRAY_INIT { NULL, 0, 0, 0 }
+/**
+ * Add an item to the set. The object ID will be placed at the end of the array
+ * (but note that some operations below may lose this ordering).
+ */
void oid_array_append(struct oid_array *array, const struct object_id *oid);
+
+/**
+ * Perform a binary search of the array for a specific object ID. If found,
+ * returns the offset (in number of elements) of the object ID. If not found,
+ * returns a negative integer. If the array is not sorted, this function has
+ * the side effect of sorting it.
+ */
int oid_array_lookup(struct oid_array *array, const struct object_id *oid);
+
+/**
+ * Free all memory associated with the array and return it to the initial,
+ * empty state.
+ */
void oid_array_clear(struct oid_array *array);
typedef int (*for_each_oid_fn)(const struct object_id *oid,
void *data);
+/**
+ * Iterate over each element of the list, executing the callback function for
+ * each one. Does not sort the list, so any custom hash order is retained.
+ * If the callback returns a non-zero value, the iteration ends immediately
+ * and the callback's return is propagated; otherwise, 0 is returned.
+ */
int oid_array_for_each(struct oid_array *array,
for_each_oid_fn fn,
void *data);
+
+/**
+ * Iterate over each unique element of the list in sorted order, but otherwise
+ * behave like `oid_array_for_each`. If the array is not sorted, this function
+ * has the side effect of sorting it.
+ */
int oid_array_for_each_unique(struct oid_array *array,
for_each_oid_fn fn,
void *data);
+
+/**
+ * Apply the callback function `want` to each entry in the array, retaining
+ * only the entries for which the function returns true. Preserve the order
+ * of the entries that are retained.
+ */
void oid_array_filter(struct oid_array *array,
for_each_oid_fn want,
void *cbdata);
diff --git a/sigchain.h b/sigchain.h
index 138b20f..8e6bada 100644
--- a/sigchain.h
+++ b/sigchain.h
@@ -1,12 +1,57 @@
#ifndef SIGCHAIN_H
#define SIGCHAIN_H
+/**
+ * Code often wants to set a signal handler to clean up temporary files or
+ * other work-in-progress when we die unexpectedly. For multiple pieces of
+ * code to do this without conflicting, each piece of code must remember
+ * the old value of the handler and restore it either when:
+ *
+ * 1. The work-in-progress is finished, and the handler is no longer
+ * necessary. The handler should revert to the original behavior
+ * (either another handler, SIG_DFL, or SIG_IGN).
+ *
+ * 2. The signal is received. We should then do our cleanup, then chain
+ * to the next handler (or die if it is SIG_DFL).
+ *
+ * Sigchain is a tiny library for keeping a stack of handlers. Your handler
+ * and installation code should look something like:
+ *
+ * ------------------------------------------
+ * void clean_foo_on_signal(int sig)
+ * {
+ * clean_foo();
+ * sigchain_pop(sig);
+ * raise(sig);
+ * }
+ *
+ * void other_func()
+ * {
+ * sigchain_push_common(clean_foo_on_signal);
+ * mess_up_foo();
+ * clean_foo();
+ * }
+ * ------------------------------------------
+ *
+ */
+
+/**
+ * Handlers are given the typedef of sigchain_fun. This is the same type
+ * that is given to signal() or sigaction(). It is perfectly reasonable to
+ * push SIG_DFL or SIG_IGN onto the stack.
+ */
typedef void (*sigchain_fun)(int);
+/* You can sigchain_push and sigchain_pop individual signals. */
int sigchain_push(int sig, sigchain_fun f);
int sigchain_pop(int sig);
+/**
+ * push the handler onto the stack for the common signals:
+ * SIGINT, SIGHUP, SIGTERM, SIGQUIT and SIGPIPE.
+ */
void sigchain_push_common(sigchain_fun f);
+
void sigchain_pop_common(void);
#endif /* SIGCHAIN_H */
diff --git a/submodule-config.c b/submodule-config.c
index b93482d..8506481 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -626,7 +626,7 @@
/*
* Note: This function is private for a reason, the '.gitmodules' file should
- * not be used as as a mechanism to retrieve arbitrary configuration stored in
+ * not be used as a mechanism to retrieve arbitrary configuration stored in
* the repository.
*
* Runs the provided config function on the '.gitmodules' file found in the
diff --git a/submodule-config.h b/submodule-config.h
index 1b4e2da..42918b5 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -7,9 +7,31 @@
#include "submodule.h"
#include "strbuf.h"
+/**
+ * The submodule config cache API allows to read submodule
+ * configurations/information from specified revisions. Internally
+ * information is lazily read into a cache that is used to avoid
+ * unnecessary parsing of the same .gitmodules files. Lookups can be done by
+ * submodule path or name.
+ *
+ * Usage
+ * -----
+ *
+ * The caller can look up information about submodules by using the
+ * `submodule_from_path()` or `submodule_from_name()` functions. They return
+ * a `struct submodule` which contains the values. The API automatically
+ * initializes and allocates the needed infrastructure on-demand. If the
+ * caller does only want to lookup values from revisions the initialization
+ * can be skipped.
+ *
+ * If the internal cache might grow too big or when the caller is done with
+ * the API, all internally cached values can be freed with submodule_free().
+ *
+ */
+
/*
* Submodule entry containing the information about a certain submodule
- * in a certain revision.
+ * in a certain revision. It is returned by the lookup functions.
*/
struct submodule {
const char *path;
@@ -41,13 +63,27 @@
int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
void repo_read_gitmodules(struct repository *repo);
void gitmodules_config_oid(const struct object_id *commit_oid);
+
+/**
+ * Same as submodule_from_path but lookup by name.
+ */
const struct submodule *submodule_from_name(struct repository *r,
const struct object_id *commit_or_tree,
const char *name);
+
+/**
+ * Given a tree-ish in the superproject and a path, return the submodule that
+ * is bound at the path in the named tree.
+ */
const struct submodule *submodule_from_path(struct repository *r,
const struct object_id *commit_or_tree,
const char *path);
+
+/**
+ * Use these to free the internally cached values.
+ */
void submodule_free(struct repository *r);
+
int print_config_from_gitmodules(struct repository *repo, const char *key);
int config_set_in_gitmodules_file_gently(const char *key, const char *value);
diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index 2be955f..b0b6060 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -2,10 +2,12 @@
# to run under Bash; primarily intended for tests of the completion
# script.
-if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then
+if test -n "$BASH" && test -z "$POSIXLY_CORRECT"
+then
# we are in full-on bash mode
true
-elif type bash >/dev/null 2>&1; then
+elif type bash >/dev/null 2>&1
+then
# execute in full-on bash mode
unset POSIXLY_CORRECT
exec bash "$0" "$@"
diff --git a/t/lib-httpd/apply-one-time-sed.sh b/t/lib-httpd/apply-one-time-sed.sh
index fcef728..bf7689d 100644
--- a/t/lib-httpd/apply-one-time-sed.sh
+++ b/t/lib-httpd/apply-one-time-sed.sh
@@ -7,11 +7,13 @@
#
# This can be used to simulate the effects of the repository changing in
# between HTTP request-response pairs.
-if [ -e one-time-sed ]; then
+if test -f one-time-sed
+then
"$GIT_EXEC_PATH/git-http-backend" >out
- sed "$(cat one-time-sed)" <out >out_modified
+ sed "$(cat one-time-sed)" out >out_modified
- if diff out out_modified >/dev/null; then
+ if cmp -s out out_modified
+ then
cat out
else
cat out_modified
diff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh
index a369152..7ee7916 100755
--- a/t/perf/p5303-many-packs.sh
+++ b/t/perf/p5303-many-packs.sh
@@ -85,4 +85,22 @@
'
done
+# Measure pack loading with 10,000 packs.
+test_expect_success 'generate lots of packs' '
+ for i in $(test_seq 10000); do
+ echo "blob"
+ echo "data <<EOF"
+ echo "blob $i"
+ echo "EOF"
+ echo "checkpoint"
+ done |
+ git -c fastimport.unpackLimit=0 fast-import
+'
+
+# The purpose of this test is to evaluate load time for a large number
+# of packs while doing as little other work as possible.
+test_perf "load 10,000 packs" '
+ git rev-parse --verify "HEAD^{commit}"
+'
+
test_done
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 2694c81..8d3d914 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -38,8 +38,8 @@
#'
test_expect_success 'run-command formats empty args properly' '
- GIT_TRACE=1 git frotz a "" b " " c 2>&1 |
- sed -ne "/run_command:/s/.*trace: run_command: //p" >actual &&
+ test_must_fail env GIT_TRACE=1 git frotz a "" b " " c 2>actual.raw &&
+ sed -ne "/run_command:/s/.*trace: run_command: //p" actual.raw >actual &&
echo "git-frotz a '\'''\'' b '\'' '\'' c" >expect &&
test_cmp expect actual
'
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index ce9a4a5..5a63369 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -21,9 +21,10 @@
parent="$2" &&
# ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
# We want to count only foo because it's the only direct child
- subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
+ git ls-files >files &&
+ subtrees=$(grep / files|cut -d / -f 1|uniq) &&
subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 != "" {++c} END {print c}') &&
- entries=$(git ls-files|wc -l) &&
+ entries=$(wc -l <files) &&
printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
for subtree in $subtrees
do
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
new file mode 100755
index 0000000..6f7e2d0
--- /dev/null
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -0,0 +1,332 @@
+#!/bin/sh
+
+test_description='sparse checkout builtin tests'
+
+. ./test-lib.sh
+
+list_files() {
+ # Do not replace this with 'ls "$1"', as "ls" with BSD-lineage
+ # enables "-A" by default for root and ends up including ".git" and
+ # such in its output. (Note, though, that running the test suite as
+ # root is generally not recommended.)
+ (cd "$1" && printf '%s\n' *)
+}
+
+test_expect_success 'setup' '
+ git init repo &&
+ (
+ cd repo &&
+ echo "initial" >a &&
+ mkdir folder1 folder2 deep &&
+ mkdir deep/deeper1 deep/deeper2 &&
+ mkdir deep/deeper1/deepest &&
+ cp a folder1 &&
+ cp a folder2 &&
+ cp a deep &&
+ cp a deep/deeper1 &&
+ cp a deep/deeper2 &&
+ cp a deep/deeper1/deepest &&
+ git add . &&
+ git commit -m "initial commit"
+ )
+'
+
+test_expect_success 'git sparse-checkout list (empty)' '
+ git -C repo sparse-checkout list >list 2>err &&
+ test_must_be_empty list &&
+ test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
+'
+
+test_expect_success 'git sparse-checkout list (populated)' '
+ test_when_finished rm -f repo/.git/info/sparse-checkout &&
+ cat >repo/.git/info/sparse-checkout <<-EOF &&
+ /folder1/*
+ /deep/
+ **/a
+ !*bin*
+ EOF
+ cp repo/.git/info/sparse-checkout expect &&
+ git -C repo sparse-checkout list >list &&
+ test_cmp expect list
+'
+
+test_expect_success 'git sparse-checkout init' '
+ git -C repo sparse-checkout init &&
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ test_cmp_config -C repo true core.sparsecheckout &&
+ list_files repo >dir &&
+ echo a >expect &&
+ test_cmp expect dir
+'
+
+test_expect_success 'git sparse-checkout list after init' '
+ git -C repo sparse-checkout list >actual &&
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'init with existing sparse-checkout' '
+ echo "*folder*" >> repo/.git/info/sparse-checkout &&
+ git -C repo sparse-checkout init &&
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ *folder*
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ list_files repo >dir &&
+ cat >expect <<-EOF &&
+ a
+ folder1
+ folder2
+ EOF
+ test_cmp expect dir
+'
+
+test_expect_success 'clone --sparse' '
+ git clone --sparse repo clone &&
+ git -C clone sparse-checkout list >actual &&
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ EOF
+ test_cmp expect actual &&
+ list_files clone >dir &&
+ echo a >expect &&
+ test_cmp expect dir
+'
+
+test_expect_success 'set enables config' '
+ git init empty-config &&
+ (
+ cd empty-config &&
+ test_commit test file &&
+ test_path_is_missing .git/config.worktree &&
+ test_must_fail git sparse-checkout set nothing &&
+ test_path_is_file .git/config.worktree &&
+ test_must_fail git config core.sparseCheckout &&
+ git sparse-checkout set "/*" &&
+ test_cmp_config true core.sparseCheckout
+ )
+'
+
+test_expect_success 'set sparse-checkout using builtin' '
+ git -C repo sparse-checkout set "/*" "!/*/" "*folder*" &&
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ *folder*
+ EOF
+ git -C repo sparse-checkout list >actual &&
+ test_cmp expect actual &&
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ list_files repo >dir &&
+ cat >expect <<-EOF &&
+ a
+ folder1
+ folder2
+ EOF
+ test_cmp expect dir
+'
+
+test_expect_success 'set sparse-checkout using --stdin' '
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ /folder1/
+ /folder2/
+ EOF
+ git -C repo sparse-checkout set --stdin <expect &&
+ git -C repo sparse-checkout list >actual &&
+ test_cmp expect actual &&
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ list_files repo >dir &&
+ cat >expect <<-EOF &&
+ a
+ folder1
+ folder2
+ EOF
+ test_cmp expect dir
+'
+
+test_expect_success 'cone mode: match patterns' '
+ git -C repo config --worktree core.sparseCheckoutCone true &&
+ rm -rf repo/a repo/folder1 repo/folder2 &&
+ git -C repo read-tree -mu HEAD 2>err &&
+ test_i18ngrep ! "disabling cone patterns" err &&
+ git -C repo reset --hard &&
+ list_files repo >dir &&
+ cat >expect <<-EOF &&
+ a
+ folder1
+ folder2
+ EOF
+ test_cmp expect dir
+'
+
+test_expect_success 'cone mode: warn on bad pattern' '
+ test_when_finished mv sparse-checkout repo/.git/info/ &&
+ cp repo/.git/info/sparse-checkout . &&
+ echo "!/deep/deeper/*" >>repo/.git/info/sparse-checkout &&
+ git -C repo read-tree -mu HEAD 2>err &&
+ test_i18ngrep "unrecognized negative pattern" err
+'
+
+test_expect_success 'sparse-checkout disable' '
+ test_when_finished rm -rf repo/.git/info/sparse-checkout &&
+ git -C repo sparse-checkout disable &&
+ test_path_is_file repo/.git/info/sparse-checkout &&
+ git -C repo config --list >config &&
+ test_must_fail git config core.sparseCheckout &&
+ list_files repo >dir &&
+ cat >expect <<-EOF &&
+ a
+ deep
+ folder1
+ folder2
+ EOF
+ test_cmp expect dir
+'
+
+test_expect_success 'cone mode: init and set' '
+ git -C repo sparse-checkout init --cone &&
+ git -C repo config --list >config &&
+ test_i18ngrep "core.sparsecheckoutcone=true" config &&
+ list_files repo >dir &&
+ echo a >expect &&
+ test_cmp expect dir &&
+ git -C repo sparse-checkout set deep/deeper1/deepest/ 2>err &&
+ test_must_be_empty err &&
+ list_files repo >dir &&
+ cat >expect <<-EOF &&
+ a
+ deep
+ EOF
+ test_cmp expect dir &&
+ list_files repo/deep >dir &&
+ cat >expect <<-EOF &&
+ a
+ deeper1
+ EOF
+ test_cmp expect dir &&
+ list_files repo/deep/deeper1 >dir &&
+ cat >expect <<-EOF &&
+ a
+ deepest
+ EOF
+ test_cmp expect dir &&
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ /deep/
+ !/deep/*/
+ /deep/deeper1/
+ !/deep/deeper1/*/
+ /deep/deeper1/deepest/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ git -C repo sparse-checkout set --stdin 2>err <<-EOF &&
+ folder1
+ folder2
+ EOF
+ test_must_be_empty err &&
+ cat >expect <<-EOF &&
+ a
+ folder1
+ folder2
+ EOF
+ list_files repo >dir &&
+ test_cmp expect dir
+'
+
+test_expect_success 'cone mode: set with nested folders' '
+ git -C repo sparse-checkout set deep deep/deeper1/deepest 2>err &&
+ test_line_count = 0 err &&
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ /deep/
+ EOF
+ test_cmp repo/.git/info/sparse-checkout expect
+'
+
+test_expect_success 'revert to old sparse-checkout on bad update' '
+ test_when_finished git -C repo reset --hard &&
+ echo update >repo/deep/deeper2/a &&
+ cp repo/.git/info/sparse-checkout expect &&
+ test_must_fail git -C repo sparse-checkout set deep/deeper1 2>err &&
+ test_i18ngrep "cannot set sparse-checkout patterns" err &&
+ test_cmp repo/.git/info/sparse-checkout expect &&
+ list_files repo/deep >dir &&
+ cat >expect <<-EOF &&
+ a
+ deeper1
+ deeper2
+ EOF
+ test_cmp dir expect
+'
+
+test_expect_success 'revert to old sparse-checkout on empty update' '
+ git init empty-test &&
+ (
+ echo >file &&
+ git add file &&
+ git commit -m "test" &&
+ test_must_fail git sparse-checkout set nothing 2>err &&
+ test_i18ngrep "Sparse checkout leaves no entry on working directory" err &&
+ test_i18ngrep ! ".git/index.lock" err &&
+ git sparse-checkout set file
+ )
+'
+
+test_expect_success 'fail when lock is taken' '
+ test_when_finished rm -rf repo/.git/info/sparse-checkout.lock &&
+ touch repo/.git/info/sparse-checkout.lock &&
+ test_must_fail git -C repo sparse-checkout set deep 2>err &&
+ test_i18ngrep "File exists" err
+'
+
+test_expect_success '.gitignore should not warn about cone mode' '
+ git -C repo config --worktree core.sparseCheckoutCone true &&
+ echo "**/bin/*" >repo/.gitignore &&
+ git -C repo reset --hard 2>err &&
+ test_i18ngrep ! "disabling cone patterns" err
+'
+
+test_expect_success 'sparse-checkout (init|set|disable) fails with dirty status' '
+ git clone repo dirty &&
+ echo dirty >dirty/folder1/a &&
+ test_must_fail git -C dirty sparse-checkout init &&
+ test_must_fail git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* &&
+ test_must_fail git -C dirty sparse-checkout disable &&
+ git -C dirty reset --hard &&
+ git -C dirty sparse-checkout init &&
+ git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* &&
+ git -C dirty sparse-checkout disable
+'
+
+test_expect_success 'cone mode: set with core.ignoreCase=true' '
+ git -C repo sparse-checkout init --cone &&
+ git -C repo -c core.ignoreCase=true sparse-checkout set folder1 &&
+ cat >expect <<-EOF &&
+ /*
+ !/*/
+ /folder1/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ list_files repo >dir &&
+ cat >expect <<-EOF &&
+ a
+ folder1
+ EOF
+ test_cmp expect dir
+'
+
+test_done
diff --git a/t/t2026-checkout-pathspec-file.sh b/t/t2026-checkout-pathspec-file.sh
new file mode 100755
index 0000000..f62fd27
--- /dev/null
+++ b/t/t2026-checkout-pathspec-file.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='checkout --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+ test_commit file0 &&
+
+ echo 1 >fileA.t &&
+ echo 1 >fileB.t &&
+ echo 1 >fileC.t &&
+ echo 1 >fileD.t &&
+ git add fileA.t fileB.t fileC.t fileD.t &&
+ git commit -m "files 1" &&
+
+ echo 2 >fileA.t &&
+ echo 2 >fileB.t &&
+ echo 2 >fileC.t &&
+ echo 2 >fileD.t &&
+ git add fileA.t fileB.t fileC.t fileD.t &&
+ git commit -m "files 2" &&
+
+ git tag checkpoint
+'
+
+restore_checkpoint () {
+ git reset --hard checkpoint
+}
+
+verify_expect () {
+ git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+ restore_checkpoint &&
+
+ echo fileA.t | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+ restore_checkpoint &&
+
+ echo fileA.t >list &&
+ git checkout --pathspec-from-file=list HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\0fileB.t\0" | git checkout --pathspec-from-file=- --pathspec-file-nul HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\nfileB.t\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+ restore_checkpoint &&
+
+ printf "fileA.t\nfileB.t" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\r\nfileB.t\r\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'quotes' '
+ restore_checkpoint &&
+
+ printf "\"file\\101.t\"" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+ restore_checkpoint &&
+
+ printf "\"file\\101.t\"" >list &&
+ test_must_fail git checkout --pathspec-from-file=list --pathspec-file-nul HEAD^1
+'
+
+test_expect_success 'only touches what was listed' '
+ restore_checkpoint &&
+
+ printf "fileB.t\nfileC.t\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileB.t
+ M fileC.t
+ EOF
+ verify_expect
+'
+
+test_done
diff --git a/t/t2072-restore-pathspec-file.sh b/t/t2072-restore-pathspec-file.sh
new file mode 100755
index 0000000..db58e83
--- /dev/null
+++ b/t/t2072-restore-pathspec-file.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='restore --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+ test_commit file0 &&
+
+ echo 1 >fileA.t &&
+ echo 1 >fileB.t &&
+ echo 1 >fileC.t &&
+ echo 1 >fileD.t &&
+ git add fileA.t fileB.t fileC.t fileD.t &&
+ git commit -m "files 1" &&
+
+ echo 2 >fileA.t &&
+ echo 2 >fileB.t &&
+ echo 2 >fileC.t &&
+ echo 2 >fileD.t &&
+ git add fileA.t fileB.t fileC.t fileD.t &&
+ git commit -m "files 2" &&
+
+ git tag checkpoint
+'
+
+restore_checkpoint () {
+ git reset --hard checkpoint
+}
+
+verify_expect () {
+ git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+ restore_checkpoint &&
+
+ echo fileA.t | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+ restore_checkpoint &&
+
+ echo fileA.t >list &&
+ git restore --pathspec-from-file=list --source=HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\0fileB.t\0" | git restore --pathspec-from-file=- --pathspec-file-nul --source=HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\nfileB.t\n" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+ restore_checkpoint &&
+
+ printf "fileA.t\nfileB.t" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\r\nfileB.t\r\n" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'quotes' '
+ restore_checkpoint &&
+
+ printf "\"file\\101.t\"" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+ restore_checkpoint &&
+
+ printf "\"file\\101.t\"" >list &&
+ test_must_fail git restore --pathspec-from-file=list --pathspec-file-nul --source=HEAD^1
+'
+
+test_expect_success 'only touches what was listed' '
+ restore_checkpoint &&
+
+ printf "fileB.t\nfileC.t\n" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileB.t
+ M fileC.t
+ EOF
+ verify_expect
+'
+
+test_done
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index ec2b456..0575dd7 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -638,7 +638,7 @@
test_cmp expect actual
'
-test_expect_success 'format-patch --range-diff with --notes' '
+test_expect_success 'format-patch --range-diff with format.notes config' '
git notes add -m "topic note" topic &&
git notes add -m "unmodified note" unmodified &&
test_when_finished git notes remove topic unmodified &&
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index d66a5f6..8f43303 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -54,7 +54,9 @@
test_path_is_missing .git/NOTES_EDITMSG &&
git ls-tree -r refs/notes/commits >actual &&
test_line_count = 1 actual &&
- test "b4" = "$(git notes show)" &&
+ echo b4 >expect &&
+ git notes show >actual &&
+ test_cmp expect actual &&
git show HEAD^ &&
test_must_fail git notes show HEAD^
'
@@ -79,14 +81,21 @@
test_path_is_missing .git/NOTES_EDITMSG &&
git ls-tree -r refs/notes/commits >actual &&
test_line_count = 1 actual &&
- test "b3" = "$(git notes show)" &&
+ echo b3 >expect &&
+ git notes show >actual &&
+ test_cmp expect actual &&
git show HEAD^ &&
test_must_fail git notes show HEAD^
'
test_expect_success 'show notes from treeish' '
- test "b3" = "$(git notes --ref commits^{tree} show)" &&
- test "b4" = "$(git notes --ref commits@{1} show)"
+ echo b3 >expect &&
+ git notes --ref commits^{tree} show >actual &&
+ test_cmp expect actual &&
+
+ echo b4 >expect &&
+ git notes --ref commits@{1} show >actual &&
+ test_cmp expect actual
'
test_expect_success 'cannot edit notes from non-ref' '
@@ -99,7 +108,9 @@
test_path_is_missing .git/NOTES_EDITMSG &&
git ls-tree -r refs/notes/commits >actual &&
test_line_count = 1 actual &&
- test "b3" = "$(git notes show)" &&
+ echo b3 >expect &&
+ git notes show >actual &&
+ test_cmp expect actual &&
git show HEAD^ &&
test_must_fail git notes show HEAD^
'
@@ -109,7 +120,9 @@
test_path_is_missing .git/NOTES_EDITMSG &&
git ls-tree -r refs/notes/commits >actual &&
test_line_count = 1 actual &&
- test "b1" = "$(git notes show)" &&
+ echo b1 >expect &&
+ git notes show >actual &&
+ test_cmp expect actual &&
git show HEAD^ &&
test_must_fail git notes show HEAD^
'
@@ -119,7 +132,9 @@
test_path_is_missing .git/NOTES_EDITMSG &&
git ls-tree -r refs/notes/commits >actual &&
test_line_count = 1 actual &&
- test "b2" = "$(git notes show)" &&
+ echo b2 >expect &&
+ git notes show >actual &&
+ test_cmp expect actual &&
git show HEAD^ &&
test_must_fail git notes show HEAD^
'
@@ -129,7 +144,9 @@
test_path_is_missing .git/NOTES_EDITMSG &&
git ls-tree -r refs/notes/commits >actual &&
test_line_count = 1 actual &&
- test "b1" = "$(git notes show)" &&
+ echo b1 >expect &&
+ git notes show >actual &&
+ test_cmp expect actual &&
git show HEAD^ &&
test_must_fail git notes show HEAD^
'
@@ -146,7 +163,8 @@
Notes:
${indent}b1
EOF
- ! (git cat-file commit HEAD | grep b1) &&
+ git cat-file commit HEAD >commits &&
+ ! grep b1 commits &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -472,9 +490,11 @@
test_expect_success 'list notes with "git notes list"' '
commit_2=$(git rev-parse 2nd) &&
commit_3=$(git rev-parse 3rd) &&
+ note_2=$(git rev-parse refs/notes/commits:$commit_2) &&
+ note_3=$(git rev-parse refs/notes/commits:$commit_3) &&
sort -t" " -k2 >expect <<-EOF &&
- $(git rev-parse refs/notes/commits:$commit_2) $commit_2
- $(git rev-parse refs/notes/commits:$commit_3) $commit_3
+ $note_2 $commit_2
+ $note_3 $commit_3
EOF
git notes list >actual &&
test_cmp expect actual
@@ -486,9 +506,7 @@
'
test_expect_success 'list specific note with "git notes list <object>"' '
- cat >expect <<-EOF &&
- $(git rev-parse refs/notes/commits:$commit_3)
- EOF
+ git rev-parse refs/notes/commits:$commit_3 >expect &&
git notes list HEAD^^ >actual &&
test_cmp expect actual
'
@@ -512,10 +530,11 @@
test_expect_success '"git notes list" does not expand to "git notes list HEAD"' '
commit_5=$(git rev-parse 5th) &&
+ note_5=$(git rev-parse refs/notes/commits:$commit_5) &&
sort -t" " -k2 >expect_list <<-EOF &&
- $(git rev-parse refs/notes/commits:$commit_2) $commit_2
- $(git rev-parse refs/notes/commits:$commit_3) $commit_3
- $(git rev-parse refs/notes/commits:$commit_5) $commit_5
+ $note_2 $commit_2
+ $note_3 $commit_3
+ $note_5 $commit_5
EOF
git notes list >actual &&
test_cmp expect_list actual
@@ -721,7 +740,8 @@
git notes show HEAD: >actual &&
test_cmp expect actual &&
echo "Note on a blob" >expect &&
- filename=$(git ls-tree --name-only HEAD | head -n1) &&
+ git ls-tree --name-only HEAD >files &&
+ filename=$(head -n1 files) &&
git notes add -m "Note on a blob" HEAD:$filename &&
git notes show HEAD:$filename >actual &&
test_cmp expect actual &&
@@ -745,10 +765,13 @@
Notes:
${indent}order test
EOF
- git notes add -C $(git notes list HEAD^) &&
+ note=$(git notes list HEAD^) &&
+ git notes add -C $note &&
git log -1 >actual &&
test_cmp expect actual &&
- test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+ git notes list HEAD^ >expect &&
+ git notes list HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success 'create note from non-existing note with "git notes add -C" fails' '
@@ -777,11 +800,12 @@
Notes:
${indent}This is a blob object
EOF
- blob=$(echo "This is a blob object" | git hash-object -w --stdin) &&
- git notes add -C $blob &&
+ echo "This is a blob object" | git hash-object -w --stdin >blob &&
+ git notes add -C $(cat blob) &&
git log -1 >actual &&
test_cmp expect actual &&
- test "$(git notes list HEAD)" = "$blob"
+ git notes list HEAD >actual &&
+ test_cmp blob actual
'
test_expect_success 'create note from other note with "git notes add -c"' '
@@ -797,7 +821,8 @@
Notes:
${indent}yet another note
EOF
- MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
+ note=$(git notes list HEAD^^) &&
+ MSG="yet another note" git notes add -c $note &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -822,7 +847,8 @@
${indent}
${indent}yet another note
EOF
- git notes append -C $(git notes list HEAD^) HEAD^ &&
+ note=$(git notes list HEAD^) &&
+ git notes append -C $note HEAD^ &&
git log -1 HEAD^ >actual &&
test_cmp expect actual
'
@@ -839,7 +865,8 @@
Notes:
${indent}other note
EOF
- MSG="other note" git notes append -c $(git notes list HEAD^) &&
+ note=$(git notes list HEAD^) &&
+ MSG="other note" git notes append -c $note &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -858,7 +885,8 @@
${indent}
${indent}yet another note
EOF
- MSG="yet another note" git notes append -c $(git notes list HEAD) &&
+ note=$(git notes list HEAD) &&
+ MSG="yet another note" git notes append -c $note &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -878,7 +906,9 @@
git notes copy 8th 4th &&
git log 3rd..4th >actual &&
test_cmp expect actual &&
- test "$(git note list 4th)" = "$(git note list 8th)"
+ git notes list 4th >expect &&
+ git notes list 8th >actual &&
+ test_cmp expect actual
'
test_expect_success 'copy note with "git notes copy" with default' '
@@ -899,14 +929,30 @@
git notes copy HEAD^ &&
git log -1 >actual &&
test_cmp expect actual &&
- test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+ git notes list HEAD^ >expect &&
+ git notes list HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success 'prevent overwrite with "git notes copy"' '
test_must_fail git notes copy HEAD~2 HEAD &&
+ cat >expect <<-EOF &&
+ commit $commit
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:23:13 2005 -0700
+
+ ${indent}11th
+
+ Notes:
+ ${indent}other note
+ ${indent}
+ ${indent}yet another note
+ EOF
git log -1 >actual &&
test_cmp expect actual &&
- test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+ git notes list HEAD^ >expect &&
+ git notes list HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success 'allow overwrite with "git notes copy -f"' '
@@ -924,7 +970,9 @@
git notes copy -f HEAD~3 HEAD &&
git log -1 >actual &&
test_cmp expect actual &&
- test "$(git notes list HEAD)" = "$(git notes list HEAD~3)"
+ git notes list HEAD~3 >expect &&
+ git notes list HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success 'allow overwrite with "git notes copy -f" with default' '
@@ -944,7 +992,9 @@
git notes copy -f HEAD~2 &&
git log -1 >actual &&
test_cmp expect actual &&
- test "$(git notes list HEAD)" = "$(git notes list HEAD~2)"
+ git notes list HEAD~2 >expect &&
+ git notes list HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success 'cannot copy note from object without notes' '
@@ -979,13 +1029,21 @@
${indent}
${indent}yet another note
EOF
- (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
- echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
- git notes copy --stdin &&
+ from=$(git rev-parse HEAD~3) &&
+ to=$(git rev-parse HEAD^) &&
+ echo "$from" "$to" >copy &&
+ from=$(git rev-parse HEAD~2) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >>copy &&
+ git notes copy --stdin <copy &&
git log -2 >actual &&
test_cmp expect actual &&
- test "$(git notes list HEAD)" = "$(git notes list HEAD~2)" &&
- test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
+ git notes list HEAD~2 >expect &&
+ git notes list HEAD >actual &&
+ test_cmp expect actual &&
+ git notes list HEAD~3 >expect &&
+ git notes list HEAD^ >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
@@ -1006,9 +1064,13 @@
${indent}14th
EOF
- (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
- echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
- git notes copy --for-rewrite=foo &&
+ from=$(git rev-parse HEAD~3) &&
+ to=$(git rev-parse HEAD^) &&
+ echo "$from" "$to" >copy &&
+ from=$(git rev-parse HEAD~2) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >>copy &&
+ git notes copy --for-rewrite=foo <copy &&
git log -2 >actual &&
test_cmp expect actual
'
@@ -1041,17 +1103,23 @@
EOF
test_config notes.rewriteMode overwrite &&
test_config notes.rewriteRef "refs/notes/*" &&
- (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
- echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
- git notes copy --for-rewrite=foo &&
+ from=$(git rev-parse HEAD~3) &&
+ to=$(git rev-parse HEAD^) &&
+ echo "$from" "$to" >copy &&
+ from=$(git rev-parse HEAD~2) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >>copy &&
+ git notes copy --for-rewrite=foo <copy &&
git log -2 >actual &&
test_cmp expect actual
'
test_expect_success 'git notes copy --for-rewrite (disabled)' '
test_config notes.rewrite.bar false &&
- echo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |
- git notes copy --for-rewrite=bar &&
+ from=$(git rev-parse HEAD~3) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
+ git notes copy --for-rewrite=bar <copy &&
git log -2 >actual &&
test_cmp expect actual
'
@@ -1071,8 +1139,10 @@
git notes add -f -m"a fresh note" HEAD^ &&
test_config notes.rewriteMode overwrite &&
test_config notes.rewriteRef "refs/notes/*" &&
- echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
- git notes copy --for-rewrite=foo &&
+ from=$(git rev-parse HEAD^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
+ git notes copy --for-rewrite=foo <copy &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -1080,8 +1150,10 @@
test_expect_success 'git notes copy --for-rewrite (ignore)' '
test_config notes.rewriteMode ignore &&
test_config notes.rewriteRef "refs/notes/*" &&
- echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
- git notes copy --for-rewrite=foo &&
+ from=$(git rev-parse HEAD^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
+ git notes copy --for-rewrite=foo <copy &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -1103,8 +1175,10 @@
git notes add -f -m"another fresh note" HEAD^ &&
test_config notes.rewriteMode concatenate &&
test_config notes.rewriteRef "refs/notes/*" &&
- echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
- git notes copy --for-rewrite=foo &&
+ from=$(git rev-parse HEAD^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
+ git notes copy --for-rewrite=foo <copy &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -1131,9 +1205,13 @@
git notes add -f -m"append 2" HEAD^^ &&
test_config notes.rewriteMode concatenate &&
test_config notes.rewriteRef "refs/notes/*" &&
- (echo $(git rev-parse HEAD^) $(git rev-parse HEAD) &&
- echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
- git notes copy --for-rewrite=foo &&
+ from=$(git rev-parse HEAD^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
+ from=$(git rev-parse HEAD^^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >>copy &&
+ git notes copy --for-rewrite=foo <copy &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -1142,8 +1220,10 @@
git notes remove HEAD^ &&
test_config notes.rewriteMode concatenate &&
test_config notes.rewriteRef "refs/notes/*" &&
- echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
- git notes copy --for-rewrite=foo &&
+ from=$(git rev-parse HEAD^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
+ git notes copy --for-rewrite=foo <copy &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -1163,8 +1243,10 @@
test_config notes.rewriteMode concatenate &&
test_config notes.rewriteRef "refs/notes/*" &&
git notes add -f -m"replacement note 1" HEAD^ &&
- echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
- GIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&
+ from=$(git rev-parse HEAD^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
+ GIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo <copy &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -1184,9 +1266,11 @@
git notes add -f -m"replacement note 2" HEAD^ &&
test_config notes.rewriteMode overwrite &&
test_unconfig notes.rewriteRef &&
- echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ from=$(git rev-parse HEAD^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
GIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \
- git notes copy --for-rewrite=foo &&
+ git notes copy --for-rewrite=foo <copy &&
git log -1 >actual &&
test_cmp expect actual
'
@@ -1195,9 +1279,11 @@
git notes add -f -m"replacement note 3" HEAD^ &&
test_config notes.rewriteMode overwrite &&
test_config notes.rewriteRef refs/notes/other &&
- echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ from=$(git rev-parse HEAD^) &&
+ to=$(git rev-parse HEAD) &&
+ echo "$from" "$to" >copy &&
GIT_NOTES_REWRITE_REF=refs/notes/commits \
- git notes copy --for-rewrite=foo &&
+ git notes copy --for-rewrite=foo <copy &&
git log -1 >actual &&
grep "replacement note 3" actual
'
@@ -1212,26 +1298,36 @@
test_expect_success 'git notes get-ref expands refs/heads/master to refs/notes/refs/heads/master' '
test_unconfig core.notesRef &&
sane_unset GIT_NOTES_REF &&
- test "$(git notes --ref=refs/heads/master get-ref)" = "refs/notes/refs/heads/master"
+ echo refs/notes/refs/heads/master >expect &&
+ git notes --ref=refs/heads/master get-ref >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes get-ref (no overrides)' '
test_unconfig core.notesRef &&
sane_unset GIT_NOTES_REF &&
- test "$(git notes get-ref)" = "refs/notes/commits"
+ echo refs/notes/commits >expect &&
+ git notes get-ref >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes get-ref (core.notesRef)' '
test_config core.notesRef refs/notes/foo &&
- test "$(git notes get-ref)" = "refs/notes/foo"
+ echo refs/notes/foo >expect &&
+ git notes get-ref >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes get-ref (GIT_NOTES_REF)' '
- test "$(GIT_NOTES_REF=refs/notes/bar git notes get-ref)" = "refs/notes/bar"
+ echo refs/notes/bar >expect &&
+ GIT_NOTES_REF=refs/notes/bar git notes get-ref >actual &&
+ test_cmp expect actual
'
test_expect_success 'git notes get-ref (--ref)' '
- test "$(GIT_NOTES_REF=refs/notes/bar git notes --ref=baz get-ref)" = "refs/notes/baz"
+ echo refs/notes/baz >expect &&
+ GIT_NOTES_REF=refs/notes/bar git notes --ref=baz get-ref >actual &&
+ test_cmp expect actual
'
test_expect_success 'setup testing of empty notes' '
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index f267f6c..221b35f 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -159,6 +159,12 @@
test_must_fail git rebase
'
+test_expect_success 'rebase works with format.useAutoBase' '
+ test_config format.useAutoBase true &&
+ git checkout topic &&
+ git rebase master
+'
+
test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
git checkout -b default-base master &&
git checkout -b default topic &&
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index bf0dc75..ae6e55c 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1265,11 +1265,11 @@
test_when_finished "reset_rebase && git checkout master" &&
git checkout collide &&
(
- unset test_tick &&
- test_tick &&
- set_fake_editor &&
- FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \
- FAKE_LINES="reword 1 2" git rebase -i HEAD~2
+ unset test_tick &&
+ test_tick &&
+ set_fake_editor &&
+ FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \
+ FAKE_LINES="reword 1 2" git rebase -i HEAD~2
)
'
diff --git a/t/t3434-rebase-i18n.sh b/t/t3434-rebase-i18n.sh
index 4b5b128..c7c835c 100755
--- a/t/t3434-rebase-i18n.sh
+++ b/t/t3434-rebase-i18n.sh
@@ -45,7 +45,7 @@
compare_msg eucJP.txt eucJP UTF-8
'
-test_expect_failure 'rebase --rebase-merges update encoding eucJP to ISO-2022-JP' '
+test_expect_success 'rebase --rebase-merges update encoding eucJP to ISO-2022-JP' '
git switch -c merge-eucJP-ISO-2022-JP first &&
git config i18n.commitencoding eucJP &&
git merge -F "$TEST_DIRECTORY/t3434/eucJP.txt" second &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 8c8cca5..0ea858d 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -113,9 +113,10 @@
echo frotz >test-file &&
git add test-file &&
git commit -m "add file for rm test" &&
- git rm test-file >rm-output &&
- test $(grep "^rm " rm-output | wc -l) = 1 &&
- rm -f test-file rm-output &&
+ git rm test-file >rm-output.raw &&
+ grep "^rm " rm-output.raw >rm-output &&
+ test_line_count = 1 rm-output &&
+ rm -f test-file rm-output.raw rm-output &&
git commit -m "remove file from rm test"
'
@@ -250,6 +251,7 @@
echo "100644 $hash 0 some-file-$i"
i=$(( $i + 1 ))
done | git update-index --index-info &&
+ # git command is intentionally placed upstream of pipe to induce SIGPIPE
git rm -n "some-file-*" | : &&
test_path_is_missing .git/index.lock
'
@@ -303,7 +305,8 @@
test_expect_success 'rm removes empty submodules from work tree' '
mkdir submod &&
- git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod &&
+ hash=$(git rev-parse HEAD) &&
+ git update-index --add --cacheinfo 160000 "$hash" submod &&
git config -f .gitmodules submodule.sub.url ./. &&
git config -f .gitmodules submodule.sub.path submod &&
git submodule init &&
@@ -622,7 +625,8 @@
git submodule update &&
(
cd submod &&
- git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) subsubmod &&
+ hash=$(git rev-parse HEAD) &&
+ git update-index --add --cacheinfo 160000 "$hash" subsubmod &&
git config -f .gitmodules submodule.sub.url ../. &&
git config -f .gitmodules submodule.sub.path subsubmod &&
git submodule init &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index d4f9386..5db6432 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -23,6 +23,17 @@
test_cmp "$1.filtered" "$2.filtered"
}
+# This function uses a trick to manipulate the interactive add to use color:
+# the `want_color()` function special-cases the situation where a pager was
+# spawned and Git now wants to output colored text: to detect that situation,
+# the environment variable `GIT_PAGER_IN_USE` is set. However, color is
+# suppressed despite that environment variable if the `TERM` variable
+# indicates a dumb terminal, so we set that variable, too.
+
+force_color () {
+ env GIT_PAGER_IN_USE=true TERM=vt100 "$@"
+}
+
test_expect_success 'setup (initial)' '
echo content >file &&
git add file &&
@@ -94,7 +105,6 @@
grep "unchanged *+3/-0 file" output
'
-
test_expect_success 'setup expected' '
cat >expected <<-\EOF
EOF
@@ -263,6 +273,35 @@
# end of tests disabled when filemode is not usable
+test_expect_success 'different prompts for mode change/deleted' '
+ git reset --hard &&
+ >file &&
+ >deleted &&
+ git add --chmod=+x file deleted &&
+ echo changed >file &&
+ rm deleted &&
+ test_write_lines n n n |
+ git -c core.filemode=true add -p >actual &&
+ sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
+ cat >expect <<-\EOF &&
+ (1/1) Stage deletion [y,n,q,a,d,?]?
+ (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,?]?
+ (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
+ EOF
+ test_cmp expect actual.filtered
+'
+
+test_expect_success 'correct message when there is nothing to do' '
+ git reset --hard &&
+ git add -p 2>err &&
+ test_i18ngrep "No changes" err &&
+ printf "\\0123" >binary &&
+ git add binary &&
+ printf "\\0abc" >binary &&
+ git add -p 2>err &&
+ test_i18ngrep "Only binary files changed" err
+'
+
test_expect_success 'setup again' '
git reset --hard &&
test_chmod +x file &&
@@ -403,6 +442,28 @@
! grep "^+31" actual
'
+test_expect_failure 'edit, adding lines to the first hunk' '
+ test_write_lines 10 11 20 30 40 50 51 60 >test &&
+ git reset &&
+ tr _ " " >patch <<-EOF &&
+ @@ -1,5 +1,6 @@
+ _10
+ +11
+ +12
+ _20
+ +21
+ +22
+ _30
+ EOF
+ # test sequence is s(plit), e(dit), n(o)
+ # q n q q is there to make sure we exit at the end.
+ printf "%s\n" s e n q n q q |
+ EDITOR=./fake_editor.sh git add -p 2>error &&
+ test_must_be_empty error &&
+ git diff --cached >actual &&
+ grep "^+22" actual
+'
+
test_expect_success 'patch mode ignores unmerged entries' '
git reset --hard &&
test_commit conflict &&
@@ -429,35 +490,48 @@
diff_cmp expected diff
'
-test_expect_success TTY 'diffs can be colorized' '
+test_expect_success 'diffs can be colorized' '
git reset --hard &&
echo content >test &&
- printf y | test_terminal git add -p >output 2>&1 &&
+ printf y >y &&
+ force_color git add -p >output 2>&1 <y &&
# We do not want to depend on the exact coloring scheme
# git uses for diffs, so just check that we saw some kind of color.
grep "$(printf "\\033")" output
'
-test_expect_success TTY 'diffFilter filters diff' '
+test_expect_success 'diffFilter filters diff' '
git reset --hard &&
echo content >test &&
test_config interactive.diffFilter "sed s/^/foo:/" &&
- printf y | test_terminal git add -p >output 2>&1 &&
+ printf y >y &&
+ force_color git add -p >output 2>&1 <y &&
# avoid depending on the exact coloring or content of the prompts,
# and just make sure we saw our diff prefixed
grep foo:.*content output
'
-test_expect_success TTY 'detect bogus diffFilter output' '
+test_expect_success 'detect bogus diffFilter output' '
git reset --hard &&
echo content >test &&
test_config interactive.diffFilter "echo too-short" &&
- printf y | test_must_fail test_terminal git add -p
+ printf y >y &&
+ test_must_fail force_color git add -p <y
+'
+
+test_expect_success 'diff.algorithm is passed to `git diff-files`' '
+ git reset --hard &&
+
+ >file &&
+ git add file &&
+ echo changed >file &&
+ test_must_fail git -c diff.algorithm=bogus add -p 2>err &&
+ test_i18ngrep "error: option diff-algorithm accepts " err
'
test_expect_success 'patch-mode via -i prompts for files' '
@@ -667,7 +741,7 @@
<BOLD;BLUE>What now<RESET>>$SP
Bye.
EOF
- test_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored &&
+ test_write_lines h | force_color git add -i >actual.colored &&
test_decode_color <actual.colored >actual &&
test_i18ncmp expect actual
'
diff --git a/t/t3704-add-pathspec-file.sh b/t/t3704-add-pathspec-file.sh
new file mode 100755
index 0000000..3cfdb66
--- /dev/null
+++ b/t/t3704-add-pathspec-file.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+test_description='add --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+ test_commit file0 &&
+ echo A >fileA.t &&
+ echo B >fileB.t &&
+ echo C >fileC.t &&
+ echo D >fileD.t
+'
+
+restore_checkpoint () {
+ git reset
+}
+
+verify_expect () {
+ git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+ restore_checkpoint &&
+
+ echo fileA.t | git add --pathspec-from-file=- &&
+
+ cat >expect <<-\EOF &&
+ A fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+ restore_checkpoint &&
+
+ echo fileA.t >list &&
+ git add --pathspec-from-file=list &&
+
+ cat >expect <<-\EOF &&
+ A fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\0fileB.t\0" | git add --pathspec-from-file=- --pathspec-file-nul &&
+
+ cat >expect <<-\EOF &&
+ A fileA.t
+ A fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\nfileB.t\n" | git add --pathspec-from-file=- &&
+
+ cat >expect <<-\EOF &&
+ A fileA.t
+ A fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+ restore_checkpoint &&
+
+ printf "fileA.t\nfileB.t" | git add --pathspec-from-file=- &&
+
+ cat >expect <<-\EOF &&
+ A fileA.t
+ A fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\r\nfileB.t\r\n" | git add --pathspec-from-file=- &&
+
+ cat >expect <<-\EOF &&
+ A fileA.t
+ A fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'quotes' '
+ restore_checkpoint &&
+
+ printf "\"file\\101.t\"" | git add --pathspec-from-file=- &&
+
+ cat >expect <<-\EOF &&
+ A fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+ restore_checkpoint &&
+
+ printf "\"file\\101.t\"" >list &&
+ test_must_fail git add --pathspec-from-file=list --pathspec-file-nul
+'
+
+test_expect_success 'only touches what was listed' '
+ restore_checkpoint &&
+
+ printf "fileB.t\nfileC.t\n" | git add --pathspec-from-file=- &&
+
+ cat >expect <<-\EOF &&
+ A fileB.t
+ A fileC.t
+ EOF
+ verify_expect
+'
+
+test_done
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 69267b1..b653dd7 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -793,6 +793,38 @@
! grep "this is note 2" out
'
+test_expect_success 'format-patch with multiple notes refs in config' '
+ test_when_finished "test_unconfig format.notes" &&
+
+ 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 &&
+ grep "this is note 1" out &&
+ ! grep "this is note 2" out &&
+ git config format.notes note2 &&
+ git format-patch -1 --stdout >out &&
+ ! grep "this is note 1" out &&
+ grep "this is note 2" out &&
+ git config --add format.notes note1 &&
+ git format-patch -1 --stdout >out &&
+ grep "this is note 1" out &&
+ grep "this is note 2" out &&
+
+ git config --replace-all format.notes note1 &&
+ git config --add format.notes false &&
+ git format-patch -1 --stdout >out &&
+ ! grep "this is note 1" out &&
+ ! grep "this is note 2" out &&
+ git config --add format.notes note2 &&
+ git format-patch -1 --stdout >out &&
+ ! grep "this is note 1" out &&
+ grep "this is note 2" out
+'
+
echo "fatal: --name-only does not make sense" >expect.name-only
echo "fatal: --name-status does not make sense" >expect.name-status
echo "fatal: --check does not make sense" >expect.check
@@ -1939,10 +1971,9 @@
test_must_fail git format-patch --base=auto -1
'
-test_expect_success 'format-patch format.useAutoBaseoption' '
- test_when_finished "git config --unset format.useAutoBase" &&
+test_expect_success 'format-patch format.useAutoBase option' '
git checkout local &&
- git config format.useAutoBase true &&
+ test_config format.useAutoBase true &&
git format-patch --stdout -1 >patch &&
grep "^base-commit:" patch >actual &&
git rev-parse upstream >commit-id-base &&
@@ -1951,8 +1982,7 @@
'
test_expect_success 'format-patch --base overrides format.useAutoBase' '
- test_when_finished "git config --unset format.useAutoBase" &&
- git config format.useAutoBase true &&
+ test_config format.useAutoBase true &&
git format-patch --stdout --base=HEAD~1 -1 >patch &&
grep "^base-commit:" patch >actual &&
git rev-parse HEAD~1 >commit-id-base &&
@@ -1960,6 +1990,12 @@
test_cmp expect actual
'
+test_expect_success 'format-patch --no-base overrides format.useAutoBase' '
+ test_config format.useAutoBase true &&
+ git format-patch --stdout --no-base -1 >patch &&
+ ! grep "^base-commit:" patch
+'
+
test_expect_success 'format-patch --base with --attach' '
git format-patch --attach=mimemime --stdout --base=HEAD~ -1 >patch &&
sed -n -e "/^base-commit:/s/.*/1/p" -e "/^---*mimemime--$/s/.*/2/p" \
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index eadaf57..65615e2 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -16,7 +16,8 @@
} while (0);
EOF
git update-index --add x &&
- before=$(git rev-parse --short $(git hash-object x)) &&
+ old_hash_x=$(git hash-object x) &&
+ before=$(git rev-parse --short "$old_hash_x") &&
cat <<-\EOF >x &&
do
@@ -25,7 +26,8 @@
}
while (0);
EOF
- after=$(git rev-parse --short $(git hash-object x)) &&
+ new_hash_x=$(git hash-object x) &&
+ after=$(git rev-parse --short "$new_hash_x") &&
cat <<-EOF >expect &&
diff --git a/x b/x
@@ -63,7 +65,8 @@
EOF
git update-index x &&
- before=$(git rev-parse --short $(git hash-object x)) &&
+ old_hash_x=$(git hash-object x) &&
+ before=$(git rev-parse --short "$old_hash_x") &&
tr "_" " " <<-\EOF >x &&
_ whitespace at beginning
@@ -73,7 +76,8 @@
unchanged line
CR at end
EOF
- after=$(git rev-parse --short $(git hash-object x)) &&
+ new_hash_x=$(git hash-object x) &&
+ after=$(git rev-parse --short "$new_hash_x") &&
tr "Q_" "\015 " <<-EOF >expect &&
diff --git a/x b/x
@@ -526,13 +530,15 @@
test_expect_success 'check mixed spaces and tabs in indent' '
# This is indented with SP HT SP.
echo " foo();" >x &&
- git diff --check | grep "space before tab in indent"
+ test_must_fail git diff --check >check &&
+ grep "space before tab in indent" check
'
test_expect_success 'check mixed tabs and spaces in indent' '
# This is indented with HT SP HT.
echo " foo();" >x &&
- git diff --check | grep "space before tab in indent"
+ test_must_fail git diff --check >check &&
+ grep "space before tab in indent" check
'
test_expect_success 'check with no whitespace errors' '
@@ -753,20 +759,23 @@
test_expect_success 'line numbers in --check output are correct' '
echo "" >x &&
echo "foo(); " >>x &&
- git diff --check | grep "x:2:"
+ test_must_fail git diff --check >check &&
+ grep "x:2:" check
'
test_expect_success 'checkdiff detects new trailing blank lines (1)' '
echo "foo();" >x &&
echo "" >>x &&
- git diff --check | grep "new blank line"
+ test_must_fail git diff --check >check &&
+ grep "new blank line" check
'
test_expect_success 'checkdiff detects new trailing blank lines (2)' '
- { echo a; echo b; echo; echo; } >x &&
+ test_write_lines a b "" "" >x &&
git add x &&
- { echo a; echo; echo; echo; echo; } >x &&
- git diff --check | grep "new blank line"
+ test_write_lines a "" "" "" "" >x &&
+ test_must_fail git diff --check >check &&
+ grep "new blank line" check
'
test_expect_success 'checkdiff allows new blank lines' '
@@ -794,14 +803,16 @@
git reset --hard &&
for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
git add x &&
- before=$(git rev-parse --short $(git hash-object x)) &&
+ hash_x=$(git hash-object x) &&
+ before=$(git rev-parse --short "$hash_x") &&
git commit -m "base" &&
sed -e "5s/^/ /" x >z &&
git rm x &&
git add z &&
- after=$(git rev-parse --short $(git hash-object z)) &&
- git diff -w -M --cached |
- sed -e "/^similarity index /s/[0-9][0-9]*/NUM/" >actual &&
+ hash_z=$(git hash-object z) &&
+ after=$(git rev-parse --short "$hash_z") &&
+ git diff -w -M --cached >actual.raw &&
+ sed -e "/^similarity index /s/[0-9][0-9]*/NUM/" actual.raw >actual &&
cat <<-EOF >expect &&
diff --git a/x b/z
similarity index NUM%
@@ -840,7 +851,8 @@
git config core.autocrlf true &&
test_must_fail git merge master &&
- git diff | sed -e "1,/^@@@/d" >actual &&
+ git diff >actual.raw &&
+ sed -e "1,/^@@@/d" actual.raw >actual &&
! grep "^-" actual
'
@@ -864,11 +876,14 @@
git config core.whitespace blank-at-eol &&
git reset --hard &&
echo "test" >x &&
- before=$(git rev-parse --short $(git hash-object x)) &&
+ old_hash_x=$(git hash-object x) &&
+ before=$(git rev-parse --short "$old_hash_x") &&
git commit -m "initial" x &&
echo "{NTN}" | tr "NT" "\n\t" >>x &&
- after=$(git rev-parse --short $(git hash-object x)) &&
- git diff --color | test_decode_color >current &&
+ new_hash_x=$(git hash-object x) &&
+ after=$(git rev-parse --short "$new_hash_x") &&
+ git diff --color >current.raw &&
+ test_decode_color <current.raw >current &&
cat >expected <<-EOF &&
<BOLD>diff --git a/x b/x<RESET>
@@ -891,17 +906,19 @@
echo "0. blank-at-eol " &&
echo "1. blank-at-eol "
} >x &&
- before=$(git rev-parse --short $(git hash-object x)) &&
+ old_hash_x=$(git hash-object x) &&
+ before=$(git rev-parse --short "$old_hash_x") &&
git commit -a --allow-empty -m preimage &&
{
echo "0. blank-at-eol " &&
echo "1. still-blank-at-eol " &&
echo "2. and a new line "
} >x &&
- after=$(git rev-parse --short $(git hash-object x)) &&
+ new_hash_x=$(git hash-object x) &&
+ after=$(git rev-parse --short "$new_hash_x") &&
- git diff --color |
- test_decode_color >current &&
+ git diff --color >current.raw &&
+ test_decode_color <current.raw >current &&
cat >expected <<-EOF &&
<BOLD>diff --git a/x b/x<RESET>
@@ -925,14 +942,16 @@
echo "0. blank-at-eol " &&
echo "1. blank-at-eol "
} >x &&
- before=$(git rev-parse --short $(git hash-object x)) &&
+ old_hash_x=$(git hash-object x) &&
+ before=$(git rev-parse --short "$old_hash_x") &&
git commit -a --allow-empty -m preimage &&
{
echo "0. blank-at-eol " &&
echo "1. still-blank-at-eol " &&
echo "2. and a new line "
} >x &&
- after=$(git rev-parse --short $(git hash-object x)) &&
+ new_hash_x=$(git hash-object x) &&
+ after=$(git rev-parse --short "$new_hash_x") &&
cat >expect.default-old <<-EOF &&
<BOLD>diff --git a/x b/x<RESET>
@@ -974,32 +993,32 @@
test_expect_success 'test --ws-error-highlight option' '
- git diff --color --ws-error-highlight=default,old |
- test_decode_color >current &&
+ git diff --color --ws-error-highlight=default,old >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.default-old current &&
- git diff --color --ws-error-highlight=all |
- test_decode_color >current &&
+ git diff --color --ws-error-highlight=all >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.all current &&
- git diff --color --ws-error-highlight=none |
- test_decode_color >current &&
+ git diff --color --ws-error-highlight=none >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.none current
'
test_expect_success 'test diff.wsErrorHighlight config' '
- git -c diff.wsErrorHighlight=default,old diff --color |
- test_decode_color >current &&
+ git -c diff.wsErrorHighlight=default,old diff --color >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.default-old current &&
- git -c diff.wsErrorHighlight=all diff --color |
- test_decode_color >current &&
+ git -c diff.wsErrorHighlight=all diff --color >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.all current &&
- git -c diff.wsErrorHighlight=none diff --color |
- test_decode_color >current &&
+ git -c diff.wsErrorHighlight=none diff --color >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.none current
'
@@ -1007,18 +1026,18 @@
test_expect_success 'option overrides diff.wsErrorHighlight' '
git -c diff.wsErrorHighlight=none \
- diff --color --ws-error-highlight=default,old |
- test_decode_color >current &&
+ diff --color --ws-error-highlight=default,old >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.default-old current &&
git -c diff.wsErrorHighlight=default \
- diff --color --ws-error-highlight=all |
- test_decode_color >current &&
+ diff --color --ws-error-highlight=all >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.all current &&
git -c diff.wsErrorHighlight=all \
- diff --color --ws-error-highlight=none |
- test_decode_color >current &&
+ diff --color --ws-error-highlight=none >current.raw &&
+ test_decode_color <current.raw >current &&
test_cmp expect.none current
'
@@ -1038,7 +1057,8 @@
git mv test.c main.c &&
test_config color.diff.oldMoved "normal red" &&
test_config color.diff.newMoved "normal green" &&
- git diff HEAD --color-moved=zebra --color --no-renames | test_decode_color >actual &&
+ git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
+ test_decode_color <actual.raw >actual &&
cat >expected <<-EOF &&
<BOLD>diff --git a/main.c b/main.c<RESET>
<BOLD>new file mode 100644<RESET>
@@ -1141,9 +1161,12 @@
bar();
}
EOF
- after_main=$(git rev-parse --short $(git hash-object main.c)) &&
- after_test=$(git rev-parse --short $(git hash-object test.c)) &&
- git diff HEAD --no-renames --color-moved=zebra --color | test_decode_color >actual &&
+ hash_main=$(git hash-object main.c) &&
+ after_main=$(git rev-parse --short "$hash_main") &&
+ hash_test=$(git hash-object test.c) &&
+ after_test=$(git rev-parse --short "$hash_test") &&
+ git diff HEAD --no-renames --color-moved=zebra --color >actual.raw &&
+ test_decode_color <actual.raw >actual &&
cat <<-EOF >expected &&
<BOLD>diff --git a/main.c b/main.c<RESET>
<BOLD>index $before_main..$after_main 100644<RESET>
@@ -1192,7 +1215,8 @@
test_config color.diff.oldMovedAlternative "blue" &&
test_config color.diff.newMovedAlternative "yellow" &&
# needs previous test as setup
- git diff HEAD --no-renames --color-moved=plain --color | test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved=plain --color >actual.raw &&
+ test_decode_color <actual.raw >actual &&
cat <<-EOF >expected &&
<BOLD>diff --git a/main.c b/main.c<RESET>
<BOLD>index $before_main..$after_main 100644<RESET>
@@ -1771,7 +1795,8 @@
! grep BRED decoded_actual &&
# nor did we mess with it another way
- git diff --submodule=diff --color | test_decode_color >expect &&
+ git diff --submodule=diff --color >expect.raw &&
+ test_decode_color <expect.raw >expect &&
test_cmp expect decoded_actual &&
rm -rf bananas &&
git submodule deinit bananas
@@ -2025,11 +2050,6 @@
test_cmp expected actual
'
-# Note that the "6" in the expected hunk header below is funny, since we only
-# show 5 lines (the missing one was blank and thus ignored). This is how
-# --ignore-blank-lines behaves even without --function-context, and this test
-# is just checking the interaction of the two features. Don't take it as an
-# endorsement of that output.
test_expect_success 'combine --ignore-blank-lines with --function-context' '
test_write_lines 1 "" 2 3 4 5 >a &&
test_write_lines 1 2 3 4 >b &&
@@ -2039,6 +2059,7 @@
cat <<-\EOF >expect &&
@@ -1,6 +1,4 @@
1
+ -
2
3
4
diff --git a/t/t4138-apply-ws-expansion.sh b/t/t4138-apply-ws-expansion.sh
index 3b636a6..b19faeb 100755
--- a/t/t4138-apply-ws-expansion.sh
+++ b/t/t4138-apply-ws-expansion.sh
@@ -17,8 +17,8 @@
printf "\t%s\n" 1 2 3 >after &&
printf "%64s\n" a b c >>after &&
printf "\t%s\n" 4 5 6 >>after &&
- git diff --no-index before after |
- sed -e "s/before/test-1/" -e "s/after/test-1/" >patch1.patch &&
+ test_expect_code 1 git diff --no-index before after >patch1.patch.raw &&
+ sed -e "s/before/test-1/" -e "s/after/test-1/" patch1.patch.raw >patch1.patch &&
printf "%64s\n" 1 2 3 4 5 6 >test-1 &&
printf "%64s\n" 1 2 3 a b c 4 5 6 >expect-1 &&
@@ -33,8 +33,8 @@
x=$(( $x + 1 ))
done &&
printf "\t%s\n" d e f >>after &&
- git diff --no-index before after |
- sed -e "s/before/test-2/" -e "s/after/test-2/" >patch2.patch &&
+ test_expect_code 1 git diff --no-index before after >patch2.patch.raw &&
+ sed -e "s/before/test-2/" -e "s/after/test-2/" patch2.patch.raw >patch2.patch &&
printf "%64s\n" a b c d e f >test-2 &&
printf "%64s\n" a b c >expect-2 &&
x=1 &&
@@ -56,8 +56,8 @@
x=$(( $x + 1 ))
done &&
printf "\t%s\n" d e f >>after &&
- git diff --no-index before after |
- sed -e "s/before/test-3/" -e "s/after/test-3/" >patch3.patch &&
+ test_expect_code 1 git diff --no-index before after >patch3.patch.raw &&
+ sed -e "s/before/test-3/" -e "s/after/test-3/" patch3.patch.raw >patch3.patch &&
printf "%64s\n" a b c d e f >test-3 &&
printf "%64s\n" a b c >expect-3 &&
x=0 &&
@@ -84,8 +84,8 @@
printf "\t%02d\n" $x >>after
x=$(( $x + 1 ))
done &&
- git diff --no-index before after |
- sed -e "s/before/test-4/" -e "s/after/test-4/" >patch4.patch &&
+ test_expect_code 1 git diff --no-index before after >patch4.patch.raw &&
+ sed -e "s/before/test-4/" -e "s/after/test-4/" patch4.patch.raw >patch4.patch &&
>test-4 &&
x=0 &&
while test $x -lt 50
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh
index 852dcd9..1ad4ecc 100755
--- a/t/t5150-request-pull.sh
+++ b/t/t5150-request-pull.sh
@@ -4,6 +4,12 @@
. ./test-lib.sh
+if ! test_have_prereq PERL
+then
+ skip_all='skipping request-pull tests, perl not available'
+ test_done
+fi
+
test_expect_success 'setup' '
git init --bare upstream.git &&
diff --git a/t/t5314-pack-cycle-detection.sh b/t/t5314-pack-cycle-detection.sh
index e525466..0aec861 100755
--- a/t/t5314-pack-cycle-detection.sh
+++ b/t/t5314-pack-cycle-detection.sh
@@ -53,7 +53,7 @@
-# Create a pack containing the the tree $1 and blob $1:file, with
+# Create a pack containing the tree $1 and blob $1:file, with
# the latter stored as a delta against $2:file.
#
# We convince pack-objects to make the delta in the direction of our choosing
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index 2d2f5d0..dc04465 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -45,12 +45,7 @@
git -C r1 index-pack ../filter.pack &&
git -C r1 verify-pack -v ../filter.pack >verify_result &&
- grep blob verify_result |
- awk -f print_1.awk |
- sort >observed &&
-
- nr=$(wc -l <observed) &&
- test 0 -eq $nr
+ ! grep blob verify_result
'
test_expect_success 'verify normal and blob:none packfiles have same commits/trees' '
@@ -72,7 +67,8 @@
echo foo >r5/foo &&
git -C r5 add foo &&
git -C r5 commit -m "foo" &&
- del=$(git -C r5 rev-parse HEAD^{tree} | sed "s|..|&/|") &&
+ git -C r5 rev-parse HEAD^{tree} >tree &&
+ del=$(sed "s|..|&/|" tree) &&
rm r5/.git/objects/$del &&
test_must_fail git -C r5 pack-objects --revs --stdout 2>bad_tree <<-EOF &&
HEAD
@@ -148,12 +144,7 @@
git -C r2 index-pack ../filter.pack &&
git -C r2 verify-pack -v ../filter.pack >verify_result &&
- grep blob verify_result |
- awk -f print_1.awk |
- sort >observed &&
-
- nr=$(wc -l <observed) &&
- test 0 -eq $nr
+ ! grep blob verify_result
'
test_expect_success 'verify blob:limit=1000' '
@@ -163,12 +154,7 @@
git -C r2 index-pack ../filter.pack &&
git -C r2 verify-pack -v ../filter.pack >verify_result &&
- grep blob verify_result |
- awk -f print_1.awk |
- sort >observed &&
-
- nr=$(wc -l <observed) &&
- test 0 -eq $nr
+ ! grep blob verify_result
'
test_expect_success 'verify blob:limit=1001' '
@@ -230,10 +216,9 @@
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
- HEAD
- $(git -C r2 rev-parse HEAD:large.10000)
- EOF
+ echo HEAD >objects &&
+ git -C r2 rev-parse HEAD:large.10000 >>objects &&
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k <objects >filter.pack &&
git -C r2 index-pack ../filter.pack &&
git -C r2 verify-pack -v ../filter.pack >verify_result &&
@@ -377,7 +362,8 @@
awk -f print_2.awk ls_files_result |
sort >expected &&
- oid=$(git -C r4 ls-files -s pattern | awk -f print_2.awk) &&
+ git -C r4 ls-files -s pattern >staged &&
+ oid=$(awk -f print_2.awk staged) &&
git -C r4 pack-objects --revs --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
HEAD
EOF
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index 3a2c143..1424fab 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -18,14 +18,16 @@
p
}' <out | test-tool pkt-line unpack-sideband >o.pack &&
git index-pack o.pack &&
- git verify-pack -v o.idx | grep commit | cut -c-40 | sort >actual_commits
+ git verify-pack -v o.idx >objs &&
+ grep commit objs | cut -c-40 | sort >actual_commits
}
check_output () {
get_actual_refs &&
test_cmp expected_refs actual_refs &&
get_actual_commits &&
- test_cmp expected_commits actual_commits
+ sort expected_commits >sorted_commits &&
+ test_cmp sorted_commits actual_commits
}
# c(o/foo) d(o/bar)
@@ -75,17 +77,19 @@
'
test_expect_success 'basic want-ref' '
+ oid=$(git rev-parse f) &&
cat >expected_refs <<-EOF &&
- $(git rev-parse f) refs/heads/master
+ $oid refs/heads/master
EOF
- git rev-parse f | sort >expected_commits &&
+ git rev-parse f >expected_commits &&
+ oid=$(git rev-parse a) &&
test-tool pkt-line pack >in <<-EOF &&
command=fetch
0001
no-progress
want-ref refs/heads/master
- have $(git rev-parse a)
+ have $oid
done
0000
EOF
@@ -95,19 +99,22 @@
'
test_expect_success 'multiple want-ref lines' '
+ oid_c=$(git rev-parse c) &&
+ oid_d=$(git rev-parse d) &&
cat >expected_refs <<-EOF &&
- $(git rev-parse c) refs/heads/o/foo
- $(git rev-parse d) refs/heads/o/bar
+ $oid_c refs/heads/o/foo
+ $oid_d refs/heads/o/bar
EOF
- git rev-parse c d | sort >expected_commits &&
+ git rev-parse c d >expected_commits &&
+ oid=$(git rev-parse b) &&
test-tool pkt-line pack >in <<-EOF &&
command=fetch
0001
no-progress
want-ref refs/heads/o/foo
want-ref refs/heads/o/bar
- have $(git rev-parse b)
+ have $oid
done
0000
EOF
@@ -117,10 +124,11 @@
'
test_expect_success 'mix want and want-ref' '
+ oid=$(git rev-parse f) &&
cat >expected_refs <<-EOF &&
- $(git rev-parse f) refs/heads/master
+ $oid refs/heads/master
EOF
- git rev-parse e f | sort >expected_commits &&
+ git rev-parse e f >expected_commits &&
test-tool pkt-line pack >in <<-EOF &&
command=fetch
@@ -138,17 +146,19 @@
'
test_expect_success 'want-ref with ref we already have commit for' '
+ oid=$(git rev-parse c) &&
cat >expected_refs <<-EOF &&
- $(git rev-parse c) refs/heads/o/foo
+ $oid refs/heads/o/foo
EOF
>expected_commits &&
+ oid=$(git rev-parse c) &&
test-tool pkt-line pack >in <<-EOF &&
command=fetch
0001
no-progress
want-ref refs/heads/o/foo
- have $(git rev-parse c)
+ have $oid
done
0000
EOF
@@ -211,13 +221,14 @@
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
+ oid=$(git -C "$REPO" rev-parse d) &&
GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
- $(git -C "$REPO" rev-parse d):refs/heads/actual &&
+ "$oid":refs/heads/actual &&
git -C "$REPO" rev-parse "d" >expected &&
git -C local rev-parse refs/heads/actual >actual &&
test_cmp expected actual &&
- grep "want $(git -C "$REPO" rev-parse d)" log
+ grep "want $oid" log
'
test_expect_success 'fetching multiple refs' '
@@ -239,13 +250,14 @@
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
+ oid=$(git -C "$REPO" rev-parse b) &&
GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
- master $(git -C "$REPO" rev-parse b):refs/heads/actual &&
+ master "$oid":refs/heads/actual &&
git -C "$REPO" rev-parse "master" "b" >expected &&
git -C local rev-parse refs/remotes/origin/master refs/heads/actual >actual &&
test_cmp expected actual &&
- grep "want $(git -C "$REPO" rev-parse b)" log &&
+ grep "want $oid" log &&
grep "want-ref refs/heads/master" log
'
@@ -312,10 +324,9 @@
# repository appears to change during negotiation, for example, when
# different servers in a load-balancing arrangement serve (stateless)
# RPCs during a single negotiation.
- printf "s/%s/%s/" \
- $(git -C "$REPO" rev-parse $1 | tr -d "\n") \
- $(git -C "$REPO" rev-parse $2 | tr -d "\n") \
- >"$HTTPD_ROOT_PATH/one-time-sed"
+ oid1=$(git -C "$REPO" rev-parse $1) &&
+ oid2=$(git -C "$REPO" rev-parse $2) &&
+ echo "s/$oid1/$oid2/" >"$HTTPD_ROOT_PATH/one-time-sed"
}
test_expect_success 'server is initially ahead - no ref in want' '
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 45047d0..09c50f3 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -1,28 +1,27 @@
#!/bin/sh
-test_description='test describe
+test_description='test describe'
- B
- .--------------o----o----o----x
- / / /
- o----o----o----o----o----. /
- \ A c /
- .------------o---o---o
- D,R e
-'
+# o---o-----o----o----o-------o----x
+# \ D,R e /
+# \---o-------------o-'
+# \ B /
+# `-o----o----o-'
+# A c
+#
+# First parent of a merge commit is on the same line, second parent below.
+
. ./test-lib.sh
check_describe () {
expect="$1"
shift
- R=$(git describe "$@" 2>err.actual)
- S=$?
- cat err.actual >&3
- test_expect_success "describe $*" '
- test $S = 0 &&
+ describe_opts="$@"
+ test_expect_success "describe $describe_opts" '
+ R=$(git describe $describe_opts 2>err.actual) &&
case "$R" in
$expect) echo happy ;;
- *) echo "Oops - $R is not $expect";
+ *) echo "Oops - $R is not $expect" &&
false ;;
esac
'
@@ -382,7 +381,7 @@
test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
'
-test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
+test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
i=1 &&
while test $i -lt 8000
do
@@ -439,4 +438,45 @@
test_cmp expect actual
'
+# A--------------master
+# \ /
+# \----------M2
+# \ /
+# \---M1-C
+# \ /
+# B
+test_expect_success 'name-rev covers all conditions while looking at parents' '
+ git init repo &&
+ (
+ cd repo &&
+
+ echo A >file &&
+ git add file &&
+ git commit -m A &&
+ A=$(git rev-parse HEAD) &&
+
+ git checkout --detach &&
+ echo B >file &&
+ git commit -m B file &&
+ B=$(git rev-parse HEAD) &&
+
+ git checkout $A &&
+ git merge --no-ff $B && # M1
+
+ echo C >file &&
+ git commit -m C file &&
+
+ git checkout $A &&
+ git merge --no-ff HEAD@{1} && # M2
+
+ git checkout master &&
+ git merge --no-ff HEAD@{1} &&
+
+ echo "$B master^2^2~1^2" >expect &&
+ git name-rev $B >actual &&
+
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index f1349af..110b4bf 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -150,7 +150,7 @@
test_expect_success 'amend commit' '
cat >editor <<-\EOF &&
#!/bin/sh
- sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
+ sed -e "s/a file/an amend commit/g" <"$1" >"$1-"
mv "$1-" "$1"
EOF
chmod 755 editor &&
@@ -263,7 +263,7 @@
test_expect_success 'editing message from other commit' '
cat >editor <<-\EOF &&
#!/bin/sh
- sed -e "s/amend/older/g" < "$1" > "$1-"
+ sed -e "s/amend/older/g" <"$1" >"$1-"
mv "$1-" "$1"
EOF
chmod 755 editor &&
@@ -285,9 +285,8 @@
'
test_expect_success PERL 'interactive add' '
- echo 7 |
- git commit --interactive |
- grep "What now"
+ echo 7 | test_must_fail git commit --interactive >out &&
+ grep "What now" out
'
test_expect_success PERL "commit --interactive doesn't change index if editor aborts" '
@@ -362,12 +361,12 @@
oldtick=$GIT_AUTHOR_DATE &&
test_tick &&
git reset --hard &&
- git cat-file -p HEAD |
+ git cat-file -p HEAD >commit &&
sed -e "s/author.*/author $author $oldtick/" \
- -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
- expected &&
+ -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" \
+ commit >expected &&
git commit --amend --author="$author" &&
- git cat-file -p HEAD > current &&
+ git cat-file -p HEAD >current &&
test_cmp expected current
'
@@ -377,12 +376,12 @@
test_tick &&
newtick=$GIT_AUTHOR_DATE &&
git reset --hard &&
- git cat-file -p HEAD |
+ git cat-file -p HEAD >commit &&
sed -e "s/author.*/author $author $newtick/" \
- -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
- expected &&
+ -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" \
+ commit >expected &&
git commit --amend --date="$newtick" &&
- git cat-file -p HEAD > current &&
+ git cat-file -p HEAD >current &&
test_cmp expected current
'
@@ -409,12 +408,13 @@
echo 1 >positive &&
git add positive &&
git commit -s -m "thank you" &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ git cat-file commit HEAD >commit &&
+ sed -e "1,/^\$/d" commit >actual &&
(
echo thank you &&
echo &&
- git var GIT_COMMITTER_IDENT |
- sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ git var GIT_COMMITTER_IDENT >ident &&
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" ident
) >expected &&
test_cmp expected actual
@@ -428,13 +428,14 @@
git commit -s -m "thank you
$existing" &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ git cat-file commit HEAD >commit &&
+ sed -e "1,/^\$/d" commit >actual &&
(
echo thank you &&
echo &&
echo $existing &&
- git var GIT_COMMITTER_IDENT |
- sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ git var GIT_COMMITTER_IDENT >ident &&
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" ident
) >expected &&
test_cmp expected actual
@@ -448,13 +449,14 @@
git commit -s -m "welcome
$alt" &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+ git cat-file commit HEAD >commit &&
+ sed -e "1,/^\$/d" commit >actual &&
(
echo welcome &&
echo &&
echo $alt &&
- git var GIT_COMMITTER_IDENT |
- sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ git var GIT_COMMITTER_IDENT >ident &&
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" ident
) >expected &&
test_cmp expected actual
'
@@ -468,15 +470,16 @@
We have now
$alt" &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+ git cat-file commit HEAD >commit &&
+ sed -e "1,/^\$/d" commit >actual &&
(
echo welcome &&
echo &&
echo We have now &&
echo $alt &&
echo &&
- git var GIT_COMMITTER_IDENT |
- sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ git var GIT_COMMITTER_IDENT >ident &&
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" ident
) >expected &&
test_cmp expected actual
'
@@ -489,7 +492,8 @@
non-trailer line
Myfooter: x" &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+ git cat-file commit HEAD >commit &&
+ sed -e "1,/^\$/d" commit >actual &&
(
echo subject &&
echo &&
@@ -506,7 +510,8 @@
non-trailer line
Myfooter: x" &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+ git cat-file commit HEAD >commit &&
+ sed -e "1,/^\$/d" commit >actual &&
(
echo subject &&
echo &&
@@ -538,7 +543,8 @@
>negative &&
git add negative &&
git commit -m "one" -m "two" -m "three" &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ git cat-file commit HEAD >commit &&
+ sed -e "1,/^\$/d" commit >actual &&
(
echo one &&
echo &&
@@ -555,23 +561,25 @@
oldtick=$GIT_AUTHOR_DATE &&
test_tick &&
git reset --hard &&
- git cat-file -p HEAD |
+ git cat-file -p HEAD >commit &&
sed -e "s/author.*/author $author $oldtick/" \
- -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
- expected &&
+ -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" \
+ commit >expected &&
git commit --amend --author="$author" &&
- git cat-file -p HEAD > current &&
+ git cat-file -p HEAD >current &&
test_cmp expected current
'
test_expect_success 'git commit <file> with dirty index' '
- echo tacocat > elif &&
- echo tehlulz > chz &&
+ echo tacocat >elif &&
+ echo tehlulz >chz &&
git add chz &&
git commit elif -m "tacocat is a palindrome" &&
- git show --stat | grep elif &&
- git diff --cached | grep chz
+ git show --stat >stat &&
+ grep elif stat &&
+ git diff --cached >diff &&
+ grep chz diff
'
test_expect_success 'same tree (single parent)' '
@@ -584,7 +592,8 @@
test_expect_success 'same tree (single parent) --allow-empty' '
git commit --allow-empty -m "forced empty" &&
- git cat-file commit HEAD | grep forced
+ git cat-file commit HEAD >commit &&
+ grep forced commit
'
diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh
index a06b683..4b58901 100755
--- a/t/t7526-commit-pathspec-file.sh
+++ b/t/t7526-commit-pathspec-file.sh
@@ -127,4 +127,10 @@
verify_expect
'
+test_expect_success '--pathspec-from-file and --all cannot be used together' '
+ restore_checkpoint &&
+ test_must_fail git commit --pathspec-from-file=- --all -m "Commit" 2>err &&
+ test_i18ngrep "[-]-pathspec-from-file with -a does not make sense" err
+'
+
test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 4e855bc2..25b235c 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -4,129 +4,104 @@
. ./test-lib.sh
-commit_and_pack() {
- test_commit "$@" >/dev/null &&
- SHA1=$(git pack-objects --all --unpacked --incremental .git/objects/pack/pack </dev/null) &&
- echo pack-${SHA1}.pack
+commit_and_pack () {
+ test_commit "$@" 1>&2 &&
+ incrpackid=$(git pack-objects --all --unpacked --incremental .git/objects/pack/pack </dev/null) &&
+ echo pack-${incrpackid}.pack
+}
+
+test_no_missing_in_packs () {
+ myidx=$(ls -1 .git/objects/pack/*.idx) &&
+ test_path_is_file "$myidx" &&
+ git verify-pack -v alt_objects/pack/*.idx >orig.raw &&
+ sed -n -e "s/^\($OID_REGEX\).*/\1/p" orig.raw | sort >orig &&
+ git verify-pack -v $myidx >dest.raw &&
+ cut -d" " -f1 dest.raw | sort >dest &&
+ comm -23 orig dest >missing &&
+ test_must_be_empty missing
+}
+
+# we expect $packid and $oid to be defined
+test_has_duplicate_object () {
+ want_duplicate_object="$1"
+ found_duplicate_object=false
+ for p in .git/objects/pack/*.idx
+ do
+ idx=$(basename $p)
+ test "pack-$packid.idx" = "$idx" && continue
+ git verify-pack -v $p >packlist || return $?
+ if grep "^$oid" packlist
+ then
+ found_duplicate_object=true
+ echo "DUPLICATE OBJECT FOUND"
+ break
+ fi
+ done &&
+ test "$want_duplicate_object" = "$found_duplicate_object"
}
test_expect_success 'objects in packs marked .keep are not repacked' '
- echo content1 > file1 &&
- echo content2 > file2 &&
+ echo content1 >file1 &&
+ echo content2 >file2 &&
git add . &&
test_tick &&
git commit -m initial_commit &&
# Create two packs
# The first pack will contain all of the objects except one
- git rev-list --objects --all | grep -v file2 |
- git pack-objects pack > /dev/null &&
+ git rev-list --objects --all >objs &&
+ grep -v file2 objs | git pack-objects pack &&
# The second pack will contain the excluded object
- packsha1=$(git rev-list --objects --all | grep file2 |
- git pack-objects pack) &&
- >pack-$packsha1.keep &&
- objsha1=$(git verify-pack -v pack-$packsha1.idx | head -n 1 |
- sed -e "s/^\([0-9a-f]\{40\}\).*/\1/") &&
+ packid=$(grep file2 objs | git pack-objects pack) &&
+ >pack-$packid.keep &&
+ git verify-pack -v pack-$packid.idx >packlist &&
+ oid=$(head -n 1 packlist | sed -e "s/^\($OID_REGEX\).*/\1/") &&
mv pack-* .git/objects/pack/ &&
git repack -A -d -l &&
git prune-packed &&
- for p in .git/objects/pack/*.idx; do
- idx=$(basename $p)
- test "pack-$packsha1.idx" = "$idx" && continue
- if git verify-pack -v $p | egrep "^$objsha1"; then
- found_duplicate_object=1
- echo "DUPLICATE OBJECT FOUND"
- break
- fi
- done &&
- test -z "$found_duplicate_object"
+ test_has_duplicate_object false
'
test_expect_success 'writing bitmaps via command-line can duplicate .keep objects' '
- # build on $objsha1, $packsha1, and .keep state from previous
+ # build on $oid, $packid, and .keep state from previous
git repack -Adbl &&
- test_when_finished "found_duplicate_object=" &&
- for p in .git/objects/pack/*.idx; do
- idx=$(basename $p)
- test "pack-$packsha1.idx" = "$idx" && continue
- if git verify-pack -v $p | egrep "^$objsha1"; then
- found_duplicate_object=1
- echo "DUPLICATE OBJECT FOUND"
- break
- fi
- done &&
- test "$found_duplicate_object" = 1
+ test_has_duplicate_object true
'
test_expect_success 'writing bitmaps via config can duplicate .keep objects' '
- # build on $objsha1, $packsha1, and .keep state from previous
+ # build on $oid, $packid, and .keep state from previous
git -c repack.writebitmaps=true repack -Adl &&
- test_when_finished "found_duplicate_object=" &&
- for p in .git/objects/pack/*.idx; do
- idx=$(basename $p)
- test "pack-$packsha1.idx" = "$idx" && continue
- if git verify-pack -v $p | egrep "^$objsha1"; then
- found_duplicate_object=1
- echo "DUPLICATE OBJECT FOUND"
- break
- fi
- done &&
- test "$found_duplicate_object" = 1
+ test_has_duplicate_object true
'
test_expect_success 'loose objects in alternate ODB are not repacked' '
mkdir alt_objects &&
- echo $(pwd)/alt_objects > .git/objects/info/alternates &&
- echo content3 > file3 &&
- objsha1=$(GIT_OBJECT_DIRECTORY=alt_objects git hash-object -w file3) &&
+ echo $(pwd)/alt_objects >.git/objects/info/alternates &&
+ echo content3 >file3 &&
+ oid=$(GIT_OBJECT_DIRECTORY=alt_objects git hash-object -w file3) &&
git add file3 &&
test_tick &&
git commit -m commit_file3 &&
git repack -a -d -l &&
git prune-packed &&
- for p in .git/objects/pack/*.idx; do
- if git verify-pack -v $p | egrep "^$objsha1"; then
- found_duplicate_object=1
- echo "DUPLICATE OBJECT FOUND"
- break
- fi
- done &&
- test -z "$found_duplicate_object"
+ test_has_duplicate_object false
'
test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
mkdir alt_objects/pack &&
mv .git/objects/pack/* alt_objects/pack &&
git repack -a &&
- myidx=$(ls -1 .git/objects/pack/*.idx) &&
- test -f "$myidx" &&
- for p in alt_objects/pack/*.idx; do
- git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
- done | while read sha1 rest; do
- if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
- echo "Missing object in local pack: $sha1"
- return 1
- fi
- done
+ test_no_missing_in_packs
'
test_expect_success 'packed obs in alt ODB are repacked when local repo has packs' '
rm -f .git/objects/pack/* &&
- echo new_content >> file1 &&
+ echo new_content >>file1 &&
git add file1 &&
test_tick &&
git commit -m more_content &&
git repack &&
git repack -a -d &&
- myidx=$(ls -1 .git/objects/pack/*.idx) &&
- test -f "$myidx" &&
- for p in alt_objects/pack/*.idx; do
- git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
- done | while read sha1 rest; do
- if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
- echo "Missing object in local pack: $sha1"
- return 1
- fi
- done
+ test_no_missing_in_packs
'
test_expect_success 'packed obs in alternate ODB kept pack are repacked' '
@@ -134,7 +109,7 @@
for p in alt_objects/pack/*.pack
do
base_name=$(basename $p .pack) &&
- if test -f alt_objects/pack/$base_name.keep
+ if test_path_is_file alt_objects/pack/$base_name.keep
then
rm alt_objects/pack/$base_name.keep
else
@@ -142,22 +117,13 @@
fi
done &&
git repack -a -d &&
- myidx=$(ls -1 .git/objects/pack/*.idx) &&
- test -f "$myidx" &&
- for p in alt_objects/pack/*.idx; do
- git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
- done | while read sha1 rest; do
- if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
- echo "Missing object in local pack: $sha1"
- return 1
- fi
- done
+ test_no_missing_in_packs
'
test_expect_success 'packed unreachable obs in alternate ODB are not loosened' '
rm -f alt_objects/pack/*.keep &&
mv .git/objects/pack/* alt_objects/pack/ &&
- csha1=$(git rev-parse HEAD^{commit}) &&
+ coid=$(git rev-parse HEAD^{commit}) &&
git reset --hard HEAD^ &&
test_tick &&
git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
@@ -167,15 +133,15 @@
--unpack-unreachable </dev/null pack &&
rm -f .git/objects/pack/* &&
mv pack-* .git/objects/pack/ &&
- test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
- egrep "^$csha1 " | sort | uniq | wc -l) &&
- echo > .git/objects/info/alternates &&
- test_must_fail git show $csha1
+ git verify-pack -v -- .git/objects/pack/*.idx >packlist &&
+ ! grep "^$coid " packlist &&
+ echo >.git/objects/info/alternates &&
+ test_must_fail git show $coid
'
test_expect_success 'local packed unreachable obs that exist in alternate ODB are not loosened' '
- echo $(pwd)/alt_objects > .git/objects/info/alternates &&
- echo "$csha1" | git pack-objects --non-empty --all --reflog pack &&
+ echo $(pwd)/alt_objects >.git/objects/info/alternates &&
+ echo "$coid" | git pack-objects --non-empty --all --reflog pack &&
rm -f .git/objects/pack/* &&
mv pack-* .git/objects/pack/ &&
# The pack-objects call on the next line is equivalent to
@@ -184,10 +150,10 @@
--unpack-unreachable </dev/null pack &&
rm -f .git/objects/pack/* &&
mv pack-* .git/objects/pack/ &&
- test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
- egrep "^$csha1 " | sort | uniq | wc -l) &&
- echo > .git/objects/info/alternates &&
- test_must_fail git show $csha1
+ git verify-pack -v -- .git/objects/pack/*.idx >packlist &&
+ ! grep "^$coid " &&
+ echo >.git/objects/info/alternates &&
+ test_must_fail git show $coid
'
test_expect_success 'objects made unreachable by grafts only are kept' '
@@ -196,7 +162,7 @@
H0=$(git rev-parse HEAD) &&
H1=$(git rev-parse HEAD^) &&
H2=$(git rev-parse HEAD^^) &&
- echo "$H0 $H2" > .git/info/grafts &&
+ echo "$H0 $H2" >.git/info/grafts &&
git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
git repack -a -d &&
git cat-file -t $H1
@@ -235,7 +201,7 @@
test_expect_success 'bitmaps can be disabled on bare repos' '
git -c repack.writeBitmaps=false -C bare.git repack -ad &&
- bitmap=$(ls bare.git/objects/pack/*.bitmap 2>/dev/null || :) &&
+ bitmap=$(ls bare.git/objects/pack/*.bitmap || :) &&
test -z "$bitmap"
'
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 05f07a1..ae9950a 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -3191,13 +3191,22 @@
exec 9<>V.output
rm V.output
- git fast-import $options <&8 >&9 &
- echo $! >V.pid
+ (
+ git fast-import $options <&8 >&9 &
+ echo $! >&9
+ wait $!
+ echo >&2 "background fast-import terminated too early with exit code $?"
+ # Un-block the read loop in the main shell process.
+ echo >&9 UNEXPECTED
+ ) &
+ sh_pid=$!
+ read fi_pid <&9
# We don't mind if fast-import has already died by the time the test
# ends.
test_when_finished "
exec 8>&-; exec 9>&-;
- kill $(cat V.pid) && wait $(cat V.pid)
+ kill $sh_pid && wait $sh_pid
+ kill $fi_pid && wait $fi_pid
true"
# Start in the background to ensure we adhere strictly to (blocking)
@@ -3217,6 +3226,9 @@
then
error=0
break
+ elif test "$output" = "UNEXPECTED"
+ then
+ break
fi
# otherwise ignore cruft
echo >&2 "cruft: $output"
@@ -3229,7 +3241,7 @@
}
background_import_still_running () {
- if ! kill -0 "$(cat V.pid)"
+ if ! kill -0 "$fi_pid"
then
echo >&2 "background fast-import terminated too early"
false
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index ec3eccf..93877ba 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1438,6 +1438,8 @@
--no-guess Z
--no-... Z
--overlay Z
+ --pathspec-file-nul Z
+ --pathspec-from-file=Z
EOF
'
diff --git a/trace.h b/trace.h
index 9fa3e7a..9826618 100644
--- a/trace.h
+++ b/trace.h
@@ -4,6 +4,82 @@
#include "git-compat-util.h"
#include "strbuf.h"
+/**
+ * The trace API can be used to print debug messages to stderr or a file. Trace
+ * code is inactive unless explicitly enabled by setting `GIT_TRACE*` environment
+ * variables.
+ *
+ * The trace implementation automatically adds `timestamp file:line ... \n` to
+ * all trace messages. E.g.:
+ *
+ * ------------
+ * 23:59:59.123456 git.c:312 trace: built-in: git 'foo'
+ * 00:00:00.000001 builtin/foo.c:99 foo: some message
+ * ------------
+ *
+ * Bugs & Caveats
+ * --------------
+ *
+ * GIT_TRACE_* environment variables can be used to tell Git to show
+ * trace output to its standard error stream. Git can often spawn a pager
+ * internally to run its subcommand and send its standard output and
+ * standard error to it.
+ *
+ * Because GIT_TRACE_PERFORMANCE trace is generated only at the very end
+ * of the program with atexit(), which happens after the pager exits, it
+ * would not work well if you send its log to the standard error output
+ * and let Git spawn the pager at the same time.
+ *
+ * As a work around, you can for example use '--no-pager', or set
+ * GIT_TRACE_PERFORMANCE to another file descriptor which is redirected
+ * to stderr, or set GIT_TRACE_PERFORMANCE to a file specified by its
+ * absolute path.
+ *
+ * For example instead of the following command which by default may not
+ * print any performance information:
+ *
+ * ------------
+ * GIT_TRACE_PERFORMANCE=2 git log -1
+ * ------------
+ *
+ * you may want to use:
+ *
+ * ------------
+ * GIT_TRACE_PERFORMANCE=2 git --no-pager log -1
+ * ------------
+ *
+ * or:
+ *
+ * ------------
+ * GIT_TRACE_PERFORMANCE=3 3>&2 git log -1
+ * ------------
+ *
+ * or:
+ *
+ * ------------
+ * GIT_TRACE_PERFORMANCE=/path/to/log/file git log -1
+ * ------------
+ *
+ */
+
+/**
+ * Defines a trace key (or category). The default (for API functions that
+ * don't take a key) is `GIT_TRACE`.
+ *
+ * E.g. to define a trace key controlled by environment variable `GIT_TRACE_FOO`:
+ *
+ * ------------
+ * static struct trace_key trace_foo = TRACE_KEY_INIT(FOO);
+ *
+ * static void trace_print_foo(const char *message)
+ * {
+ * trace_printf_key(&trace_foo, "%s", message);
+ * }
+ * ------------
+ *
+ * Note: don't use `const` as the trace implementation stores internal state in
+ * the `trace_key` structure.
+ */
struct trace_key {
const char * const key;
int fd;
@@ -18,31 +94,84 @@
extern struct trace_key trace_setup_key;
void trace_repo_setup(const char *prefix);
+
+/**
+ * Checks whether the trace key is enabled. Used to prevent expensive
+ * string formatting before calling one of the printing APIs.
+ */
int trace_want(struct trace_key *key);
+
+/**
+ * Disables tracing for the specified key, even if the environment variable
+ * was set.
+ */
void trace_disable(struct trace_key *key);
+
+/**
+ * Returns nanoseconds since the epoch (01/01/1970), typically used
+ * for performance measurements.
+ * Currently there are high precision timer implementations for Linux (using
+ * `clock_gettime(CLOCK_MONOTONIC)`) and Windows (`QueryPerformanceCounter`).
+ * Other platforms use `gettimeofday` as time source.
+ */
uint64_t getnanotime(void);
+
void trace_command_performance(const char **argv);
void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);
uint64_t trace_performance_enter(void);
#ifndef HAVE_VARIADIC_MACROS
+/**
+ * Prints a formatted message, similar to printf.
+ */
__attribute__((format (printf, 1, 2)))
void trace_printf(const char *format, ...);
__attribute__((format (printf, 2, 3)))
void trace_printf_key(struct trace_key *key, const char *format, ...);
+/**
+ * Prints a formatted message, followed by a quoted list of arguments.
+ */
__attribute__((format (printf, 2, 3)))
void trace_argv_printf(const char **argv, const char *format, ...);
+/**
+ * Prints the strbuf, without additional formatting (i.e. doesn't
+ * choke on `%` or even `\0`).
+ */
void trace_strbuf(struct trace_key *key, const struct strbuf *data);
-/* Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled. */
+/**
+ * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.
+ *
+ * Example:
+ * ------------
+ * uint64_t t = 0;
+ * for (;;) {
+ * // ignore
+ * t -= getnanotime();
+ * // code section to measure
+ * t += getnanotime();
+ * // ignore
+ * }
+ * trace_performance(t, "frotz");
+ * ------------
+ */
__attribute__((format (printf, 2, 3)))
void trace_performance(uint64_t nanos, const char *format, ...);
-/* Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled. */
+/**
+ * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.
+ *
+ * Example:
+ * ------------
+ * uint64_t start = getnanotime();
+ * // code section to measure
+ * trace_performance_since(start, "foobar");
+ * ------------
+ */
__attribute__((format (printf, 2, 3)))
void trace_performance_since(uint64_t start, const char *format, ...);
diff --git a/trace2.h b/trace2.h
index 050bf3c..e5e81c0 100644
--- a/trace2.h
+++ b/trace2.h
@@ -1,6 +1,40 @@
#ifndef TRACE2_H
#define TRACE2_H
+/**
+ * The Trace2 API can be used to print debug, performance, and telemetry
+ * information to stderr or a file. The Trace2 feature is inactive unless
+ * explicitly enabled by enabling one or more Trace2 Targets.
+ *
+ * The Trace2 API is intended to replace the existing (Trace1)
+ * printf-style tracing provided by the existing `GIT_TRACE` and
+ * `GIT_TRACE_PERFORMANCE` facilities. During initial implementation,
+ * Trace2 and Trace1 may operate in parallel.
+ *
+ * The Trace2 API defines a set of high-level messages with known fields,
+ * such as (`start`: `argv`) and (`exit`: {`exit-code`, `elapsed-time`}).
+ *
+ * Trace2 instrumentation throughout the Git code base sends Trace2
+ * messages to the enabled Trace2 Targets. Targets transform these
+ * messages content into purpose-specific formats and write events to
+ * their data streams. In this manner, the Trace2 API can drive
+ * many different types of analysis.
+ *
+ * Targets are defined using a VTable allowing easy extension to other
+ * formats in the future. This might be used to define a binary format,
+ * for example.
+ *
+ * Trace2 is controlled using `trace2.*` config values in the system and
+ * global config files and `GIT_TRACE2*` environment variables. Trace2 does
+ * not read from repo local or worktree config files or respect `-c`
+ * command line config settings.
+ *
+ * For more info about: trace2 targets, conventions for public functions and
+ * macros, trace2 target formats and examples on trace2 API usage refer to
+ * Documentation/technical/api-trace2.txt
+ *
+ */
+
struct child_process;
struct repository;
struct json_writer;
@@ -39,7 +73,12 @@
/*
* Initialize TRACE2 tracing facility if any of the builtin TRACE2
* targets are enabled in the system config or the environment.
- * Emits a 'version' event.
+ * This includes setting up the Trace2 thread local storage (TLS).
+ * Emits a 'version' message containing the version of git
+ * and the Trace2 protocol.
+ *
+ * This function should be called from `main()` as early as possible in
+ * the life of the process after essential process initialization.
*
* Cleanup/Termination is handled automatically by a registered
* atexit() routine.
@@ -49,7 +88,7 @@
#define trace2_initialize() trace2_initialize_fl(__FILE__, __LINE__)
/*
- * Return true if trace2 is enabled.
+ * Return 1 if trace2 is enabled (at least one target is active).
*/
int trace2_is_enabled(void);
@@ -114,7 +153,8 @@
#define trace2_cmd_mode(sv) trace2_cmd_mode_fl(__FILE__, __LINE__, (sv))
/*
- * Emit an 'alias' expansion event.
+ * Emits an "alias" message containing the alias used and the argument
+ * expansion.
*/
void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
const char **argv);
@@ -123,7 +163,7 @@
trace2_cmd_alias_fl(__FILE__, __LINE__, (alias), (argv))
/*
- * Emit one or more 'def_param' events for "interesting" configuration
+ * Emit one or more 'def_param' events for "important" configuration
* settings.
*
* Use the TR2_SYSENV_CFG_PARAM setting to register a comma-separated
@@ -144,7 +184,7 @@
/*
* Emit a "def_param" event for the given config key/value pair IF
- * we consider the key to be "interesting".
+ * we consider the key to be "important".
*
* Use this for new/updated config settings created/updated after
* trace2_cmd_list_config() is called.
@@ -155,20 +195,34 @@
#define trace2_cmd_set_config(k, v) \
trace2_cmd_set_config_fl(__FILE__, __LINE__, (k), (v))
-/*
- * Emit a 'child_start' event prior to spawning a child process.
+/**
+ * Emits a "child_start" message containing the "child-id",
+ * "child-argv", and "child-classification".
*
* Before calling optionally set "cmd->trace2_child_class" to a string
* describing the type of the child process. For example, "editor" or
* "pager".
+ *
+ * This function assigns a unique "child-id" to `cmd->trace2_child_id`.
+ * This field is used later during the "child_exit" message to associate
+ * it with the "child_start" message.
+ *
+ * This function should be called before spawning the child process.
*/
void trace2_child_start_fl(const char *file, int line,
struct child_process *cmd);
#define trace2_child_start(cmd) trace2_child_start_fl(__FILE__, __LINE__, (cmd))
-/*
- * Emit a 'child_exit' event after the child process completes.
+/**
+ * Emits a "child_exit" message containing the "child-id",
+ * the child's elapsed time and exit-code.
+ *
+ * The reported elapsed time includes the process creation overhead and
+ * time spend waiting for it to exit, so it may be slightly longer than
+ * the time reported by the child itself.
+ *
+ * This function should be called after reaping the child process.
*/
void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
int child_exit_code);
@@ -176,21 +230,22 @@
#define trace2_child_exit(cmd, code) \
trace2_child_exit_fl(__FILE__, __LINE__, (cmd), (code))
-/*
+/**
* Emit an 'exec' event prior to calling one of exec(), execv(),
* execvp(), and etc. On Unix-derived systems, this will be the
* last event emitted for the current process, unless the exec
* fails. On Windows, exec() behaves like 'child_start' and a
* waitpid(), so additional events may be emitted.
*
- * Returns the "exec_id".
+ * Returns a unique "exec-id". This value is used later
+ * if the exec() fails and a "exec-result" message is necessary.
*/
int trace2_exec_fl(const char *file, int line, const char *exe,
const char **argv);
#define trace2_exec(exe, argv) trace2_exec_fl(__FILE__, __LINE__, (exe), (argv))
-/*
+/**
* Emit an 'exec_result' when possible. On Unix-derived systems,
* this should be called after exec() returns (which only happens
* when there is an error starting the new process). On Windows,
@@ -226,11 +281,12 @@
#define trace2_thread_exit() trace2_thread_exit_fl(__FILE__, __LINE__)
/*
- * Emit a 'param' event.
+ * Emits a "def_param" message containing a key/value pair.
*
- * Write a "<param> = <value>" pair describing some aspect of the
- * run such as an important configuration setting or command line
- * option that significantly changes command behavior.
+ * This message is intended to report some global aspect of the current
+ * command, such as a configuration setting or command line switch that
+ * significantly affects program performance or behavior, such as
+ * `core.abbrev`, `status.showUntrackedFiles`, or `--no-ahead-behind`.
*/
void trace2_def_param_fl(const char *file, int line, const char *param,
const char *value);
@@ -243,18 +299,35 @@
* a trace2-repo-id to be used in subsequent activity events.
*
* Emits a 'worktree' event for this repo instance.
+ *
+ * Region and data messages may refer to this repo-id.
+ *
+ * The main/top-level repository will have repo-id value 1 (aka "r1").
+ *
+ * The repo-id field is in anticipation of future in-proc submodule
+ * repositories.
*/
void trace2_def_repo_fl(const char *file, int line, struct repository *repo);
#define trace2_def_repo(repo) trace2_def_repo_fl(__FILE__, __LINE__, repo)
-/*
+/**
* Emit a 'region_enter' event for <category>.<label> with optional
* repo-id and printf message.
*
- * Enter a new nesting level on the current thread and remember the
- * current time. This controls the indenting of all subsequent events
- * on this thread.
+ * This function pushes a new region nesting stack level on the current
+ * thread and starts a clock for the new stack frame.
+ *
+ * The `category` field is an arbitrary category name used to classify
+ * regions by feature area, such as "status" or "index". At this time
+ * it is only just printed along with the rest of the message. It may
+ * be used in the future to filter messages.
+ *
+ * The `label` field is an arbitrary label used to describe the activity
+ * being started, such as "read_recursive" or "do_read_index".
+ *
+ * The `repo` field, if set, will be used to get the "repo-id", so that
+ * recursive oerations can be attributed to the correct repository.
*/
void trace2_region_enter_fl(const char *file, int line, const char *category,
const char *label, const struct repository *repo, ...);
@@ -289,12 +362,17 @@
/* clang-format on */
#endif
-/*
+/**
* Emit a 'region_leave' event for <category>.<label> with optional
* repo-id and printf message.
*
* Leave current nesting level and report the elapsed time spent
* in this nesting level.
+ *
+ * The `category`, `label`, and `repo` fields are the same as
+ * trace2_region_enter_fl. The `category` and `label` do not
+ * need to match the corresponding "region_enter" message,
+ * but it makes the data stream easier to understand.
*/
void trace2_region_leave_fl(const char *file, int line, const char *category,
const char *label, const struct repository *repo, ...);
@@ -329,10 +407,12 @@
/* clang-format on */
#endif
-/*
+/**
* Emit a key-value pair 'data' event of the form <category>.<key> = <value>.
* This event implicitly contains information about thread, nesting region,
* and optional repo-id.
+ * This could be used to print the number of files in a directory during
+ * a multi-threaded recursive tree walk.
*
* On event-based TRACE2 targets, this generates a 'data' event suitable
* for post-processing. On printf-based TRACE2 targets, this is converted
diff --git a/tree-walk.h b/tree-walk.h
index abe2caf..826396c 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -3,6 +3,13 @@
#include "cache.h"
+/**
+ * The tree walking API is used to traverse and inspect trees.
+ */
+
+/**
+ * An entry in a tree. Each entry has a sha1 identifier, pathname, and mode.
+ */
struct name_entry {
struct object_id oid;
const char *path;
@@ -10,12 +17,29 @@
unsigned int mode;
};
+/**
+ * A semi-opaque data structure used to maintain the current state of the walk.
+ */
struct tree_desc {
+ /*
+ * pointer into the memory representation of the tree. It always
+ * points at the current entry being visited.
+ */
const void *buffer;
+
+ /* points to the current entry being visited. */
struct name_entry entry;
+
+ /* counts the number of bytes left in the `buffer`. */
unsigned int size;
};
+/**
+ * Decode the entry currently being visited (the one pointed to by
+ * `tree_desc's` `entry` member) and return the sha1 of the entry. The
+ * `pathp` and `modep` arguments are set to the entry's pathname and mode
+ * respectively.
+ */
static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)
{
*pathp = desc->entry.path;
@@ -23,6 +47,11 @@
return &desc->entry.oid;
}
+/**
+ * Calculate the length of a tree entry's pathname. This utilizes the
+ * memory structure of a tree entry to avoid the overhead of using a
+ * generic strlen().
+ */
static inline int tree_entry_len(const struct name_entry *ne)
{
return ne->pathlen;
@@ -33,52 +62,141 @@
* corrupt tree entry rather than dying,
*/
+/**
+ * Walk to the next entry in a tree. This is commonly used in conjunction
+ * with `tree_entry_extract` to inspect the current entry.
+ */
void update_tree_entry(struct tree_desc *);
+
int update_tree_entry_gently(struct tree_desc *);
+
+/**
+ * Initialize a `tree_desc` and decode its first entry. The buffer and
+ * size parameters are assumed to be the same as the buffer and size
+ * members of `struct tree`.
+ */
void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
+
int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size);
/*
- * Helper function that does both tree_entry_extract() and update_tree_entry()
- * and returns true for success
+ * Visit the next entry in a tree. Returns 1 when there are more entries
+ * left to visit and 0 when all entries have been visited. This is
+ * commonly used in the test of a while loop.
*/
int tree_entry(struct tree_desc *, struct name_entry *);
+
int tree_entry_gently(struct tree_desc *, struct name_entry *);
+/**
+ * Initialize a `tree_desc` and decode its first entry given the
+ * object ID of a tree. Returns the `buffer` member if the latter
+ * is a valid tree identifier and NULL otherwise.
+ */
void *fill_tree_descriptor(struct repository *r,
struct tree_desc *desc,
const struct object_id *oid);
struct traverse_info;
typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
+
+/**
+ * Traverse `n` number of trees in parallel. The `fn` callback member of
+ * `traverse_info` is called once for each tree entry.
+ */
int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);
enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r, struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode);
+/**
+ * A structure used to maintain the state of a traversal.
+ */
struct traverse_info {
const char *traverse_path;
+
+ /*
+ * points to the traverse_info which was used to descend into the
+ * current tree. If this is the top-level tree `prev` will point to
+ * a dummy traverse_info.
+ */
struct traverse_info *prev;
+
+ /* is the entry for the current tree (if the tree is a subtree). */
const char *name;
+
size_t namelen;
unsigned mode;
+ /* is the length of the full path for the current tree. */
size_t pathlen;
+
struct pathspec *pathspec;
+ /* can be used by callbacks to maintain directory-file conflicts. */
unsigned long df_conflicts;
+
+ /* a callback called for each entry in the tree.
+ *
+ * The arguments passed to the traverse callback are as follows:
+ *
+ * - `n` counts the number of trees being traversed.
+ *
+ * - `mask` has its nth bit set if something exists in the nth entry.
+ *
+ * - `dirmask` has its nth bit set if the nth tree's entry is a directory.
+ *
+ * - `entry` is an array of size `n` where the nth entry is from the nth tree.
+ *
+ * - `info` maintains the state of the traversal.
+ *
+ * Returning a negative value will terminate the traversal. Otherwise the
+ * return value is treated as an update mask. If the nth bit is set the nth tree
+ * will be updated and if the bit is not set the nth tree entry will be the
+ * same in the next callback invocation.
+ */
traverse_callback_t fn;
+
+ /* can be anything the `fn` callback would want to use. */
void *data;
+
+ /* tells whether to stop at the first error or not. */
int show_all_errors;
};
+/**
+ * Find an entry in a tree given a pathname and the sha1 of a tree to
+ * search. Returns 0 if the entry is found and -1 otherwise. The third
+ * and fourth parameters are set to the entry's sha1 and mode respectively.
+ */
int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *);
+
+/**
+ * Generate the full pathname of a tree entry based from the root of the
+ * traversal. For example, if the traversal has recursed into another
+ * tree named "bar" the pathname of an entry "baz" in the "bar"
+ * tree would be "bar/baz".
+ */
char *make_traverse_path(char *path, size_t pathlen, const struct traverse_info *info,
const char *name, size_t namelen);
+
+/**
+ * Convenience wrapper to `make_traverse_path` into a strbuf.
+ */
void strbuf_make_traverse_path(struct strbuf *out,
const struct traverse_info *info,
const char *name, size_t namelen);
+
+/**
+ * Initialize a `traverse_info` given the pathname of the tree to start
+ * traversing from.
+ */
void setup_traverse_info(struct traverse_info *info, const char *base);
+/**
+ * Calculate the length of a pathname returned by `make_traverse_path`.
+ * This utilizes the memory structure of a tree entry to avoid the
+ * overhead of using a generic strlen().
+ */
static inline size_t traverse_path_len(const struct traverse_info *info,
size_t namelen)
{
diff --git a/unpack-trees.c b/unpack-trees.c
index a75b068..2399b68 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1269,7 +1269,8 @@
struct strbuf *prefix,
int select_mask, int clear_mask,
struct pattern_list *pl,
- enum pattern_match_result default_match);
+ enum pattern_match_result default_match,
+ int progress_nr);
/* Whole directory matching */
static int clear_ce_flags_dir(struct index_state *istate,
@@ -1278,20 +1279,23 @@
char *basename,
int select_mask, int clear_mask,
struct pattern_list *pl,
- enum pattern_match_result default_match)
+ enum pattern_match_result default_match,
+ int progress_nr)
{
struct cache_entry **cache_end;
int dtype = DT_DIR;
int rc;
- enum pattern_match_result ret;
- ret = path_matches_pattern_list(prefix->buf, prefix->len,
- basename, &dtype, pl, istate);
+ enum pattern_match_result ret, orig_ret;
+ orig_ret = path_matches_pattern_list(prefix->buf, prefix->len,
+ basename, &dtype, pl, istate);
strbuf_addch(prefix, '/');
/* If undecided, use matching result of parent dir in defval */
- if (ret == UNDECIDED)
+ if (orig_ret == UNDECIDED)
ret = default_match;
+ else
+ ret = orig_ret;
for (cache_end = cache; cache_end != cache + nr; cache_end++) {
struct cache_entry *ce = *cache_end;
@@ -1299,17 +1303,24 @@
break;
}
- /*
- * TODO: check pl, if there are no patterns that may conflict
- * with ret (iow, we know in advance the incl/excl
- * decision for the entire directory), clear flag here without
- * calling clear_ce_flags_1(). That function will call
- * the expensive path_matches_pattern_list() on every entry.
- */
- rc = clear_ce_flags_1(istate, cache, cache_end - cache,
- prefix,
- select_mask, clear_mask,
- pl, ret);
+ if (pl->use_cone_patterns && orig_ret == MATCHED_RECURSIVE) {
+ struct cache_entry **ce = cache;
+ rc = (cache_end - cache) / sizeof(struct cache_entry *);
+
+ while (ce < cache_end) {
+ (*ce)->ce_flags &= ~clear_mask;
+ ce++;
+ }
+ } else if (pl->use_cone_patterns && orig_ret == NOT_MATCHED) {
+ rc = (cache_end - cache) / sizeof(struct cache_entry *);
+ } else {
+ rc = clear_ce_flags_1(istate, cache, cache_end - cache,
+ prefix,
+ select_mask, clear_mask,
+ pl, ret,
+ progress_nr);
+ }
+
strbuf_setlen(prefix, prefix->len - 1);
return rc;
}
@@ -1334,7 +1345,8 @@
struct strbuf *prefix,
int select_mask, int clear_mask,
struct pattern_list *pl,
- enum pattern_match_result default_match)
+ enum pattern_match_result default_match,
+ int progress_nr)
{
struct cache_entry **cache_end = cache + nr;
@@ -1348,8 +1360,11 @@
int len, dtype;
enum pattern_match_result ret;
+ display_progress(istate->progress, progress_nr);
+
if (select_mask && !(ce->ce_flags & select_mask)) {
cache++;
+ progress_nr++;
continue;
}
@@ -1370,20 +1385,26 @@
prefix,
prefix->buf + prefix->len - len,
select_mask, clear_mask,
- pl, default_match);
+ pl, default_match,
+ progress_nr);
/* clear_c_f_dir eats a whole dir already? */
if (processed) {
cache += processed;
+ progress_nr += processed;
strbuf_setlen(prefix, prefix->len - len);
continue;
}
strbuf_addch(prefix, '/');
- cache += clear_ce_flags_1(istate, cache, cache_end - cache,
- prefix,
- select_mask, clear_mask, pl,
- default_match);
+ processed = clear_ce_flags_1(istate, cache, cache_end - cache,
+ prefix,
+ select_mask, clear_mask, pl,
+ default_match, progress_nr);
+
+ cache += processed;
+ progress_nr += processed;
+
strbuf_setlen(prefix, prefix->len - len - 1);
continue;
}
@@ -1398,24 +1419,41 @@
if (ret == MATCHED)
ce->ce_flags &= ~clear_mask;
cache++;
+ progress_nr++;
}
+
+ display_progress(istate->progress, progress_nr);
return nr - (cache_end - cache);
}
static int clear_ce_flags(struct index_state *istate,
int select_mask, int clear_mask,
- struct pattern_list *pl)
+ struct pattern_list *pl,
+ int show_progress)
{
static struct strbuf prefix = STRBUF_INIT;
+ char label[100];
+ int rval;
strbuf_reset(&prefix);
+ if (show_progress)
+ istate->progress = start_delayed_progress(
+ _("Updating index flags"),
+ istate->cache_nr);
- return clear_ce_flags_1(istate,
+ xsnprintf(label, sizeof(label), "clear_ce_flags(0x%08lx,0x%08lx)",
+ (unsigned long)select_mask, (unsigned long)clear_mask);
+ trace2_region_enter("unpack_trees", label, the_repository);
+ rval = clear_ce_flags_1(istate,
istate->cache,
istate->cache_nr,
&prefix,
select_mask, clear_mask,
- pl, 0);
+ pl, 0, 0);
+ trace2_region_leave("unpack_trees", label, the_repository);
+
+ stop_progress(&istate->progress);
+ return rval;
}
/*
@@ -1423,7 +1461,8 @@
*/
static void mark_new_skip_worktree(struct pattern_list *pl,
struct index_state *istate,
- int select_flag, int skip_wt_flag)
+ int select_flag, int skip_wt_flag,
+ int show_progress)
{
int i;
@@ -1447,7 +1486,7 @@
* 2. Widen worktree according to sparse-checkout file.
* Matched entries will have skip_wt_flag cleared (i.e. "in")
*/
- clear_ce_flags(istate, select_flag, skip_wt_flag, pl);
+ clear_ce_flags(istate, select_flag, skip_wt_flag, pl, show_progress);
}
static int verify_absent(const struct cache_entry *,
@@ -1472,8 +1511,9 @@
memset(&pl, 0, sizeof(pl));
if (!core_apply_sparse_checkout || !o->update)
o->skip_sparse_checkout = 1;
- if (!o->skip_sparse_checkout) {
+ if (!o->skip_sparse_checkout && !o->pl) {
char *sparse = git_pathdup("info/sparse-checkout");
+ pl.use_cone_patterns = core_sparse_checkout_cone;
if (add_patterns_from_file_to_list(sparse, "", 0, &pl, NULL) < 0)
o->skip_sparse_checkout = 1;
else
@@ -1511,7 +1551,8 @@
* Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
*/
if (!o->skip_sparse_checkout)
- mark_new_skip_worktree(o->pl, o->src_index, 0, CE_NEW_SKIP_WORKTREE);
+ mark_new_skip_worktree(o->pl, o->src_index, 0,
+ CE_NEW_SKIP_WORKTREE, o->verbose_update);
if (!dfc)
dfc = xcalloc(1, cache_entry_size(0));
@@ -1576,7 +1617,9 @@
* If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
* so apply_sparse_checkout() won't attempt to remove it from worktree
*/
- mark_new_skip_worktree(o->pl, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
+ mark_new_skip_worktree(o->pl, &o->result,
+ CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE,
+ o->verbose_update);
ret = 0;
for (i = 0; i < o->result.cache_nr; i++) {
@@ -1644,7 +1687,8 @@
done:
trace_performance_leave("unpack_trees");
- clear_pattern_list(&pl);
+ if (!o->keep_pattern_list)
+ clear_pattern_list(&pl);
return ret;
return_failed:
diff --git a/unpack-trees.h b/unpack-trees.h
index f2eee0c..ca94a42 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -59,7 +59,8 @@
quiet,
exiting_early,
show_all_errors,
- dry_run;
+ dry_run,
+ keep_pattern_list;
const char *prefix;
int cache_bottom;
struct dir_struct *dir;
diff --git a/userdiff.c b/userdiff.c
index 324916f..efbe05e 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -34,8 +34,9 @@
"|[-+*/%&^|!~]|>>|<<|&&|\\|\\|"),
PATTERNS("elixir",
"^[ \t]*((def(macro|module|impl|protocol|p)?|test)[ \t].*)$",
+ /* -- */
/* Atoms, names, and module attributes */
- "|[@:]?[a-zA-Z0-9@_?!]+"
+ "[@:]?[a-zA-Z0-9@_?!]+"
/* Numbers with specific base */
"|[-+]?0[xob][0-9a-fA-F]+"
/* Numbers */
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 30713ae..9d7d6c5 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -172,10 +172,12 @@
struct func_line func_line = { 0 };
for (xch = xscr; xch; xch = xche->next) {
+ xdchange_t *xchp = xch;
xche = xdl_get_hunk(&xch, xecfg);
if (!xch)
break;
+pre_context_calculation:
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
@@ -212,6 +214,21 @@
if (fs1 < s1) {
s2 = XDL_MAX(s2 - (s1 - fs1), 0);
s1 = fs1;
+
+ /*
+ * Did we extend context upwards into an
+ * ignored change?
+ */
+ while (xchp != xch &&
+ xchp->i1 + xchp->chg1 <= s1 &&
+ xchp->i2 + xchp->chg2 <= s2)
+ xchp = xchp->next;
+
+ /* If so, show it after all. */
+ if (xchp != xch) {
+ xch = xchp;
+ goto pre_context_calculation;
+ }
}
}