*.[1-8]
*.made
*.texi
+*.pdf
git.info
gitman.info
howto-index.txt
gitrepository-layout.txt
MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \
gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \
- gitdiffcore.txt gitrevisions.txt gitworkflows.txt
+ gitdiffcore.txt gitnamespaces.txt gitrevisions.txt gitworkflows.txt
MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
clean:
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
$(RM) *.texi *.texi+ *.texi++ git.info gitman.info
+ $(RM) *.pdf
$(RM) howto-index.txt howto/*.html doc.dep
$(RM) technical/api-*.html technical/api-index.txt
$(RM) $(cmds_txt) *.made
--- /dev/null
+Git v1.7.6.1 Release Notes
+==========================
+
+Fixes since v1.7.6
+------------------
+
+ * Various codepaths that invoked zlib deflate/inflate assumed that these
+ functions can compress or uncompress more than 4GB data in one call on
+ platforms with 64-bit long, which has been corrected.
+
+ * "git unexecutable" reported that "unexecutable" was not found, even
+ though the actual error was that "unexecutable" was found but did
+ not have a proper she-bang line to be executed.
+
+ * Error exits from $PAGER were silently ignored.
+
+ * "git checkout -b <branch>" was confused when attempting to create a
+ branch whose name ends with "-g" followed by hexadecimal digits,
+ and refused to work.
+
+ * "git checkout -b <branch>" sometimes wrote a bogus reflog entry,
+ causing later "git checkout -" to fail.
+
+ * "git diff --cc" learned to correctly ignore binary files.
+
+ * "git diff -c/--cc" mishandled a deletion that resolves a conflict, and
+ looked in the working tree instead.
+
+ * "git fast-export" forgot to quote pathnames with unsafe characters
+ in its output.
+
+ * "git fetch" over smart-http transport used to abort when the
+ repository was updated between the initial connection and the
+ subsequent object transfer.
+
+ * "git fetch" did not recurse into submodules in subdirectories.
+
+ * "git ls-tree" did not error out when asked to show a corrupt tree.
+
+ * "git pull" without any argument left an extra whitespace after the
+ command name in its reflog.
+
+ * "git push --quiet" was not really quiet.
+
+ * "git rebase -i -p" incorrectly dropped commits from side branches.
+
+ * "git reset [<commit>] paths..." did not reset the index entry correctly
+ for unmerged paths.
+
+ * "git submodule add" did not allow a relative repository path when
+ the superproject did not have any default remote url.
+
+ * "git submodule foreach" failed to correctly give the standard input to
+ the user-supplied command it invoked.
+
+ * submodules that the user has never showed interest in by running
+ "git submodule init" was incorrectly marked as interesting by "git
+ submodule sync".
+
+ * "git submodule update --quiet" was not really quiet.
+
+ * "git tag -l <glob>..." did not take multiple glob patterns from the
+ command line.
* The scripting part of the codebase is getting prepared for i18n/l10n.
- * Interix and Cygwin ports got updated.
+ * Interix, Cygwin and Minix ports got updated.
+
+ * A handful of patches to update git-p4 (in contrib/).
+
+ * Gitweb learned to read from /etc/gitweb-common.conf when it exists,
+ before reading from gitweb_config.perl or from /etc/gitweb.conf
+ (this last one is read only when per-repository gitweb_config.perl
+ does not exist).
* Various codepaths that invoked zlib deflate/inflate assumed that these
functions can compress or uncompress more than 4GB data in one call on
platforms with 64-bit long, which has been corrected.
+ * Git now recognizes loose objects written by other implementations that
+ uses non-standard window size for zlib deflation (e.g. Agit running on
+ Android with 4kb window). We used to reject anything that was not
+ deflated with 32kb window.
+
+ * "git am" learned to pass "--exclude=<path>" option through to underlying
+ "git apply".
+
+ * You can now feed many empty lines before feeding a mbox file to
+ "git am".
+
* "git archive" can be told to pass the output to gzip compression and
produce "archive.tar.gz".
+ * "git bisect" can be used in a bare repository (provided if the test
+ you perform per each iteration does not need a working tree, of
+ course).
+
+ * "git check-attr" can take relative paths from the command line.
+
+ * "git check-attr" learned "--all" option to list the attributes for a
+ given path.
+
* "git checkout" (both the code to update the files upon checking out a
different branch, the code to checkout specific set of files) learned
to stream the data from object store when possible, without having to
- read the entire contents of a file in memory first.
+ read the entire contents of a file in memory first. An earlier round
+ of this code that is not in any released version had a large leak but
+ now it has been plugged.
* "git clone" can now take "--config key=value" option to set the
repository configuration options that affect the initial checkout.
+ * "git commit <paths>..." now lets you feed relative pathspecs that
+ refer outside your current subdirectory.
+
* "git diff --stat" learned --stat-count option to limit the output of
diffstat report.
+ * "git diff" learned "--histogram" option, to use a different diff
+ generation machinery stolen from jgit, which might give better
+ performance.
+
* "git fetch", "git push" and friends no longer show connection
errors for addresses that couldn't be connected when at least one
address succeeds (this is arguably a regression but a deliberate
* "git grep" learned --break and --heading options, to let users mimic
output format of "ack".
+ * "git grep" learned "-W" option that shows wider context using the same
+ logic used by "git diff" to determine the hunk header.
+
* "git rebase master topci" no longer spews usage hints after giving
"fatal: no such branch: topci" error message.
submodule; it now goes on to update other submodules that can be
updated, and reports the ones with errors at the end.
+ * "git upload-pack" and "git receive-pack" learned to pretend only a
+ subset of the refs exist in a repository. This may help a site to
+ put many tiny repositories into one repository (this would not be
+ useful for larger repositories as repacking would be problematic).
+
* "git verify-pack" has been rewritten to use the "index-pack" machinery
that is more efficient in reading objects in packfiles.
Unless otherwise noted, all the fixes in 1.7.6.X maintenance track are
included in this release.
- * "git checkout -b <branch>" sometimes wrote a bogus reflog entry,
- causing later "git checkout -" fail.
- (merge 71ee7fd jc/checkout-reflog-fix~1 later).
-
- * "git diff --cc" learned to correctly ignore binary files.
- (merge 0508fe5 jk/combine-diff-binary-etc later)
-
- * "git fetch" did not recurse into submodules in subdirectories.
- (merge ea2d325 jl/maint-fetch-recursive-fix later)
-
- * "git rebase -i -p" incorrectly dropped commits from side branches.
- (merge 12bf828 aw/rebase-i-p later)
-
- * "git submodule add" did not allow a relative repository path when
- the superproject did not have any default remote url.
- (merge f22a17e8 jl/submodule-add-relurl-wo-upstream later)
-
- * "git submodule foreach" failed to correctly give the standard input to
- the user-supplied command it invoked.
- (merge 4dca1aa bc/submodule-foreach-stdin-fix-1.7.4 later)
-
- * submodules that the user has never showed interest in by running
- "git submodule init" was incorrectly marked as interesting by "git
- submodule sync".
- (merge 2cd9de3 jc/submodule-sync-no-auto-vivify later)
-
- * "git tag -l <glob>..." did not take multiple glob patterns from the
- command line.
- (merge 588d0e8 jk/tag-list-multiple-patterns later)
+ * "ls-files ../$path" that is run from a subdirectory reported errors
+ incorrectly when there is no such path that matches the given pathspec.
+ (merge 0f64bfa cb/maint-ls-files-error-report later to 'maint').
--
exec >/var/tmp/1
echo O=$(git describe master)
-O=v1.7.6-344-g22f4128
+O=v1.7.6-576-g6fcb384
git log --first-parent --oneline $O..master
echo
git shortlog --no-merges ^maint ^$O master
[--3way] [--interactive] [--committer-date-is-author-date]
[--ignore-date] [--ignore-space-change | --ignore-whitespace]
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
- [--reject] [-q | --quiet] [--scissors | --no-scissors]
+ [--exclude=<path>] [--reject] [-q | --quiet]
+ [--scissors | --no-scissors]
[(<mbox> | <Maildir>)...]
'git am' (--continue | --skip | --abort)
-C<n>::
-p<n>::
--directory=<dir>::
+--exclude=<path>::
--reject::
These flags are passed to the 'git apply' (see linkgit:git-apply[1])
program that applies
on the subcommand:
git bisect help
- git bisect start [<bad> [<good>...]] [--] [<paths>...]
+ git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
git bisect bad [<rev>]
git bisect good [<rev>...]
git bisect skip [(<rev>|<range>)...]
with the status of the real test to let the "git bisect run" command loop
determine the eventual outcome of the bisect session.
+OPTIONS
+-------
+--no-checkout::
++
+Do not checkout the new working tree at each iteration of the bisection
+process. Instead just update a special reference named 'BISECT_HEAD' to make
+it point to the commit that should be tested.
++
+This option may be useful when the test you would perform in each step
+does not require a checked out tree.
++
+If the repository is bare, `--no-checkout` is assumed.
+
EXAMPLES
--------
This shows that you can do without a run script if you write the test
on a single line.
+* Locate a good region of the object graph in a damaged repository
++
+------------
+$ git bisect start HEAD <known-good-commit> [ <boundary-commit> ... ] --no-checkout
+$ git bisect run sh -c '
+ GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) &&
+ git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$$ &&
+ git pack-objects --stdout >/dev/null <tmp.$$
+ rc=$?
+ rm -f tmp.$$
+ test $rc = 0'
+
+------------
++
+In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit that
+has at least one parent whose reachable graph is fully traversable in the sense
+required by 'git pack objects'.
+
+
SEE ALSO
--------
link:git-bisect-lk2009.html[Fighting regressions with git bisect],
SYNOPSIS
--------
[verse]
-'git check-attr' attr... [--] pathname...
-'git check-attr' --stdin [-z] attr... < <list-of-paths>
+'git check-attr' [-a | --all | attr...] [--] pathname...
+'git check-attr' --stdin [-z] [-a | --all | attr...] < <list-of-paths>
DESCRIPTION
-----------
OPTIONS
-------
+-a, --all::
+ List all attributes that are associated with the specified
+ paths. If this option is used, then 'unspecified' attributes
+ will not be included in the output.
+
--stdin::
Read file names from stdin instead of from the command-line.
\--::
Interpret all preceding arguments as attributes and all following
- arguments as path names. If not supplied, only the first argument will
- be treated as an attribute.
+ arguments as path names.
+
+If none of `--stdin`, `--all`, or `--` is used, the first argument
+will be treated as an attribute and the rest of the arguments as
+pathnames.
OUTPUT
------
org/example/MyClass.java: myAttr: set
---------------
+* Listing all attributes for a file:
+---------------
+$ git check-attr --all -- org/example/MyClass.java
+org/example/MyClass.java: diff: java
+org/example/MyClass.java: myAttr: set
+---------------
+
* Listing an attribute for multiple files:
---------------
$ git check-attr myAttr -- org/example/MyClass.java org/example/NoMyAttr.java
Show the filename above the matches in that file instead of
at the start of each shown line.
--[ABC] <context>::
- Show `context` trailing (`A` -- after), or leading (`B`
- -- before), or both (`C` -- context) lines, and place a
- line containing `--` between contiguous groups of
- matches.
-
--<num>::
- A shortcut for specifying `-C<num>`.
-
-p::
--show-function::
Show the preceding line that contains the function name of
patch hunk headers (see 'Defining a custom hunk-header' in
linkgit:gitattributes[5]).
+-<num>::
+-C <num>::
+--context <num>::
+ Show <num> leading and trailing lines, and place a line
+ containing `--` between contiguous groups of matches.
+
+-A <num>::
+--after-context <num>::
+ Show <num> trailing lines, and place a line containing
+ `--` between contiguous groups of matches.
+
+-B <num>::
+--before-context <num>::
+ Show <num> leading lines, and place a line containing
+ `--` between contiguous groups of matches.
+
+-W::
+--function-context::
+ Show the surrounding text from the previous line containing a
+ function name up to the one before the next function name,
+ effectively showing the whole function in which the match was
+ found.
+
-f <file>::
Read patterns from <file>, one per line.
ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/
----------------------------------------------------------------
++
+To serve multiple repositories from different linkgit:gitnamespaces[7] in a
+single repository:
++
+----------------------------------------------------------------
+SetEnvIf Request_URI "^/git/([^/]*)" GIT_NAMESPACE=$1
+ScriptAliasMatch ^/git/[^/]*(.*) /usr/libexec/git-core/git-http-backend/storage.git$1
+----------------------------------------------------------------
Accelerated static Apache 2.x::
Similar to the above, but Apache can be used to return static
SYNOPSIS
--------
[verse]
-'git-receive-pack' <directory>
+'git-receive-pack' [--quiet] <directory>
DESCRIPTION
-----------
OPTIONS
-------
+--quiet::
+ Print only error messages.
+
<directory>::
The repository to sync into.
SEE ALSO
--------
-linkgit:git-send-pack[1]
+linkgit:git-send-pack[1], linkgit:gitnamespaces[7]
GIT
---
SYNOPSIS
--------
[verse]
-'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
+'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--quiet] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
DESCRIPTION
-----------
the remote repository can lose commits; use it with
care.
+--quiet::
+ Print only error messages.
+
--verbose::
Run verbosely.
'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
[--reference <repository>] [--merge] [--recursive] [--] [<path>...]
-'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
+ [commit] [--] [<path>...]
'git submodule' [--quiet] foreach [--recursive] <command>
'git submodule' [--quiet] sync [--] [<path>...]
repository and `U` if the submodule has merge conflicts.
This command is the default command for 'git submodule'.
+
-If '--recursive' is specified, this command will recurse into nested
+If `--recursive` is specified, this command will recurse into nested
submodules, and show their status as well.
++
+If you are only interested in changes of the currently initialized
+submodules with respect to the commit recorded in the index or the HEAD,
+linkgit:git-status[1] and linkgit:git-diff[1] will provide that information
+too (and can also report changes to a submodule's work tree).
init::
Initialize the submodules, i.e. register each submodule name
update::
Update the registered submodules, i.e. clone missing submodules and
checkout the commit specified in the index of the containing repository.
- This will make the submodules HEAD be detached unless '--rebase' or
- '--merge' is specified or the key `submodule.$name.update` is set to
+ This will make the submodules HEAD be detached unless `--rebase` or
+ `--merge` is specified or the key `submodule.$name.update` is set to
`rebase` or `merge`.
+
If the submodule is not yet initialized, and you just want to use the
setting as stored in .gitmodules, you can automatically initialize the
-submodule with the --init option.
+submodule with the `--init` option.
+
-If '--recursive' is specified, this command will recurse into the
+If `--recursive` is specified, this command will recurse into the
registered submodules, and update any nested submodules within.
summary::
Show commit summary between the given commit (defaults to HEAD) and
working tree/index. For a submodule in question, a series of commits
in the submodule between the given super project commit and the
- index or working tree (switched by --cached) are shown. If the option
- --files is given, show the series of commits in the submodule between
+ index or working tree (switched by `--cached`) are shown. If the option
+ `--files` is given, show the series of commits in the submodule between
the index of the super project and the working tree of the submodule
- (this option doesn't allow to use the --cached option or to provide an
+ (this option doesn't allow to use the `--cached` option or to provide an
explicit commit).
++
+Using the `--submodule=log` option with linkgit:git-diff[1] will provide that
+information too.
foreach::
Evaluates an arbitrary shell command in each checked out submodule.
superproject, $sha1 is the commit as recorded in the superproject,
and $toplevel is the absolute path to the top-level of the superproject.
Any submodules defined in the superproject but not checked out are
- ignored by this command. Unless given --quiet, foreach prints the name
+ ignored by this command. Unless given `--quiet`, foreach prints the name
of each submodule before evaluating the command.
- If --recursive is given, submodules are traversed recursively (i.e.
+ If `--recursive` is given, submodules are traversed recursively (i.e.
the given shell command is evaluated in nested submodules as well).
A non-zero return from the command in any submodule causes
the processing to terminate. This can be overridden by adding '|| :'
If the key `submodule.$name.update` is set to `rebase`, this option is
implicit.
+--init::
+ This option is only valid for the update command.
+ Initialize all submodules for which "git submodule init" has not been
+ called so far before updating.
+
--reference <repository>::
This option is only valid for add and update commands. These
commands sometimes need to clone a remote repository. In this case,
this option will be passed to the linkgit:git-clone[1] command.
+
*NOTE*: Do *not* use this option unless you have read the note
-for linkgit:git-clone[1]'s --reference and --shared options carefully.
+for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
--recursive::
This option is only valid for foreach, update and status commands.
<directory>::
The repository to sync from.
+SEE ALSO
+--------
+linkgit:gitnamespaces[7]
+
GIT
---
Part of the linkgit:git[1] suite
--------
[verse]
'git' [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
- [-p|--paginate|--no-pager] [--no-replace-objects]
- [--bare] [--git-dir=<path>] [--work-tree=<path>]
+ [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
+ [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
[-c <name>=<value>]
[--help] <command> [<args>]
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.7.6/git.html[documentation for release 1.7.6]
+* link:v1.7.6.1/git.html[documentation for release 1.7.6.1]
* release notes for
+ link:RelNotes/1.7.6.1.txt[1.7.6.1].
link:RelNotes/1.7.6.txt[1.7.6].
* link:v1.7.5.4/git.html[documentation for release 1.7.5.4]
variable (see core.worktree in linkgit:git-config[1] for a
more detailed discussion).
+--namespace=<path>::
+ Set the git namespace. See linkgit:gitnamespaces[7] for more
+ details. Equivalent to setting the `GIT_NAMESPACE` environment
+ variable.
+
--bare::
Treat the repository as a bare repository. If GIT_DIR
environment is not set, it is set to the current working
This can also be controlled by the '--work-tree' command line
option and the core.worktree configuration variable.
+'GIT_NAMESPACE'::
+ Set the git namespace; see linkgit:gitnamespaces[7] for details.
+ The '--namespace' command-line option also sets this value.
+
'GIT_CEILING_DIRECTORIES'::
This should be a colon-separated list of absolute paths.
If set, it is a list of directories that git should not chdir
----------------------------------------------------------------
+SEE ALSO
+--------
+linkgit:git-check-attr[1].
GIT
---
--- /dev/null
+gitnamespaces(7)
+================
+
+NAME
+----
+gitnamespaces - Git namespaces
+
+DESCRIPTION
+-----------
+
+Git supports dividing the refs of a single repository into multiple
+namespaces, each of which has its own branches, tags, and HEAD. Git can
+expose each namespace as an independent repository to pull from and push
+to, while sharing the object store, and exposing all the refs to
+operations such as linkgit:git-gc[1].
+
+Storing multiple repositories as namespaces of a single repository
+avoids storing duplicate copies of the same objects, such as when
+storing multiple branches of the same source. The alternates mechanism
+provides similar support for avoiding duplicates, but alternates do not
+prevent duplication between new objects added to the repositories
+without ongoing maintenance, while namespaces do.
+
+To specify a namespace, set the `GIT_NAMESPACE` environment variable to
+the namespace. For each ref namespace, git stores the corresponding
+refs in a directory under `refs/namespaces/`. For example,
+`GIT_NAMESPACE=foo` will store refs under `refs/namespaces/foo/`. You
+can also specify namespaces via the `--namespace` option to
+linkgit:git[1].
+
+Note that namespaces which include a `/` will expand to a hierarchy of
+namespaces; for example, `GIT_NAMESPACE=foo/bar` will store refs under
+`refs/namespaces/foo/refs/namespaces/bar/`. This makes paths in
+`GIT_NAMESPACE` behave hierarchically, so that cloning with
+`GIT_NAMESPACE=foo/bar` produces the same result as cloning with
+`GIT_NAMESPACE=foo` and cloning from that repo with `GIT_NAMESPACE=bar`. It
+also avoids ambiguity with strange namespace paths such as `foo/refs/heads/`,
+which could otherwise generate directory/file conflicts within the `refs`
+directory.
+
+linkgit:git-upload-pack[1] and linkgit:git-receive-pack[1] rewrite the
+names of refs as specified by `GIT_NAMESPACE`. git-upload-pack and
+git-receive-pack will ignore all references outside the specified
+namespace.
+
+The smart HTTP server, linkgit:git-http-backend[1], will pass
+GIT_NAMESPACE through to the backend programs; see
+linkgit:git-http-backend[1] for sample configuration to expose
+repository namespaces as repositories.
+
+For a simple local test, you can use linkgit:git-remote-ext[1]:
+
+----------
+git clone ext::'git --namespace=foo %s /tmp/prefixed.git'
+----------
+
+SECURITY
+--------
+
+Anyone with access to any namespace within a repository can potentially
+access objects from any other namespace stored in the same repository.
+You can't directly say "give me object ABCD" if you don't have a ref to
+it, but you can do some other sneaky things like:
+
+. Claiming to push ABCD, at which point the server will optimize out the
+ need for you to actually send it. Now you have a ref to ABCD and can
+ fetch it (claiming not to have it, of course).
+
+. Requesting other refs, claiming that you have ABCD, at which point the
+ server may generate deltas against ABCD.
+
+None of this causes a problem if you only host public repositories, or
+if everyone who may read one namespace may also read everything in every
+other namespace (for instance, if everyone in an organization has read
+permission to every repository).
`struct git_attr`::
An attribute is an opaque object that is identified by its name.
- Pass the name and its length 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.
+ 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_check`::
This structure represents a set of attributes to check in a call
- to `git_checkattr()` function, and receives the results.
-
-
-Calling Sequence
-----------------
-
-* Prepare an array of `struct git_attr_check` to define the list of
- attributes you would want to check. To populate this array, you would
- need to define necessary attributes by calling `git_attr()` function.
-
-* Call git_checkattr() to check the attributes for the path.
-
-* Inspect `git_attr_check` structure to see how each of the attribute in
- the array is defined for the path.
+ to `git_check_attr()` function, and receives the results.
Attribute Values
value of the attribute for the path.
+Querying Specific Attributes
+----------------------------
+
+* Prepare an array of `struct git_attr_check` to define the list of
+ attributes you would want to check. To populate this array, you would
+ need to define necessary attributes by calling `git_attr()` function.
+
+* Call `git_check_attr()` to check the attributes for the path.
+
+* Inspect `git_attr_check` structure to see how each of the attribute in
+ the array is defined for the path.
+
+
Example
-------
{
if (check[0].attr)
return; /* already done */
- check[0].attr = git_attr("crlf", 4);
- check[1].attr = git_attr("ident", 5);
+ check[0].attr = git_attr("crlf");
+ check[1].attr = git_attr("ident");
}
------------
-. Call `git_checkattr()` with the prepared array of `struct git_attr_check`:
+. Call `git_check_attr()` with the prepared array of `struct git_attr_check`:
------------
const char *path;
setup_check();
- git_checkattr(path, ARRAY_SIZE(check), check);
+ git_check_attr(path, ARRAY_SIZE(check), check);
------------
. Act on `.value` member of the result, left in `check[]`:
}
------------
-(JC)
+
+Querying All Attributes
+-----------------------
+
+To get the values of all attributes associated with a file:
+
+* Call `git_all_attrs()`, which returns an array of `git_attr_check`
+ structures.
+
+* Iterate over the `git_attr_check` array to examine the attribute
+ names and values. The name of the attribute described by a
+ `git_attr_check` object can be retrieved via
+ `git_attr_name(check[i].attr)`. (Please note that no items will be
+ returned for unset attributes, so `ATTR_UNSET()` will return false
+ for all returned `git_array_check` objects.)
+
+* Free the `git_array_check` array.
--- /dev/null
+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 unsigned char *sha1,
+ 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.
+
+* `head_ref_submodule()`, `for_each_ref_submodule()`,
+ `for_each_ref_in_submodule()`, `for_each_tag_ref_submodule()`,
+ `for_each_branch_ref_submodule()`, `for_each_remote_ref_submodule()`
+ do the same as the functions descibed above but for a specified
+ submodule.
+
+* `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 an non-zero value 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 can not 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);
+----
# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
#
-# Define NO_CURL if you do not have libcurl installed. git-http-pull and
+# Define NO_CURL if you do not have libcurl installed. git-http-fetch and
# git-http-push are not built, and you cannot use http:// and https://
-# transports.
+# transports (neither smart nor dumb).
#
# Define CURLDIR=/foo/bar if your curl header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
#
# Define NO_EXPAT if you do not have expat installed. git-http-push is
-# not built, and you cannot push using http:// and https:// transports.
+# not built, and you cannot push using http:// and https:// transports (dumb).
#
# Define EXPATDIR=/foo/bar if your expat header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
GIT_OBJS += http.o http-walker.o remote-curl.o
endif
XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
- xdiff/xmerge.o xdiff/xpatience.o
+ xdiff/xmerge.o xdiff/xpatience.o xdiff/xhistogram.o
VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o \
vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o
VCSSVN_TEST_OBJS = test-obj-pool.o test-string-pool.o \
path_without_prefix = path.buf + args->baselen;
setup_archive_check(check);
- if (!git_checkattr(path_without_prefix, ARRAY_SIZE(check), check)) {
+ if (!git_check_attr(path_without_prefix, ARRAY_SIZE(check), check)) {
if (ATTR_TRUE(check[0].value))
return 0;
convert = ATTR_TRUE(check[1].value);
static struct git_attr_check *check_all_attr;
static struct git_attr *(git_attr_hash[HASHSIZE]);
+char *git_attr_name(struct git_attr *attr)
+{
+ return attr->name;
+}
+
static unsigned hash_name(const char *name, int namelen)
{
unsigned val = 0, c;
static int invalid_attr_name(const char *name, int namelen)
{
/*
- * Attribute name cannot begin with '-' and from
- * [-A-Za-z0-9_.]. We'd specifically exclude '=' for now,
- * as we might later want to allow non-binary value for
- * attributes, e.g. "*.svg merge=special-merge-program-for-svg"
+ * Attribute name cannot begin with '-' and must consist of
+ * characters from [-A-Za-z0-9_.].
*/
- if (*name == '-')
+ if (namelen <= 0 || *name == '-')
return -1;
while (namelen--) {
char ch = *name++;
}
}
-static void prepare_attr_stack(const char *path, int dirlen)
+static void prepare_attr_stack(const char *path)
{
struct attr_stack *elem, *info;
- int len;
+ int dirlen, len;
struct strbuf pathbuf;
+ const char *cp;
+
+ cp = strrchr(path, '/');
+ if (!cp)
+ dirlen = 0;
+ else
+ dirlen = cp - path;
strbuf_init(&pathbuf, dirlen+2+strlen(GITATTRIBUTES_FILE));
* .gitattributes in deeper directories to shallower ones,
* and finally use the built-in set as the default.
*/
- if (!attr_stack)
- bootstrap_attr_stack();
+ bootstrap_attr_stack();
/*
* Pop the "info" one that is always at the top of the stack.
return rem;
}
-int git_checkattr(const char *path, int num, struct git_attr_check *check)
+/*
+ * Collect all attributes for path into the array pointed to by
+ * check_all_attr.
+ */
+static void collect_all_attrs(const char *path)
{
struct attr_stack *stk;
- const char *cp;
- int dirlen, pathlen, i, rem;
+ int i, pathlen, rem;
- bootstrap_attr_stack();
+ prepare_attr_stack(path);
for (i = 0; i < attr_nr; i++)
check_all_attr[i].value = ATTR__UNKNOWN;
pathlen = strlen(path);
- cp = strrchr(path, '/');
- if (!cp)
- dirlen = 0;
- else
- dirlen = cp - path;
- prepare_attr_stack(path, dirlen);
rem = attr_nr;
for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
rem = fill(path, pathlen, stk, rem);
+}
+
+int git_check_attr(const char *path, int num, struct git_attr_check *check)
+{
+ int i;
+
+ collect_all_attrs(path);
for (i = 0; i < num; i++) {
const char *value = check_all_attr[check[i].attr->attr_nr].value;
return 0;
}
+int git_all_attrs(const char *path, int *num, struct git_attr_check **check)
+{
+ int i, count, j;
+
+ collect_all_attrs(path);
+
+ /* Count the number of attributes that are set. */
+ count = 0;
+ for (i = 0; i < attr_nr; i++) {
+ const char *value = check_all_attr[i].value;
+ if (value != ATTR__UNSET && value != ATTR__UNKNOWN)
+ ++count;
+ }
+ *num = count;
+ *check = xmalloc(sizeof(**check) * count);
+ j = 0;
+ for (i = 0; i < attr_nr; i++) {
+ const char *value = check_all_attr[i].value;
+ if (value != ATTR__UNSET && value != ATTR__UNKNOWN) {
+ (*check)[j].attr = check_all_attr[i].attr;
+ (*check)[j].value = value;
+ ++j;
+ }
+ }
+
+ return 0;
+}
+
void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)
{
enum git_attr_direction old = direction;
#define ATTR_UNSET(v) ((v) == NULL)
/*
- * Send one or more git_attr_check to git_checkattr(), and
+ * Send one or more git_attr_check to git_check_attr(), and
* each 'value' member tells what its value is.
* Unset one is returned as NULL.
*/
const char *value;
};
-int git_checkattr(const char *path, int, struct git_attr_check *);
+/*
+ * Return the name of the attribute represented by the argument. The
+ * return value is a pointer to a null-delimited string that is part
+ * of the internal data structure; it should not be modified or freed.
+ */
+char *git_attr_name(struct git_attr *);
+
+int git_check_attr(const char *path, int, struct git_attr_check *);
+
+/*
+ * Retrieve all attributes that apply to the specified path. *num
+ * will be set the the number of attributes on the path; **check will
+ * be set to point at a newly-allocated array of git_attr_check
+ * objects describing the attributes and their values. *check must be
+ * free()ed by the caller.
+ */
+int git_all_attrs(const char *path, int *num, struct git_attr_check **check);
enum git_attr_direction {
GIT_ATTR_CHECKIN,
static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
+static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
/* bits #0-15 in revision.h */
die("closing file %s: %s", filename, strerror(errno));
}
-static int bisect_checkout(char *bisect_rev_hex)
+static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
{
int res;
mark_expected_rev(bisect_rev_hex);
argv_checkout[2] = bisect_rev_hex;
- res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
- if (res)
- exit(res);
+ if (no_checkout) {
+ argv_update_ref[3] = bisect_rev_hex;
+ if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))
+ die("update-ref --no-deref HEAD failed on %s",
+ bisect_rev_hex);
+ } else {
+ res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
+ if (res)
+ exit(res);
+ }
argv_show_branch[1] = bisect_rev_hex;
return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
* - If one is "skipped", we can't know but we should warn.
* - If we don't know, we should check it out and ask the user to test.
*/
-static void check_merge_bases(void)
+static void check_merge_bases(int no_checkout)
{
struct commit_list *result;
int rev_nr;
handle_skipped_merge_base(mb);
} else {
printf("Bisecting: a merge base must be tested\n");
- exit(bisect_checkout(sha1_to_hex(mb)));
+ exit(bisect_checkout(sha1_to_hex(mb), no_checkout));
}
}
* If a merge base must be tested by the user, its source code will be
* checked out to be tested by the user and we will exit.
*/
-static void check_good_are_ancestors_of_bad(const char *prefix)
+static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
{
const char *filename = git_path("BISECT_ANCESTORS_OK");
struct stat st;
/* Check if all good revs are ancestor of the bad rev. */
if (check_ancestors(prefix))
- check_merge_bases();
+ check_merge_bases(no_checkout);
/* Create file BISECT_ANCESTORS_OK. */
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
* We use the convention that exiting with an exit code 10 means that
* the bisection process finished successfully.
* In this case the calling shell script should exit 0.
+ *
+ * If no_checkout is non-zero, the bisection process does not
+ * checkout the trial commit but instead simply updates BISECT_HEAD.
*/
-int bisect_next_all(const char *prefix)
+int bisect_next_all(const char *prefix, int no_checkout)
{
struct rev_info revs;
struct commit_list *tried;
if (read_bisect_refs())
die("reading bisect refs failed");
- check_good_are_ancestors_of_bad(prefix);
+ check_good_are_ancestors_of_bad(prefix, no_checkout);
bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
revs.limited = 1;
"(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
steps, (steps == 1 ? "" : "s"));
- return bisect_checkout(bisect_rev_hex);
+ return bisect_checkout(bisect_rev_hex, no_checkout);
}
const char *header_prefix;
};
-extern int bisect_next_all(const char *prefix);
+extern int bisect_next_all(const char *prefix, int no_checkout);
extern int estimate_bisect_steps(int all);
#include "bisect.h"
static const char * const git_bisect_helper_usage[] = {
- "git bisect--helper --next-all",
+ "git bisect--helper --next-all [--no-checkout]",
NULL
};
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
int next_all = 0;
+ int no_checkout = 0;
struct option options[] = {
OPT_BOOLEAN(0, "next-all", &next_all,
"perform 'git bisect next'"),
+ OPT_BOOLEAN(0, "no-checkout", &no_checkout,
+ "update BISECT_HEAD instead of checking out the current commit"),
OPT_END()
};
usage_with_options(git_bisect_helper_usage, options);
/* next-all */
- return bisect_next_all(prefix);
+ return bisect_next_all(prefix, no_checkout);
}
#include "quote.h"
#include "parse-options.h"
+static int all_attrs;
static int stdin_paths;
static const char * const check_attr_usage[] = {
-"git check-attr attr... [--] pathname...",
-"git check-attr --stdin attr... < <list-of-paths>",
+"git check-attr [-a | --all | attr...] [--] pathname...",
+"git check-attr --stdin [-a | --all | attr...] < <list-of-paths>",
NULL
};
static int null_term_line;
static const struct option check_attr_options[] = {
+ OPT_BOOLEAN('a', "all", &all_attrs, "report all attributes set on file"),
OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"),
OPT_BOOLEAN('z', NULL, &null_term_line,
"input paths are terminated by a null character"),
OPT_END()
};
-static void check_attr(int cnt, struct git_attr_check *check,
- const char** name, const char *file)
+static void output_attr(int cnt, struct git_attr_check *check,
+ const char *file)
{
int j;
- if (git_checkattr(file, cnt, check))
- die("git_checkattr died");
for (j = 0; j < cnt; j++) {
const char *value = check[j].value;
value = "unspecified";
quote_c_style(file, NULL, stdout, 0);
- printf(": %s: %s\n", name[j], value);
+ printf(": %s: %s\n", git_attr_name(check[j].attr), value);
}
}
-static void check_attr_stdin_paths(int cnt, struct git_attr_check *check,
- const char** name)
+static void check_attr(const char *prefix, int cnt,
+ struct git_attr_check *check, const char *file)
+{
+ char *full_path =
+ prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
+ if (check != NULL) {
+ if (git_check_attr(full_path, cnt, check))
+ die("git_check_attr died");
+ output_attr(cnt, check, file);
+ } else {
+ if (git_all_attrs(full_path, &cnt, &check))
+ die("git_all_attrs died");
+ output_attr(cnt, check, file);
+ free(check);
+ }
+ free(full_path);
+}
+
+static void check_attr_stdin_paths(const char *prefix, int cnt,
+ struct git_attr_check *check)
{
struct strbuf buf, nbuf;
int line_termination = null_term_line ? 0 : '\n';
die("line is badly quoted");
strbuf_swap(&buf, &nbuf);
}
- check_attr(cnt, check, name, buf.buf);
+ check_attr(prefix, cnt, check, buf.buf);
maybe_flush_or_die(stdout, "attribute to stdout");
}
strbuf_release(&buf);
strbuf_release(&nbuf);
}
+static NORETURN void error_with_usage(const char *msg)
+{
+ error("%s", msg);
+ usage_with_options(check_attr_usage, check_attr_options);
+}
+
int cmd_check_attr(int argc, const char **argv, const char *prefix)
{
struct git_attr_check *check;
- int cnt, i, doubledash;
- const char *errstr = NULL;
+ int cnt, i, doubledash, filei;
argc = parse_options(argc, argv, prefix, check_attr_options,
check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
- if (!argc)
- usage_with_options(check_attr_usage, check_attr_options);
if (read_cache() < 0) {
die("invalid cache");
doubledash = i;
}
- /* If there is no double dash, we handle only one attribute */
- if (doubledash < 0) {
- cnt = 1;
- doubledash = 0;
- } else
+ /* Process --all and/or attribute arguments: */
+ if (all_attrs) {
+ if (doubledash >= 1)
+ error_with_usage("Attributes and --all both specified");
+
+ cnt = 0;
+ filei = doubledash + 1;
+ } else if (doubledash == 0) {
+ error_with_usage("No attribute specified");
+ } else if (doubledash < 0) {
+ if (!argc)
+ error_with_usage("No attribute specified");
+
+ if (stdin_paths) {
+ /* Treat all arguments as attribute names. */
+ cnt = argc;
+ filei = argc;
+ } else {
+ /* Treat exactly one argument as an attribute name. */
+ cnt = 1;
+ filei = 1;
+ }
+ } else {
cnt = doubledash;
- doubledash++;
-
- if (cnt <= 0)
- errstr = "No attribute specified";
- else if (stdin_paths && doubledash < argc)
- errstr = "Can't specify files with --stdin";
- if (errstr) {
- error("%s", errstr);
- usage_with_options(check_attr_usage, check_attr_options);
+ filei = doubledash + 1;
}
- check = xcalloc(cnt, sizeof(*check));
- for (i = 0; i < cnt; i++) {
- const char *name;
- struct git_attr *a;
- name = argv[i];
- a = git_attr(name);
- if (!a)
- return error("%s: not a valid attribute name", name);
- check[i].attr = a;
+ /* Check file argument(s): */
+ if (stdin_paths) {
+ if (filei < argc)
+ error_with_usage("Can't specify files with --stdin");
+ } else {
+ if (filei >= argc)
+ error_with_usage("No file specified");
+ }
+
+ if (all_attrs) {
+ check = NULL;
+ } else {
+ check = xcalloc(cnt, sizeof(*check));
+ for (i = 0; i < cnt; i++) {
+ const char *name;
+ struct git_attr *a;
+ name = argv[i];
+ a = git_attr(name);
+ if (!a)
+ return error("%s: not a valid attribute name",
+ name);
+ check[i].attr = a;
+ }
}
if (stdin_paths)
- check_attr_stdin_paths(cnt, check, argv);
+ check_attr_stdin_paths(prefix, cnt, check);
else {
- for (i = doubledash; i < argc; i++)
- check_attr(cnt, check, argv, argv[i]);
+ for (i = filei; i < argc; i++)
+ check_attr(prefix, cnt, check, argv[i]);
maybe_flush_or_die(stdout, "attribute to stdout");
}
return 0;
*
* Copyright (C) 2005 Linus Torvalds
*
- * Careful: order of argument flags does matter. For example,
- *
- * git checkout-index -a -f file.c
- *
- * Will first check out all files listed in the cache (but not
- * overwrite any old ones), and then force-checkout "file.c" a
- * second time (ie that one _will_ overwrite any old contents
- * with the same filename).
- *
- * Also, just doing "git checkout-index" does nothing. You probably
- * meant "git checkout-index -a". And if you want to force it, you
- * want "git checkout-index -f -a".
- *
- * Intuitiveness is not the goal here. Repeatability is. The
- * reason for the "no arguments means no work" thing is that
- * from scripts you are supposed to be able to do things like
- *
- * find . -name '*.h' -print0 | xargs -0 git checkout-index -f --
- *
- * or:
- *
- * find . -name '*.h' -print0 | git checkout-index -f -z --stdin
- *
- * which will force all existing *.h files to be replaced with
- * their cached copies. If an empty command line implied "all",
- * then this would force-refresh everything in the cache, which
- * was not the point.
- *
- * Oh, and the "--" is just a good idea when you know the rest
- * will be filenames. Just so that you wouldn't have a filename
- * of "-a" causing problems (not possible in the above example,
- * but get used to it in scripting!).
*/
#include "builtin.h"
#include "cache.h"
}
static int checkout_paths(struct tree *source_tree, const char **pathspec,
- struct checkout_opts *opts)
+ const char *prefix, struct checkout_opts *opts)
{
int pos;
struct checkout state;
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
}
- if (report_path_error(ps_matched, pathspec, 0))
+ if (report_path_error(ps_matched, pathspec, prefix))
return 1;
/* "checkout -m path" to recreate conflicted state */
if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
- return checkout_paths(source_tree, pathspec, &opts);
+ return checkout_paths(source_tree, pathspec, prefix, &opts);
}
if (patch_mode)
;
m = xcalloc(1, i);
- if (with_tree)
- overlay_tree_on_cache(with_tree, prefix);
+ if (with_tree) {
+ const char *max_prefix = pathspec_prefix(prefix, pattern);
+ overlay_tree_on_cache(with_tree, max_prefix);
+ }
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
item->util = item; /* better a valid pointer than a fake one */
}
- return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
+ return report_path_error(m, pattern, prefix);
}
static void add_remove_files(struct string_list *list)
#include "string-list.h"
#include "utf8.h"
#include "parse-options.h"
+#include "quote.h"
static const char *fast_export_usage[] = {
"git fast-export [rev-list-opts]",
return (a->status == 'R') - (b->status == 'R');
}
+static void print_path(const char *path)
+{
+ int need_quote = quote_c_style(path, NULL, NULL, 0);
+ if (need_quote)
+ quote_c_style(path, NULL, stdout, 0);
+ else
+ printf("%s", path);
+}
+
static void show_filemodify(struct diff_queue_struct *q,
struct diff_options *options, void *data)
{
switch (q->queue[i]->status) {
case DIFF_STATUS_DELETED:
- printf("D %s\n", spec->path);
+ printf("D ");
+ print_path(spec->path);
+ putchar('\n');
break;
case DIFF_STATUS_COPIED:
case DIFF_STATUS_RENAMED:
- printf("%c \"%s\" \"%s\"\n", q->queue[i]->status,
- ospec->path, spec->path);
+ printf("%c ", q->queue[i]->status);
+ print_path(ospec->path);
+ putchar(' ');
+ print_path(spec->path);
+ putchar('\n');
if (!hashcmp(ospec->sha1, spec->sha1) &&
ospec->mode == spec->mode)
* output the SHA-1 verbatim.
*/
if (no_data || S_ISGITLINK(spec->mode))
- printf("M %06o %s %s\n", spec->mode,
- sha1_to_hex(spec->sha1), spec->path);
+ printf("M %06o %s ", spec->mode,
+ sha1_to_hex(spec->sha1));
else {
struct object *object = lookup_object(spec->sha1);
- printf("M %06o :%d %s\n", spec->mode,
- get_object_mark(object), spec->path);
+ printf("M %06o :%d ", spec->mode,
+ get_object_mark(object));
}
+ print_path(spec->path);
+ putchar('\n');
break;
default:
case ACK_continue: {
struct commit *commit =
lookup_commit(result_sha1);
+ if (!commit)
+ die("invalid commit %s", sha1_to_hex(result_sha1));
if (args.stateless_rpc
&& ack == ACK_common
&& !(commit->object.flags & COMMON)) {
OPT_BOOLEAN(0, "heading", &opt.heading,
"show filename only once above matches from same file"),
OPT_GROUP(""),
- OPT_CALLBACK('C', NULL, &opt, "n",
+ OPT_CALLBACK('C', "context", &opt, "n",
"show <n> context lines before and after matches",
context_callback),
- OPT_INTEGER('B', NULL, &opt.pre_context,
+ OPT_INTEGER('B', "before-context", &opt.pre_context,
"show <n> context lines before matches"),
- OPT_INTEGER('A', NULL, &opt.post_context,
+ OPT_INTEGER('A', "after-context", &opt.post_context,
"show <n> context lines after matches"),
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
context_callback),
OPT_BOOLEAN('p', "show-function", &opt.funcname,
"show a line with the function name before matches"),
+ OPT_BOOLEAN('W', "function-context", &opt.funcbody,
+ "show the surrounding function"),
OPT_GROUP(""),
OPT_CALLBACK('f', NULL, &opt, "file",
"read patterns from file", file_callback),
use_threads = 0;
if (use_threads) {
- if (opt.pre_context || opt.post_context || opt.file_break)
+ if (opt.pre_context || opt.post_context || opt.file_break ||
+ opt.funcbody)
skip_first_line = 1;
start_threads(&opt);
}
active_nr = last;
}
-static const char *pathspec_prefix(const char *prefix)
-{
- const char **p, *n, *prev;
- unsigned long max;
-
- if (!pathspec) {
- max_prefix_len = prefix ? strlen(prefix) : 0;
- return prefix;
- }
-
- prev = NULL;
- max = PATH_MAX;
- for (p = pathspec; (n = *p) != NULL; p++) {
- int i, len = 0;
- for (i = 0; i < max; i++) {
- char c = n[i];
- if (prev && prev[i] != c)
- break;
- if (!c || c == '*' || c == '?')
- break;
- if (c == '/')
- len = i+1;
- }
- prev = n;
- if (len < max) {
- max = len;
- if (!max)
- break;
- }
- }
-
- max_prefix_len = max;
- return max ? xmemdupz(prev, max) : NULL;
-}
-
static void strip_trailing_slash_from_submodules(void)
{
const char **p;
}
}
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
+int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix)
{
/*
* Make sure all pathspec matched; otherwise it is an error.
*/
+ struct strbuf sb = STRBUF_INIT;
+ const char *name;
int num, errors = 0;
for (num = 0; pathspec[num]; num++) {
int other, found_dup;
if (found_dup)
continue;
+ name = quote_path_relative(pathspec[num], -1, &sb, prefix);
error("pathspec '%s' did not match any file(s) known to git.",
- pathspec[num] + prefix_len);
+ name);
errors++;
}
+ strbuf_release(&sb);
return errors;
}
strip_trailing_slash_from_submodules();
/* Find common prefix for all pathspec's */
- max_prefix = pathspec_prefix(prefix);
+ max_prefix = pathspec_prefix(prefix, pathspec);
+ max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
/* Treat unmatching pathspec elements as errors */
if (pathspec && error_unmatch) {
if (ps_matched) {
int bad;
- bad = report_path_error(ps_matched, pathspec, prefix_len);
+ bad = report_path_error(ps_matched, pathspec, prefix);
if (bad)
fprintf(stderr, "Did you forget to 'git add'?\n");
tree = parse_tree_indirect(sha1);
if (!tree)
die("not a tree object");
- read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
-
- return 0;
+ return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
}
struct git_attr_check check[1];
setup_delta_attr_check(check);
- if (git_checkattr(path, ARRAY_SIZE(check), check))
+ if (git_check_attr(path, ARRAY_SIZE(check), check))
return 0;
if (ATTR_FALSE(check->value))
return 1;
return 0;
}
+static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+ path = strip_namespace(path);
+ /*
+ * Advertise refs outside our current namespace as ".have"
+ * refs, so that the client can use them to minimize data
+ * transfer but will otherwise ignore them. This happens to
+ * cover ".have" that are thrown in by add_one_alternate_ref()
+ * to mark histories that are complete in our alternates as
+ * well.
+ */
+ if (!path)
+ path = ".have";
+ return show_ref(path, sha1, flag, cb_data);
+}
+
static void write_head_info(void)
{
- for_each_ref(show_ref, NULL);
+ for_each_ref(show_ref_cb, NULL);
if (!sent_capabilities)
show_ref("capabilities^{}", null_sha1, 0, NULL);
static const char *update(struct command *cmd)
{
const char *name = cmd->ref_name;
+ struct strbuf namespaced_name_buf = STRBUF_INIT;
+ const char *namespaced_name;
unsigned char *old_sha1 = cmd->old_sha1;
unsigned char *new_sha1 = cmd->new_sha1;
struct ref_lock *lock;
return "funny refname";
}
- if (is_ref_checked_out(name)) {
+ strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
+ namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
+
+ if (is_ref_checked_out(namespaced_name)) {
switch (deny_current_branch) {
case DENY_IGNORE:
break;
return "deletion prohibited";
}
- if (!strcmp(name, head_name)) {
+ if (!strcmp(namespaced_name, head_name)) {
switch (deny_delete_current) {
case DENY_IGNORE:
break;
rp_warning("Allowing deletion of corrupt ref.");
old_sha1 = NULL;
}
- if (delete_ref(name, old_sha1, 0)) {
+ if (delete_ref(namespaced_name, old_sha1, 0)) {
rp_error("failed to delete %s", name);
return "failed to delete";
}
return NULL; /* good */
}
else {
- lock = lock_any_ref_for_update(name, old_sha1, 0);
+ lock = lock_any_ref_for_update(namespaced_name, old_sha1, 0);
if (!lock) {
rp_error("failed to lock %s", name);
return "failed to lock";
static void check_aliased_update(struct command *cmd, struct string_list *list)
{
+ struct strbuf buf = STRBUF_INIT;
+ const char *dst_name;
struct string_list_item *item;
struct command *dst_cmd;
unsigned char sha1[20];
char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
int flag;
- const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
+ strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
+ dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+ strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
return;
+ dst_name = strip_namespace(dst_name);
+ if (!dst_name) {
+ rp_error("refusing update to broken symref '%s'", cmd->ref_name);
+ cmd->skip_update = 1;
+ cmd->error_string = "broken symref";
+ return;
+ }
+
if ((item = string_list_lookup(list, dst_name)) == NULL)
return;
static const char *pack_lockfile;
-static const char *unpack(void)
+static const char *unpack(int quiet)
{
struct pack_header hdr;
const char *hdr_err;
if (ntohl(hdr.hdr_entries) < unpack_limit) {
int code, i = 0;
- const char *unpacker[4];
+ const char *unpacker[5];
unpacker[i++] = "unpack-objects";
+ if (quiet)
+ unpacker[i++] = "-q";
if (receive_fsck_objects)
unpacker[i++] = "--strict";
unpacker[i++] = hdr_arg;
int cmd_receive_pack(int argc, const char **argv, const char *prefix)
{
+ int quiet = 0;
int advertise_refs = 0;
int stateless_rpc = 0;
int i;
const char *arg = *argv++;
if (*arg == '-') {
+ if (!strcmp(arg, "--quiet")) {
+ quiet = 1;
+ continue;
+ }
+
if (!strcmp(arg, "--advertise-refs")) {
advertise_refs = 1;
continue;
const char *unpack_status = NULL;
if (!delete_only(commands))
- unpack_status = unpack();
+ unpack_status = unpack(quiet);
execute_commands(commands, unpack_status);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
if (!strcmp(argv[1], "delete"))
return cmd_reflog_delete(argc - 1, argv + 1, prefix);
- /* Not a recognized reflog command..*/
- usage(reflog_usage);
+ return cmd_log_reflog(argc, argv, prefix);
}
url = states.remote->url;
url_nr = states.remote->url_nr;
}
- for (i=0; i < url_nr; i++)
+ for (i = 0; i < url_nr; i++)
printf(" Push URL: %s\n", url[i]);
if (!i)
printf(" Push URL: %s\n", "(no URL)");
N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL
};
-static char *args_to_str(const char **argv)
-{
- char *buf = NULL;
- unsigned long len, space = 0, nr = 0;
-
- for (; *argv; argv++) {
- len = strlen(*argv);
- ALLOC_GROW(buf, nr + 1 + len, space);
- if (nr)
- buf[nr++] = ' ';
- memcpy(buf + nr, *argv, len);
- nr += len;
- }
- ALLOC_GROW(buf, nr + 1, space);
- buf[nr] = '\0';
-
- return buf;
-}
-
static inline int is_merge(void)
{
return !access(git_path("MERGE_HEAD"), F_OK);
return update_index_refresh(index_fd, lock, refresh_flags);
}
-static void prepend_reflog_action(const char *action, char *buf, size_t size)
+static void set_reflog_message(struct strbuf *sb, const char *action,
+ const char *rev)
{
- const char *sep = ": ";
const char *rla = getenv("GIT_REFLOG_ACTION");
- if (!rla)
- rla = sep = "";
- if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size)
- warning(_("Reflog action message too long: %.*s..."), 50, buf);
+
+ strbuf_reset(sb);
+ if (rla)
+ strbuf_addf(sb, "%s: %s", rla, action);
+ else if (rev)
+ strbuf_addf(sb, "reset: moving to %s", rev);
+ else
+ strbuf_addf(sb, "reset: %s", action);
}
static void die_if_unmerged_cache(int reset_type)
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20];
struct commit *commit;
- char *reflog_action, msg[1024];
+ struct strbuf msg = STRBUF_INIT;
const struct option options[] = {
OPT__QUIET(&quiet, "be quiet, only report errors"),
OPT_SET_INT(0, "mixed", &reset_type,
argc = parse_options(argc, argv, prefix, options, git_reset_usage,
PARSE_OPT_KEEP_DASHDASH);
- reflog_action = args_to_str(argv);
- setenv("GIT_REFLOG_ACTION", reflog_action, 0);
/*
* Possible arguments are:
old_orig = sha1_old_orig;
if (!get_sha1("HEAD", sha1_orig)) {
orig = sha1_orig;
- prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg));
- update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+ set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
+ update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
}
else if (old_orig)
delete_ref("ORIG_HEAD", old_orig, 0);
- prepend_reflog_action("updating HEAD", msg, sizeof(msg));
- update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+ set_reflog_message(&msg, "updating HEAD", rev);
+ update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
switch (reset_type) {
case HARD:
remove_branch_state();
- free(reflog_action);
+ strbuf_release(&msg);
return update_ref_status;
}
args.force_update = 1;
continue;
}
+ if (!strcmp(arg, "--quiet")) {
+ args.quiet = 1;
+ continue;
+ }
if (!strcmp(arg, "--verbose")) {
args.verbose = 1;
continue;
fd[0] = 0;
fd[1] = 1;
} else {
- conn = git_connect(fd, dest, receivepack,
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addstr(&sb, receivepack);
+ if (args.quiet)
+ strbuf_addstr(&sb, " --quiet");
+ conn = git_connect(fd, dest, sb.buf,
args.verbose ? CONNECT_VERBOSE : 0);
+ strbuf_release(&sb);
}
memset(&extra_have, 0, sizeof(extra_have));
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
- const char *refname, *oldval, *msg=NULL;
+ const char *refname, *oldval, *msg = NULL;
unsigned char sha1[20], oldsha1[20];
int delete = 0, no_deref = 0, flags = 0;
struct option options[] = {
}
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
extern char *get_index_file(void);
extern char *get_graft_file(void);
extern int set_git_dir(const char *path);
+extern const char *get_git_namespace(void);
+extern const char *strip_namespace(const char *namespaced_ref);
extern const char *get_git_work_tree(void);
extern const char *read_gitfile_gently(const char *path);
extern void set_git_work_tree(const char *tree);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
extern const char **get_pathspec(const char *prefix, const char **pathspec);
+extern const char *pathspec_prefix(const char *prefix, const char **pathspec);
extern void setup_work_tree(void);
extern const char *setup_git_directory_gently(int *);
extern const char *setup_git_directory(void);
#define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK)
/* ls-files */
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
+int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
char *alias_lookup(const char *alias);
}
static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
- int dense, struct rev_info *rev)
+ int dense, int working_tree_file,
+ struct rev_info *rev)
{
struct diff_options *opt = &rev->diffopt;
unsigned long result_size, cnt, lno;
struct sline *sline; /* survived lines */
int mode_differs = 0;
int i, show_hunks;
- int working_tree_file = is_null_sha1(elem->sha1);
mmfile_t result_file;
struct userdiff_driver *userdiff;
struct userdiff_driver *textconv = NULL;
write_name_quoted(p->path, stdout, line_termination);
}
+/*
+ * The result (p->elem) is from the working tree and their
+ * parents are typically from multiple stages during a merge
+ * (i.e. diff-files) or the state in HEAD and in the index
+ * (i.e. diff-index).
+ */
void show_combined_diff(struct combine_diff_path *p,
int num_parent,
int dense,
DIFF_FORMAT_NAME_STATUS))
show_raw_diff(p, num_parent, rev);
else if (opt->output_format & DIFF_FORMAT_PATCH)
- show_patch_diff(p, num_parent, dense, rev);
+ show_patch_diff(p, num_parent, dense, 1, rev);
}
void diff_tree_combined(const unsigned char *sha1,
for (p = paths; p; p = p->next) {
if (p->len)
show_patch_diff(p, num_parent, dense,
- rev);
+ 0, rev);
}
}
}
commit = work_item->item;
for (parents = commit->parents; parents ; parents = parents->next) {
- struct commit *parent=parents->item;
+ struct commit *parent = parents->item;
if (!parent->indegree)
continue;
*/
static int git_tcp_connect_sock(char *host, int flags)
{
- int sockfd = -1, saved_errno = 0;
+ struct strbuf error_message = STRBUF_INIT;
+ int sockfd = -1;
const char *port = STR(DEFAULT_GIT_PORT);
char *ep;
struct hostent *he;
fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {
- sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
- if (sockfd < 0) {
- saved_errno = errno;
- continue;
- }
-
memset(&sa, 0, sizeof sa);
sa.sin_family = he->h_addrtype;
sa.sin_port = htons(nport);
memcpy(&sa.sin_addr, *ap, he->h_length);
- if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
- saved_errno = errno;
- fprintf(stderr, "%s[%d: %s]: errno=%s\n",
+ sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
+ if ((sockfd < 0) ||
+ connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
+ strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n",
host,
cnt,
inet_ntoa(*(struct in_addr *)&sa.sin_addr),
- strerror(saved_errno));
- close(sockfd);
+ strerror(errno));
+ if (0 <= sockfd)
+ close(sockfd);
sockfd = -1;
continue;
}
}
if (sockfd < 0)
- die("unable to connect a socket (%s)", strerror(saved_errno));
+ die("unable to connect to %s:\n%s", host, error_message.buf);
if (flags & CONNECT_VERBOSE)
fprintf(stderr, "done.\n");
__gitcomp "$__git_all_commands $(__git_aliases)
attributes cli core-tutorial cvs-migration
diffcore gitk glossary hooks ignore modules
- repository-layout tutorial tutorial-2
+ namespaces repository-layout tutorial tutorial-2
workflows
"
}
--exec-path
--html-path
--work-tree=
+ --namespace=
--help
"
;;
git_config(read_convert_config, NULL);
}
- if (!git_checkattr(path, NUM_CONV_ATTRS, ccheck)) {
+ if (!git_check_attr(path, NUM_CONV_ATTRS, ccheck)) {
ca->crlf_action = git_path_check_crlf(path, ccheck + 4);
if (ca->crlf_action == CRLF_GUESS)
ca->crlf_action = git_path_check_crlf(path, ccheck + 0);
return 0;
}
-int run_diff_index(struct rev_info *revs, int cached)
+static int diff_cache(struct rev_info *revs,
+ const unsigned char *tree_sha1,
+ const char *tree_name,
+ int cached)
{
- struct object *ent;
struct tree *tree;
- const char *tree_name;
- struct unpack_trees_options opts;
struct tree_desc t;
+ struct unpack_trees_options opts;
- ent = revs->pending.objects[0].item;
- tree_name = revs->pending.objects[0].name;
- tree = parse_tree_indirect(ent->sha1);
+ tree = parse_tree_indirect(tree_sha1);
if (!tree)
- return error("bad tree object %s", tree_name);
-
+ return error("bad tree object %s",
+ tree_name ? tree_name : sha1_to_hex(tree_sha1));
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
opts.index_only = cached;
opts.dst_index = NULL;
init_tree_desc(&t, tree->buffer, tree->size);
- if (unpack_trees(1, &t, &opts))
+ return unpack_trees(1, &t, &opts);
+}
+
+int run_diff_index(struct rev_info *revs, int cached)
+{
+ struct object_array_entry *ent;
+
+ ent = revs->pending.objects;
+ if (diff_cache(revs, ent->item->sha1, ent->name, cached))
exit(128);
diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
{
- struct tree *tree;
struct rev_info revs;
- int i;
- struct cache_entry **dst;
- struct cache_entry *last = NULL;
- struct unpack_trees_options opts;
- struct tree_desc t;
-
- /*
- * This is used by git-blame to run diff-cache internally;
- * it potentially needs to repeatedly run this, so we will
- * start by removing the higher order entries the last round
- * left behind.
- */
- dst = active_cache;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (ce_stage(ce)) {
- if (last && !strcmp(ce->name, last->name))
- continue;
- cache_tree_invalidate_path(active_cache_tree,
- ce->name);
- last = ce;
- ce->ce_flags |= CE_REMOVE;
- }
- *dst++ = ce;
- }
- active_nr = dst - active_cache;
init_revisions(&revs, NULL);
init_pathspec(&revs.prune_data, opt->pathspec.raw);
- tree = parse_tree_indirect(tree_sha1);
- if (!tree)
- die("bad tree object %s", sha1_to_hex(tree_sha1));
+ revs.diffopt = *opt;
- memset(&opts, 0, sizeof(opts));
- opts.head_idx = 1;
- opts.index_only = 1;
- opts.diff_index_cached = !DIFF_OPT_TST(opt, FIND_COPIES_HARDER);
- opts.merge = 1;
- opts.fn = oneway_diff;
- opts.unpack_data = &revs;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
-
- init_tree_desc(&t, tree->buffer, tree->size);
- if (unpack_trees(1, &t, &opts))
+ if (diff_cache(&revs, tree_sha1, NULL, 1))
exit(128);
return 0;
}
DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
else if (!strcmp(arg, "--patience"))
DIFF_XDL_SET(options, PATIENCE_DIFF);
+ else if (!strcmp(arg, "--histogram"))
+ DIFF_XDL_SET(options, HISTOGRAM_DIFF);
/* flags options */
else if (!strcmp(arg, "--binary")) {
* are.
*/
#include "cache.h"
+#include "refs.h"
char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
char *git_work_tree_cfg;
static char *work_tree;
+static const char *namespace;
+static size_t namespace_len;
+
static const char *git_dir;
static char *git_object_dir, *git_index_file, *git_graft_file;
NULL
};
+static char *expand_namespace(const char *raw_namespace)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct strbuf **components, **c;
+
+ if (!raw_namespace || !*raw_namespace)
+ return xstrdup("");
+
+ strbuf_addstr(&buf, raw_namespace);
+ components = strbuf_split(&buf, '/');
+ strbuf_reset(&buf);
+ for (c = components; *c; c++)
+ if (strcmp((*c)->buf, "/") != 0)
+ strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf);
+ strbuf_list_free(components);
+ if (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK)
+ die("bad git namespace path \"%s\"", raw_namespace);
+ strbuf_addch(&buf, '/');
+ return strbuf_detach(&buf, NULL);
+}
+
static void setup_git_env(void)
{
git_dir = getenv(GIT_DIR_ENVIRONMENT);
git_graft_file = git_pathdup("info/grafts");
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
read_replace_refs = 0;
+ namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
+ namespace_len = strlen(namespace);
}
int is_bare_repository(void)
return git_dir;
}
+const char *get_git_namespace(void)
+{
+ if (!namespace)
+ setup_git_env();
+ return namespace;
+}
+
+const char *strip_namespace(const char *namespaced_ref)
+{
+ if (prefixcmp(namespaced_ref, get_git_namespace()) != 0)
+ return NULL;
+ return namespaced_ref + namespace_len;
+}
+
static int git_work_tree_initialized;
/*
sed -n '
/^NAME/,/git-'"$cmd"'/H
${
- x
- s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
+ x
+ s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
p
}' "Documentation/git-$cmd.txt"
done
ignore-space-change pass it through git-apply
ignore-whitespace pass it through git-apply
directory= pass it through git-apply
+exclude= pass it through git-apply
C= pass it through git-apply
p= pass it through git-apply
patch-format= format the patch(es) are in
return 0
fi
- # otherwise, check the first few lines of the first patch to try
- # to detect its format
+ # otherwise, check the first few non-blank lines of the first
+ # patch to try to detect its format
{
- read l1
+ # Start from first line containing non-whitespace
+ l1=
+ while test -z "$l1"
+ do
+ read l1
+ done
read l2
read l3
case "$l1" in
;;
--resolvemsg)
shift; resolvemsg=$1 ;;
- --whitespace|--directory)
+ --whitespace|--directory|--exclude)
git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
-C|-p)
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
fi
fi
+git update-index -q --refresh
+
case "$resolved" in
'')
case "$HAS_HEAD" in
USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
LONG_USAGE='git bisect help
- print this long help message.
-git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
- reset bisect state and start bisection.
+ print this long help message.
+git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
+ reset bisect state and start bisection.
git bisect bad [<rev>]
- mark <rev> a known-bad revision.
+ mark <rev> a known-bad revision.
git bisect good [<rev>...]
- mark <rev>... known-good revisions.
+ mark <rev>... known-good revisions.
git bisect skip [(<rev>|<range>)...]
- mark <rev>... untestable revisions.
+ mark <rev>... untestable revisions.
git bisect next
- find next bisection to test and check it out.
+ find next bisection to test and check it out.
git bisect reset [<commit>]
- finish bisection search and go back to commit.
+ finish bisection search and go back to commit.
git bisect visualize
- show bisect status in gitk.
+ show bisect status in gitk.
git bisect replay <logfile>
- replay bisection log.
+ replay bisection log.
git bisect log
- show bisect log.
+ show bisect log.
git bisect run <cmd>...
- use <cmd>... to automatically bisect.
+ use <cmd>... to automatically bisect.
Please use "git help bisect" to get the full man page.'
OPTIONS_SPEC=
. git-sh-setup
. git-sh-i18n
-require_work_tree
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+bisect_head()
+{
+ if test -f "$GIT_DIR/BISECT_HEAD"
+ then
+ echo BISECT_HEAD
+ else
+ echo HEAD
+ fi
+}
+
bisect_autostart() {
test -s "$GIT_DIR/BISECT_START" || {
(
# TRANSLATORS: Make sure to include [Y] and [n] in your
# translation. The program will only accept English input
# at this point.
- gettext "Do you want me to do it for you [Y/n]? " >&2
+ gettext "Do you want me to do it for you [Y/n]? " >&2
read yesno
case "$yesno" in
[Nn]*)
}
bisect_start() {
+ #
+ # Check for one bad and then some good revisions.
+ #
+ has_double_dash=0
+ for arg; do
+ case "$arg" in --) has_double_dash=1; break ;; esac
+ done
+ orig_args=$(git rev-parse --sq-quote "$@")
+ bad_seen=0
+ eval=''
+ if test "z$(git rev-parse --is-bare-repository)" != zfalse
+ then
+ mode=--no-checkout
+ else
+ mode=''
+ fi
+ while [ $# -gt 0 ]; do
+ arg="$1"
+ case "$arg" in
+ --)
+ shift
+ break
+ ;;
+ --no-checkout)
+ mode=--no-checkout
+ shift ;;
+ --*)
+ die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
+ *)
+ rev=$(git rev-parse -q --verify "$arg^{commit}") || {
+ test $has_double_dash -eq 1 &&
+ die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
+ break
+ }
+ case $bad_seen in
+ 0) state='bad' ; bad_seen=1 ;;
+ *) state='good' ;;
+ esac
+ eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
+ shift
+ ;;
+ esac
+ done
+
#
# Verify HEAD.
#
then
# Reset to the rev from where we started.
start_head=$(cat "$GIT_DIR/BISECT_START")
- git checkout "$start_head" -- || exit
+ if test "z$mode" != "z--no-checkout"
+ then
+ git checkout "$start_head" --
+ fi
else
# Get rev from where we start.
case "$head" in
#
bisect_clean_state || exit
- #
- # Check for one bad and then some good revisions.
- #
- has_double_dash=0
- for arg; do
- case "$arg" in --) has_double_dash=1; break ;; esac
- done
- orig_args=$(git rev-parse --sq-quote "$@")
- bad_seen=0
- eval=''
- while [ $# -gt 0 ]; do
- arg="$1"
- case "$arg" in
- --)
- shift
- break
- ;;
- *)
- rev=$(git rev-parse -q --verify "$arg^{commit}") || {
- test $has_double_dash -eq 1 &&
- die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
- break
- }
- case $bad_seen in
- 0) state='bad' ; bad_seen=1 ;;
- *) state='good' ;;
- esac
- eval="$eval bisect_write '$state' '$rev' 'nolog'; "
- shift
- ;;
- esac
- done
-
#
# Change state.
# In case of mistaken revs or checkout error, or signals received,
#
# Write new start state.
#
- echo "$start_head" >"$GIT_DIR/BISECT_START" &&
+ echo "$start_head" >"$GIT_DIR/BISECT_START" && {
+ test "z$mode" != "z--no-checkout" ||
+ git update-ref --no-deref BISECT_HEAD "$start_head"
+ } &&
git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
- eval "$eval" &&
+ eval "$eval true" &&
echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
#
# Check if we can proceed to the next bisect state.
check_expected_revs() {
for _rev in "$@"; do
- if ! is_expected_rev "$_rev"; then
+ if ! is_expected_rev "$_rev"
+ then
rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
return
}
bisect_skip() {
- all=''
+ all=''
for arg in "$@"
do
- case "$arg" in
- *..*)
- revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
- *)
- revs=$(git rev-parse --sq-quote "$arg") ;;
- esac
- all="$all $revs"
- done
- eval bisect_state 'skip' $all
+ case "$arg" in
+ *..*)
+ revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
+ *)
+ revs=$(git rev-parse --sq-quote "$arg") ;;
+ esac
+ all="$all $revs"
+ done
+ eval bisect_state 'skip' $all
}
bisect_state() {
0,*)
die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
1,bad|1,good|1,skip)
- rev=$(git rev-parse --verify HEAD) ||
- die "$(gettext "Bad rev input: HEAD")"
+ rev=$(git rev-parse --verify $(bisect_head)) ||
+ die "$(gettext "Bad rev input: $(bisect_head)")"
bisect_write "$state" "$rev"
check_expected_revs "$rev" ;;
2,bad|*,good|*,skip)
bisect_next_check good
# Perform all bisection computation, display and checkout
- git bisect--helper --next-all
+ git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
res=$?
- # Check if we should exit because bisection is finished
+ # Check if we should exit because bisection is finished
test $res -eq 10 && exit 0
# Check for an error in the bisection process
if test $# = 0
then
if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
- type gitk >/dev/null 2>&1; then
+ type gitk >/dev/null 2>&1
+ then
set gitk
else
set git log
case "$#" in
0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
- invalid="$1"
- die "$(eval_gettext "'\$invalid' is not a valid commit")"
- }
- branch="$1" ;;
+ invalid="$1"
+ die "$(eval_gettext "'\$invalid' is not a valid commit")"
+ }
+ branch="$1" ;;
*)
- usage ;;
+ usage ;;
esac
- if git checkout "$branch" -- ; then
- bisect_clean_state
- else
+
+ if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
+ then
die "$(eval_gettext "Could not check out original HEAD '\$branch'.
Try 'git bisect reset <commit>'.")"
fi
+ bisect_clean_state
}
bisect_clean_state() {
rm -f "$GIT_DIR/BISECT_RUN" &&
# Cleanup head-name if it got left by an old version of git-bisect
rm -f "$GIT_DIR/head-name" &&
-
+ git update-ref -d --no-deref BISECT_HEAD &&
+ # clean up BISECT_START last
rm -f "$GIT_DIR/BISECT_START"
}
while read git bisect command rev
do
test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
- if test "$git" = "git-bisect"; then
+ if test "$git" = "git-bisect"
+ then
rev="$command"
command="$bisect"
fi
}
bisect_run () {
- bisect_next_check fail
-
- while true
- do
- command="$@"
- eval_gettext "running \$command"; echo
- "$@"
- res=$?
-
- # Check for really bad run error.
- if [ $res -lt 0 -o $res -ge 128 ]; then
- (
- eval_gettext "bisect run failed:
+ bisect_next_check fail
+
+ while true
+ do
+ command="$@"
+ eval_gettext "running \$command"; echo
+ "$@"
+ res=$?
+
+ # Check for really bad run error.
+ if [ $res -lt 0 -o $res -ge 128 ]
+ then
+ (
+ eval_gettext "bisect run failed:
exit code \$res from '\$command' is < 0 or >= 128" &&
- echo
- ) >&2
- exit $res
- fi
-
- # Find current state depending on run success or failure.
- # A special exit code of 125 means cannot test.
- if [ $res -eq 125 ]; then
- state='skip'
- elif [ $res -gt 0 ]; then
- state='bad'
- else
- state='good'
- fi
-
- # We have to use a subshell because "bisect_state" can exit.
- ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
- res=$?
-
- cat "$GIT_DIR/BISECT_RUN"
-
- if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
- > /dev/null; then
- (
- gettext "bisect run cannot continue any more" &&
- echo
- ) >&2
- exit $res
- fi
-
- if [ $res -ne 0 ]; then
- (
- eval_gettext "bisect run failed:
+ echo
+ ) >&2
+ exit $res
+ fi
+
+ # Find current state depending on run success or failure.
+ # A special exit code of 125 means cannot test.
+ if [ $res -eq 125 ]
+ then
+ state='skip'
+ elif [ $res -gt 0 ]
+ then
+ state='bad'
+ else
+ state='good'
+ fi
+
+ # We have to use a subshell because "bisect_state" can exit.
+ ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
+ res=$?
+
+ cat "$GIT_DIR/BISECT_RUN"
+
+ if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
+ > /dev/null
+ then
+ (
+ gettext "bisect run cannot continue any more" &&
+ echo
+ ) >&2
+ exit $res
+ fi
+
+ if [ $res -ne 0 ]
+ then
+ (
+ eval_gettext "bisect run failed:
'bisect_state \$state' exited with error code \$res" &&
- echo
- ) >&2
- exit $res
- fi
+ echo
+ ) >&2
+ exit $res
+ fi
- if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
- gettext "bisect run success"; echo
- exit 0;
- fi
+ if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
+ then
+ gettext "bisect run success"; echo
+ exit 0;
+ fi
- done
+ done
}
bisect_log () {
case "$#" in
0)
- usage ;;
+ usage ;;
*)
- cmd="$1"
- shift
- case "$cmd" in
- help)
- git bisect -h ;;
- start)
- bisect_start "$@" ;;
- bad|good)
- bisect_state "$cmd" "$@" ;;
- skip)
- bisect_skip "$@" ;;
- next)
- # Not sure we want "next" at the UI level anymore.
- bisect_next "$@" ;;
- visualize|view)
- bisect_visualize "$@" ;;
- reset)
- bisect_reset "$@" ;;
- replay)
- bisect_replay "$@" ;;
- log)
- bisect_log ;;
- run)
- bisect_run "$@" ;;
- *)
- usage ;;
- esac
+ cmd="$1"
+ shift
+ case "$cmd" in
+ help)
+ git bisect -h ;;
+ start)
+ bisect_start "$@" ;;
+ bad|good)
+ bisect_state "$cmd" "$@" ;;
+ skip)
+ bisect_skip "$@" ;;
+ next)
+ # Not sure we want "next" at the UI level anymore.
+ bisect_next "$@" ;;
+ visualize|view)
+ bisect_visualize "$@" ;;
+ reset)
+ bisect_reset "$@" ;;
+ replay)
+ bisect_replay "$@" ;;
+ log)
+ bisect_log ;;
+ run)
+ bisect_run "$@" ;;
+ *)
+ usage ;;
+ esac
esac
/* General helper functions */
extern void vreportf(const char *prefix, const char *err, va_list params);
+extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
extern NORETURN void usage(const char *err);
extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
+extern void set_error_routine(void (*routine)(const char *err, va_list params));
extern int prefixcmp(const char *str, const char *prefix);
extern int suffixcmp(const char *str, const char *suffix);
functions=$(cat << \EOF
warn () {
- echo "$*" >&2
+ echo "$*" >&2
}
map()
}
USAGE="[--env-filter <command>] [--tree-filter <command>]
- [--index-filter <command>] [--parent-filter <command>]
- [--msg-filter <command>] [--commit-filter <command>]
- [--tag-name-filter <command>] [--subdirectory-filter <directory>]
- [--original <namespace>] [-d <directory>] [-f | --force]
- [<rev-list options>...]"
+ [--index-filter <command>] [--parent-filter <command>]
+ [--msg-filter <command>] [--commit-filter <command>]
+ [--tag-name-filter <command>] [--subdirectory-filter <directory>]
+ [--original <namespace>] [-d <directory>] [-f | --force]
+ [<rev-list options>...]"
OPTIONS_SPEC=
. git-sh-setup
sed -e '1,/^$/d' <../commit | \
eval "$filter_msg" > ../message ||
die "msg filter failed: $filter_msg"
- @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
+ workdir=$workdir @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
$(git write-tree) $parentstr < ../message > ../map/$commit ||
die "could not write rewritten commit"
done <../revs
fi
if diff_mode; then
echo "$(git config difftool.$merge_tool.cmd ||
- git config mergetool.$merge_tool.cmd)"
+ git config mergetool.$merge_tool.cmd)"
else
echo "$(git config mergetool.$merge_tool.cmd)"
fi
fi
if diff_mode; then
merge_tool_path=$(git config difftool."$merge_tool".path ||
- git config mergetool."$merge_tool".path)
+ git config mergetool."$merge_tool".path)
else
merge_tool_path=$(git config mergetool."$merge_tool".path)
fi
if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
! type "$merge_tool_path" > /dev/null 2>&1; then
echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
- "'$merge_tool_path'"
+ "'$merge_tool_path'"
exit 1
fi
echo "$merge_tool_path"
OPTIONS_SPEC=
. git-sh-setup
. git-sh-i18n
-set_reflog_action "pull $*"
+set_reflog_action "pull${1+ $*}"
require_work_tree
cd_to_toplevel
Example: git-rebase master~1 topic
- A---B---C topic A'\''--B'\''--C'\'' topic
+ A---B---C topic A'\''--B'\''--C'\'' topic
/ --> /
D---E---F---G master D---E---F---G master
'
path=$1
url=$2
reference="$3"
+ quiet=
+ if test -n "$GIT_QUIET"
+ then
+ quiet=-q
+ fi
if test -n "$reference"
then
- git-clone "$reference" -n "$url" "$path"
+ git-clone $quiet "$reference" -n "$url" "$path"
else
- git-clone -n "$url" "$path"
+ git-clone $quiet -n "$url" "$path"
fi ||
die "$(eval_gettext "Clone of '\$url' into submodule path '\$path' failed")"
}
const char git_usage_string[] =
"git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
- " [-p|--paginate|--no-pager] [--no-replace-objects]\n"
- " [--bare] [--git-dir=<path>] [--work-tree=<path>]\n"
+ " [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n"
+ " [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
" [-c name=value] [--help]\n"
" <command> [<args>]";
setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--namespace")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No namespace given for --namespace.\n" );
+ usage(git_usage_string);
+ }
+ setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (!prefixcmp(cmd, "--namespace=")) {
+ setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+ if (envchanged)
+ *envchanged = 1;
} else if (!strcmp(cmd, "--work-tree")) {
if (*argc < 2) {
fprintf(stderr, "No directory given for --work-tree.\n" );
{ "annotate", cmd_annotate, RUN_SETUP },
{ "apply", cmd_apply, RUN_SETUP_GENTLY },
{ "archive", cmd_archive },
- { "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
+ { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP },
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
See also "Runtime gitweb configuration" section in README file
for gitweb (in gitweb/README).
-- You can configure gitweb further using the gitweb configuration file;
+- You can configure gitweb further using the per-instance gitweb configuration file;
by default this is a file named gitweb_config.perl in the same place as
gitweb.cgi script. You can control the default place for the config file
using the GITWEB_CONFIG build configuration variable, and you can set it
GITWEB_CONFIG_SYSTEM build configuration variable, and override it
through the GITWEB_CONFIG_SYSTEM environment variable.
+ Note that if per-instance configuration file exists, then system-wide
+ configuration is _not used at all_. This is quite untypical and suprising
+ behavior. On the other hand changing current behavior would break backwards
+ compatibility and can lead to unexpected changes in gitweb behavior.
+ Therefore gitweb also looks for common system-wide configuration file,
+ normally /etc/gitweb-common.conf (set during build time using build time
+ configuration variable GITWEB_CONFIG_COMMON, set it at runtime using
+ environment variable with the same name). Settings from per-instance or
+ system-wide configuration file override those from common system-wide
+ configuration file.
+
- The gitweb config file is a fragment of perl code. You can set variables
using "our $variable = value"; text from "#" character until the end
of a line is ignored. See perlsyn(1) for details.
# default configuration for gitweb
GITWEB_CONFIG = gitweb_config.perl
GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
+GITWEB_CONFIG_COMMON = /etc/gitweb-common.conf
GITWEB_HOME_LINK_STR = projects
GITWEB_SITENAME =
GITWEB_PROJECTROOT = /pub/git
-e 's|++GIT_BINDIR++|$(bindir)|g' \
-e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
-e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
+ -e 's|++GITWEB_CONFIG_COMMON++|$(GITWEB_CONFIG_COMMON)|g' \
-e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
-e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
-e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
Runtime gitweb configuration
----------------------------
-You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
-(defaults to 'gitweb_config.perl' in the same directory as the CGI), and
-as a fallback `GITWEB_CONFIG_SYSTEM` (defaults to /etc/gitweb.conf).
+Gitweb obtains configuration data from the following sources in the
+following order:
+
+1. built-in values (some set during build stage),
+2. common system-wide configuration file (`GITWEB_CONFIG_COMMON`,
+ defaults to '/etc/gitweb-common.conf'),
+3. either per-instance configuration file (`GITWEB_CONFIG`, defaults to
+ 'gitweb_config.perl' in the same directory as the installed gitweb),
+ or if it does not exists then system-wide configuration file
+ (`GITWEB_CONFIG_SYSTEM`, defaults to '/etc/gitweb.conf').
+
+Values obtained in later configuration files override values obtained earlier
+in above sequence.
+
+You can read defaults in system-wide GITWEB_CONFIG_SYSTEM from GITWEB_CONFIG
+by adding
+
+ read_config_file($GITWEB_CONFIG_SYSTEM);
+
+at very beginning of per-instance GITWEB_CONFIG file. In this case
+settings in said per-instance file will override settings from
+system-wide configuration file. Note that read_config_file checks
+itself that the $GITWEB_CONFIG_SYSTEM file exists.
+
The most notable thing that is not configurable at compile time are the
optional features, stored in the '%features' variable.
return;
}
-our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM);
+our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM, $GITWEB_CONFIG_COMMON);
sub evaluate_gitweb_config {
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
+ our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "++GITWEB_CONFIG_COMMON++";
- # use first config file that exists
- read_config_file($GITWEB_CONFIG) or
+ # Protect agains duplications of file names, to not read config twice.
+ # Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
+ # there possibility of duplication of filename there doesn't matter.
+ $GITWEB_CONFIG = "" if ($GITWEB_CONFIG eq $GITWEB_CONFIG_COMMON);
+ $GITWEB_CONFIG_SYSTEM = "" if ($GITWEB_CONFIG_SYSTEM eq $GITWEB_CONFIG_COMMON);
+
+ # Common system-wide settings for convenience.
+ # Those settings can be ovverriden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
+ read_config_file($GITWEB_CONFIG_COMMON);
+
+ # Use first config file that exists. This means use the per-instance
+ # GITWEB_CONFIG if exists, otherwise use GITWEB_SYSTEM_CONFIG.
+ read_config_file($GITWEB_CONFIG) and return;
read_config_file($GITWEB_CONFIG_SYSTEM);
}
# key sanity check
return unless ($key);
+ # only subsection, if exists, is case sensitive,
+ # and not lowercased by 'git config -z -l'
+ if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
+ $key = join(".", lc($hi), $mi, lc($lo));
+ } else {
+ $key = lc($key);
+ }
$key =~ s/^gitweb\.//;
return if ($key =~ m/\W/);
if (opt->file_break && opt->last_shown == 0) {
if (opt->show_hunk_mark)
opt->output(opt, "\n", 1);
- } else if (opt->pre_context || opt->post_context) {
+ } else if (opt->pre_context || opt->post_context || opt->funcbody) {
if (opt->last_shown == 0) {
if (opt->show_hunk_mark) {
output_color(opt, "--", 2, opt->color_sep);
}
static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
- char *bol, unsigned lno)
+ char *bol, char *end, unsigned lno)
{
unsigned cur = lno, from = 1, funcname_lno = 0;
- int funcname_needed = opt->funcname;
+ int funcname_needed = !!opt->funcname;
+
+ if (opt->funcbody && !match_funcname(opt, bol, end))
+ funcname_needed = 2;
if (opt->pre_context < lno)
from = lno - opt->pre_context;
from = opt->last_shown + 1;
/* Rewind. */
- while (bol > buf && cur > from) {
+ while (bol > buf &&
+ cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
char *eol = --bol;
while (bol > buf && bol[-1] != '\n')
int binary_match_only = 0;
unsigned count = 0;
int try_lookahead = 0;
+ int show_function = 0;
enum grep_context ctx = GREP_CONTEXT_HEAD;
xdemitconf_t xecfg;
if (!opt->output)
opt->output = std_output;
- if (opt->pre_context || opt->post_context || opt->file_break) {
+ if (opt->pre_context || opt->post_context || opt->file_break ||
+ opt->funcbody) {
/* Show hunk marks, except for the first file. */
if (opt->last_shown)
opt->show_hunk_mark = 1;
*/
if (try_lookahead
&& !(last_hit
- && lno <= last_hit + opt->post_context)
+ && (show_function ||
+ lno <= last_hit + opt->post_context))
&& look_ahead(opt, &left, &lno, &bol))
break;
eol = end_of_line(bol, &left);
/* Hit at this line. If we haven't shown the
* pre-context lines, we would need to show them.
*/
- if (opt->pre_context)
- show_pre_context(opt, name, buf, bol, lno);
+ if (opt->pre_context || opt->funcbody)
+ show_pre_context(opt, name, buf, bol, eol, lno);
else if (opt->funcname)
show_funcname_line(opt, name, buf, bol, lno);
show_line(opt, bol, eol, name, lno, ':');
last_hit = lno;
+ if (opt->funcbody)
+ show_function = 1;
+ goto next_line;
}
- else if (last_hit &&
- lno <= last_hit + opt->post_context) {
+ if (show_function && match_funcname(opt, bol, eol))
+ show_function = 0;
+ if (show_function ||
+ (last_hit && lno <= last_hit + opt->post_context)) {
/* If the last hit is within the post context,
* we need to show this line.
*/
int color;
int max_depth;
int funcname;
+ int funcbody;
char color_context[COLOR_MAXLEN];
char color_filename[COLOR_MAXLEN];
char color_function[COLOR_MAXLEN];
return error("Remote HEAD is not a symref");
/* Remote branch must not be the remote HEAD */
- for (i=0; symref && i<MAXDEPTH; i++) {
+ for (i = 0; symref && i < MAXDEPTH; i++) {
if (!strcmp(remote_ref->name, symref))
return error("Remote branch %s is the current HEAD",
remote_ref->name);
struct strbuf buf = STRBUF_INIT;
struct http_pack_request *preq;
- preq = xmalloc(sizeof(*preq));
+ preq = xcalloc(1, sizeof(*preq));
preq->target = target;
- preq->range_header = NULL;
end_url_with_slash(&buf, base_url);
strbuf_addf(&buf, "objects/pack/pack-%s.pack",
struct curl_slist *range_header = NULL;
struct http_object_request *freq;
- freq = xmalloc(sizeof(*freq));
+ freq = xcalloc(1, sizeof(*freq));
hashcpy(freq->sha1, sha1);
freq->localfile = -1;
goto abort;
}
- memset(&freq->stream, 0, sizeof(freq->stream));
-
git_inflate_init(&freq->stream);
git_SHA1_Init(&freq->c);
return freq;
abort:
- free(filename);
free(freq->url);
free(freq);
return NULL;
check[0].attr = git_attr("merge");
check[1].attr = git_attr("conflict-marker-size");
}
- return git_checkattr(path, 2, check);
+ return git_check_attr(path, 2, check);
}
static void normalize_file(mmfile_t *mm, const char *path)
if (!check.attr)
check.attr = git_attr("conflict-marker-size");
- if (!git_checkattr(path, 1, &check) && check.value) {
+ if (!git_check_attr(path, 1, &check) && check.value) {
marker_size = atoi(check.value);
if (marker_size <= 0)
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
o->subtree_shift = s + strlen("subtree=");
else if (!strcmp(s, "patience"))
o->xdl_opts |= XDF_PATIENCE_DIFF;
+ else if (!strcmp(s, "histogram"))
+ o->xdl_opts |= XDF_HISTOGRAM_DIFF;
else if (!strcmp(s, "ignore-space-change"))
o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(s, "ignore-all-space"))
hashcpy(l.key_sha1, object_sha1);
hashclr(l.val_sha1);
note_tree_remove(t, t->root, 0, &l);
- if (is_null_sha1(l.val_sha1)) // no note was removed
+ if (is_null_sha1(l.val_sha1)) /* no note was removed */
return 1;
t->dirty = 1;
return 0;
if (len < 0)
len = strlen(in);
- if (prefix && prefix_len < 0)
- prefix_len = strlen(prefix);
+ if (prefix_len < 0) {
+ if (prefix)
+ prefix_len = strlen(prefix);
+ else
+ prefix_len = 0;
+ }
off = 0;
i = 0;
{
if (in_porcelain && *first && header_msg) {
printf("%s\n", header_msg);
- *first=0;
+ *first = 0;
}
printf(fmt, name);
}
static int do_one_ref(const char *base, each_ref_fn fn, int trim,
int flags, void *cb_data, struct ref_list *entry)
{
- if (strncmp(base, entry->name, trim))
+ if (prefixcmp(entry->name, base))
return 0;
if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
int for_each_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(NULL, "refs/", fn, 0, 0, cb_data);
+ return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
}
int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(submodule, "refs/", fn, 0, 0, cb_data);
+ return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
}
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
}
+int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+{
+ struct strbuf buf = STRBUF_INIT;
+ int ret = 0;
+ unsigned char sha1[20];
+ int flag;
+
+ strbuf_addf(&buf, "%sHEAD", get_git_namespace());
+ if (resolve_ref(buf.buf, sha1, 1, &flag))
+ ret = fn(buf.buf, sha1, flag, cb_data);
+ strbuf_release(&buf);
+
+ return ret;
+}
+
+int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+{
+ struct strbuf buf = STRBUF_INIT;
+ int ret;
+ strbuf_addf(&buf, "%srefs/", get_git_namespace());
+ ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+ strbuf_release(&buf);
+ return ret;
+}
+
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data)
{
int for_each_rawref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(NULL, "refs/", fn, 0,
+ return do_for_each_ref(NULL, "", fn, 0,
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int head_ref_namespaced(each_ref_fn fn, void *cb_data);
+extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
+
static inline const char *has_glob_specials(const char *pattern)
{
return strpbrk(pattern, "?*[");
argv[argc++] = "--thin";
if (options.dry_run)
argv[argc++] = "--dry-run";
- if (options.verbosity > 1)
+ if (options.verbosity < 0)
+ argv[argc++] = "--quiet";
+ else if (options.verbosity > 1)
argv[argc++] = "--verbose";
argv[argc++] = url;
for (i = 0; i < nr_spec; i++)
static NORETURN void die_child(const char *err, va_list params)
{
- char msg[4096];
- int len = vsnprintf(msg, sizeof(msg), err, params);
- if (len > sizeof(msg))
- len = sizeof(msg);
-
- write_in_full(child_err, "fatal: ", 7);
- write_in_full(child_err, msg, len);
- write_in_full(child_err, "\n", 1);
+ vwritef(child_err, "fatal: ", err, params);
exit(128);
}
+
+static void error_child(const char *err, va_list params)
+{
+ vwritef(child_err, "error: ", err, params);
+}
#endif
static inline void set_cloexec(int fd)
if (code == 127) {
code = -1;
failed_errno = ENOENT;
- if (!silent_exec_failure)
- error("cannot run %s: %s", argv0,
- strerror(ENOENT));
}
} else {
error("waitpid is confused (%s)", argv0);
set_cloexec(child_err);
}
set_die_routine(die_child);
+ set_error_routine(error_child);
close(notify_pipe[0]);
set_cloexec(notify_pipe[1]);
} else {
execvp(cmd->argv[0], (char *const*) cmd->argv);
}
- /*
- * Do not check for cmd->silent_exec_failure; the parent
- * process will check it when it sees this exit code.
- */
- if (errno == ENOENT)
+ if (errno == ENOENT) {
+ if (!cmd->silent_exec_failure)
+ error("cannot run %s: %s", cmd->argv[0],
+ strerror(ENOENT));
exit(127);
- else
+ } else {
die_errno("cannot exec '%s'", cmd->argv[0]);
+ }
}
if (cmd->pid < 0)
error("cannot fork() for %s: %s", cmd->argv[0],
return pathspec;
}
+const char *pathspec_prefix(const char *prefix, const char **pathspec)
+{
+ const char **p, *n, *prev;
+ unsigned long max;
+
+ if (!pathspec)
+ return prefix ? xmemdupz(prefix, strlen(prefix)) : NULL;
+
+ prev = NULL;
+ max = PATH_MAX;
+ for (p = pathspec; (n = *p) != NULL; p++) {
+ int i, len = 0;
+ for (i = 0; i < max; i++) {
+ char c = n[i];
+ if (prev && prev[i] != c)
+ break;
+ if (!c || c == '*' || c == '?')
+ break;
+ if (c == '/')
+ len = i+1;
+ }
+ prev = n;
+ if (len < max) {
+ max = len;
+ if (!max)
+ break;
+ }
+ }
+
+ return max ? xmemdupz(prev, max) : NULL;
+}
+
/*
* Test if it looks like we're at a git directory.
* We want to see:
unsigned int word;
/*
- * Is it a zlib-compressed buffer? If so, the first byte
- * must be 0x78 (15-bit window size, deflated), and the
- * first 16-bit word is evenly divisible by 31. If so,
- * we are looking at the official format, not the experimental
- * one.
+ * We must determine if the buffer contains the standard
+ * zlib-deflated stream or the experimental format based
+ * on the in-pack object format. Compare the header byte
+ * for each format:
+ *
+ * RFC1950 zlib w/ deflate : 0www1000 : 0 <= www <= 7
+ * Experimental pack-based : Stttssss : ttt = 1,2,3,4
+ *
+ * If bit 7 is clear and bits 0-3 equal 8, the buffer MUST be
+ * in standard loose-object format, UNLESS it is a Git-pack
+ * format object *exactly* 8 bytes in size when inflated.
+ *
+ * However, RFC1950 also specifies that the 1st 16-bit word
+ * must be divisible by 31 - this checksum tells us our buffer
+ * is in the standard format, giving a false positive only if
+ * the 1st word of the Git-pack format object happens to be
+ * divisible by 31, ie:
+ * ((byte0 * 256) + byte1) % 31 = 0
+ * => 0ttt10000www1000 % 31 = 0
+ *
+ * As it happens, this case can only arise for www=3 & ttt=1
+ * - ie, a Commit object, which would have to be 8 bytes in
+ * size. As no Commit can be that small, we find that the
+ * combination of these two criteria (bitmask & checksum)
+ * can always correctly determine the buffer format.
*/
word = (map[0] << 8) + map[1];
- if (map[0] == 0x78 && !(word % 31))
+ if ((map[0] & 0x8F) == 0x08 && !(word % 31))
return 0;
else
return 1;
--- /dev/null
+#!/bin/sh
+
+test_diff_frobnitz() {
+ cat >file1 <<\EOF
+#include <stdio.h>
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+ int i;
+ for(i = 0; i < 10; i++)
+ {
+ printf("Your answer is: ");
+ printf("%d\n", foo);
+ }
+}
+
+int fact(int n)
+{
+ if(n > 1)
+ {
+ return fact(n-1) * n;
+ }
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ frobnitz(fact(10));
+}
+EOF
+
+ cat >file2 <<\EOF
+#include <stdio.h>
+
+int fib(int n)
+{
+ if(n > 2)
+ {
+ return fib(n-1) + fib(n-2);
+ }
+ return 1;
+}
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+ int i;
+ for(i = 0; i < 10; i++)
+ {
+ printf("%d\n", foo);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ frobnitz(fib(10));
+}
+EOF
+
+ cat >expect <<\EOF
+diff --git a/file1 b/file2
+index 6faa5a3..e3af329 100644
+--- a/file1
++++ b/file2
+@@ -1,26 +1,25 @@
+ #include <stdio.h>
+
++int fib(int n)
++{
++ if(n > 2)
++ {
++ return fib(n-1) + fib(n-2);
++ }
++ return 1;
++}
++
+ // Frobs foo heartily
+ int frobnitz(int foo)
+ {
+ int i;
+ for(i = 0; i < 10; i++)
+ {
+- printf("Your answer is: ");
+ printf("%d\n", foo);
+ }
+ }
+
+-int fact(int n)
+-{
+- if(n > 1)
+- {
+- return fact(n-1) * n;
+- }
+- return 1;
+-}
+-
+ int main(int argc, char **argv)
+ {
+- frobnitz(fact(10));
++ frobnitz(fib(10));
+ }
+EOF
+
+ STRATEGY=$1
+
+ test_expect_success "$STRATEGY diff" '
+ test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output &&
+ test_cmp expect output
+ '
+
+ test_expect_success "$STRATEGY diff output is valid" '
+ mv file2 expect &&
+ git apply < output &&
+ test_cmp expect file2
+ '
+}
+
+test_diff_unique() {
+ cat >uniq1 <<\EOF
+1
+2
+3
+4
+5
+6
+EOF
+
+ cat >uniq2 <<\EOF
+a
+b
+c
+d
+e
+f
+EOF
+
+ cat >expect <<\EOF
+diff --git a/uniq1 b/uniq2
+index b414108..0fdf397 100644
+--- a/uniq1
++++ b/uniq2
+@@ -1,6 +1,6 @@
+-1
+-2
+-3
+-4
+-5
+-6
++a
++b
++c
++d
++e
++f
+EOF
+
+ STRATEGY=$1
+
+ test_expect_success 'completely different files' '
+ test_must_fail git diff --no-index "--$STRATEGY" uniq1 uniq2 > output &&
+ test_cmp expect output
+ '
+}
+
path="$1"
expect="$2"
- git check-attr test -- "$path" >actual &&
+ git check-attr test -- "$path" >actual 2>err &&
echo "$path: test: $2" >expect &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ test_line_count = 0 err
}
test_expect_success 'setup' '
- mkdir -p a/b/d a/c &&
+ mkdir -p a/b/d a/c b &&
(
echo "[attr]notest !test"
echo "f test=f"
echo "h test=a/b/h" &&
echo "d/* test=a/b/d/*"
echo "d/yes notest"
- ) >a/b/.gitattributes
+ ) >a/b/.gitattributes &&
(
echo "global test=global"
- ) >"$HOME"/global-gitattributes
+ ) >"$HOME"/global-gitattributes &&
+ cat <<EOF >expect-all
+f: test: f
+a/f: test: f
+a/c/f: test: f
+a/g: test: a/g
+a/b/g: test: a/b/g
+b/g: test: unspecified
+a/b/h: test: a/b/h
+a/b/d/g: test: a/b/d/*
+onoff: test: unset
+offon: test: set
+no: notest: set
+no: test: unspecified
+a/b/d/no: notest: set
+a/b/d/no: test: a/b/d/*
+a/b/d/yes: notest: set
+a/b/d/yes: test: unspecified
+EOF
+
+'
+
+test_expect_success 'command line checks' '
+
+ test_must_fail git check-attr &&
+ test_must_fail git check-attr -- &&
+ test_must_fail git check-attr test &&
+ test_must_fail git check-attr test -- &&
+ test_must_fail git check-attr -- f &&
+ echo "f" | test_must_fail git check-attr --stdin &&
+ echo "f" | test_must_fail git check-attr --stdin -- f &&
+ echo "f" | test_must_fail git check-attr --stdin test -- f &&
+ test_must_fail git check-attr "" -- f
'
'
+test_expect_success 'unnormalized paths' '
+
+ attr_check ./f f &&
+ attr_check ./a/g a/g &&
+ attr_check a/./g a/g &&
+ attr_check a/c/../b/g a/b/g
+
+'
+
+test_expect_success 'relative paths' '
+
+ (cd a && attr_check ../f f) &&
+ (cd a && attr_check f f) &&
+ (cd a && attr_check i a/i) &&
+ (cd a && attr_check g a/g) &&
+ (cd a && attr_check b/g a/b/g) &&
+ (cd b && attr_check ../a/f f) &&
+ (cd b && attr_check ../a/g a/g) &&
+ (cd b && attr_check ../a/b/g a/b/g)
+
+'
+
test_expect_success 'core.attributesfile' '
attr_check global unspecified &&
git config core.attributesfile "$HOME/global-gitattributes" &&
test_expect_success 'attribute test: read paths from stdin' '
- cat <<EOF > expect &&
-f: test: f
-a/f: test: f
-a/c/f: test: f
-a/g: test: a/g
-a/b/g: test: a/b/g
-b/g: test: unspecified
-a/b/h: test: a/b/h
-a/b/d/g: test: a/b/d/*
-onoff: test: unset
-offon: test: set
-no: test: unspecified
-a/b/d/no: test: a/b/d/*
-a/b/d/yes: test: unspecified
-EOF
-
+ grep -v notest < expect-all > expect &&
sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
test_cmp expect actual
'
+test_expect_success 'attribute test: --all option' '
+
+ grep -v unspecified < expect-all | sort > expect &&
+ sed -e "s/:.*//" < expect-all | uniq |
+ git check-attr --stdin --all | sort > actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'root subdir attribute test' '
attr_check a/i a/i &&
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2011 Roberto Tyley
+#
+
+test_description='Correctly identify and parse loose object headers
+
+There are two file formats for loose objects - the original standard
+format, and the experimental format introduced with Git v1.4.3, later
+deprecated with v1.5.3. Although Git no longer writes the
+experimental format, objects in both formats must be read, with the
+format for a given file being determined by the header.
+
+Detecting file format based on header is not entirely trivial, not
+least because the first byte of a zlib-deflated stream will vary
+depending on how much memory was allocated for the deflation window
+buffer when the object was written out (for example 4KB on Android,
+rather that 32KB on a normal PC).
+
+The loose objects used as test vectors have been generated with the
+following Git versions:
+
+standard format: Git v1.7.4.1
+experimental format: Git v1.4.3 (legacyheaders=false)
+standard format, deflated with 4KB window size: Agit/JGit on Android
+'
+
+. ./test-lib.sh
+
+assert_blob_equals() {
+ printf "%s" "$2" >expected &&
+ git cat-file -p "$1" >actual &&
+ test_cmp expected actual
+}
+
+test_expect_success setup '
+ cp -R "$TEST_DIRECTORY/t1013/objects" .git/
+ git --version
+'
+
+test_expect_success 'read standard-format loose objects' '
+ git cat-file tag 8d4e360d6c70fbd72411991c02a09c442cf7a9fa &&
+ git cat-file commit 6baee0540ea990d9761a3eb9ab183003a71c3696 &&
+ git ls-tree 7a37b887a73791d12d26c0d3e39568a8fb0fa6e8 &&
+ assert_blob_equals "257cc5642cb1a054f08cc83f2d943e56fd3ebe99" "foo$LF"
+'
+
+test_expect_success 'read experimental-format loose objects' '
+ git cat-file tag 76e7fa9941f4d5f97f64fea65a2cba436bc79cbb &&
+ git cat-file commit 7875c6237d3fcdd0ac2f0decc7d3fa6a50b66c09 &&
+ git ls-tree 95b1625de3ba8b2214d1e0d0591138aea733f64f &&
+ assert_blob_equals "2e65efe2a145dda7ee51d1741299f848e5bf752e" "a" &&
+ assert_blob_equals "9ae9e86b7bd6cb1472d9373702d8249973da0832" "ab" &&
+ assert_blob_equals "85df50785d62d3b05ab03d9cbf7e4a0b49449730" "abcd" &&
+ assert_blob_equals "1656f9233d999f61ef23ef390b9c71d75399f435" "abcdefgh" &&
+ assert_blob_equals "1e72a6b2c4a577ab0338860fa9fe87f761fc9bbd" "abcdefghi" &&
+ assert_blob_equals "70e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd" "abcdefghijklmnop" &&
+ assert_blob_equals "bd15045f6ce8ff75747562173640456a394412c8" "abcdefghijklmnopqrstuvwx"
+'
+
+test_expect_success 'read standard-format objects deflated with smaller window buffer' '
+ git cat-file tag f816d5255855ac160652ee5253b06cd8ee14165a &&
+ git cat-file tag 149cedb5c46929d18e0f118e9fa31927487af3b6
+'
+
+test_done
--- /dev/null
+Â\vx\9c%ÌA\ e\820\10@Ñ}O1{cSZ(\98\18ãνá\ 2Ãthª\94\92Z\8cÜÞ Ëÿ\16?\r\ f¦\ 2m×6dµi\9d\19É9\85¤Gå\98h\a´Ø¨ÁZR'Q¶\85\81R\8c¡\88\82\1eø³p\ e\91ç\82ÓqL9âÏ=g¸§\81sIÐo\13opÎÿ\94eÏ«_1»\80³¤$×ç\ 5*Si«ëNwpP\95RBôûÅÁú
+³\87[(ð®d-\8dø\ 2ÁL9á
\ No newline at end of file
--- /dev/null
+H\89\15ÌÁ\ e\820\f\80aÏ{\8aÞ\rI»e\1d&Æø*¥\1d\88\ 1G°\17ß^¸ýù\ e¿Ë\ 4DåÒ\86wU\87Ò\97¬\1cS±4ª\19\8aÆ\11ª\9e ,\19\afÅ[ðßVAÛºÎ\1eüxÈÇö6[wtG§Lu\a¸?\97¦²¼Ú×\1f@\89"gì{\86+\12b\b\7fy¾%M
\ No newline at end of file
cp one original.one &&
cp dir/two original.two
'
-LF='
-'
test_expect_success 'update-index and ls-files' '
git update-index --add one &&
test_expect_success 'reflog shows all entries' '
cat >expect <<-\EOF
- topic@{0} two: updating HEAD
- topic@{1} one: updating HEAD
- topic@{2} two: updating HEAD
- topic@{3} one: updating HEAD
+ topic@{0} reset: moving to two
+ topic@{1} reset: moving to one
+ topic@{2} reset: moving to two
+ topic@{3} reset: moving to one
topic@{4} branch: Created from HEAD
EOF
git log -g --format="%gd %gs" topic >actual &&
--- /dev/null
+#!/bin/sh
+
+test_description='ls-files tests with relative paths
+
+This test runs git ls-files with various relative path arguments.
+'
+
+. ./test-lib.sh
+
+new_line='
+'
+sq=\'
+
+test_expect_success 'prepare' '
+ : >never-mind-me &&
+ git add never-mind-me &&
+ mkdir top &&
+ (
+ cd top &&
+ mkdir sub &&
+ x="x xa xbc xdef xghij xklmno" &&
+ y=$(echo "$x" | tr x y) &&
+ touch $x &&
+ touch $y &&
+ cd sub &&
+ git add ../x*
+ )
+'
+
+test_expect_success 'ls-files with mixed levels' '
+ (
+ cd top/sub &&
+ cat >expect <<-EOF &&
+ ../../never-mind-me
+ ../x
+ EOF
+ git ls-files $(cat expect) >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'ls-files -c' '
+ (
+ cd top/sub &&
+ for f in ../y*
+ do
+ echo "error: pathspec $sq$f$sq did not match any file(s) known to git."
+ done >expect &&
+ echo "Did you forget to ${sq}git add${sq}?" >>expect &&
+ ls ../x* >>expect &&
+ test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual 2>&1 &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'ls-files -o' '
+ (
+ cd top/sub &&
+ for f in ../x*
+ do
+ echo "error: pathspec $sq$f$sq did not match any file(s) known to git."
+ done >expect &&
+ echo "Did you forget to ${sq}git add${sq}?" >>expect &&
+ ls ../y* >>expect &&
+ test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual 2>&1 &&
+ test_cmp expect actual
+ )
+'
+
+test_done
--- /dev/null
+#!/bin/sh
+
+test_description='
+Miscellaneous tests for git ls-tree.
+
+ 1. git ls-tree fails in presence of tree damage.
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir a &&
+ touch a/one &&
+ git add a/one &&
+ git commit -m test
+'
+
+test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '
+ rm -f .git/objects/5f/cffbd6e4c5c5b8d81f5e9314b20e338e3ffff5 &&
+ test_must_fail git ls-tree -r HEAD
+'
+
+test_done
git commit -a -m "intermediate commit" &&
test_tick &&
echo $H $flag >>F &&
- git commit -a --$flag HEAD~1 $3 &&
+ git commit -a --$flag HEAD~1 &&
E=$(git cat-file commit '$H-$flag' |
sed -ne "s/^encoding //p") &&
test "z$E" = "z$H" &&
test_commit_autosquash_flags eucJP fixup
-test_commit_autosquash_flags ISO-2022-JP squash '-m "squash message"'
+test_commit_autosquash_flags ISO-2022-JP squash
test_done
FN='濱野'
GN='純'
HT=' '
-LF='
-'
DQ='"'
echo foo 2>/dev/null > "Name and an${HT}HT"
test_description='patience diff algorithm'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-alternative.sh
-cat >file1 <<\EOF
-#include <stdio.h>
+test_diff_frobnitz "patience"
-// Frobs foo heartily
-int frobnitz(int foo)
-{
- int i;
- for(i = 0; i < 10; i++)
- {
- printf("Your answer is: ");
- printf("%d\n", foo);
- }
-}
-
-int fact(int n)
-{
- if(n > 1)
- {
- return fact(n-1) * n;
- }
- return 1;
-}
-
-int main(int argc, char **argv)
-{
- frobnitz(fact(10));
-}
-EOF
-
-cat >file2 <<\EOF
-#include <stdio.h>
-
-int fib(int n)
-{
- if(n > 2)
- {
- return fib(n-1) + fib(n-2);
- }
- return 1;
-}
-
-// Frobs foo heartily
-int frobnitz(int foo)
-{
- int i;
- for(i = 0; i < 10; i++)
- {
- printf("%d\n", foo);
- }
-}
-
-int main(int argc, char **argv)
-{
- frobnitz(fib(10));
-}
-EOF
-
-cat >expect <<\EOF
-diff --git a/file1 b/file2
-index 6faa5a3..e3af329 100644
---- a/file1
-+++ b/file2
-@@ -1,26 +1,25 @@
- #include <stdio.h>
-
-+int fib(int n)
-+{
-+ if(n > 2)
-+ {
-+ return fib(n-1) + fib(n-2);
-+ }
-+ return 1;
-+}
-+
- // Frobs foo heartily
- int frobnitz(int foo)
- {
- int i;
- for(i = 0; i < 10; i++)
- {
-- printf("Your answer is: ");
- printf("%d\n", foo);
- }
- }
-
--int fact(int n)
--{
-- if(n > 1)
-- {
-- return fact(n-1) * n;
-- }
-- return 1;
--}
--
- int main(int argc, char **argv)
- {
-- frobnitz(fact(10));
-+ frobnitz(fib(10));
- }
-EOF
-
-test_expect_success 'patience diff' '
-
- test_must_fail git diff --no-index --patience file1 file2 > output &&
- test_cmp expect output
-
-'
-
-test_expect_success 'patience diff output is valid' '
-
- mv file2 expect &&
- git apply < output &&
- test_cmp expect file2
-
-'
-
-cat >uniq1 <<\EOF
-1
-2
-3
-4
-5
-6
-EOF
-
-cat >uniq2 <<\EOF
-a
-b
-c
-d
-e
-f
-EOF
-
-cat >expect <<\EOF
-diff --git a/uniq1 b/uniq2
-index b414108..0fdf397 100644
---- a/uniq1
-+++ b/uniq2
-@@ -1,6 +1,6 @@
--1
--2
--3
--4
--5
--6
-+a
-+b
-+c
-+d
-+e
-+f
-EOF
-
-test_expect_success 'completely different files' '
-
- test_must_fail git diff --no-index --patience uniq1 uniq2 > output &&
- test_cmp expect output
-
-'
+test_diff_unique "patience"
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='histogram diff algorithm'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-alternative.sh
+
+test_diff_frobnitz "histogram"
+
+test_diff_unique "histogram"
+
+test_done
echo "X-Fake-Field: Line Three" &&
git format-patch --stdout first | sed -e "1d"
} | append_cr >patch1-crlf.eml &&
+ {
+ printf "%255s\\n" ""
+ echo "X-Fake-Field: Line One" &&
+ echo "X-Fake-Field: Line Two" &&
+ echo "X-Fake-Field: Line Three" &&
+ git format-patch --stdout first | sed -e "1d"
+ } > patch1-ws.eml &&
sed -n -e "3,\$p" msg >file &&
git add file &&
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
+test_expect_success 'am applies patch e-mail with preceding whitespace' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ git am patch1-ws.eml &&
+ ! test -d .git/rebase-apply &&
+ git diff --exit-code second &&
+ test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
test_expect_success 'setup: new author and committer' '
GIT_AUTHOR_NAME="Another Thor" &&
GIT_AUTHOR_EMAIL="a.thor@example.com" &&
--- /dev/null
+#!/bin/sh
+
+test_description='fetch/push involving ref namespaces'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_tick &&
+ git init original &&
+ (
+ cd original &&
+ echo 0 >count &&
+ git add count &&
+ test_commit 0 &&
+ echo 1 >count &&
+ git add count &&
+ test_commit 1 &&
+ git remote add pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
+ git remote add pushee-unnamespaced ../pushee
+ ) &&
+ commit0=$(cd original && git rev-parse HEAD^) &&
+ commit1=$(cd original && git rev-parse HEAD) &&
+ git init pushee &&
+ git init puller
+'
+
+test_expect_success 'pushing into a repository using a ref namespace' '
+ (
+ cd original &&
+ git push pushee-namespaced master &&
+ git ls-remote pushee-namespaced >actual &&
+ printf "$commit1\trefs/heads/master\n" >expected &&
+ test_cmp expected actual &&
+ git push pushee-namespaced --tags &&
+ git ls-remote pushee-namespaced >actual &&
+ printf "$commit0\trefs/tags/0\n" >>expected &&
+ printf "$commit1\trefs/tags/1\n" >>expected &&
+ test_cmp expected actual &&
+ # Verify that the GIT_NAMESPACE environment variable works as well
+ GIT_NAMESPACE=namespace git ls-remote "ext::git %s ../pushee" >actual &&
+ test_cmp expected actual &&
+ # Verify that --namespace overrides GIT_NAMESPACE
+ GIT_NAMESPACE=garbage git ls-remote pushee-namespaced >actual &&
+ test_cmp expected actual &&
+ # Try a namespace with no content
+ git ls-remote "ext::git --namespace=garbage %s ../pushee" >actual &&
+ test_cmp /dev/null actual &&
+ git ls-remote pushee-unnamespaced >actual &&
+ sed -e "s|refs/|refs/namespaces/namespace/refs/|" expected >expected.unnamespaced &&
+ test_cmp expected.unnamespaced actual
+ )
+'
+
+test_expect_success 'pulling from a repository using a ref namespace' '
+ (
+ cd puller &&
+ git remote add -f pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
+ git for-each-ref refs/ >actual &&
+ printf "$commit1 commit\trefs/remotes/pushee-namespaced/master\n" >expected &&
+ printf "$commit0 commit\trefs/tags/0\n" >>expected &&
+ printf "$commit1 commit\trefs/tags/1\n" >>expected &&
+ test_cmp expected actual
+ )
+'
+
+# This test with clone --mirror checks for possible regressions in clone
+# or the machinery underneath it. It ensures that no future change
+# causes clone to ignore refs in refs/namespaces/*. In particular, it
+# protects against a regression caused by any future change to the refs
+# machinery that might cause it to ignore refs outside of refs/heads/*
+# or refs/tags/*. More generally, this test also checks the high-level
+# functionality of using clone --mirror to back up a set of repos hosted
+# in the namespaces of a single repo.
+test_expect_success 'mirroring a repository using a ref namespace' '
+ git clone --mirror pushee mirror &&
+ (
+ cd mirror &&
+ git for-each-ref refs/ >actual &&
+ printf "$commit1 commit\trefs/namespaces/namespace/refs/heads/master\n" >expected &&
+ printf "$commit0 commit\trefs/namespaces/namespace/refs/tags/0\n" >>expected &&
+ printf "$commit1 commit\trefs/namespaces/namespace/refs/tags/1\n" >>expected &&
+ test_cmp expected actual
+ )
+'
+
+test_done
test -z "$(git for-each-ref "refs/heads/bisect")"
'
+test_expect_success 'bisect reset removes bisect state after --no-checkout' '
+ git bisect reset &&
+ git bisect start --no-checkout &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH3 &&
+ git bisect next &&
+ git bisect reset &&
+ test -z "$(git for-each-ref "refs/bisect/*")" &&
+ test -z "$(git for-each-ref "refs/heads/bisect")" &&
+ test -z "$(git for-each-ref "BISECT_HEAD")"
+'
+
test_expect_success 'bisect start: back in good branch' '
git branch > branch.output &&
grep "* other" branch.output > /dev/null &&
grep "* other" branch.output > /dev/null
'
-test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' '
- git bisect start $HASH4 $HASH1 -- &&
- git bisect good &&
+test_expect_success 'bisect start: no ".git/BISECT_START" created if junk rev' '
+ git bisect reset &&
test_must_fail git bisect start $HASH4 foo -- &&
git branch > branch.output &&
grep "* other" branch.output > /dev/null &&
test_must_fail test -e .git/BISECT_START
'
+test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if junk rev' '
+ git bisect start $HASH4 $HASH1 -- &&
+ git bisect good &&
+ cp .git/BISECT_START saved &&
+ test_must_fail git bisect start $HASH4 foo -- &&
+ git branch > branch.output &&
+ grep "* (no branch)" branch.output > /dev/null &&
+ test_cmp saved .git/BISECT_START
+'
test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
git bisect start $HASH4 $HASH1 -- &&
git bisect good &&
grep "bad path parameters" error.txt
'
+test_expect_success 'test bisection on bare repo - --no-checkout specified' '
+ git clone --bare . bare.nocheckout &&
+ (
+ cd bare.nocheckout &&
+ git bisect start --no-checkout &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run eval \
+ "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
+ >../nocheckout.log &&
+ git bisect reset
+ ) &&
+ grep "$HASH3 is the first bad commit" nocheckout.log
+'
+
+
+test_expect_success 'test bisection on bare repo - --no-checkout defaulted' '
+ git clone --bare . bare.defaulted &&
+ (
+ cd bare.defaulted &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run eval \
+ "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
+ >../defaulted.log &&
+ git bisect reset
+ ) &&
+ grep "$HASH3 is the first bad commit" defaulted.log
+'
+
#
+# This creates a broken branch which cannot be checked out because
+# the tree created has been deleted.
#
+# H1-H2-H3-H4-H5-H6-H7 <--other
+# \
+# S5-S6'-S7'-S8'-S9 <--broken
+#
+# Commits marked with ' have a missing tree.
+#
+test_expect_success 'broken branch creation' '
+ git bisect reset &&
+ git checkout -b broken $HASH4 &&
+ git tag BROKEN_HASH4 $HASH4 &&
+ add_line_into_file "5(broken): first line on a broken branch" hello2 &&
+ git tag BROKEN_HASH5 &&
+ mkdir missing &&
+ :> missing/MISSING &&
+ git add missing/MISSING &&
+ git commit -m "6(broken): Added file that will be deleted"
+ git tag BROKEN_HASH6 &&
+ add_line_into_file "7(broken): second line on a broken branch" hello2 &&
+ git tag BROKEN_HASH7 &&
+ add_line_into_file "8(broken): third line on a broken branch" hello2 &&
+ git tag BROKEN_HASH8 &&
+ git rm missing/MISSING &&
+ git commit -m "9(broken): Remove missing file"
+ git tag BROKEN_HASH9 &&
+ rm .git/objects/39/f7e61a724187ab767d2e08442d9b6b9dab587d
+'
+
+echo "" > expected.ok
+cat > expected.missing-tree.default <<EOF
+fatal: unable to read tree 39f7e61a724187ab767d2e08442d9b6b9dab587d
+EOF
+
+test_expect_success 'bisect fails if tree is broken on start commit' '
+ git bisect reset &&
+ test_must_fail git bisect start BROKEN_HASH7 BROKEN_HASH4 2>error.txt &&
+ test_cmp expected.missing-tree.default error.txt
+'
+
+test_expect_success 'bisect fails if tree is broken on trial commit' '
+ git bisect reset &&
+ test_must_fail git bisect start BROKEN_HASH9 BROKEN_HASH4 2>error.txt &&
+ git reset --hard broken &&
+ git checkout broken &&
+ test_cmp expected.missing-tree.default error.txt
+'
+
+check_same()
+{
+ echo "Checking $1 is the same as $2" &&
+ git rev-parse "$1" > expected.same &&
+ git rev-parse "$2" > expected.actual &&
+ test_cmp expected.same expected.actual
+}
+
+test_expect_success 'bisect: --no-checkout - start commit bad' '
+ git bisect reset &&
+ git bisect start BROKEN_HASH7 BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - trial commit bad' '
+ git bisect reset &&
+ git bisect start broken BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target before breakage' '
+ git bisect reset &&
+ git bisect start broken BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect bad BISECT_HEAD &&
+ check_same BROKEN_HASH5 BISECT_HEAD &&
+ git bisect bad BISECT_HEAD &&
+ check_same BROKEN_HASH5 bisect/bad &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target in breakage' '
+ git bisect reset &&
+ git bisect start broken BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect bad BISECT_HEAD &&
+ check_same BROKEN_HASH5 BISECT_HEAD &&
+ git bisect good BISECT_HEAD &&
+ check_same BROKEN_HASH6 bisect/bad &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target after breakage' '
+ git bisect reset &&
+ git bisect start broken BROKEN_HASH4 --no-checkout &&
+ check_same BROKEN_HASH6 BISECT_HEAD &&
+ git bisect good BISECT_HEAD &&
+ check_same BROKEN_HASH8 BISECT_HEAD &&
+ git bisect good BISECT_HEAD &&
+ check_same BROKEN_HASH9 bisect/bad &&
+ git bisect reset
+'
+
+test_expect_success 'bisect: demonstrate identification of damage boundary' "
+ git bisect reset &&
+ git checkout broken &&
+ git bisect start broken master --no-checkout &&
+ git bisect run sh -c '
+ GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) &&
+ git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ &&
+ git pack-objects --stdout >/dev/null < tmp.\$\$
+ rc=\$?
+ rm -f tmp.\$\$
+ test \$rc = 0' &&
+ check_same BROKEN_HASH6 bisect/bad &&
+ git bisect reset
+"
+
test_done
(
cd addtest &&
- git submodule add "$submodurl" submod &&
+ git submodule add -q "$submodurl" submod >actual &&
+ test ! -s actual &&
git submodule init
) &&
echo "$rev1" >expect &&
mkdir init &&
- git submodule update &&
+ git submodule update -q >update.out &&
+ test ! -s update.out &&
inspect init &&
test_cmp expect head-sha1
. ./test-lib.sh
-LF='
-'
-
remove_config_vars()
{
# Unset all config variables used by git-difftool
test_cmp expected actual
'
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c- printf("Hello world.\n");
+hello.c: return 0;
+hello.c- /* char ?? */
+hello.c-}
+EOF
+
+test_expect_success 'grep -W' '
+ git grep -W return >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'grep from a subdirectory to search wider area (1)' '
mkdir -p s &&
(
mkdir new &&
git --git-dir=new/.git init &&
git fast-export -C -C --signed-tags=strip --all > output &&
- grep "^C \"file6\" \"file7\"\$" output &&
+ grep "^C file6 file7\$" output &&
cat output |
(cd new &&
git fast-import &&
(cd result && git show master:foo)
'
+test_expect_success 'fast-export quotes pathnames' '
+ git init crazy-paths &&
+ (cd crazy-paths &&
+ blob=`echo foo | git hash-object -w --stdin` &&
+ git update-index --add \
+ --cacheinfo 100644 $blob "$(printf "path with\\nnewline")" \
+ --cacheinfo 100644 $blob "path with \"quote\"" \
+ --cacheinfo 100644 $blob "path with \\backslash" \
+ --cacheinfo 100644 $blob "path with space" &&
+ git commit -m addition &&
+ git ls-files -z -s | perl -0pe "s{\\t}{$&subdir/}" >index &&
+ git read-tree --empty &&
+ git update-index -z --index-info <index &&
+ git commit -m rename &&
+ git read-tree --empty &&
+ git commit -m deletion &&
+ git fast-export HEAD >export.out &&
+ git rev-list HEAD >expect &&
+ git init result &&
+ cd result &&
+ git fast-import <../export.out &&
+ git rev-list HEAD >actual &&
+ test_cmp ../expect actual
+ )
+'
+
test_done
# Zero SHA-1
_z40=0000000000000000000000000000000000000000
+# Line feed
+LF='
+'
+
# Each test should start with something like this, after copyright notices:
#
# test_description='Description of this test...
test "$debug" = "" || eval "$1"
}
+test_eval_ () {
+ # This is a separate function because some tests use
+ # "return" to end a test_expect_success block early.
+ eval >&3 2>&4 "$*"
+}
+
test_run_ () {
test_cleanup=:
expecting_failure=$2
- eval >&3 2>&4 "$1"
+ test_eval_ "$1"
eval_ret=$?
if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
then
- eval >&3 2>&4 "$test_cleanup"
+ test_eval_ "$test_cleanup"
fi
if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
echo ""
fi
- return 0
+ return "$eval_ret"
}
test_skip () {
if ! test_skip "$@"
then
say >&3 "checking known breakage: $2"
- test_run_ "$2" expecting_failure
- if [ "$?" = 0 -a "$eval_ret" = 0 ]
+ if test_run_ "$2" expecting_failure
then
test_known_broken_ok_ "$1"
else
if ! test_skip "$@"
then
say >&3 "expecting success: $2"
- test_run_ "$2"
- if [ "$?" = 0 -a "$eval_ret" = 0 ]
+ if test_run_ "$2"
then
test_ok_ "$1"
else
return 0;
}
+ if (argc >= 2 && !strcmp(argv[1], "absolute_path")) {
+ while (argc > 2) {
+ puts(absolute_path(argv[2]));
+ argc--;
+ argv++;
+ }
+ return 0;
+ }
+
if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
int len = longest_ancestor_length(argv[2], argv[3]);
printf("%d\n", len);
return 0;
}
+ if (argc >= 4 && !strcmp(argv[1], "prefix_path")) {
+ char *prefix = argv[2];
+ int prefix_len = strlen(prefix);
+ int nongit_ok;
+ setup_git_directory_gently(&nongit_ok);
+ while (argc > 3) {
+ puts(prefix_path(prefix, prefix_len, argv[3]));
+ argc--;
+ argv++;
+ }
+ return 0;
+ }
+
if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) {
char *prefix = strip_path_suffix(argv[2], argv[3]);
printf("%s\n", prefix ? prefix : "(null)");
static int connect_setup(struct transport *transport, int for_push, int verbose)
{
struct git_transport_data *data = transport->data;
+ struct strbuf sb = STRBUF_INIT;
if (data->conn)
return 0;
- data->conn = git_connect(data->fd, transport->url,
- for_push ? data->options.receivepack :
- data->options.uploadpack,
+ strbuf_addstr(&sb, for_push ? data->options.receivepack :
+ data->options.uploadpack);
+ if (for_push && transport->verbose < 0)
+ strbuf_addstr(&sb, " --quiet");
+ data->conn = git_connect(data->fd, transport->url, sb.buf,
verbose ? CONNECT_VERBOSE : 0);
+ strbuf_release(&sb);
return 0;
}
#include "revision.h"
#include "list-objects.h"
#include "run-command.h"
+#include "sigchain.h"
static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
}
}
+static void check_non_tip(void)
+{
+ static const char *argv[] = {
+ "rev-list", "--stdin", NULL,
+ };
+ static struct child_process cmd;
+ struct object *o;
+ char namebuf[42]; /* ^ + SHA-1 + LF */
+ int i;
+
+ /* In the normal in-process case non-tip request can never happen */
+ if (!stateless_rpc)
+ goto error;
+
+ cmd.argv = argv;
+ cmd.git_cmd = 1;
+ cmd.no_stderr = 1;
+ cmd.in = -1;
+ cmd.out = -1;
+
+ if (start_command(&cmd))
+ goto error;
+
+ /*
+ * If rev-list --stdin encounters an unknown commit, it
+ * terminates, which will cause SIGPIPE in the write loop
+ * below.
+ */
+ sigchain_push(SIGPIPE, SIG_IGN);
+
+ namebuf[0] = '^';
+ namebuf[41] = '\n';
+ for (i = get_max_object_index(); 0 < i; ) {
+ o = get_indexed_object(--i);
+ if (!o)
+ continue;
+ if (!(o->flags & OUR_REF))
+ continue;
+ memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
+ if (write_in_full(cmd.in, namebuf, 42) < 0)
+ goto error;
+ }
+ namebuf[40] = '\n';
+ for (i = 0; i < want_obj.nr; i++) {
+ o = want_obj.objects[i].item;
+ if (o->flags & OUR_REF)
+ continue;
+ memcpy(namebuf, sha1_to_hex(o->sha1), 40);
+ if (write_in_full(cmd.in, namebuf, 41) < 0)
+ goto error;
+ }
+ close(cmd.in);
+
+ sigchain_pop(SIGPIPE);
+
+ /*
+ * The commits out of the rev-list are not ancestors of
+ * our ref.
+ */
+ i = read_in_full(cmd.out, namebuf, 1);
+ if (i)
+ goto error;
+ close(cmd.out);
+
+ /*
+ * rev-list may have died by encountering a bad commit
+ * in the history, in which case we do want to bail out
+ * even when it showed no commit.
+ */
+ if (finish_command(&cmd))
+ goto error;
+
+ /* All the non-tip ones are ancestors of what we advertised */
+ return;
+
+error:
+ /* Pick one of them (we know there at least is one) */
+ for (i = 0; i < want_obj.nr; i++) {
+ o = want_obj.objects[i].item;
+ if (!(o->flags & OUR_REF))
+ die("git upload-pack: not our ref %s",
+ sha1_to_hex(o->sha1));
+ }
+}
+
static void receive_needs(void)
{
struct object_array shallows = OBJECT_ARRAY_INIT;
static char line[1000];
int len, depth = 0;
+ int has_non_tip = 0;
shallow_nr = 0;
if (debug_fd)
if (strstr(line+45, "include-tag"))
use_include_tag = 1;
- /* We have sent all our refs already, and the other end
- * should have chosen out of them; otherwise they are
- * asking for nonsense.
- *
- * Hmph. We may later want to allow "want" line that
- * asks for something like "master~10" (symbolic)...
- * would it make sense? I don't know.
- */
o = lookup_object(sha1_buf);
- if (!o || !(o->flags & OUR_REF))
+ if (!o)
die("git upload-pack: not our ref %s",
sha1_to_hex(sha1_buf));
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
+ if (!(o->flags & OUR_REF))
+ has_non_tip = 1;
add_object_array(o, NULL, &want_obj);
}
}
if (debug_fd)
write_str_in_full(debug_fd, "#E\n");
+ /*
+ * We have sent all our refs already, and the other end
+ * should have chosen out of them. When we are operating
+ * in the stateless RPC mode, however, their choice may
+ * have been based on the set of older refs advertised
+ * by another process that handled the initial request.
+ */
+ if (has_non_tip)
+ check_non_tip();
+
if (!use_sideband && daemon_mode)
no_progress = 1;
" side-band-64k ofs-delta shallow no-progress"
" include-tag multi_ack_detailed";
struct object *o = parse_object(sha1);
+ const char *refname_nons = strip_namespace(refname);
if (!o)
die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
if (capabilities)
- packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname,
+ packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname_nons,
0, capabilities,
stateless_rpc ? " no-done" : "");
else
- packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
+ packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
capabilities = NULL;
if (!(o->flags & OUR_REF)) {
o->flags |= OUR_REF;
if (o->type == OBJ_TAG) {
o = deref_tag(o, refname, 0);
if (o)
- packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
+ packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname_nons);
}
return 0;
}
{
if (advertise_refs || !stateless_rpc) {
reset_timeout();
- head_ref(send_ref, NULL);
- for_each_ref(send_ref, NULL);
+ head_ref_namespaced(send_ref, NULL);
+ for_each_namespaced_ref(send_ref, NULL);
packet_flush(1);
} else {
- head_ref(mark_our_ref, NULL);
- for_each_ref(mark_our_ref, NULL);
+ head_ref_namespaced(mark_our_ref, NULL);
+ for_each_namespaced_ref(mark_our_ref, NULL);
}
if (advertise_refs)
return;
* Copyright (C) Linus Torvalds, 2005
*/
#include "git-compat-util.h"
+#include "cache.h"
void vreportf(const char *prefix, const char *err, va_list params)
{
fprintf(stderr, "%s%s\n", prefix, msg);
}
+void vwritef(int fd, const char *prefix, const char *err, va_list params)
+{
+ char msg[4096];
+ int len = vsnprintf(msg, sizeof(msg), err, params);
+ if (len > sizeof(msg))
+ len = sizeof(msg);
+
+ write_in_full(fd, prefix, strlen(prefix));
+ write_in_full(fd, msg, len);
+ write_in_full(fd, "\n", 1);
+}
+
static NORETURN void usage_builtin(const char *err, va_list params)
{
vreportf("usage: ", err, params);
die_routine = routine;
}
+void set_error_routine(void (*routine)(const char *err, va_list params))
+{
+ error_routine = routine;
+}
+
void NORETURN usagef(const char *err, ...)
{
va_list params;
if (!path)
return NULL;
- if (git_checkattr(path, 1, &check))
+ if (git_check_attr(path, 1, &check))
return NULL;
if (ATTR_TRUE(check.value))
struct git_attr_check attr_whitespace_rule;
setup_whitespace_attr_check(&attr_whitespace_rule);
- if (!git_checkattr(pathname, 1, &attr_whitespace_rule)) {
+ if (!git_check_attr(pathname, 1, &attr_whitespace_rule)) {
const char *value;
value = attr_whitespace_rule.value;
#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
#define XDF_PATIENCE_DIFF (1 << 5)
+#define XDF_HISTOGRAM_DIFF (1 << 6)
#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
#define XDL_PATCH_NORMAL '-'
#define xdl_realloc(ptr,x) realloc(ptr,x)
void *xdl_mmfile_first(mmfile_t *mmf, long *size);
-void *xdl_mmfile_next(mmfile_t *mmf, long *size);
long xdl_mmfile_size(mmfile_t *mmf);
int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
if (xpp->flags & XDF_PATIENCE_DIFF)
return xdl_do_patience_diff(mf1, mf2, xpp, xe);
+ if (xpp->flags & XDF_HISTOGRAM_DIFF)
+ return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+
if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
return -1;
xdemitconf_t const *xecfg);
int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *env);
+int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *env);
#endif /* #if !defined(XDIFFI_H) */
--- /dev/null
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in JGit's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+#define MAX_PTR UINT_MAX
+#define MAX_CNT UINT_MAX
+
+#define LINE_END(n) (line##n + count##n - 1)
+#define LINE_END_PTR(n) (*line##n + *count##n - 1)
+
+struct histindex {
+ struct record {
+ unsigned int ptr, cnt;
+ struct record *next;
+ } **records, /* an ocurrence */
+ **line_map; /* map of line to record chain */
+ chastore_t rcha;
+ unsigned int *next_ptrs;
+ unsigned int table_bits,
+ records_size,
+ line_map_size;
+
+ unsigned int max_chain_length,
+ key_shift,
+ ptr_shift;
+
+ unsigned int cnt,
+ has_common;
+
+ xdfenv_t *env;
+ xpparam_t const *xpp;
+};
+
+struct region {
+ unsigned int begin1, end1;
+ unsigned int begin2, end2;
+};
+
+#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
+
+#define NEXT_PTR(index, ptr) \
+ (index->next_ptrs[(ptr) - index->ptr_shift])
+
+#define CNT(index, ptr) \
+ ((LINE_MAP(index, ptr))->cnt)
+
+#define REC(env, s, l) \
+ (env->xdf##s.recs[l - 1])
+
+static int cmp_recs(xpparam_t const *xpp,
+ xrecord_t *r1, xrecord_t *r2)
+{
+ return r1->ha == r2->ha &&
+ xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,
+ xpp->flags);
+}
+
+#define CMP_ENV(xpp, env, s1, l1, s2, l2) \
+ (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))
+
+#define CMP(i, s1, l1, s2, l2) \
+ (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))
+
+#define TABLE_HASH(index, side, line) \
+ XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
+
+static int scanA(struct histindex *index, int line1, int count1)
+{
+ unsigned int ptr, tbl_idx;
+ unsigned int chain_len;
+ struct record **rec_chain, *rec;
+
+ for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+ tbl_idx = TABLE_HASH(index, 1, ptr);
+ rec_chain = index->records + tbl_idx;
+ rec = *rec_chain;
+
+ chain_len = 0;
+ while (rec) {
+ if (CMP(index, 1, rec->ptr, 1, ptr)) {
+ /*
+ * ptr is identical to another element. Insert
+ * it onto the front of the existing element
+ * chain.
+ */
+ NEXT_PTR(index, ptr) = rec->ptr;
+ rec->ptr = ptr;
+ /* cap rec->cnt at MAX_CNT */
+ rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
+ LINE_MAP(index, ptr) = rec;
+ goto continue_scan;
+ }
+
+ rec = rec->next;
+ chain_len++;
+ }
+
+ if (chain_len == index->max_chain_length)
+ return -1;
+
+ /*
+ * This is the first time we have ever seen this particular
+ * element in the sequence. Construct a new chain for it.
+ */
+ if (!(rec = xdl_cha_alloc(&index->rcha)))
+ return -1;
+ rec->ptr = ptr;
+ rec->cnt = 1;
+ rec->next = *rec_chain;
+ *rec_chain = rec;
+ LINE_MAP(index, ptr) = rec;
+
+continue_scan:
+ ; /* no op */
+ }
+
+ return 0;
+}
+
+static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr,
+ int line1, int count1, int line2, int count2)
+{
+ unsigned int b_next = b_ptr + 1;
+ struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
+ unsigned int as, ae, bs, be, np, rc;
+ int should_break;
+
+ for (; rec; rec = rec->next) {
+ if (rec->cnt > index->cnt) {
+ if (!index->has_common)
+ index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
+ continue;
+ }
+
+ as = rec->ptr;
+ if (!CMP(index, 1, as, 2, b_ptr))
+ continue;
+
+ index->has_common = 1;
+ for (;;) {
+ should_break = 0;
+ np = NEXT_PTR(index, as);
+ bs = b_ptr;
+ ae = as;
+ be = bs;
+ rc = rec->cnt;
+
+ while (line1 < as && line2 < bs
+ && CMP(index, 1, as - 1, 2, bs - 1)) {
+ as--;
+ bs--;
+ if (1 < rc)
+ rc = XDL_MIN(rc, CNT(index, as));
+ }
+ while (ae < LINE_END(1) && be < LINE_END(2)
+ && CMP(index, 1, ae + 1, 2, be + 1)) {
+ ae++;
+ be++;
+ if (1 < rc)
+ rc = XDL_MIN(rc, CNT(index, ae));
+ }
+
+ if (b_next <= be)
+ b_next = be + 1;
+ if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
+ lcs->begin1 = as;
+ lcs->begin2 = bs;
+ lcs->end1 = ae;
+ lcs->end2 = be;
+ index->cnt = rc;
+ }
+
+ if (np == 0)
+ break;
+
+ while (np <= ae) {
+ np = NEXT_PTR(index, np);
+ if (np == 0) {
+ should_break = 1;
+ break;
+ }
+ }
+
+ if (should_break)
+ break;
+
+ as = np;
+ }
+ }
+ return b_next;
+}
+
+static int find_lcs(struct histindex *index, struct region *lcs,
+ int line1, int count1, int line2, int count2) {
+ int b_ptr;
+
+ if (scanA(index, line1, count1))
+ return -1;
+
+ index->cnt = index->max_chain_length + 1;
+
+ for (b_ptr = line2; b_ptr <= LINE_END(2); )
+ b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2);
+
+ return index->has_common && index->max_chain_length < index->cnt;
+}
+
+static int fall_back_to_classic_diff(struct histindex *index,
+ int line1, int count1, int line2, int count2)
+{
+ xpparam_t xpp;
+ xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
+
+ return xdl_fall_back_diff(index->env, &xpp,
+ line1, count1, line2, count2);
+}
+
+static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2)
+{
+ struct histindex index;
+ struct region lcs;
+ int sz;
+ int result = -1;
+
+ if (count1 <= 0 && count2 <= 0)
+ return 0;
+
+ if (LINE_END(1) >= MAX_PTR)
+ return -1;
+
+ if (!count1) {
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ } else if (!count2) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ return 0;
+ }
+
+ memset(&index, 0, sizeof(index));
+
+ index.env = env;
+ index.xpp = xpp;
+
+ index.records = NULL;
+ index.line_map = NULL;
+ /* in case of early xdl_cha_free() */
+ index.rcha.head = NULL;
+
+ index.table_bits = xdl_hashbits(count1);
+ sz = index.records_size = 1 << index.table_bits;
+ sz *= sizeof(struct record *);
+ if (!(index.records = (struct record **) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.records, 0, sz);
+
+ sz = index.line_map_size = count1;
+ sz *= sizeof(struct record *);
+ if (!(index.line_map = (struct record **) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.line_map, 0, sz);
+
+ sz = index.line_map_size;
+ sz *= sizeof(unsigned int);
+ if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.next_ptrs, 0, sz);
+
+ /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
+ if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
+ goto cleanup;
+
+ index.ptr_shift = line1;
+ index.max_chain_length = 64;
+
+ memset(&lcs, 0, sizeof(lcs));
+ if (find_lcs(&index, &lcs, line1, count1, line2, count2))
+ result = fall_back_to_classic_diff(&index, line1, count1, line2, count2);
+ else {
+ if (lcs.begin1 == 0 && lcs.begin2 == 0) {
+ while (count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ while (count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ result = 0;
+ } else {
+ result = histogram_diff(xpp, env,
+ line1, lcs.begin1 - line1,
+ line2, lcs.begin2 - line2);
+ if (result)
+ goto cleanup;
+ result = histogram_diff(xpp, env,
+ lcs.end1 + 1, LINE_END(1) - lcs.end1,
+ lcs.end2 + 1, LINE_END(2) - lcs.end2);
+ if (result)
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ xdl_free(index.records);
+ xdl_free(index.line_map);
+ xdl_free(index.next_ptrs);
+ xdl_cha_free(&index.rcha);
+
+ return result;
+}
+
+int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env)
+{
+ if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+ return -1;
+
+ return histogram_diff(xpp, env,
+ env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
+ env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
+}
static int fall_back_to_classic_diff(struct hashmap *map,
int line1, int count1, int line2, int count2)
{
- /*
- * This probably does not work outside Git, since
- * we have a very simple mmfile structure.
- *
- * Note: ideally, we would reuse the prepared environment, but
- * the libxdiff interface does not (yet) allow for diffing only
- * ranges of lines instead of the whole files.
- */
- mmfile_t subfile1, subfile2;
xpparam_t xpp;
- xdfenv_t env;
-
- subfile1.ptr = (char *)map->env->xdf1.recs[line1 - 1]->ptr;
- subfile1.size = map->env->xdf1.recs[line1 + count1 - 2]->ptr +
- map->env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
- subfile2.ptr = (char *)map->env->xdf2.recs[line2 - 1]->ptr;
- subfile2.size = map->env->xdf2.recs[line2 + count2 - 2]->ptr +
- map->env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
- if (xdl_do_diff(&subfile1, &subfile2, &xpp, &env) < 0)
- return -1;
- memcpy(map->env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
- memcpy(map->env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
-
- xdl_free_env(&env);
-
- return 0;
+ return xdl_fall_back_diff(map->env, &xpp,
+ line1, count1, line2, count2);
}
/*
#define XDL_KPDIS_RUN 4
#define XDL_MAX_EQLIMIT 1024
#define XDL_SIMSCAN_WINDOW 100
+#define XDL_GUESS_NLINES1 256
+#define XDL_GUESS_NLINES2 20
typedef struct s_xdlclass {
static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
- long i;
-
cf->flags = flags;
cf->hbits = xdl_hashbits((unsigned int) size);
xdl_cha_free(&cf->ncha);
return -1;
}
- for (i = 0; i < cf->hsize; i++)
- cf->rchash[i] = NULL;
+ memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
cf->count = 0;
static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
xdlclassifier_t *cf, xdfile_t *xdf) {
unsigned int hbits;
- long i, nrec, hsize, bsize;
+ long nrec, hsize, bsize;
unsigned long hav;
char const *blk, *cur, *top, *prev;
xrecord_t *crec;
char *rchg;
long *rindex;
- if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0) {
-
- return -1;
- }
- if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) {
-
- xdl_cha_free(&xdf->rcha);
- return -1;
- }
-
- hbits = xdl_hashbits((unsigned int) narec);
- hsize = 1 << hbits;
- if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) {
-
- xdl_free(recs);
- xdl_cha_free(&xdf->rcha);
- return -1;
+ ha = NULL;
+ rindex = NULL;
+ rchg = NULL;
+ rhash = NULL;
+ recs = NULL;
+
+ if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
+ goto abort;
+ if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
+ goto abort;
+
+ if (xpp->flags & XDF_HISTOGRAM_DIFF)
+ hbits = hsize = 0;
+ else {
+ hbits = xdl_hashbits((unsigned int) narec);
+ hsize = 1 << hbits;
+ if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+ goto abort;
+ memset(rhash, 0, hsize * sizeof(xrecord_t *));
}
- for (i = 0; i < hsize; i++)
- rhash[i] = NULL;
nrec = 0;
if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
- for (top = blk + bsize;;) {
- if (cur >= top) {
- if (!(cur = blk = xdl_mmfile_next(mf, &bsize)))
- break;
- top = blk + bsize;
- }
+ for (top = blk + bsize; cur < top; ) {
prev = cur;
hav = xdl_hash_record(&cur, top, xpp->flags);
if (nrec >= narec) {
narec *= 2;
- if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) {
-
- xdl_free(rhash);
- xdl_free(recs);
- xdl_cha_free(&xdf->rcha);
- return -1;
- }
+ if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
+ goto abort;
recs = rrecs;
}
- if (!(crec = xdl_cha_alloc(&xdf->rcha))) {
-
- xdl_free(rhash);
- xdl_free(recs);
- xdl_cha_free(&xdf->rcha);
- return -1;
- }
+ if (!(crec = xdl_cha_alloc(&xdf->rcha)))
+ goto abort;
crec->ptr = prev;
crec->size = (long) (cur - prev);
crec->ha = hav;
recs[nrec++] = crec;
- if (xdl_classify_record(cf, rhash, hbits, crec) < 0) {
-
- xdl_free(rhash);
- xdl_free(recs);
- xdl_cha_free(&xdf->rcha);
- return -1;
- }
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+ xdl_classify_record(cf, rhash, hbits, crec) < 0)
+ goto abort;
}
}
- if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char)))) {
-
- xdl_free(rhash);
- xdl_free(recs);
- xdl_cha_free(&xdf->rcha);
- return -1;
- }
+ if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
+ goto abort;
memset(rchg, 0, (nrec + 2) * sizeof(char));
- if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long)))) {
-
- xdl_free(rchg);
- xdl_free(rhash);
- xdl_free(recs);
- xdl_cha_free(&xdf->rcha);
- return -1;
- }
- if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) {
-
- xdl_free(rindex);
- xdl_free(rchg);
- xdl_free(rhash);
- xdl_free(recs);
- xdl_cha_free(&xdf->rcha);
- return -1;
- }
+ if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
+ goto abort;
+ if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
+ goto abort;
xdf->nrec = nrec;
xdf->recs = recs;
xdf->dend = nrec - 1;
return 0;
+
+abort:
+ xdl_free(ha);
+ xdl_free(rindex);
+ xdl_free(rchg);
+ xdl_free(rhash);
+ xdl_free(recs);
+ xdl_cha_free(&xdf->rcha);
+ return -1;
}
int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe) {
- long enl1, enl2;
+ long enl1, enl2, sample;
xdlclassifier_t cf;
- enl1 = xdl_guess_lines(mf1) + 1;
- enl2 = xdl_guess_lines(mf2) + 1;
+ /*
+ * For histogram diff, we can afford a smaller sample size and
+ * thus a poorer estimate of the number of lines, as the hash
+ * table (rhash) won't be filled up/grown. The number of lines
+ * (nrecs) will be updated correctly anyway by
+ * xdl_prepare_ctx().
+ */
+ sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+
+ enl1 = xdl_guess_lines(mf1, sample) + 1;
+ enl2 = xdl_guess_lines(mf2, sample) + 1;
- if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+ xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
return -1;
}
return -1;
}
- xdl_free_classifier(&cf);
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+ xdl_free_classifier(&cf);
if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
+ !(xpp->flags & XDF_HISTOGRAM_DIFF) &&
xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) {
xdl_free_ctx(&xe->xdf2);
-#define XDL_GUESS_NLINES 256
-
-
-
long xdl_bogosqrt(long n) {
long i;
}
-void *xdl_mmfile_next(mmfile_t *mmf, long *size)
-{
- return NULL;
-}
-
-
long xdl_mmfile_size(mmfile_t *mmf)
{
return mmf->size;
}
-long xdl_guess_lines(mmfile_t *mf) {
+long xdl_guess_lines(mmfile_t *mf, long sample) {
long nl = 0, size, tsize = 0;
char const *data, *cur, *top;
if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
- for (top = data + size; nl < XDL_GUESS_NLINES;) {
- if (cur >= top) {
- tsize += (long) (cur - data);
- if (!(cur = data = xdl_mmfile_next(mf, &size)))
- break;
- top = data + size;
- }
+ for (top = data + size; nl < sample && cur < top; ) {
nl++;
if (!(cur = memchr(cur, '\n', top - cur)))
cur = top;
return 0;
}
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+ int line1, int count1, int line2, int count2)
+{
+ /*
+ * This probably does not work outside Git, since
+ * we have a very simple mmfile structure.
+ *
+ * Note: ideally, we would reuse the prepared environment, but
+ * the libxdiff interface does not (yet) allow for diffing only
+ * ranges of lines instead of the whole files.
+ */
+ mmfile_t subfile1, subfile2;
+ xdfenv_t env;
+
+ subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+ subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+ diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+ subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+ subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+ diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+ if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+ return -1;
+
+ memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+ memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+ xdl_free_env(&env);
+
+ return 0;
+}
void *xdl_cha_alloc(chastore_t *cha);
void *xdl_cha_first(chastore_t *cha);
void *xdl_cha_next(chastore_t *cha);
-long xdl_guess_lines(mmfile_t *mf);
+long xdl_guess_lines(mmfile_t *mf, long sample);
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
unsigned long xdl_hash_record(char const **data, char const *top, long flags);
unsigned int xdl_hashbits(unsigned int size);
long xdl_atol(char const *str, char const **next);
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
const char *func, long funclen, xdemitcb_t *ecb);
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+ int line1, int count1, int line2, int count2);