gitrepository-layout.txt
MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \
gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \
- gitdiffcore.txt gitworkflows.txt
+ gitdiffcore.txt gitrevisions.txt gitworkflows.txt
MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
--- /dev/null
+Git v1.7.0.7 Release Notes
+==========================
+
+Fixes since v1.7.0.6
+--------------------
+
+ * "make NO_CURL=NoThanks install" was broken.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+ access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+ that uses "~/" short-hand segfaulted when $HOME environment variable
+ was not set.
+
+And other minor fixes and documentation updates.
--- /dev/null
+Git v1.7.1.2 Release Notes
+==========================
+
+Fixes since v1.7.1.1
+--------------------
+
+ * "git commit" did not honor GIT_REFLOG_ACTION environment variable, resulting
+ reflog messages for cherry-pick and revert actions to be recorded as "commit".
+
+ * "git clone/fetch/pull" issued an incorrect error message when a ref and
+ a symref that points to the ref were updated at the same time. This
+ obviously would update them to the same value, and should not result in
+ an error condition.
+
+ * "git diff" inside a tree with many pathnames that have certain
+ characters has become very slow in 1.7.0 by mistake.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+ when --keep-dashdash was in effect.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+ access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+ that uses "~/" short-hand segfaulted when $HOME environment variable
+ was not set.
+
+And other minor fixes and documentation updates.
--- /dev/null
+Git v1.7.2.1 Release Notes
+==========================
+
+Fixes since v1.7.2
+------------------
+
+ * "git instaweb" wasn't useful when your Apache was installed under a
+ name other than apache2 (e.g. "httpd").
+
+ * Similarly, "git web--browse" (invoked by "git help -w") learned that
+ chrome browser is sometimes called google-chrome.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+ access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+ that uses "~/" short-hand segfaulted when $HOME environment variable
+ was not set.
+
+ * Documentation on Cygwin failed to build.
+
+ * The error message from "git pull blarg" when 'blarg' is an unknown
+ remote name has been improved.
+
+And other minor fixes and documentation updates.
-Git v1.7.2 Release Notes (draft)
-================================
+Git v1.7.2 Release Notes
+========================
Updates since v1.7.1
--------------------
- * core.eol configuration and eol attribute are the new way to control
- the end of line conventions for files in the working tree;
- core.autocrlf overrides it, keeping the traditional behaviour by
- default.
+ * core.eol configuration and text/eol attributes are the new way to control
+ the end of line conventions for files in the working tree.
+
+ * core.autocrlf has been made safer - it will now only handle line
+ endings for new files and files that are LF-only in the
+ repository. To normalize content that has been checked in with
+ CRLF, use the new eol/text attributes.
* The whitespace rules used in "git apply --whitespace" and "git diff"
gained a new member in the family (tab-in-indent) to help projects with
commit.
* "git cherry-pick" learned to pick a range of commits
- (e.g. "cherry-pick A..B" and "cherry-pick --stdin"); this does not
- have nicer sequencing control "rebase [-i]" has, though.
+ (e.g. "cherry-pick A..B" and "cherry-pick --stdin"), so did "git
+ revert"; these do not support the nicer sequencing control "rebase
+ [-i]" has, though.
+
+ * "git cherry-pick" and "git revert" learned --strategy option to specify
+ the merge strategy to be used when performing three-way merges.
* "git cvsserver" can be told to use pserver; its password file can be
stored outside the repository.
* Various options to "git grep" (e.g. --count, --name-only) work better
with binary files.
- * "git grep" learned "-Ovi" to open the files with hits in yoru editor.
+ * "git grep" learned "-Ovi" to open the files with hits in your editor.
* "git help -w" learned "chrome" and "chromium" browsers.
* "git remote" learned "set-branches" subcommand.
- * "git revert" learned --strategy option to specify the merge strategy.
-
* "git rev-list A..B" learned --ancestry-path option to further limit
the result to the commits that are on the ancestry chain between A and
B (i.e. commits that are not descendants of A are excluded).
highlighting, "plackup" support for instaweb, .fcgi suffix to run
it as FastCGI script, etc.
+ * The test harness has been updated to produce TAP-friendly output.
+
+ * Many documentation improvement patches are also included.
+
Fixes since v1.7.1
------------------
release, unless otherwise noted.
* We didn't URL decode "file:///path/to/repo" correctly when path/to/repo
- had percent-encoded characters (638794c, 9d2e942).
-
- * "git commit" did not honor GIT_REFLOG_ACTION environment variable, resulting
- reflog messages for cherry-pick and revert actions to be recorded as "commit".
-
- * "git clone/fetch/pull" issued an incorrect error message when a ref and
- a symref that points to the ref were updated at the same time. This
- obviously would update them to the same value, and should not result in
- an error condition (0e71bc3).
+ had percent-encoded characters (638794c, 9d2e942, ce83eda, 3c73a1d).
* "git clone" did not configure remote.origin.url correctly for bare
clones (df61c889).
- * "git diff" inside a tree with many pathnames that have certain
- characters has become very slow in 1.7.0 by mistake (will merge
- e53e6b443 to 'maint').
-
* "git diff --graph" works better with "--color-words" and other options
(81fa024..4297c0a).
* "git diff" could show ambiguous abbreviation of blob object names on
its "index" line (3e5a188).
- * "git rebase" did not faithfully reproduce a malformed author ident, that
- is often seen in a repository converted from foreign SCMs (43c23251).
-
* "git reset --hard" started from a wrong directory and a working tree in
a nonstandard location is in use got confused (560fb6a1).
---
-exec >/var/tmp/1
-O=v1.7.2-rc0-60-g2927a50
-echo O=$(git describe HEAD)
-git shortlog --no-merges HEAD ^maint ^$O
+ * "git read-tree -m A B" used to switch to branch B while retaining
+ local changes added an incorrect cache-tree information (b1f47514).
before committing
- do not check in commented out code or unneeded files
- the first line of the commit message should be a short
- description and should skip the full stop
+ description (50 characters is the soft limit, see DISCUSSION
+ in git-commit(1)), and should skip the full stop
- the body should provide a meaningful commit message, which:
- uses the imperative, present tense: "change",
not "changed" or "changes".
- includes motivation for the change, and contrasts
its implementation with previous behaviour
- - if you want your work included in git.git, add a
- "Signed-off-by: Your Name <you@example.com>" line to the
- commit message (or just use the option "-s" when
- committing) to confirm that you agree to the Developer's
- Certificate of Origin
+ - add a "Signed-off-by: Your Name <you@example.com>" line to the
+ commit message (or just use the option "-s" when committing)
+ to confirm that you agree to the Developer's Certificate of Origin
- make sure that you have tests for the bug you are fixing
- make sure that the test suite passes after your commit
when writing commit messages. Default: true.
commitBeforeMerge::
Advice shown when linkgit:git-merge[1] refuses to
- merge to avoid overwritting local changes.
+ merge to avoid overwriting local changes.
Default: true.
resolveConflict::
Advices shown by various commands when conflicts
core.deltaBaseCacheLimit::
Maximum number of bytes to reserve for caching base objects
- that multiple deltafied objects reference. By storing the
+ that may be referenced by multiple deltified objects. By storing the
entire decompressed base objects in a cache Git is able
to avoid unpacking and decompressing frequently used base
objects multiple times.
am.keepcr::
If true, git-am will call git-mailsplit for patches in mbox format
with parameter '--keep-cr'. In this case git-mailsplit will
- not remove `\r` from lines ending with `\r\n`. Can be overrriden
+ not remove `\r` from lines ending with `\r\n`. Can be overridden
by giving '--no-keep-cr' from the command line.
See linkgit:git-am[1], linkgit:git-mailsplit[1].
will enable basic rename detection. If set to "copies" or
"copy", it will detect copies, as well.
+diff.ignoreSubmodules::
+ Sets the default value of --ignore-submodules. Note that this
+ affects only 'git diff' Porcelain, and not lower level 'diff'
+ commands such as 'git diff-files'.
+
diff.suppressBlankEmpty::
A boolean to inhibit the standard behavior of printing a space
before each empty output line. Defaults to false.
Additional email headers to include in a patch to be submitted
by mail. See linkgit:git-format-patch[1].
+format.to::
format.cc::
- Additional "Cc:" headers to include in a patch to be submitted
- by mail. See the --cc option in linkgit:git-format-patch[1].
+ Additional recipients to include in a patch to be submitted
+ by mail. See the --to and --cc options in
+ linkgit:git-format-patch[1].
format.subjectprefix::
The default for format-patch is to output files with the '[PATCH]'
If true, the server will look up the end-of-line conversion
attributes for files to determine the '-k' modes to use. If
the attributes force git to treat a file as text,
- the '-k' mode will be left blank so cvs clients will
+ the '-k' mode will be left blank so CVS clients will
treat it as text. If they suppress text conversion, the file
will be set with '-kb' mode, which suppresses any newline munging
the client might otherwise do. If the attributes do not allow
be bound to the local IP (127.0.0.1).
instaweb.modulepath::
- The module path for an apache httpd used by linkgit:git-instaweb[1].
+ The default module path for linkgit:git-instaweb[1] to use
+ instead of /usr/lib/apache2/modules. Only used if httpd
+ is Apache.
instaweb.port::
The port number to bind the gitweb httpd to. See
If set to true, git-receive-pack will deny a ref update that deletes
the ref. Use this to prevent such a ref deletion via a push.
+receive.denyDeleteCurrent::
+ If set to true, git-receive-pack will deny a ref update that
+ deletes the currently checked out branch of a non-bare repository.
+
receive.denyCurrentBranch::
If set to true or "refuse", git-receive-pack will deny a ref update
to the currently checked out branch of a non-bare repository.
summary of commits for modified submodules will be shown (see
--summary-limit option of linkgit:git-submodule[1]).
+submodule.<name>.path::
+submodule.<name>.url::
+submodule.<name>.update::
+ The path within this project, URL, and the updating strategy
+ for a submodule. These variables are initially populated
+ by 'git submodule init'; edit them to override the
+ URL and other values found in the `.gitmodules` file. See
+ linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
+
+submodule.<name>.ignore::
+ Defines under what circumstances "git status" and the diff family show
+ a submodule as modified. When set to "all", it will never be considered
+ modified, "dirty" will ignore all changes to the submodules work tree and
+ takes only differences between the HEAD of the submodule and the commit
+ recorded in the superproject into account. "untracked" will additionally
+ let submodules with modified tracked files in their work tree show up.
+ Using "none" (the default when this option is not set) also shows
+ submodules that have untracked files in their work tree as changed.
+ This setting overrides any setting made in .gitmodules for this submodule,
+ both settings can be overriden on the command line by using the
+ "--ignore-submodules" option.
+
tar.umask::
This variable can be used to restrict the permission bits of
tar archive entries. The default is 0002, which turns off the
the diff-patch output format. Non default number of
digits can be specified with `--abbrev=<n>`.
--B::
- Break complete rewrite changes into pairs of delete and create.
-
--M::
+-B[<n>][/<m>]::
+ Break complete rewrite changes into pairs of delete and
+ create. This serves two purposes:
++
+It affects the way a change that amounts to a total rewrite of a file
+not as a series of deletion and insertion mixed together with a very
+few lines that happen to match textually as the context, but as a
+single deletion of everything old followed by a single insertion of
+everything new, and the number `m` controls this aspect of the -B
+option (defaults to 60%). `-B/70%` specifies that less than 30% of the
+original should remain in the result for git to consider it a total
+rewrite (i.e. otherwise the resulting patch will be a series of
+deletion and insertion mixed together with context lines).
++
+When used with -M, a totally-rewritten file is also considered as the
+source of a rename (usually -M only considers a file that disappeared
+as the source of a rename), and the number `n` controls this aspect of
+the -B option (defaults to 50%). `-B20%` specifies that a change with
+addition and deletion compared to 20% or more of the file's size are
+eligible for being picked up as a possible source of a rename to
+another file.
+
+-M[<n>]::
ifndef::git-log[]
Detect renames.
endif::git-log[]
For following files across renames while traversing history, see
`--follow`.
endif::git-log[]
+ If `n` is specified, it is a is a threshold on the similarity
+ index (i.e. amount of addition/deletions compared to the
+ file's size). For example, `-M90%` means git should consider a
+ delete/add pair to be a rename if more than 90% of the file
+ hasn't changed.
--C::
+-C[<n>]::
Detect copies as well as renames. See also `--find-copies-harder`.
+ If `n` is specified, it has the same meaning as for `-M<n>`.
ifndef::git-format-patch[]
--diff-filter=[ACDMRTUXB*]::
--ignore-submodules[=<when>]::
Ignore changes to submodules in the diff generation. <when> can be
- either "untracked", "dirty" or "all", which is the default. When
+ either "none", "untracked", "dirty" or "all", which is the default
+ Using "none" will consider the submodule modified when it either contains
+ untracked or modified files or its HEAD differs from the commit recorded
+ in the superproject and can be used to override any settings of the
+ 'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
"untracked" is used submodules are not considered dirty when they only
contain untracked content (but they are still scanned for modified
content). Using "dirty" ignores all changes to the work tree of submodules,
Allow several <repository> and <group> arguments to be
specified. No <refspec>s may be specified.
+-p::
--prune::
After fetching, remove any remote tracking branches which
no longer exist on the remote.
[verse]
'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
[--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
- [--refresh] [--ignore-errors] [--] [<filepattern>...]
+ [--refresh] [--ignore-errors] [--ignore-missing] [--]
+ [<filepattern>...]
DESCRIPTION
-----------
-n::
--dry-run::
- Don't actually add the file(s), just show if they exist.
+ Don't actually add the file(s), just show if they exist and/or will
+ be ignored.
-v::
--verbose::
them, do not abort the operation, but continue adding the
others. The command shall still exit with non-zero status.
+--ignore-missing::
+ This option can only be used together with --dry-run. By using
+ this option the user can check if any of the given files would
+ be ignored, no matter if they are already present in the work
+ tree or not.
+
\--::
This option can be used to separate command-line options from
the list of files, (useful when filenames might be mistaken
Without these options, the command applies the patch only to files,
and does not require them to be in a git repository.
+This command applies the patch but does not create a commit. Use
+linkgit:git-am[1] to create commits from patches generated by
+linkgit:git-format-patch[1] and/or received by email.
+
OPTIONS
-------
<patch>...::
are ignored and only the absence or presence of the corresponding
subdirectory is checked and (if possible) updated.
+
+SEE ALSO
+--------
+linkgit:git-am[1].
+
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
The smaller the changes in your commit, the most effective "git
bisect" will be. And you will probably need "git bisect" less in the
first place, as small changes are easier to review even if they are
-only reviewed by the commiter.
+only reviewed by the committer.
Another good idea is to have good commit messages. They can be very
helpful to understand why some changes were made.
<object>::
The name of the object to show.
For a more complete list of ways to spell object names, see
- the "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+ the "SPECIFYING REVISIONS" section in linkgit:gitrevisions[1].
-t::
Instead of the content, show the object type identified by
These rules make it easy for shell script based tools to parse
reference names, pathname expansion by the shell when a reference name is used
unquoted (by mistake), and also avoids ambiguities in certain
-reference name expressions (see linkgit:git-rev-parse[1]):
+reference name expressions (see linkgit:gitrevisions[1]):
. A double-dot `..` is often used as in `ref1..ref2`, and in some
contexts this notation means `{caret}ref1 ref2` (i.e. not in
--------
[verse]
'git checkout' [-q] [-f] [-m] [<branch>]
-'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>]
+'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
'git checkout' --patch [<tree-ish>] [--] [<paths>...]
branch.
'git checkout' [<branch>]::
-'git checkout' -b <new branch> [<start point>]::
+'git checkout' -b|-B <new_branch> [<start point>]::
This form switches branches by updating the index, working
tree, and HEAD to reflect the specified branch.
use the `--track` or `--no-track` options, which will be passed to
'git branch'. As a convenience, `--track` without `-b` implies branch
creation; see the description of `--track` below.
++
+If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+is reset. This is the transactional equivalent of
++
+------------
+$ git branch -f <branch> [<start point>]
+$ git checkout <branch>
+------------
++
+that is to say, the branch is not reset/created unless "git checkout" is
+successful.
'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::
Create a new branch named <new_branch> and start it at
<start_point>; see linkgit:git-branch[1] for details.
+-B::
+ Creates the branch <new_branch> and start it at <start_point>;
+ if it already exists, then reset it to <start_point>. This is
+ equivalent to running "git branch" with "-f"; see
+ linkgit:git-branch[1] for details.
+
-t::
--track::
When creating a new branch, set up "upstream" configuration. See
+
------------
$ git checkout mytopic
-fatal: Entry 'frotz' not uptodate. Cannot merge.
+error: You have local changes to 'frotz'; not switching branches.
------------
+
You can give the `-m` flag to the command, which would try a
-------
<commit>...::
Commits to cherry-pick.
- For a more complete list of ways to spell commits, see the
- "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+ For a more complete list of ways to spell commits, see
+ linkgit:gitrevisions[1].
Sets of commits can be passed but no traversal is done by
default, as if the '--no-walk' option was specified, see
linkgit:git-rev-list[1].
SYNOPSIS
--------
[verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
DESCRIPTION
-----------
Be quiet, only report errors, but not the files that are
successfully removed.
+-e <pattern>::
+--exclude=<pattern>::
+ Specify special exceptions to not be cleaned. Each <pattern> is
+ the same form as in $GIT_DIR/info/excludes and this option can be
+ given multiple times.
+
-x::
Don't use the ignore rules. This allows removing all untracked
files, including build products. This can be used (possibly in
Usually recording a commit that has the exact same tree as its
sole parent commit is a mistake, and the command prevents you
from making such a commit. This option bypasses the safety, and
- is primarily for use by foreign scm interface scripts.
+ is primarily for use by foreign SCM interface scripts.
--allow-empty-message::
Like --allow-empty this command is primarily for use by foreign
- scm interface scripts. It allows you to create a commit with an
+ SCM interface scripts. It allows you to create a commit with an
empty commit message without using plumbing commands like
linkgit:git-commit-tree[1].
------
Problems related to timestamps:
- * If timestamps of commits in the cvs repository are not stable enough
+ * If timestamps of commits in the CVS repository are not stable enough
to be used for ordering commits changes may show up in the wrong
order.
* If any files were ever "cvs import"ed more than once (e.g., import of
* Branches on which no commits have been made are not imported.
* All files from the branching point are added to a branch even if
- never added in cvs.
+ never added in CVS.
* This applies to files added to the source branch *after* a daughter
branch was created: if previously no commit was made on the daughter
branch they will erroneously be added to the daughter branch in git.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default the server leaves the '-k' mode blank for all files,
-which causes the cvs client to treat them as a text files, subject
-to crlf conversion on some platforms.
+which causes the CVS client to treat them as a text files, subject
+to end-of-line conversion on some platforms.
You can make the server use the end-of-line conversion attributes to
set the '-k' modes for files by setting the `gitcvs.usecrlfattr`
<tree-ish>.
For a more complete list of ways to spell <commit>, see
-"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+"SPECIFYING REVISIONS" section in linkgit:gitrevisions[1].
However, "diff" is about comparing two _endpoints_, not ranges,
and the range notations ("<commit>..<commit>" and
"<commit>\...<commit>") do not mean a range as defined in the
-"SPECIFYING RANGES" section in linkgit:git-rev-parse[1].
+"SPECIFYING RANGES" section in linkgit:gitrevisions[1].
OPTIONS
-------
* A complete 40 byte or abbreviated commit SHA-1 in hex.
* Any valid Git SHA-1 expression that resolves to a commit. See
- ``SPECIFYING REVISIONS'' in linkgit:git-rev-parse[1] for details.
+ ``SPECIFYING REVISIONS'' in linkgit:gitrevisions[1] for details.
The special case of restarting an incremental import from the
current branch value should be written as:
'M' SP <mode> SP <dataref> SP <path> LF
....
+
-Here `<dataref>` can be either a mark reference (`:<idnum>`)
+Here usually `<dataref>` must be either a mark reference (`:<idnum>`)
set by a prior `blob` command, or a full 40-byte SHA-1 of an
-existing Git blob object.
+existing Git blob object. If `<mode>` is `040000`` then
+`<dataref>` must be the full 40-byte SHA-1 of an existing
+Git tree object or a mark reference set with `--import-marks`.
Inline data format::
The data content for the file has not been supplied yet.
* `160000`: A gitlink, SHA-1 of the object refers to a commit in
another repository. Git links can only be specified by SHA or through
a commit mark. They are used to implement submodules.
+* `040000`: A subdirectory. Subdirectories can only be specified by
+ SHA or through a tree mark set with `--import-marks`.
In both formats `<path>` is the complete path of the file to be added
(if not already existing) or modified (if already existing).
that leads to the <since> to be output.
2. Generic <revision range> expression (see "SPECIFYING
- REVISIONS" section in linkgit:git-rev-parse[1]) means the
+ REVISIONS" section in linkgit:gitrevisions[1]) means the
commits in the specified range.
The first rule takes precedence in the case of a single <commit>. To
all of those locations and decide whether it makes sense in your case to
remove those references.
+HOOKS
+-----
+
+The 'git gc --auto' command will run the 'pre-auto-gc' hook. See
+linkgit:githooks[5] for more information.
+
+
SEE ALSO
--------
linkgit:git-prune[1]
--no-filters::
Hash the contents as is, ignoring any input filter that would
- have been chosen by the attributes mechanism, including crlf
+ have been chosen by the attributes mechanism, including the end-of-line
conversion. If the file is read from standard input then this
is always implied, unless the --path option is given.
linkgit:git-web--browse[1] for more information about this. If
the script fails, the URL will be printed to stdout.
+start::
--start::
Start the httpd instance and exit. This does not generate
any of the configuration files for spawning a new instance.
+stop::
--stop::
Stop the httpd instance and exit. This does not generate
any of the configuration files for spawning a new instance,
nor does it close the browser.
+restart::
--restart::
Restart the httpd instance and exit. This does not generate
any of the configuration files for spawning a new instance.
OPTIONS
-------
-:git-log: 1
-include::diff-options.txt[]
-
-<n>::
Limits the number of commits to show.
either <since> or <until> is omitted, it defaults to
`HEAD`, i.e. the tip of the current branch.
For a more complete list of ways to spell <since>
- and <until>, see "SPECIFYING REVISIONS" section in
- linkgit:git-rev-parse[1].
+ and <until>, see linkgit:gitrevisions[1].
+
+--follow::
+ Continue listing the history of a file beyond renames
+ (works only for a single file).
--no-decorate::
--decorate[=short|full|no]::
paths. With this, the full diff is shown for commits that touch
the specified paths; this means that "<path>..." limits only
commits, and doesn't limit diff for those commits.
-
---follow::
- Continue listing the history of a file beyond renames/copies.
++
+Note that this affects all diff-based output types, e.g. those
+produced by --stat etc.
--log-size::
Before the log message print out its size in bytes. Intended
to be prefixed with "\-- " to separate them from options or
refnames.
+Common diff options
+~~~~~~~~~~~~~~~~~~~
+
+:git-log: 1
+include::diff-options.txt[]
include::rev-list-options.txt[]
with `-s` or `-u` options does not make any sense.
-t::
- Identify the file status with the following tags (followed by
- a space) at the start of each line:
+ This feature is semi-deprecated. For scripting purpose,
+ linkgit:git-status[1] `--porcelain` and
+ linkgit:git-diff-files[1] `--name-status` are almost always
+ superior alternatives, and users should look at
+ linkgit:git-status[1] `--short` or linkgit:git-diff[1]
+ `--name-status` for more user-friendly alternatives.
++
+This option identifies the file status with the following tags (followed by
+a space) at the start of each line:
+
H:: cached
S:: skip-worktree
M:: unmerged
lines, show only a partial prefix.
Non default number of digits can be specified with --abbrev=<n>.
+--debug::
+ After each line that describes a file, add more data about its
+ cache entry. This is intended to show as much information as
+ possible for manual inspection; the exact format may change at
+ any time.
+
\--::
Do not interpret any more arguments as options.
This is the default behaviour; the option is provided to
override any configuration settings.
+TEMPORARY FILES
+---------------
+`git mergetool` creates `*.orig` backup files while resolving merges.
+These are safe to remove once a file has been merged and its
+`git mergetool` session has completed.
+
+Setting the `mergetool.keepBackup` configuration variable to `false`
+causes `git mergetool` to automatically remove the backup as files
+are successfully merged.
+
Author
------
Written by Theodore Y Ts'o <tytso@mit.edu>
is taken to be in `refs/notes/` if it is not qualified.
-n::
+--dry-run::
Do not remove anything; just report the object names whose notes
would be removed.
-v::
+--verbose::
Report all object names whose notes are removed.
-------
-n::
+--dry-run::
Do not remove anything; just report what it would
remove.
-v::
+--verbose::
Report all removed objects.
\--::
SYNOPSIS
--------
-'git pull' <options> <repository> <refspec>...
+'git pull' [options] [<repository> [<refspec>...]]
DESCRIPTION
-----------
-Runs 'git fetch' with the given parameters, and calls 'git merge'
-to merge the retrieved head(s) into the current branch.
-With `--rebase`, calls 'git rebase' instead of 'git merge'.
-Note that you can use `.` (current directory) as the
-<repository> to pull from the local repository -- this is useful
-when merging local branches into the current branch.
+Incorporates changes from a remote repository into the current
+branch. In its default mode, `git pull` is shorthand for
+`git fetch` followed by `git merge FETCH_HEAD`.
-Also note that options meant for 'git pull' itself and underlying
-'git merge' must be given before the options meant for 'git fetch'.
+More precisely, 'git pull' runs 'git fetch' with the given
+parameters and calls 'git merge' to merge the retrieved branch
+heads into the current branch.
+With `--rebase`, it runs 'git rebase' instead of 'git merge'.
-*Warning*: Running 'git pull' (actually, the underlying 'git merge')
+<repository> should be the name of a remote repository as
+passed to linkgit:git-fetch[1]. <refspec> can name an
+arbitrary remote ref (for example, the name of a tag) or even
+a collection of refs with corresponding remote tracking branches
+(e.g., refs/heads/*:refs/remotes/origin/*), but usually it is
+the name of a branch in the remote repository.
+
+Default values for <repository> and <branch> are read from the
+"remote" and "merge" configuration for the current branch
+as set by linkgit:git-branch[1] `--track`.
+
+Assume the following history exists and the current branch is
+"`master`":
+
+------------
+ A---B---C master on origin
+ /
+ D---E---F---G master
+------------
+
+Then "`git pull`" will fetch and replay the changes from the remote
+`master` branch since it diverged from the local `master` (i.e., `E`)
+until its current commit (`C`) on top of `master` and record the
+result in a new commit along with the names of the two parent commits
+and a log message from the user describing the changes.
+
+------------
+ A---B---C remotes/origin/master
+ / \
+ D---E---F---G---H master
+------------
+
+See linkgit:git-merge[1] for details, including how conflicts
+are presented and handled.
+
+In git 1.7.0 or later, to cancel a conflicting merge, use
+`git reset --merge`. *Warning*: In older versions of git, running 'git pull'
with uncommitted changes is discouraged: while possible, it leaves you
-in a state that is hard to back out of in the case of a conflict.
+in a state that may be hard to back out of in the case of a conflict.
+
+If any of the remote changes overlap with local uncommitted changes,
+the merge will be automatically cancelled and the work tree untouched.
+It is generally best to get any local changes in working order before
+pulling or stash them away with linkgit:git-stash[1].
OPTIONS
-------
+Options meant for 'git pull' itself and the underlying 'git merge'
+must be given before the options meant for 'git fetch'.
+
-q::
--quiet::
This is passed to both underlying git-fetch to squelch reporting of
+
The <src> is often the name of the branch you would want to push, but
it can be any arbitrary "SHA-1 expression", such as `master~4` or
-`HEAD` (see linkgit:git-rev-parse[1]).
+`HEAD` (see linkgit:gitrevisions[1]).
+
The <dst> tells which ref on the remote side is updated with this
push. Arbitrary expressions cannot be used here, an actual ref must
For a successfully pushed ref, the summary shows the old and new
values of the ref in a form suitable for using as an argument to
`git log` (this is `<old>..<new>` in most cases, and
- `<old>...<new>` for forced non-fast-forward updates). For a
- failed update, more details are given for the failure.
- The string `rejected` indicates that git did not try to send the
- ref at all (typically because it is not a fast-forward). The
- string `remote rejected` indicates that the remote end refused
- the update; this rejection is typically caused by a hook on the
- remote side. The string `remote failure` indicates that the
- remote end did not report the successful update of the ref
- (perhaps because of a temporary error on the remote side, a
- break in the network connection, or other transient error).
+ `<old>...<new>` for forced non-fast-forward updates).
++
+For a failed update, more details are given:
++
+--
+rejected::
+ Git did not try to send the ref at all, typically because it
+ is not a fast-forward and you did not force the update.
+
+remote rejected::
+ The remote end refused the update. Usually caused by a hook
+ on the remote side, or because the remote repository has one
+ of the following safety options in effect:
+ `receive.denyCurrentBranch` (for pushes to the checked out
+ branch), `receive.denyNonFastForwards` (for forced
+ non-fast-forward updates), `receive.denyDeletes` or
+ `receive.denyDeleteCurrent`. See linkgit:git-config[1].
+
+remote failure::
+ The remote end did not report the successful update of the ref,
+ perhaps because of a temporary error on the remote side, a
+ break in the network connection, or other transient error.
+--
from::
The name of the local ref being pushed, minus its
support.
+BUGS
+----
+In order to match a directory with $GIT_DIR/info/sparse-checkout,
+trailing slash must be used. The form without trailing slash, while
+works with .gitignore, does not work with sparse checkout.
+
+
SEE ALSO
--------
linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
the 'ours' strategy simply discards all patches from the <branch>,
which makes little sense.
+-X <strategy-option>::
+--strategy-option=<strategy-option>::
+ Pass the <strategy-option> through to the merge strategy.
+ This implies `\--merge` and, if no strategy has been
+ specified, `-s recursive`. Note the reversal of 'ours' and
+ 'theirs' as noted in above for the `-m` option.
+
-q::
--quiet::
Be quiet. Implies --no-stat.
The reflog is useful in various git commands, to specify the old value
of a reference. For example, `HEAD@\{2\}` means "where HEAD used to be
two moves ago", `master@\{one.week.ago\}` means "where master used to
-point to one week ago", and so on. See linkgit:git-rev-parse[1] for
+point to one week ago", and so on. See linkgit:gitrevisions[1] for
more details.
To delete single entries from the reflog, use the subcommand "delete"
SYNOPSIS
--------
-'git request-pull' <start> <url> [<end>]
+'git request-pull' [-p] <start> <url> [<end>]
DESCRIPTION
-----------
OPTIONS
-------
+-p::
+ Show patch text
+
<start>::
Commit to start at.
SYNOPSIS
--------
-'git rerere' ['clear'|'diff'|'status'|'gc']
+'git rerere' ['clear'|'forget' [<pathspec>]|'diff'|'status'|'gc']
DESCRIPTION
-----------
aborted. Calling 'git am [--skip|--abort]' or 'git rebase [--skip|--abort]'
will automatically invoke this command.
+'forget' <pathspec>::
+
+This resets the conflict resolutions which rerere has recorded for the current
+conflict in <pathspec>. The <pathspec> is optional.
+
'diff'::
This displays diffs for the current state of the resolution. It is
SYNOPSIS
--------
[verse]
-'git reset' [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]
'git reset' [-q] [<commit>] [--] <paths>...
'git reset' --patch [<commit>] [--] [<paths>...]
+'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
DESCRIPTION
-----------
-Sets the current head to the specified commit and optionally resets the
-index and working tree to match.
-
-This command is useful if you notice some small error in a recent
-commit (or set of commits) and want to redo that part without showing
-the undo in the history.
-
-If you want to undo a commit other than the latest on a branch,
-linkgit:git-revert[1] is your friend.
-
-The second and third forms with 'paths' and/or --patch are used to
-revert selected paths in the index from a given commit, without moving
-HEAD.
-
+In the first and second form, copy entries from <commit> to the index.
+In the third form, set the current branch to <commit>, optionally
+modifying index and worktree to match. The <commit> defaults to HEAD
+in all forms.
+
+'git reset' [-q] [<commit>] [--] <paths>...::
+ This form resets the index entries for all <paths> to their
+ state at the <commit>. (It does not affect the worktree, nor
+ the current branch.)
++
+This means that `git reset <paths>` is the opposite of `git add
+<paths>`.
-OPTIONS
--------
---mixed::
- Resets the index but not the working tree (i.e., the changed files
- are preserved but not marked for commit) and reports what has not
- been updated. This is the default action.
+'git reset' --patch|-p [<commit>] [--] [<paths>...]::
+ Interactively select hunks in the difference between the index
+ and <commit> (defaults to HEAD). The chosen hunks are applied
+ in reverse to the index.
++
+This means that `git reset -p` is the opposite of `git add -p` (see
+linkgit:git-add[1]).
+'git reset' [--<mode>] [<commit>]::
+ This form points the current branch to <commit> and then
+ updates index and working tree according to <mode>, which must
+ be one of the following:
++
+--
--soft::
Does not touch the index file nor the working tree at all, but
requires them to be in a good order. This leaves all your changed
files "Changes to be committed", as 'git status' would
put it.
+--mixed::
+ Resets the index but not the working tree (i.e., the changed files
+ are preserved but not marked for commit) and reports what has not
+ been updated. This is the default action.
+
--hard::
Matches the working tree and index to that of the tree being
switched to. Any changes to tracked files in the working tree
the given commit. If a file that is different between the
current commit and the given commit has local changes, reset
is aborted.
+--
--p::
---patch::
- Interactively select hunks in the difference between the index
- and <commit> (defaults to HEAD). The chosen hunks are applied
- in reverse to the index.
-+
-This means that `git reset -p` is the opposite of `git add -p` (see
-linkgit:git-add[1]).
+If you want to undo a commit other than the latest on a branch,
+linkgit:git-revert[1] is your friend.
+
+
+OPTIONS
+-------
-q::
--quiet::
Be quiet, only report errors.
-<commit>::
- Commit to make the current HEAD. If not given defaults to HEAD.
-
-DISCUSSION
-----------
-The tables below show what happens when running:
-
-----------
-git reset --option target
-----------
-
-to reset the HEAD to another commit (`target`) with the different
-reset options depending on the state of the files.
-
-In these tables, A, B, C and D are some different states of a
-file. For example, the first line of the first table means that if a
-file is in state A in the working tree, in state B in the index, in
-state C in HEAD and in state D in the target, then "git reset --soft
-target" will put the file in state A in the working tree, in state B
-in the index and in state D in HEAD.
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- A B C D --soft A B D
- --mixed A D D
- --hard D D D
- --merge (disallowed)
- --keep (disallowed)
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- A B C C --soft A B C
- --mixed A C C
- --hard C C C
- --merge (disallowed)
- --keep A C C
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- B B C D --soft B B D
- --mixed B D D
- --hard D D D
- --merge D D D
- --keep (disallowed)
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- B B C C --soft B B C
- --mixed B C C
- --hard C C C
- --merge C C C
- --keep B C C
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- B C C D --soft B C D
- --mixed B D D
- --hard D D D
- --merge (disallowed)
- --keep (disallowed)
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- B C C C --soft B C C
- --mixed B C C
- --hard C C C
- --merge B C C
- --keep B C C
-
-"reset --merge" is meant to be used when resetting out of a conflicted
-merge. Any mergy operation guarantees that the work tree file that is
-involved in the merge does not have local change wrt the index before
-it starts, and that it writes the result out to the work tree. So if
-we see some difference between the index and the target and also
-between the index and the work tree, then it means that we are not
-resetting out from a state that a mergy operation left after failing
-with a conflict. That is why we disallow --merge option in this case.
-
-"reset --keep" is meant to be used when removing some of the last
-commits in the current branch while keeping changes in the working
-tree. If there could be conflicts between the changes in the commit we
-want to remove and the changes in the working tree we want to keep,
-the reset is disallowed. That's why it is disallowed if there are both
-changes between the working tree and HEAD, and between HEAD and the
-target. To be safe, it is also disallowed when there are unmerged
-entries.
-
-The following tables show what happens when there are unmerged
-entries:
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- X U A B --soft (disallowed)
- --mixed X B B
- --hard B B B
- --merge B B B
- --keep (disallowed)
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- X U A A --soft (disallowed)
- --mixed X A A
- --hard A A A
- --merge A A A
- --keep (disallowed)
-
-X means any state and U means an unmerged index.
-
-Examples
+EXAMPLES
--------
+Undo add::
++
+------------
+$ edit <1>
+$ git add frotz.c filfre.c
+$ mailx <2>
+$ git reset <3>
+$ git pull git://info.example.com/ nitfol <4>
+------------
++
+<1> You are happily working on something, and find the changes
+in these files are in good order. You do not want to see them
+when you run "git diff", because you plan to work on other files
+and changes with these files are distracting.
+<2> Somebody asks you to pull, and the changes sounds worthy of merging.
+<3> However, you already dirtied the index (i.e. your index does
+not match the HEAD commit). But you know the pull you are going
+to make does not affect frotz.c nor filfre.c, so you revert the
+index changes for these two files. Your changes in working tree
+remain there.
+<4> Then you can pull and merge, leaving frotz.c and filfre.c
+changes still in the working tree.
+
Undo a commit and redo::
+
------------
+
See also the --amend option to linkgit:git-commit[1].
-Undo commits permanently::
-+
-------------
-$ git commit ...
-$ git reset --hard HEAD~3 <1>
-------------
-+
-<1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
-and you do not want to ever see them again. Do *not* do this if
-you have already given these commits to somebody else. (See the
-"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
-the implications of doing so.)
-
Undo a commit, making it a topic branch::
+
------------
<2> Rewind the master branch to get rid of those three commits.
<3> Switch to "topic/wip" branch and keep working.
-Undo add::
+Undo commits permanently::
+
------------
-$ edit <1>
-$ git add frotz.c filfre.c
-$ mailx <2>
-$ git reset <3>
-$ git pull git://info.example.com/ nitfol <4>
+$ git commit ...
+$ git reset --hard HEAD~3 <1>
------------
+
-<1> You are happily working on something, and find the changes
-in these files are in good order. You do not want to see them
-when you run "git diff", because you plan to work on other files
-and changes with these files are distracting.
-<2> Somebody asks you to pull, and the changes sounds worthy of merging.
-<3> However, you already dirtied the index (i.e. your index does
-not match the HEAD commit). But you know the pull you are going
-to make does not affect frotz.c nor filfre.c, so you revert the
-index changes for these two files. Your changes in working tree
-remain there.
-<4> Then you can pull and merge, leaving frotz.c and filfre.c
-changes still in the working tree.
+<1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
+and you do not want to ever see them again. Do *not* do this if
+you have already given these commits to somebody else. (See the
+"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
+the implications of doing so.)
Undo a merge or pull::
+
Suppose you are working on something and you commit it, and then you
continue working a bit more, but now you think that what you have in
your working tree should be in another branch that has nothing to do
-with what you commited previously. You can start a new branch and
+with what you committed previously. You can start a new branch and
reset it while keeping the changes in your work tree.
+
------------
<3> But you can use "reset --keep" to remove the unwanted commit after
you switched to "branch2".
+
+DISCUSSION
+----------
+
+The tables below show what happens when running:
+
+----------
+git reset --option target
+----------
+
+to reset the HEAD to another commit (`target`) with the different
+reset options depending on the state of the files.
+
+In these tables, A, B, C and D are some different states of a
+file. For example, the first line of the first table means that if a
+file is in state A in the working tree, in state B in the index, in
+state C in HEAD and in state D in the target, then "git reset --soft
+target" will put the file in state A in the working tree, in state B
+in the index and in state D in HEAD.
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ A B C D --soft A B D
+ --mixed A D D
+ --hard D D D
+ --merge (disallowed)
+ --keep (disallowed)
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ A B C C --soft A B C
+ --mixed A C C
+ --hard C C C
+ --merge (disallowed)
+ --keep A C C
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ B B C D --soft B B D
+ --mixed B D D
+ --hard D D D
+ --merge D D D
+ --keep (disallowed)
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ B B C C --soft B B C
+ --mixed B C C
+ --hard C C C
+ --merge C C C
+ --keep B C C
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ B C C D --soft B C D
+ --mixed B D D
+ --hard D D D
+ --merge (disallowed)
+ --keep (disallowed)
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ B C C C --soft B C C
+ --mixed B C C
+ --hard C C C
+ --merge B C C
+ --keep B C C
+
+"reset --merge" is meant to be used when resetting out of a conflicted
+merge. Any mergy operation guarantees that the work tree file that is
+involved in the merge does not have local change wrt the index before
+it starts, and that it writes the result out to the work tree. So if
+we see some difference between the index and the target and also
+between the index and the work tree, then it means that we are not
+resetting out from a state that a mergy operation left after failing
+with a conflict. That is why we disallow --merge option in this case.
+
+"reset --keep" is meant to be used when removing some of the last
+commits in the current branch while keeping changes in the working
+tree. If there could be conflicts between the changes in the commit we
+want to remove and the changes in the working tree we want to keep,
+the reset is disallowed. That's why it is disallowed if there are both
+changes between the working tree and HEAD, and between HEAD and the
+target. To be safe, it is also disallowed when there are unmerged
+entries.
+
+The following tables show what happens when there are unmerged
+entries:
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ X U A B --soft (disallowed)
+ --mixed X B B
+ --hard B B B
+ --merge B B B
+ --keep (disallowed)
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ X U A A --soft (disallowed)
+ --mixed X A A
+ --hard A A A
+ --merge A A A
+ --keep (disallowed)
+
+X means any state and U means an unmerged index.
+
+
Author
------
Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds <torvalds@osdl.org>
Flags and parameters to be parsed.
-SPECIFYING REVISIONS
---------------------
-
-A revision parameter typically, but not necessarily, names a
-commit object. They use what is called an 'extended SHA1'
-syntax. Here are various ways to spell object names. The
-ones listed near the end of this list are to name trees and
-blobs contained in a commit.
-
-* The full SHA1 object name (40-byte hexadecimal string), or
- a substring of such that is unique within the repository.
- E.g. dae86e1950b1277e545cee180551750029cfe735 and dae86e both
- name the same commit object if there are no other object in
- your repository whose object name starts with dae86e.
-
-* An output from 'git describe'; i.e. a closest tag, optionally
- followed by a dash and a number of commits, followed by a dash, a
- `g`, and an abbreviated object name.
-
-* A symbolic ref name. E.g. 'master' typically means the commit
- object referenced by refs/heads/master. If you
- happen to have both heads/master and tags/master, you can
- explicitly say 'heads/master' to tell git which one you mean.
- When ambiguous, a `<name>` is disambiguated by taking the
- first match in the following rules:
-
- . if `$GIT_DIR/<name>` exists, that is what you mean (this is usually
- useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`);
-
- . otherwise, `refs/<name>` if exists;
-
- . otherwise, `refs/tags/<name>` if exists;
-
- . otherwise, `refs/heads/<name>` if exists;
-
- . otherwise, `refs/remotes/<name>` if exists;
-
- . otherwise, `refs/remotes/<name>/HEAD` if exists.
-+
-HEAD names the commit your changes in the working tree is based on.
-FETCH_HEAD records the branch you fetched from a remote repository
-with your last 'git fetch' invocation.
-ORIG_HEAD is created by commands that moves your HEAD in a drastic
-way, to record the position of the HEAD before their operation, so that
-you can change the tip of the branch back to the state before you ran
-them easily.
-MERGE_HEAD records the commit(s) you are merging into your branch
-when you run 'git merge'.
-+
-Note that any of the `refs/*` cases above may come either from
-the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
-
-* A ref followed by the suffix '@' with a date specification
- enclosed in a brace
- pair (e.g. '\{yesterday\}', '\{1 month 2 weeks 3 days 1 hour 1
- second ago\}' or '\{1979-02-26 18:30:00\}') to specify the value
- of the ref at a prior point in time. This suffix may only be
- used immediately following a ref name and the ref must have an
- existing log ($GIT_DIR/logs/<ref>). Note that this looks up the state
- of your *local* ref at a given time; e.g., what was in your local
- `master` branch last week. If you want to look at commits made during
- certain times, see `--since` and `--until`.
-
-* A ref followed by the suffix '@' with an ordinal specification
- enclosed in a brace pair (e.g. '\{1\}', '\{15\}') to specify
- the n-th prior value of that ref. For example 'master@\{1\}'
- is the immediate prior value of 'master' while 'master@\{5\}'
- is the 5th prior value of 'master'. This suffix may only be used
- immediately following a ref name and the ref must have an existing
- log ($GIT_DIR/logs/<ref>).
-
-* You can use the '@' construct with an empty ref part to get at a
- reflog of the current branch. For example, if you are on the
- branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'.
-
-* The special construct '@\{-<n>\}' means the <n>th branch checked out
- before the current one.
-
-* The suffix '@\{upstream\}' to a ref (short form 'ref@\{u\}') refers to
- the branch the ref is set to build on top of. Missing ref defaults
- to the current branch.
-
-* A suffix '{caret}' to a revision parameter (e.g. 'HEAD{caret}') means the first parent of
- that commit object. '{caret}<n>' means the <n>th parent (i.e.
- 'rev{caret}'
- is equivalent to 'rev{caret}1'). As a special rule,
- 'rev{caret}0' means the commit itself and is used when 'rev' is the
- object name of a tag object that refers to a commit object.
-
-* A suffix '{tilde}<n>' to a revision parameter means the commit
- object that is the <n>th generation grand-parent of the named
- commit object, following only the first parent. I.e. rev~3 is
- equivalent to rev{caret}{caret}{caret} which is equivalent to
- rev{caret}1{caret}1{caret}1. See below for a illustration of
- the usage of this form.
-
-* A suffix '{caret}' followed by an object type name enclosed in
- brace pair (e.g. `v0.99.8{caret}\{commit\}`) means the object
- could be a tag, and dereference the tag recursively until an
- object of that type is found or the object cannot be
- dereferenced anymore (in which case, barf). `rev{caret}0`
- introduced earlier is a short-hand for `rev{caret}\{commit\}`.
-
-* A suffix '{caret}' followed by an empty brace pair
- (e.g. `v0.99.8{caret}\{\}`) means the object could be a tag,
- and dereference the tag recursively until a non-tag object is
- found.
-
-* A colon, followed by a slash, followed by a text (e.g. `:/fix nasty bug`): this names
- a commit whose commit message starts with the specified text.
- This name returns the youngest matching commit which is
- reachable from any ref. If the commit message starts with a
- '!', you have to repeat that; the special sequence ':/!',
- followed by something else than '!' is reserved for now.
-
-* A suffix ':' followed by a path (e.g. `HEAD:README`); this names the blob or tree
- at the given path in the tree-ish object named by the part
- before the colon.
- ':path' (with an empty part before the colon, e.g. `:README`)
- is a special case of the syntax described next: content
- recorded in the index at the given path.
-
-* A colon, optionally followed by a stage number (0 to 3) and a
- colon, followed by a path (e.g. `:0:README`); this names a blob object in the
- index at the given path. Missing stage number (and the colon
- that follows it, e.g. `:README`) names a stage 0 entry. During a merge, stage
- 1 is the common ancestor, stage 2 is the target branch's version
- (typically the current branch), and stage 3 is the version from
- the branch being merged.
-
-Here is an illustration, by Jon Loeliger. Both commit nodes B
-and C are parents of commit node A. Parent commits are ordered
-left-to-right.
-
-........................................
-G H I J
- \ / \ /
- D E F
- \ | / \
- \ | / |
- \|/ |
- B C
- \ /
- \ /
- A
-........................................
-
- A = = A^0
- B = A^ = A^1 = A~1
- C = A^2 = A^2
- D = A^^ = A^1^1 = A~2
- E = B^2 = A^^2
- F = B^3 = A^^3
- G = A^^^ = A^1^1^1 = A~3
- H = D^2 = B^^2 = A^^^2 = A~2^2
- I = F^ = B^3^ = A^^3^
- J = F^2 = B^3^2 = A^^3^2
-
-
-SPECIFYING RANGES
------------------
-
-History traversing commands such as 'git log' operate on a set
-of commits, not just a single commit. To these commands,
-specifying a single revision with the notation described in the
-previous section means the set of commits reachable from that
-commit, following the commit ancestry chain.
-
-To exclude commits reachable from a commit, a prefix `{caret}`
-notation is used. E.g. `{caret}r1 r2` means commits reachable
-from `r2` but exclude the ones reachable from `r1`.
-
-This set operation appears so often that there is a shorthand
-for it. When you have two commits `r1` and `r2` (named according
-to the syntax explained in SPECIFYING REVISIONS above), you can ask
-for commits that are reachable from r2 excluding those that are reachable
-from r1 by `{caret}r1 r2` and it can be written as `r1..r2`.
-
-A similar notation `r1\...r2` is called symmetric difference
-of `r1` and `r2` and is defined as
-`r1 r2 --not $(git merge-base --all r1 r2)`.
-It is the set of commits that are reachable from either one of
-`r1` or `r2` but not from both.
-
-Two other shorthands for naming a set that is formed by a commit
-and its parent commits exist. The `r1{caret}@` notation means all
-parents of `r1`. `r1{caret}!` includes commit `r1` but excludes
-all of its parents.
-
-Here are a handful of examples:
-
- D G H D
- D F G H I J D F
- ^G D H D
- ^D B E I J F B
- B...C G H D E B C
- ^D B C E I J F B C
- C^@ I J F
- F^! D G H D F
+include::revisions.txt[]
PARSEOPT
--------
(e.g. splits single switches aggregate values), a bit like `getopt(1)` does.
It takes on the standard input the specification of the options to parse and
-understand, and echoes on the standard output a line suitable for `sh(1)` `eval`
+understand, and echoes on the standard output a string suitable for `sh(1)` `eval`
to replace the arguments with normalized ones. In case of error, it outputs
usage on the standard error stream, and exits with code 129.
+Note: Make sure you quote the result when passing it to `eval`. See
+below for an example.
+
Input Format
~~~~~~~~~~~~
An option group Header
C? option C with an optional argument"
-eval `echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?`
+eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
------------
SQ-QUOTE
<commit>...::
Commits to revert.
For a more complete list of ways to spell commit names, see
- "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+ linkgit:gitrevisions[1].
Sets of commits can also be given but no traversal is done by
default, see linkgit:git-rev-list[1] and its '--no-walk'
option.
OPTIONS
-------
<rev>::
- Arbitrary extended SHA1 expression (see linkgit:git-rev-parse[1])
+ Arbitrary extended SHA1 expression (see linkgit:gitrevisions[1])
that typically names a branch head or a tag.
<glob>::
to get a listing of all tags together with what they dereference.
+FILES
+-----
+`.git/refs/*`, `.git/packed-refs`
+
SEE ALSO
--------
-linkgit:git-ls-remote[1]
+linkgit:git-ls-remote[1],
+linkgit:git-update-ref[1],
+linkgit:gitrepository-layout[5]
AUTHORS
-------
<object>...::
The names of objects to show.
For a more complete list of ways to spell object names, see
- "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+ "SPECIFYING REVISIONS" section in linkgit:gitrevisions[1].
include::pretty-options.txt[]
--ignore-submodules[=<when>]::
Ignore changes to submodules when looking for changes. <when> can be
- either "untracked", "dirty" or "all", which is the default. When
+ either "none", "untracked", "dirty" or "all", which is the default.
+ Using "none" will consider the submodule modified when it either contains
+ untracked or modified files or its HEAD differs from the commit recorded
+ in the superproject and can be used to override any settings of the
+ 'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
"untracked" is used submodules are not considered dirty when they only
contain untracked content (but they are still scanned for modified
content). Using "dirty" ignores all changes to the work tree of submodules,
SYNOPSIS
--------
[verse]
-'git submodule' [--quiet] add [-b branch]
+'git submodule' [--quiet] add [-b branch] [-f|--force]
[--reference <repository>] [--] <repository> [<path>]
'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...]
--branch::
Branch of repository to add as submodule.
+-f::
+--force::
+ This option is only valid for the add command.
+ Allow adding an otherwise ignored submodule path.
+
--cached::
This option is only valid for status and summary commands. These
commands typically use the commit found in the submodule HEAD, but
revision fetched. If unset, 'git svn' assumes this option to
be "true".
+svn.pathnameencoding::
+ This instructs git svn to recode pathnames to a given encoding.
+ It can be used by windows users and by those who work in non-utf8
+ locales to avoid corrupted file names with non-ASCII characters.
+ Valid encodings are the ones supported by Perl's Encode module.
+
Since the noMetadata, rewriteRoot, rewriteUUID, useSvnsyncProps and useSvmProps
options all affect the metadata generated and used by 'git svn'; they
*must* be set in the configuration file before any history is imported
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.7.1.1/git.html[documentation for release 1.7.1.1]
+* link:v1.7.2.1/git.html[documentation for release 1.7.2.1]
* release notes for
+ link:RelNotes-1.7.2.1.txt[1.7.2.1],
+ link:RelNotes-1.7.2.txt[1.7.2].
+
+* link:v1.7.1.2/git.html[documentation for release 1.7.1.2]
+
+* release notes for
+ link:RelNotes-1.7.1.2.txt[1.7.1.2],
link:RelNotes-1.7.1.1.txt[1.7.1.1],
link:RelNotes-1.7.1.txt[1.7.1].
-* link:v1.7.0.6/git.html[documentation for release 1.7.0.6]
+* link:v1.7.0.7/git.html[documentation for release 1.7.0.7]
* release notes for
+ link:RelNotes-1.7.0.7.txt[1.7.0.7],
link:RelNotes-1.7.0.6.txt[1.7.0.6],
link:RelNotes-1.7.0.5.txt[1.7.0.5],
link:RelNotes-1.7.0.4.txt[1.7.0.4],
(i.e. the contents of `$GIT_DIR/refs/heads/<head>`).
For a more complete list of ways to spell object names, see
-"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+"SPECIFYING REVISIONS" section in linkgit:gitrevisions[1].
File/Directory Structure
<david@dgreaves.com>, and later enhanced greatly by the
contributors on the git-list <git@vger.kernel.org>.
+Reporting Bugs
+--------------
+
+Report bugs to the Git mailing list <git@vger.kernel.org> where the
+development and maintenance is primarily done. You do not have to be
+subscribed to the list to send a message there.
+
SEE ALSO
--------
linkgit:gittutorial[7], linkgit:gittutorial-2[7],
before the commit log message is a short name you can use to
name the commit. In the above example, 'master' and 'mybranch'
are branch heads. 'master^' is the first parent of 'master'
-branch head. Please see linkgit:git-rev-parse[1] if you want to
+branch head. Please see linkgit:gitrevisions[1] if you want to
see more complex cases.
[NOTE]
the form "'<from>'..'<to>'" to show all revisions between '<from>' and
back to '<to>'. Note, more advanced revision selection can be applied.
For a more complete list of ways to spell object names, see
- "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+ linkgit:gitrevisions[1].
<path>...::
submodule.<name>.url::
Defines an url from where the submodule repository can be cloned.
+ This may be either an absolute URL ready to be passed to
+ linkgit:git-clone[1] or (if it begins with ./ or ../) a location
+ relative to the superproject's origin repository.
submodule.<name>.update::
Defines what to do when the submodule is updated by the superproject.
This config option is overridden if 'git submodule update' is given
the '--merge' or '--rebase' options.
+submodule.<name>.ignore::
+ Defines under what circumstances "git status" and the diff family show
+ a submodule as modified. When set to "all", it will never be considered
+ modified, "dirty" will ignore all changes to the submodules work tree and
+ takes only differences between the HEAD of the submodule and the commit
+ recorded in the superproject into account. "untracked" will additionally
+ let submodules with modified tracked files in their work tree show up.
+ Using "none" (the default when this option is not set) also shows
+ submodules that have untracked files in their work tree as changed.
+ If this option is also present in the submodules entry in .git/config of
+ the superproject, the setting there will override the one found in
+ .gitmodules.
+ Both settings can be overriden on the command line by using the
+ "--ignore-submodule" option.
+
EXAMPLES
--------
directory for a repository associated with your working tree, or
`<project>.git` directory for a public 'bare' repository. It is
also possible to have a working tree where `.git` is a plain
-ascii file containing `gitdir: <path>`, i.e. the path to the
+ASCII file containing `gitdir: <path>`, i.e. the path to the
real git repository).
objects::
--- /dev/null
+gitrevisions(7)
+================
+
+NAME
+----
+gitrevisions - specifying revisions and ranges for git
+
+SYNOPSIS
+--------
+gitrevisions
+
+
+DESCRIPTION
+-----------
+
+Many Git commands take revision parameters as arguments. Depending on
+the command, they denote a specific commit or, for commands which
+walk the revision graph (such as linkgit:git-log[1]), all commits which can
+be reached from that commit. In the latter case one can also specify a
+range of revisions explicitly.
+
+In addition, some Git commands (such as linkgit:git-show[1]) also take
+revision parameters which denote other objects than commits, e.g. blobs
+("files") or trees ("directories of files").
+
+include::revisions.txt[]
+
+
+SEE ALSO
+--------
+linkgit:git-rev-parse[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
--pretty[='<format>']::
---format[='<format>']::
+--format='<format>'::
Pretty-print the contents of the commit logs in a given format,
where '<format>' can be one of 'oneline', 'short', 'medium',
--- /dev/null
+SPECIFYING REVISIONS
+--------------------
+
+A revision parameter typically, but not necessarily, names a
+commit object. They use what is called an 'extended SHA1'
+syntax. Here are various ways to spell object names. The
+ones listed near the end of this list are to name trees and
+blobs contained in a commit.
+
+* The full SHA1 object name (40-byte hexadecimal string), or
+ a substring of such that is unique within the repository.
+ E.g. dae86e1950b1277e545cee180551750029cfe735 and dae86e both
+ name the same commit object if there are no other object in
+ your repository whose object name starts with dae86e.
+
+* An output from 'git describe'; i.e. a closest tag, optionally
+ followed by a dash and a number of commits, followed by a dash, a
+ `g`, and an abbreviated object name.
+
+* A symbolic ref name. E.g. 'master' typically means the commit
+ object referenced by refs/heads/master. If you
+ happen to have both heads/master and tags/master, you can
+ explicitly say 'heads/master' to tell git which one you mean.
+ When ambiguous, a `<name>` is disambiguated by taking the
+ first match in the following rules:
+
+ . if `$GIT_DIR/<name>` exists, that is what you mean (this is usually
+ useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`);
+
+ . otherwise, `refs/<name>` if exists;
+
+ . otherwise, `refs/tags/<name>` if exists;
+
+ . otherwise, `refs/heads/<name>` if exists;
+
+ . otherwise, `refs/remotes/<name>` if exists;
+
+ . otherwise, `refs/remotes/<name>/HEAD` if exists.
++
+HEAD names the commit your changes in the working tree is based on.
+FETCH_HEAD records the branch you fetched from a remote repository
+with your last 'git fetch' invocation.
+ORIG_HEAD is created by commands that moves your HEAD in a drastic
+way, to record the position of the HEAD before their operation, so that
+you can change the tip of the branch back to the state before you ran
+them easily.
+MERGE_HEAD records the commit(s) you are merging into your branch
+when you run 'git merge'.
++
+Note that any of the `refs/*` cases above may come either from
+the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
+
+* A ref followed by the suffix '@' with a date specification
+ enclosed in a brace
+ pair (e.g. '\{yesterday\}', '\{1 month 2 weeks 3 days 1 hour 1
+ second ago\}' or '\{1979-02-26 18:30:00\}') to specify the value
+ of the ref at a prior point in time. This suffix may only be
+ used immediately following a ref name and the ref must have an
+ existing log ($GIT_DIR/logs/<ref>). Note that this looks up the state
+ of your *local* ref at a given time; e.g., what was in your local
+ `master` branch last week. If you want to look at commits made during
+ certain times, see `--since` and `--until`.
+
+* A ref followed by the suffix '@' with an ordinal specification
+ enclosed in a brace pair (e.g. '\{1\}', '\{15\}') to specify
+ the n-th prior value of that ref. For example 'master@\{1\}'
+ is the immediate prior value of 'master' while 'master@\{5\}'
+ is the 5th prior value of 'master'. This suffix may only be used
+ immediately following a ref name and the ref must have an existing
+ log ($GIT_DIR/logs/<ref>).
+
+* You can use the '@' construct with an empty ref part to get at a
+ reflog of the current branch. For example, if you are on the
+ branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'.
+
+* The special construct '@\{-<n>\}' means the <n>th branch checked out
+ before the current one.
+
+* The suffix '@\{upstream\}' to a ref (short form 'ref@\{u\}') refers to
+ the branch the ref is set to build on top of. Missing ref defaults
+ to the current branch.
+
+* A suffix '{caret}' to a revision parameter (e.g. 'HEAD{caret}') means the first parent of
+ that commit object. '{caret}<n>' means the <n>th parent (i.e.
+ 'rev{caret}'
+ is equivalent to 'rev{caret}1'). As a special rule,
+ 'rev{caret}0' means the commit itself and is used when 'rev' is the
+ object name of a tag object that refers to a commit object.
+
+* A suffix '{tilde}<n>' to a revision parameter means the commit
+ object that is the <n>th generation grand-parent of the named
+ commit object, following only the first parent. I.e. rev~3 is
+ equivalent to rev{caret}{caret}{caret} which is equivalent to
+ rev{caret}1{caret}1{caret}1. See below for a illustration of
+ the usage of this form.
+
+* A suffix '{caret}' followed by an object type name enclosed in
+ brace pair (e.g. `v0.99.8{caret}\{commit\}`) means the object
+ could be a tag, and dereference the tag recursively until an
+ object of that type is found or the object cannot be
+ dereferenced anymore (in which case, barf). `rev{caret}0`
+ introduced earlier is a short-hand for `rev{caret}\{commit\}`.
+
+* A suffix '{caret}' followed by an empty brace pair
+ (e.g. `v0.99.8{caret}\{\}`) means the object could be a tag,
+ and dereference the tag recursively until a non-tag object is
+ found.
+
+* A colon, followed by a slash, followed by a text (e.g. `:/fix nasty bug`): this names
+ a commit whose commit message starts with the specified text.
+ This name returns the youngest matching commit which is
+ reachable from any ref. If the commit message starts with a
+ '!', you have to repeat that; the special sequence ':/!',
+ followed by something else than '!' is reserved for now.
+
+* A suffix ':' followed by a path (e.g. `HEAD:README`); this names the blob or tree
+ at the given path in the tree-ish object named by the part
+ before the colon.
+ ':path' (with an empty part before the colon, e.g. `:README`)
+ is a special case of the syntax described next: content
+ recorded in the index at the given path.
+
+* A colon, optionally followed by a stage number (0 to 3) and a
+ colon, followed by a path (e.g. `:0:README`); this names a blob object in the
+ index at the given path. Missing stage number (and the colon
+ that follows it, e.g. `:README`) names a stage 0 entry. During a merge, stage
+ 1 is the common ancestor, stage 2 is the target branch's version
+ (typically the current branch), and stage 3 is the version from
+ the branch being merged.
+
+Here is an illustration, by Jon Loeliger. Both commit nodes B
+and C are parents of commit node A. Parent commits are ordered
+left-to-right.
+
+........................................
+G H I J
+ \ / \ /
+ D E F
+ \ | / \
+ \ | / |
+ \|/ |
+ B C
+ \ /
+ \ /
+ A
+........................................
+
+ A = = A^0
+ B = A^ = A^1 = A~1
+ C = A^2 = A^2
+ D = A^^ = A^1^1 = A~2
+ E = B^2 = A^^2
+ F = B^3 = A^^3
+ G = A^^^ = A^1^1^1 = A~3
+ H = D^2 = B^^2 = A^^^2 = A~2^2
+ I = F^ = B^3^ = A^^3^
+ J = F^2 = B^3^2 = A^^3^2
+
+
+SPECIFYING RANGES
+-----------------
+
+History traversing commands such as 'git log' operate on a set
+of commits, not just a single commit. To these commands,
+specifying a single revision with the notation described in the
+previous section means the set of commits reachable from that
+commit, following the commit ancestry chain.
+
+To exclude commits reachable from a commit, a prefix `{caret}`
+notation is used. E.g. `{caret}r1 r2` means commits reachable
+from `r2` but exclude the ones reachable from `r1`.
+
+This set operation appears so often that there is a shorthand
+for it. When you have two commits `r1` and `r2` (named according
+to the syntax explained in SPECIFYING REVISIONS above), you can ask
+for commits that are reachable from r2 excluding those that are reachable
+from r1 by `{caret}r1 r2` and it can be written as `r1..r2`.
+
+A similar notation `r1\...r2` is called symmetric difference
+of `r1` and `r2` and is defined as
+`r1 r2 --not $(git merge-base --all r1 r2)`.
+It is the set of commits that are reachable from either one of
+`r1` or `r2` but not from both.
+
+Two other shorthands for naming a set that is formed by a commit
+and its parent commits exist. The `r1{caret}@` notation means all
+parents of `r1`. `r1{caret}!` includes commit `r1` but excludes
+all of its parents.
+
+Here are a handful of examples:
+
+ D G H D
+ D F G H I J D F
+ ^G D H D
+ ^D B E I J F B
+ B...C G H D E B C
+ ^D B C E I J F B C
+ C^@ I J F
+ F^! D G H D F
ofs-delta
---------
-Server can send, and client understand PACKv2 with delta refering to
+Server can send, and client understand PACKv2 with delta referring to
its base by position in pack rather than by an obj-id. That is, they can
send/read OBJ_OFS_DELTA (aka type 6) in a packfile.
- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
-For local respositories, also supported by git natively, the following
+For local repositories, also supported by git natively, the following
syntaxes may be used:
- /path/to/repo.git/
For the complete list of paths which git checks for references, and
the order it uses to decide which to choose when there are multiple
references with the same shorthand name, see the "SPECIFYING
-REVISIONS" section of linkgit:git-rev-parse[1].
+REVISIONS" section of linkgit:gitrevisions[1].
[[Updating-a-repository-With-git-fetch]]
Updating a repository with git fetch
- HEAD: refers to the head of the current branch
There are many more; see the "SPECIFYING REVISIONS" section of the
-linkgit:git-rev-parse[1] man page for the complete list of ways to
+linkgit:gitrevisions[1] man page for the complete list of ways to
name revisions. Some examples:
-------------------------------------------------
$ gitk $( git show-ref --heads ) --not $( git show-ref --tags )
-------------------------------------------------
-(See linkgit:git-rev-parse[1] for explanations of commit-selecting
+(See linkgit:gitrevisions[1] for explanations of commit-selecting
syntax such as `--not`.)
[[making-a-release]]
The reflogs are kept by default for 30 days, after which they may be
pruned. See linkgit:git-reflog[1] and linkgit:git-gc[1] to learn
how to control this pruning, and see the "SPECIFYING REVISIONS"
-section of linkgit:git-rev-parse[1] for details.
+section of linkgit:gitrevisions[1] for details.
Note that the reflog history is very different from normal git history.
While normal history is shared by every repository that works on the
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.1.GIT
+DEF_VER=v1.7.2.GIT
LF='
'
It has been reported that docbook-xsl version 1.72 and 1.73 are
buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
the patch in contrib/patches/docbook-xsl-manpages-charmap.patch
+
+ Users attempting to build the documentation on Cygwin may need to ensure
+ that the /etc/xml/catalog file looks something like this:
+
+ <?xml version="1.0"?>
+ <!DOCTYPE catalog PUBLIC
+ "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN"
+ "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"
+ >
+ <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
+ <rewriteURI
+ uriStartString = "http://docbook.sourceforge.net/release/xsl/current"
+ rewritePrefix = "/usr/share/sgml/docbook/xsl-stylesheets"
+ />
+ <rewriteURI
+ uriStartString="http://www.oasis-open.org/docbook/xml/4.5"
+ rewritePrefix="/usr/share/sgml/docbook/xml-dtd-4.5"
+ />
+ </catalog>
+
+ This can be achieved with the following two xmlcatalog commands:
+
+ xmlcatalog --noout \
+ --add rewriteURI \
+ http://docbook.sourceforge.net/release/xsl/current \
+ /usr/share/sgml/docbook/xsl-stylesheets \
+ /etc/xml/catalog
+
+ xmlcatalog --noout \
+ --add rewriteURI \
+ http://www.oasis-open.org/docbook/xml/4.5/xsl/current \
+ /usr/share/sgml/docbook/xml-dtd-4.5 \
+ /etc/xml/catalog
builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
builtin/tar-tree.o archive-tar.o: tar.h
builtin/pack-objects.o: thread-utils.h
+connect.o transport.o http-backend.o: url.h
http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
-http.o http-walker.o http-push.o remote-curl.o: http.h
+http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h
xdiff-interface.o $(XDIFF_OBJS): \
xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
{ test "$$bindir/" = "$$execdir/" || \
- { $(RM) "$$execdir/git$X" && \
+ for p in git$X $(filter $(install_bindir_programs),$(ALL_PROGRAMS)); do \
+ $(RM) "$$execdir/$$p" && \
test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
- ln "$$bindir/git$X" "$$execdir/git$X" 2>/dev/null || \
- cp "$$bindir/git$X" "$$execdir/git$X"; } ; } && \
- { for p in $(BUILT_INS); do \
+ ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
+ cp "$$bindir/$$p" "$$execdir/$$p" || exit; \
+ done; \
+ } && \
+ for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
+ $(RM) "$$bindir/$$p" && \
+ ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
+ ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
+ cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
+ done && \
+ for p in $(BUILT_INS); do \
$(RM) "$$execdir/$$p" && \
ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
- done; } && \
- { test x"$(REMOTE_CURL_ALIASES)" = x || \
- { for p in $(REMOTE_CURL_ALIASES); do \
+ done && \
+ remote_curl_aliases="$(REMOTE_CURL_ALIASES)" && \
+ for p in $$remote_curl_aliases; do \
$(RM) "$$execdir/$$p" && \
ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
- done; } ; } && \
+ done && \
./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
install-gitweb:
documented,gitglossary | \
documented,githooks | \
documented,gitrepository-layout | \
+ documented,gitrevisions | \
documented,gittutorial | \
documented,gittutorial-2 | \
documented,git-bisect-lk2009 | \
-Documentation/RelNotes-1.7.2.txt
\ No newline at end of file
+Documentation/RelNotes-1.7.2.1.txt
\ No newline at end of file
struct strbuf fmt = STRBUF_INIT;
struct pretty_print_context ctx = {0};
ctx.date_mode = DATE_NORMAL;
+ ctx.abbrev = DEFAULT_ABBREV;
if (src == buf->buf)
to_free = strbuf_detach(buf, NULL);
#define say1(a,b) fprintf(stderr, a, b)
#define say2(a,b,c) fprintf(stderr, a, b, c)
#else
-#define say(a) do {} while(0)
-#define say1(a,b) do {} while(0)
-#define say2(a,b,c) do {} while(0)
+#define say(a) do { /* nothing */ } while (0)
+#define say1(a,b) do { /* nothing */ } while (0)
+#define say2(a,b,c) do { /* nothing */ } while (0)
#endif
static const char en85[] = {
enum object_type type;
unsigned long size;
char *buf = read_sha1_file(commit->object.sha1, &type, &size);
- char *ep, *sp;
+ const char *subject_start;
+ int subject_len;
fprintf(stderr, "%c%c%c ",
(flags & TREESAME) ? ' ' : 'T',
fprintf(stderr, " %.*s", 8,
sha1_to_hex(pp->item->object.sha1));
- sp = strstr(buf, "\n\n");
- if (sp) {
- sp += 2;
- for (ep = sp; *ep && *ep != '\n'; ep++)
- ;
- fprintf(stderr, " %.*s", (int)(ep - sp), sp);
- }
+ subject_len = find_commit_subject(buf, &subject_start);
+ if (subject_len)
+ fprintf(stderr, " %.*s", subject_len, subject_start);
fprintf(stderr, "\n");
}
}
dont_change_ref = 1;
else if (!force)
die("A branch named '%s' already exists.", name);
- else if (!is_bare_repository() && !strcmp(head, name))
+ else if (!is_bare_repository() && head && !strcmp(head, name))
die("Cannot force update the current branch.");
forcing = 1;
}
"The following paths are ignored by one of your .gitignore files:\n";
static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
-static int ignore_add_errors, addremove, intent_to_add;
+static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
static struct option builtin_add_options[] = {
OPT__DRY_RUN(&show_only),
OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
+ OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
OPT_END(),
};
if (addremove && take_worktree_changes)
die("-A and -u are mutually incompatible");
+ if (!show_only && ignore_missing)
+ die("Option --ignore-missing can only be used together with --dry-run");
if ((addremove || take_worktree_changes) && !argc) {
static const char *here[2] = { ".", NULL };
argc = 1;
seen = find_used_pathspec(pathspec);
for (i = 0; pathspec[i]; i++) {
if (!seen[i] && pathspec[i][0]
- && !file_exists(pathspec[i]))
- die("pathspec '%s' did not match any files",
- pathspec[i]);
+ && !file_exists(pathspec[i])) {
+ if (ignore_missing) {
+ if (excluded(&dir, pathspec[i], DT_UNKNOWN))
+ dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
+ } else
+ die("pathspec '%s' did not match any files",
+ pathspec[i]);
+ }
}
free(seen);
}
else if (get_sha1(patch->old_sha1_prefix, sha1))
/* git diff has no index line for mode/type changes */
if (!patch->lines_added && !patch->lines_deleted) {
- if (get_current_sha1(patch->new_name, sha1) ||
- get_current_sha1(patch->old_name, sha1))
+ if (get_current_sha1(patch->old_name, sha1))
die("mode change for %s, which is not "
"in current HEAD", name);
sha1_ptr = sha1;
int detailed)
{
int len;
- char *tmp, *endp, *reencoded, *message;
+ const char *subject;
+ char *reencoded, *message;
static char author_name[1024];
static char author_mail[1024];
static char committer_name[1024];
&ret->committer_time, &ret->committer_tz);
ret->summary = summary_buf;
- tmp = strstr(message, "\n\n");
- if (!tmp) {
- error_out:
+ len = find_commit_subject(message, &subject);
+ if (len && len < sizeof(summary_buf)) {
+ memcpy(summary_buf, subject, len);
+ summary_buf[len] = 0;
+ } else {
sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
- free(reencoded);
- return;
}
- tmp += 2;
- endp = strchr(tmp, '\n');
- if (!endp)
- endp = tmp + strlen(tmp);
- len = endp - tmp;
- if (len >= sizeof(summary_buf) || len == 0)
- goto error_out;
- memcpy(summary_buf, tmp, len);
- summary_buf[len] = 0;
free(reencoded);
}
*dst = '\0';
}
+static int check_ref_format_branch(const char *arg)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int nongit;
+
+ setup_git_directory_gently(&nongit);
+ if (strbuf_check_branch_ref(&sb, arg))
+ die("'%s' is not a valid branch name", arg);
+ printf("%s\n", sb.buf + 11);
+ return 0;
+}
+
+static int check_ref_format_print(const char *arg)
+{
+ char *refname = xmalloc(strlen(arg) + 1);
+
+ if (check_ref_format(arg))
+ return 1;
+ collapse_slashes(refname, arg);
+ printf("%s\n", refname);
+ return 0;
+}
+
int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
{
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_check_ref_format_usage);
- if (argc == 3 && !strcmp(argv[1], "--branch")) {
- struct strbuf sb = STRBUF_INIT;
-
- if (strbuf_check_branch_ref(&sb, argv[2]))
- die("'%s' is not a valid branch name", argv[2]);
- printf("%s\n", sb.buf + 11);
- exit(0);
- }
- if (argc == 3 && !strcmp(argv[1], "--print")) {
- char *refname = xmalloc(strlen(argv[2]) + 1);
-
- if (check_ref_format(argv[2]))
- exit(1);
- collapse_slashes(refname, argv[2]);
- printf("%s\n", refname);
- exit(0);
- }
+ if (argc == 3 && !strcmp(argv[1], "--branch"))
+ return check_ref_format_branch(argv[2]);
+ if (argc == 3 && !strcmp(argv[1], "--print"))
+ return check_ref_format_print(argv[2]);
if (argc != 2)
usage(builtin_check_ref_format_usage);
return !!check_ref_format(argv[1]);
int writeout_stage;
int writeout_error;
+ /* not set by parse_options */
+ int branch_exists;
+
const char *new_branch;
+ const char *new_branch_force;
const char *new_orphan_branch;
int new_branch_log;
enum branch_track track;
struct rev_info rev;
/* I think we want full paths, even if we're in a subdirectory. */
init_revisions(&rev, NULL);
- rev.abbrev = 0;
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
if (diff_setup_done(&rev.diffopt) < 0)
die("diff_setup_done failed");
}
}
else
- create_branch(old->name, opts->new_branch, new->name, 0,
+ create_branch(old->name, opts->new_branch, new->name,
+ opts->new_branch_force ? 1 : 0,
opts->new_branch_log, opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
new->name);
else
fprintf(stderr, "Switched to%s branch '%s'\n",
- opts->new_branch ? " a new" : "",
+ opts->branch_exists ? " and reset" : " a new",
new->name);
}
if (old->path && old->name) {
int dwim_new_local_branch = 1;
struct option options[] = {
OPT__QUIET(&opts.quiet),
- OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+ OPT_STRING('b', NULL, &opts.new_branch, "branch",
+ "create and checkout a new branch"),
+ OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
+ "create/reset and checkout a branch"),
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
OPT_SET_INT('t', "track", &opts.track, "track",
BRANCH_TRACK_EXPLICIT),
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
+ /* we can assume from now on new_branch = !new_branch_force */
+ if (opts.new_branch && opts.new_branch_force)
+ die("-B cannot be used with -b");
+
+ /* copy -B over to -b, so that we can just check the latter */
+ if (opts.new_branch_force)
+ opts.new_branch = opts.new_branch_force;
+
if (patch_mode && (opts.track > 0 || opts.new_branch
|| opts.new_branch_log || opts.merge || opts.force))
die ("--patch is incompatible with all other options");
if (opts.new_orphan_branch) {
if (opts.new_branch)
- die("--orphan and -b are mutually exclusive");
+ die("--orphan and -b|-B are mutually exclusive");
if (opts.track > 0)
die("--orphan cannot be used with -t");
opts.new_branch = opts.new_orphan_branch;
if (strbuf_check_branch_ref(&buf, opts.new_branch))
die("git checkout: we do not like '%s' as a branch name.",
opts.new_branch);
- if (!get_sha1(buf.buf, rev))
- die("git checkout: branch %s already exists", opts.new_branch);
+ if (!get_sha1(buf.buf, rev)) {
+ opts.branch_exists = 1;
+ if (!opts.new_branch_force)
+ die("git checkout: branch %s already exists",
+ opts.new_branch);
+ }
strbuf_release(&buf);
}
#include "cache.h"
#include "dir.h"
#include "parse-options.h"
+#include "string-list.h"
#include "quote.h"
static int force = -1; /* unset */
static const char *const builtin_clean_usage[] = {
- "git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
+ "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...",
NULL
};
return git_default_config(var, value, cb);
}
+static int exclude_cb(const struct option *opt, const char *arg, int unset)
+{
+ struct string_list *exclude_list = opt->value;
+ string_list_append(exclude_list, arg);
+ return 0;
+}
+
int cmd_clean(int argc, const char **argv, const char *prefix)
{
int i;
struct dir_struct dir;
static const char **pathspec;
struct strbuf buf = STRBUF_INIT;
+ struct string_list exclude_list = { NULL, 0, 0, 0 };
const char *qname;
char *seen = NULL;
struct option options[] = {
OPT_BOOLEAN('f', "force", &force, "force"),
OPT_BOOLEAN('d', NULL, &remove_directories,
"remove whole directories"),
+ { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
+ "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb },
OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
OPT_BOOLEAN('X', NULL, &ignored_only,
"remove only ignored files"),
if (!ignored)
setup_standard_excludes(&dir);
+ for (i = 0; i < exclude_list.nr; i++)
+ add_exclude(exclude_list.items[i].string, "", 0, dir.exclude_list);
+
pathspec = get_pathspec(prefix, argv);
fill_directory(&dir, pathspec);
free(seen);
strbuf_release(&directory);
+ string_list_clear(&exclude_list, 0);
return (errors != 0);
}
#include "rerere.h"
#include "unpack-trees.h"
#include "quote.h"
+#include "submodule.h"
static const char * const builtin_commit_usage[] = {
"git commit [options] [--] <filepattern>...",
"terminate entries with NUL"),
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
- { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+ { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
/* end commit contents options */
{ OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
if (!pathspec || !*pathspec) {
fd = hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
- if (write_cache(fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock))
- die("unable to write new_index file");
+ if (active_cache_changed) {
+ if (write_cache(fd, active_cache, active_nr) ||
+ commit_locked_index(&index_lock))
+ die("unable to write new_index file");
+ } else {
+ rollback_lock_file(&index_lock);
+ }
commit_style = COMMIT_AS_IS;
return get_index_file();
}
status_format = STATUS_FORMAT_PORCELAIN;
wt_status_prepare(&s);
+ gitmodules_config();
git_config(git_status_config, &s);
in_merge = file_exists(git_path("MERGE_HEAD"));
argc = parse_options(argc, argv, prefix,
fd = hold_locked_index(&index_lock, 0);
if (0 <= fd) {
- if (!write_cache(fd, active_cache, active_nr))
+ if (active_cache_changed &&
+ !write_cache(fd, active_cache, active_nr))
commit_locked_index(&index_lock);
- rollback_lock_file(&index_lock);
+ else
+ rollback_lock_file(&index_lock);
}
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
- rev.abbrev = 0;
rev.diff = 1;
rev.diffopt.output_format =
DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
#include "commit.h"
#include "revision.h"
#include "builtin.h"
+#include "submodule.h"
static const char diff_files_usage[] =
"git diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
unsigned options = 0;
init_revisions(&rev, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
#include "commit.h"
#include "revision.h"
#include "builtin.h"
+#include "submodule.h"
static const char diff_cache_usage[] =
"git diff-index [-m] [--cached] "
int result;
init_revisions(&rev, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
#include "commit.h"
#include "log-tree.h"
#include "builtin.h"
+#include "submodule.h"
static struct rev_info log_tree_opt;
int read_stdin = 0;
init_revisions(opt, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
opt->abbrev = 0;
opt->diff = 1;
#include "revision.h"
#include "log-tree.h"
#include "builtin.h"
+#include "submodule.h"
struct blobinfo {
unsigned char sha1[20];
*/
prefix = setup_git_directory_gently(&nongit);
+ gitmodules_config();
git_config(git_diff_ui_config, NULL);
if (diff_use_color_default == -1)
result = builtin_diff_index(&rev, argc, argv);
else if (ents == 2)
result = builtin_diff_tree(&rev, argc, argv, ent);
- else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
- /* diff A...B where there is one sane merge base between
- * A and B. We have ent[0] == merge-base, ent[1] == A,
- * and ent[2] == B. Show diff between the base and B.
+ else if (ent[0].item->flags & UNINTERESTING) {
+ /*
+ * diff A...B where there is at least one merge base
+ * between A and B. We have ent[0] == merge-base,
+ * ent[ents-2] == A, and ent[ents-1] == B. Show diff
+ * between the base and B. Note that we pick one
+ * merge base at random if there are more than one.
*/
- ent[1] = ent[2];
+ ent[1] = ent[ents-1];
result = builtin_diff_tree(&rev, argc, argv, ent);
- }
- else
+ } else
result = builtin_diff_combined(&rev, argc, argv,
- ent, ents);
+ ent, ents);
result = diff_result_code(&rev.diffopt, result);
if (1 < rev.diffopt.skip_stat_unmatch)
refresh_index_quietly();
int exit_code;
if (!remote)
- die("Where do you want to fetch from today?");
+ die("No remote repository specified. Please, specify either a URL or a\n"
+ "remote name from which new revisions should be fetched.");
transport = transport_get(remote, NULL);
transport_set_verbosity(transport, verbosity, progress);
if (show_in_pager == default_pager)
show_in_pager = git_pager(1);
if (show_in_pager) {
+ opt.color = 0;
opt.name_only = 1;
opt.null_following_name = 1;
opt.output_priv = &path_list;
if (!path)
path = "emacsclient";
strbuf_addf(&man_page, "(woman \"%s\")", page);
- execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+ execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
warning("failed to exec '%s': %s", path, strerror(errno));
}
}
} else
path = "kfmclient";
strbuf_addf(&man_page, "man:%s(1)", page);
- execlp(path, filename, "newTab", man_page.buf, NULL);
+ execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
warning("failed to exec '%s': %s", path, strerror(errno));
}
}
{
if (!path)
path = "man";
- execlp(path, "man", page, NULL);
+ execlp(path, "man", page, (char *)NULL);
warning("failed to exec '%s': %s", path, strerror(errno));
}
{
struct strbuf shell_cmd = STRBUF_INIT;
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
- execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+ execl("/bin/sh", "sh", "-c", shell_cmd.buf, (char *)NULL);
warning("failed to exec '%s': %s", cmd, strerror(errno));
}
{
const char *page = cmd_to_page(git_cmd);
setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
- execlp("info", "info", "gitman", page, NULL);
+ execlp("info", "info", "gitman", page, (char *)NULL);
die("no info viewer handled the request");
}
#ifndef open_html
static void open_html(const char *path)
{
- execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
+ execl_git_cmd("web--browse", "-c", "help.browser", path, (char *)NULL);
}
#endif
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(index_pack_usage);
+ read_replace_refs = 0;
+
/*
* We wish to read the repository's config file if any, and
* for that it is necessary to call setup_git_directory_gently().
static int show_killed;
static int show_valid_bit;
static int line_terminator = '\n';
+static int debug_mode;
static const char *prefix;
static int max_prefix_len;
ce_stage(ce));
}
write_name(ce->name, ce_namelen(ce));
+ if (debug_mode) {
+ printf(" ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
+ printf(" mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
+ printf(" dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
+ printf(" uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
+ printf(" size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
+ }
}
static void show_ru_info(void)
OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
"pretend that paths removed since <tree-ish> are still present"),
OPT__ABBREV(&abbrev),
+ OPT_BOOLEAN(0, "debug", &debug_mode, "show debugging data"),
OPT_END()
};
{
enum object_type type;
struct blob *base, *our, *their;
+ const char *path = entry->path;
if (!entry->stage)
return read_sha1_file(entry->blob->object.sha1, &type, size);
their = NULL;
if (entry)
their = entry->blob;
- return merge_file(entry->path, base, our, their, size);
+ return merge_file(path, base, our, their, size);
}
static void *origin(struct merge_list *entry, unsigned long *size)
struct notes_tree *t;
int show_only = 0, verbose = 0;
struct option options[] = {
- OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"),
- OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"),
+ OPT_BOOLEAN('n', "dry-run", &show_only,
+ "do not remove, show only"),
+ OPT_BOOLEAN('v', "verbose", &verbose, "report pruned notes"),
OPT_END()
};
{
struct rev_info revs;
const struct option options[] = {
- OPT_BOOLEAN('n', NULL, &show_only,
+ OPT_BOOLEAN('n', "dry-run", &show_only,
"do not remove, show only"),
- OPT_BOOLEAN('v', NULL, &verbose,
- "report pruned objects"),
+ OPT_BOOLEAN('v', "verbose", &verbose, "report pruned objects"),
OPT_DATE(0, "expire", &expire,
"expire objects older than <time>"),
OPT_END()
static const char **refspec;
static int refspec_nr;
+static int refspec_alloc;
static void add_refspec(const char *ref)
{
- int nr = refspec_nr + 1;
- refspec = xrealloc(refspec, nr * sizeof(char *));
- refspec[nr-1] = ref;
- refspec_nr = nr;
+ refspec_nr++;
+ ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
+ refspec[refspec_nr-1] = ref;
}
static void set_refspecs(const char **refs, int nr)
if (nonfastforward && advice_push_nonfastforward) {
fprintf(stderr, "To prevent you from losing history, non-fast-forward updates were rejected\n"
- "Merge the remote changes before pushing again. See the 'Note about\n"
- "fast-forwards' section of 'git push --help' for details.\n");
+ "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n"
+ "'Note about fast-forwards' section of 'git push --help' for details.\n");
}
return 1;
* "-m ent" or "--reset ent" form), we can obtain a fully
* valid cache-tree because the index must match exactly
* what came from the tree.
- *
- * The same holds true if we are switching between two trees
- * using read-tree -m A B. The index must match B after that.
*/
if (nr_trees == 1 && !opts.prefix)
prime_cache_tree(&active_cache_tree, trees[0]);
- else if (nr_trees == 2 && opts.merge)
- prime_cache_tree(&active_cache_tree, trees[1]);
if (write_cache(newfd, active_cache, active_nr) ||
commit_locked_index(&lock_file))
if (!has_rerere_resolution(name))
unlink_rr_item(name);
}
- unlink_or_warn(git_path("rr-cache/MERGE_RR"));
+ unlink_or_warn(git_path("MERGE_RR"));
} else if (!strcmp(argv[1], "gc"))
garbage_collect(&merge_rr);
else if (!strcmp(argv[1], "status"))
ALLOC_GROW(opts, onb + 1, osz);
memset(opts + onb, 0, sizeof(opts[onb]));
argc = parse_options(argc, argv, prefix, opts, usage,
- keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 |
- stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0 |
+ (keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
+ (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
PARSE_OPT_SHELL_EVAL);
strbuf_addf(&parsed, " --");
static int get_message(const char *raw_message, struct commit_message *out)
{
const char *encoding;
- const char *p, *abbrev, *eol;
+ const char *abbrev, *subject;
+ int abbrev_len, subject_len;
char *q;
- int abbrev_len, oneline_len;
if (!raw_message)
return -1;
abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
abbrev_len = strlen(abbrev);
- /* Find beginning and end of commit subject. */
- p = out->message;
- while (*p && (*p != '\n' || p[1] != '\n'))
- p++;
- if (*p) {
- p += 2;
- for (eol = p + 1; *eol && *eol != '\n'; eol++)
- ; /* do nothing */
- } else
- eol = p;
- oneline_len = eol - p;
+ subject_len = find_commit_subject(out->message, &subject);
out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
- strlen("... ") + oneline_len + 1);
+ strlen("... ") + subject_len + 1);
q = out->parent_label;
q = mempcpy(q, "parent of ", strlen("parent of "));
out->label = q;
q = mempcpy(q, abbrev, abbrev_len);
q = mempcpy(q, "... ", strlen("... "));
out->subject = q;
- q = mempcpy(q, p, oneline_len);
+ q = mempcpy(q, subject, subject_len);
*q = '\0';
return 0;
}
mode = ce->ce_mode;
entlen = pathlen - baselen;
}
- if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
+ if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
+ strbuf_release(&buffer);
return error("invalid object %06o %s for '%.*s'",
mode, sha1_to_hex(sha1), entlen+baselen, path);
+ }
if (ce->ce_flags & CE_REMOVE)
continue; /* entry being removed */
alloc = alloc_nr(alloc); \
x = xrealloc((x), alloc * sizeof(*(x))); \
} \
- } while(0)
+ } while (0)
/* Initialize and use the cache information */
extern int read_index(struct index_state *);
char *timebuf,
size_t timebuf_size);
int parse_date(const char *date, char *buf, int bufsize);
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
void datestamp(char *buf, int bufsize);
#define approxidate(s) approxidate_careful((s), NULL)
unsigned long approxidate_careful(const char *, int *);
return ret;
}
+int find_commit_subject(const char *commit_buffer, const char **subject)
+{
+ const char *eol;
+ const char *p = commit_buffer;
+
+ while (*p && (*p != '\n' || p[1] != '\n'))
+ p++;
+ if (*p) {
+ p += 2;
+ for (eol = p; *eol && *eol != '\n'; eol++)
+ ; /* do nothing */
+ } else
+ eol = p;
+
+ *subject = p;
+
+ return eol - p;
+}
+
struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
{
struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
int parse_commit(struct commit *item);
+/* Find beginning and length of commit subject. */
+int find_commit_subject(const char *commit_buffer, const char **subject);
+
struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
unsigned commit_list_count(const struct commit_list *l);
struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
if test -z "$quiet"
then
commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
- --summary --root HEAD --`
+ --abbrev --summary --root HEAD --`
echo "Created${initial_commit:+ initial} commit $commit"
fi
fi
# "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo"
# Be careful if "..." contains things that will be expanded by shell "eval"
# or printf.
+# hooks.emailmaxlines
+# The maximum number of lines that should be included in the generated
+# email body. If not specified, there is no limit.
+# Lines beyond the limit are suppressed and counted, and a final
+# line is added indicating the number of suppressed lines.
#
# Notes
# -----
oldrev=$(git rev-parse $1)
newrev=$(git rev-parse $2)
refname="$3"
+ maxlines=$4
# --- Interpret
# 0000->1234 (create)
fn_name=atag
;;
esac
- generate_${change_type}_${fn_name}_email
+
+ if [ -z "$maxlines" ]; then
+ generate_${change_type}_${fn_name}_email
+ else
+ generate_${change_type}_${fn_name}_email | limit_lines $maxlines
+ fi
generate_email_footer
}
# Generate header
cat <<-EOF
To: $recipients
- Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
+ Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe
X-Git-Refname: $refname
X-Git-Reftype: $refname_type
X-Git-Oldrev: $oldrev
}
+limit_lines()
+{
+ lines=0
+ skipped=0
+ while IFS="" read -r line; do
+ lines=$((lines + 1))
+ if [ $lines -gt $1 ]; then
+ skipped=$((skipped + 1))
+ else
+ printf "%s\n" "$line"
+ fi
+ done
+ if [ $skipped -ne 0 ]; then
+ echo "... $skipped lines suppressed ..."
+ fi
+}
+
+
send_mail()
{
if [ -n "$envelopesender" ]; then
envelopesender=$(git config hooks.envelopesender)
emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
custom_showrev=$(git config hooks.showrev)
+maxlines=$(git config hooks.emailmaxlines)
# --- Main loop
# Allow dual mode: run from the command line just like the update hook, or
else
while read oldrev newrev refname
do
- generate_email $oldrev $newrev $refname | send_mail
+ generate_email $oldrev $newrev $refname $maxlines | send_mail
done
fi
--- /dev/null
+/*.xml
+/*.1
+/*.html
+/svn-fe
--- /dev/null
+all:: svn-fe$X
+
+CC = gcc
+RM = rm -f
+MV = mv
+
+CFLAGS = -g -O2 -Wall
+LDFLAGS =
+ALL_CFLAGS = $(CFLAGS)
+ALL_LDFLAGS = $(LDFLAGS)
+EXTLIBS =
+
+GIT_LIB = ../../libgit.a
+VCSSVN_LIB = ../../vcs-svn/lib.a
+LIBS = $(VCSSVN_LIB) $(GIT_LIB) $(EXTLIBS)
+
+QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1 =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+ QUIET_CC = @echo ' ' CC $@;
+ QUIET_LINK = @echo ' ' LINK $@;
+ QUIET_SUBDIR0 = +@subdir=
+ QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
+ $(MAKE) $(PRINT_DIR) -C $$subdir
+endif
+endif
+
+svn-fe$X: svn-fe.o $(VCSSVN_LIB) $(GIT_LIB)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ svn-fe.o \
+ $(ALL_LDFLAGS) $(LIBS)
+
+svn-fe.o: svn-fe.c ../../vcs-svn/svndump.h
+ $(QUIET_CC)$(CC) -I../../vcs-svn -o $*.o -c $(ALL_CFLAGS) $<
+
+svn-fe.html: svn-fe.txt
+ $(QUIET_SUBDIR0)../../Documentation $(QUIET_SUBDIR1) \
+ MAN_TXT=../contrib/svn-fe/svn-fe.txt \
+ ../contrib/svn-fe/$@
+
+svn-fe.1: svn-fe.txt
+ $(QUIET_SUBDIR0)../../Documentation $(QUIET_SUBDIR1) \
+ MAN_TXT=../contrib/svn-fe/svn-fe.txt \
+ ../contrib/svn-fe/$@
+ $(MV) ../../Documentation/svn-fe.1 .
+
+../../vcs-svn/lib.a: FORCE
+ $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) vcs-svn/lib.a
+
+../../libgit.a: FORCE
+ $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) libgit.a
+
+clean:
+ $(RM) svn-fe$X svn-fe.o svn-fe.html svn-fe.xml svn-fe.1
+
+.PHONY: all clean FORCE
--- /dev/null
+/*
+ * This file is in the public domain.
+ * You may freely use, modify, distribute, and relicense it.
+ */
+
+#include <stdlib.h>
+#include "svndump.h"
+
+int main(int argc, char **argv)
+{
+ svndump_init(NULL);
+ svndump_read((argc > 1) ? argv[1] : NULL);
+ svndump_reset();
+ return 0;
+}
--- /dev/null
+svn-fe(1)
+=========
+
+NAME
+----
+svn-fe - convert an SVN "dumpfile" to a fast-import stream
+
+SYNOPSIS
+--------
+svnadmin dump --incremental REPO | svn-fe [url] | git fast-import
+
+DESCRIPTION
+-----------
+
+Converts a Subversion dumpfile (version: 2) into input suitable for
+git-fast-import(1) and similar importers. REPO is a path to a
+Subversion repository mirrored on the local disk. Remote Subversion
+repositories can be mirrored on local disk using the `svnsync`
+command.
+
+INPUT FORMAT
+------------
+Subversion's repository dump format is documented in full in
+`notes/dump-load-format.txt` from the Subversion source tree.
+Files in this format can be generated using the 'svnadmin dump' or
+'svk admin dump' command.
+
+OUTPUT FORMAT
+-------------
+The fast-import format is documented by the git-fast-import(1)
+manual page.
+
+NOTES
+-----
+Subversion dumps do not record a separate author and committer for
+each revision, nor a separate display name and email address for
+each author. Like git-svn(1), 'svn-fe' will use the name
+
+---------
+user <user@UUID>
+---------
+
+as committer, where 'user' is the value of the `svn:author` property
+and 'UUID' the repository's identifier.
+
+To support incremental imports, 'svn-fe' will put a `git-svn-id`
+line at the end of each commit log message if passed an url on the
+command line. This line has the form `git-svn-id: URL@REVNO UUID`.
+
+Empty directories and unknown properties are silently discarded.
+
+The resulting repository will generally require further processing
+to put each project in its own repository and to separate the history
+of each branch. The 'git filter-branch --subdirectory-filter' command
+may be useful for this purpose.
+
+BUGS
+----
+Litters the current working directory with .bin files for
+persistence. Will be fixed when the svn-fe infrastructure is aware of
+a Git working directory.
+
+SEE ALSO
+--------
+git-svn(1), svn2git(1), svk(1), git-filter-branch(1), git-fast-import(1),
+https://svn.apache.org/repos/asf/subversion/trunk/notes/dump-load-format.txt
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
(i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date_toffset(const char *date, unsigned long *timestamp, int *offset)
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
{
struct tm tm;
int tm_gmt;
/* mktime uses local timezone */
*timestamp = tm_to_time_t(&tm);
if (*offset == -1)
- *offset = (*timestamp - mktime(&tm)) / 60;
+ *offset = ((time_t)*timestamp - mktime(&tm)) / 60;
if (*timestamp == -1)
return -1;
if (!tm_gmt)
*timestamp -= *offset * 60;
- return 1; /* success */
+ return 0; /* success */
}
int parse_date(const char *date, char *result, int maxlen)
{
unsigned long timestamp;
int offset;
- if (parse_date_toffset(date, ×tamp, &offset) > 0)
- return date_string(timestamp, offset, result, maxlen);
- else
+ if (parse_date_basic(date, ×tamp, &offset))
return -1;
+ return date_string(timestamp, offset, result, maxlen);
}
enum date_mode parse_date_format(const char *format)
int offset;
int errors = 0;
- if (parse_date_toffset(date, ×tamp, &offset) > 0)
+ if (!parse_date_basic(date, ×tamp, &offset))
return timestamp;
-
return approxidate_str(date, tv, &errors);
}
if (!error_ret)
error_ret = &dummy;
- if (parse_date_toffset(date, ×tamp, &offset) > 0) {
+ if (!parse_date_basic(date, ×tamp, &offset)) {
*error_ret = 0;
return timestamp;
}
unsigned ce_option, unsigned *dirty_submodule)
{
int changed = ce_match_stat(ce, st, ce_option);
- if (S_ISGITLINK(ce->ce_mode)
- && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
- && !DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
- && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) {
- *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+ if (S_ISGITLINK(ce->ce_mode)) {
+ unsigned orig_flags = diffopt->flags;
+ if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG))
+ set_diffopt_flags_from_submodule_config(diffopt, ce->name);
+ if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES))
+ changed = 0;
+ else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
+ && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES)))
+ *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+ diffopt->flags = orig_flags;
}
return changed;
}
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
+static struct diff_options default_diff_options;
static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
if (!strcmp(var, "diff.wordregex"))
return git_config_string(&diff_word_regex_cfg, var, value);
+ if (!strcmp(var, "diff.ignoresubmodules"))
+ handle_ignore_submodules_arg(&default_diff_options, value);
+
return git_diff_basic_config(var, value, cb);
}
return 0;
}
+ if (!prefixcmp(var, "submodule."))
+ return parse_submodule_config_option(var, value);
+
return git_color_default_config(var, value, cb);
}
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
abbrev = 40;
}
- strbuf_addf(msg, "%s%sindex %s..", set,
- line_prefix,
+ strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
find_unique_abbrev(one->sha1, abbrev));
strbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));
if (one->mode == two->mode)
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
{
/* Strip the prefix but do not molest /dev/null and absolute paths */
- if (*namep && **namep != '/')
+ if (*namep && **namep != '/') {
*namep += prefix_length;
- if (*otherp && **otherp != '/')
+ if (**namep == '/')
+ ++*namep;
+ }
+ if (*otherp && **otherp != '/') {
*otherp += prefix_length;
+ if (**otherp == '/')
+ ++*otherp;
+ }
}
static void run_diff(struct diff_filepair *p, struct diff_options *o)
void diff_setup(struct diff_options *options)
{
- memset(options, 0, sizeof(*options));
- memset(&diff_queued_diff, 0, sizeof(diff_queued_diff));
+ memcpy(options, &default_diff_options, sizeof(*options));
options->file = stdout;
DIFF_OPT_SET(options, ALLOW_TEXTCONV);
else if (!strcmp(arg, "--no-textconv"))
DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
- else if (!strcmp(arg, "--ignore-submodules"))
+ else if (!strcmp(arg, "--ignore-submodules")) {
+ DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(options, "all");
- else if (!prefixcmp(arg, "--ignore-submodules="))
+ } else if (!prefixcmp(arg, "--ignore-submodules=")) {
+ DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(options, arg + 20);
- else if (!strcmp(arg, "--submodule"))
+ } else if (!strcmp(arg, "--submodule"))
DIFF_OPT_SET(options, SUBMODULE_LOG);
else if (!prefixcmp(arg, "--submodule=")) {
if (!strcmp(arg + 12, "log"))
return result;
}
+/*
+ * Shall changes to this submodule be ignored?
+ *
+ * Submodule changes can be configured to be ignored separately for each path,
+ * but that configuration can be overridden from the command line.
+ */
+static int is_submodule_ignored(const char *path, struct diff_options *options)
+{
+ int ignored = 0;
+ unsigned orig_flags = options->flags;
+ if (!DIFF_OPT_TST(options, OVERRIDE_SUBMODULE_CONFIG))
+ set_diffopt_flags_from_submodule_config(options, path);
+ if (DIFF_OPT_TST(options, IGNORE_SUBMODULES))
+ ignored = 1;
+ options->flags = orig_flags;
+ return ignored;
+}
+
void diff_addremove(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
{
struct diff_filespec *one, *two;
- if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode))
+ if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options))
return;
/* This may look odd, but it is a preparation for
{
struct diff_filespec *one, *two;
- if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode)
- && S_ISGITLINK(new_mode))
+ if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
+ is_submodule_ignored(concatpath, options))
return;
if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
#define DIFF_OPT_DIRTY_SUBMODULES (1 << 24)
#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
+#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
#define MAX_SCORE 60000.0
#define DEFAULT_RENAME_SCORE 30000 /* rename/copy similarity minimum (50%) */
#define DEFAULT_BREAK_SCORE 30000 /* minimum for break to happen (50%) */
-#define DEFAULT_MERGE_SCORE 36000 /* maximum for break-merge to happen 60%) */
+#define DEFAULT_MERGE_SCORE 36000 /* maximum for break-merge to happen (60%) */
#define MINIMUM_BREAK_SIZE 400 /* do not break a file smaller than this */
(q)->queue = NULL; \
(q)->nr = (q)->alloc = 0; \
(q)->run = 0; \
- } while(0);
+ } while (0)
extern struct diff_queue_struct diff_queued_diff;
extern struct diff_filepair *diff_queue(struct diff_queue_struct *,
void diff_debug_filepair(const struct diff_filepair *, int);
void diff_debug_queue(const char *, struct diff_queue_struct *);
#else
-#define diff_debug_filespec(a,b,c) do {} while(0)
-#define diff_debug_filepair(a,b) do {} while(0)
-#define diff_debug_queue(a,b) do {} while(0)
+#define diff_debug_filespec(a,b,c) do { /* nothing */ } while (0)
+#define diff_debug_filepair(a,b) do { /* nothing */ } while (0)
+#define diff_debug_queue(a,b) do { /* nothing */ } while (0)
#endif
extern int diffcore_count_changes(struct diff_filespec *src,
return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
}
-static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
+struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
{
if (!cache_name_is_other(pathname, len))
return NULL;
extern int excluded_from_list(const char *pathname, int pathlen, const char *basename,
int *dtype, struct exclude_list *el);
extern int excluded(struct dir_struct *, const char *, int *);
+struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);
extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
char **buf_p, struct exclude_list *which, int check_index);
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
if (m->shift) {
for (k = 0; k < 1024; k++) {
if (m->data.sets[k])
- dump_marks_helper(f, (base + k) << m->shift,
+ dump_marks_helper(f, base + (k << m->shift),
m->data.sets[k]);
}
} else {
case S_IFREG | 0644:
case S_IFREG | 0755:
case S_IFLNK:
+ case S_IFDIR:
case S_IFGITLINK:
/* ok */
break;
* another repository.
*/
} else if (inline_data) {
+ if (S_ISDIR(mode))
+ die("Directories cannot be specified 'inline': %s",
+ command_buf.buf);
if (p != uq.buf) {
strbuf_addstr(&uq, p);
p = uq.buf;
}
read_next_command();
parse_and_store_blob(&last_blob, sha1, 0);
- } else if (oe) {
- if (oe->type != OBJ_BLOB)
- die("Not a blob (actually a %s): %s",
- typename(oe->type), command_buf.buf);
} else {
- enum object_type type = sha1_object_info(sha1, NULL);
+ enum object_type expected = S_ISDIR(mode) ?
+ OBJ_TREE: OBJ_BLOB;
+ enum object_type type = oe ? oe->type :
+ sha1_object_info(sha1, NULL);
if (type < 0)
- die("Blob not found: %s", command_buf.buf);
- if (type != OBJ_BLOB)
- die("Not a blob (actually a %s): %s",
- typename(type), command_buf.buf);
+ die("%s not found: %s",
+ S_ISDIR(mode) ? "Tree" : "Blob",
+ command_buf.buf);
+ if (type != expected)
+ die("Not a %s (actually a %s): %s",
+ typename(expected), typename(type),
+ command_buf.buf);
}
tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
static inline size_t xsize_t(off_t len)
{
+ if (len > (size_t) len)
+ die("Cannot handle files this big");
return (size_t)len;
}
tk_messageBox \
-icon error \
-type ok \
- -title [mc "git-gui: fatal error"] \
+ -title "git-gui: fatal error" \
-message $err
exit 1
}
}
}
+proc is_config_false {name} {
+ global repo_config
+ if {[catch {set v $repo_config($name)}]} {
+ return 0
+ } elseif {$v eq {false} || $v eq {0} || $v eq {no}} {
+ return 1
+ } else {
+ return 0
+ }
+}
+
proc get_config {name} {
global repo_config
if {[catch {set v $repo_config($name)}]} {
puts stderr $d
}
+#'" fix poor old emacs font-lock mode
+
proc _git_cmd {name} {
global _git_cmd_path
if {![info exists _nice]} {
set _nice [_which nice]
+ if {[catch {exec $_nice git version}]} {
+ set _nice {}
+ }
}
if {$_nice ne {}} {
lappend cmd $_nice
return $text
}
+wm withdraw .
set root_exists 0
bind . <Visibility> {
bind . <Visibility> {}
set default_config(gui.encoding) [encoding system]
set default_config(gui.matchtrackingbranch) false
+set default_config(gui.textconv) true
set default_config(gui.pruneduringfetch) false
set default_config(gui.trustmtime) false
set default_config(gui.fastcopyblame) false
# try to set work tree from environment, falling back to core.worktree
if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
set _gitworktree [get_config core.worktree]
+ if {$_gitworktree eq ""} {
+ set _gitworktree [file dirname [file normalize $_gitdir]]
+ }
}
if {$_prefix ne {}} {
if {$_gitworktree eq {}} {
# freedesktop.org-conforming system is our best shot
set explorer "xdg-open"
}
- eval exec $explorer $_gitworktree &
+ eval exec $explorer [list [file nativename $_gitworktree]] &
}
set is_quitting 0
set current_branch $head
}
+ wm deiconify .
switch -- $subcommand {
browser {
if {$jump_spec ne {}} usage
$ctxmsm add separator
create_common_diff_popup $ctxmsm
+proc has_textconv {path} {
+ if {[is_config_false gui.textconv]} {
+ return 0
+ }
+ set filter [gitattr $path diff set]
+ set textconv [get_config [join [list diff $filter textconv] .]]
+ if {$filter ne {set} && $textconv ne {}} {
+ return 1
+ } else {
+ return 0
+ }
+}
+
proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
global current_diff_path file_states
set ::cursorX $x
|| {__} eq $state
|| {_O} eq $state
|| {_T} eq $state
- || {T_} eq $state} {
+ || {T_} eq $state
+ || [has_textconv $current_diff_path]} {
set s disabled
} else {
set s normal
# -- Load geometry
#
-catch {
-set gm $repo_config(gui.geometry)
-wm geometry . [lindex $gm 0]
-if {$use_ttk} {
- .vpane sashpos 0 [lindex $gm 1]
- .vpane.files sashpos 0 [lindex $gm 2]
-} else {
- .vpane sash place 0 \
- [lindex $gm 1] \
- [lindex [.vpane sash coord 0] 1]
- .vpane.files sash place 0 \
- [lindex [.vpane.files sash coord 0] 0] \
- [lindex $gm 2]
+proc on_ttk_pane_mapped {w pane pos} {
+ bind $w <Map> {}
+ after 0 [list after idle [list $w sashpos $pane $pos]]
+}
+proc on_tk_pane_mapped {w pane x y} {
+ bind $w <Map> {}
+ after 0 [list after idle [list $w sash place $pane $x $y]]
+}
+proc on_application_mapped {} {
+ global repo_config use_ttk
+ bind . <Map> {}
+ set gm $repo_config(gui.geometry)
+ if {$use_ttk} {
+ bind .vpane <Map> \
+ [list on_ttk_pane_mapped %W 0 [lindex $gm 1]]
+ bind .vpane.files <Map> \
+ [list on_ttk_pane_mapped %W 0 [lindex $gm 2]]
+ } else {
+ bind .vpane <Map> \
+ [list on_tk_pane_mapped %W 0 \
+ [lindex $gm 1] \
+ [lindex [.vpane sash coord 0] 1]]
+ bind .vpane.files <Map> \
+ [list on_tk_pane_mapped %W 0 \
+ [lindex [.vpane.files sash coord 0] 0] \
+ [lindex $gm 2]]
+ }
+ wm geometry . [lindex $gm 0]
}
-unset gm
+if {[info exists repo_config(gui.geometry)]} {
+ bind . <Map> [list on_application_mapped]
+ wm geometry . [lindex $repo_config(gui.geometry) 0]
}
# -- Load window state
#
-catch {
-set gws $repo_config(gui.wmstate)
-wm state . $gws
-unset gws
+if {[info exists repo_config(gui.wmstate)]} {
+ catch {wm state . $repo_config(gui.wmstate)}
}
# -- Key Bindings
$status show [mc "Reading %s..." "$commit:[escape_path $path]"]
$w_path conf -text [escape_path $path]
+
+ set do_textconv 0
+ if {![is_config_false gui.textconv] && [git-version >= 1.7.2]} {
+ set filter [gitattr $path diff set]
+ set textconv [get_config [join [list diff $filter textconv] .]]
+ if {$filter ne {set} && $textconv ne {}} {
+ set do_textconv 1
+ }
+ }
if {$commit eq {}} {
- set fd [open $path r]
+ if {$do_textconv ne 0} {
+ set fd [open |[list $textconv $path] r]
+ } else {
+ set fd [open $path r]
+ }
fconfigure $fd -eofchar {}
} else {
- set fd [git_read cat-file blob "$commit:$path"]
+ if {$do_textconv ne 0} {
+ set fd [git_read cat-file --textconv "$commit:$path"]
+ } else {
+ set fd [git_read cat-file blob "$commit:$path"]
+ }
}
fconfigure $fd \
-blocking 0 \
$opts insert end [mc "Clone Existing Repository"] link_clone
$opts insert end "\n"
if {$m_repo ne {}} {
+ if {[tk windowingsystem] eq "win32"} {
+ set key L
+ } else {
+ set key C
+ }
$m_repo add command \
-command [cb _next clone] \
- -accelerator $M1T-C \
+ -accelerator $M1T-$key \
-label [mc "Clone..."]
- bind $top <$M1B-c> [cb _next clone]
- bind $top <$M1B-C> [cb _next clone]
+ bind $top <$M1B-[string tolower $key]> [cb _next clone]
+ bind $top <$M1B-[string toupper $key]> [cb _next clone]
}
$opts tag conf link_open -foreground blue -underline 1
set path $current_diff_path
set s $file_states($path)
- if {[lindex $s 0] ne {_M}} return
+ if {[lindex $s 0] ne {_M} || [has_textconv $path]} return
# Prevent infinite rescan loops
incr diff_empty_count
lappend cmd diff-files
}
}
+ if {![is_config_false gui.textconv] && [git-version >= 1.6.1]} {
+ lappend cmd --textconv
+ }
if {[string match {160000 *} [lindex $s 2]]
|| [string match {160000 *} [lindex $s 3]]} {
{b gui.trustmtime {mc "Trust File Modification Timestamps"}}
{b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}}
{b gui.matchtrackingbranch {mc "Match Tracking Branches"}}
+ {b gui.textconv {mc "Use Textconv For Diffs and Blames"}}
{b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}}
{i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}}
{i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}}
[info nameofexecutable] \
[file normalize $::argv0] \
] \
- [file normalize [$_gitworktree]]
+ [file normalize $_gitworktree]
} err]} {
error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
}
$sh -c \
"CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \
] \
- [file normalize [$_gitworktree]]
+ [file normalize $_gitworktree]
} err]} {
error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
}
}
constructor two_line {path} {
+ global NS
set w $path
set w_l $w.l
set w_c $w.c
eval [list exec wscript.exe \
/E:jscript \
/nologo \
- [file join $oguilib win32_shortcut.js] \
+ [file nativename [file join $oguilib win32_shortcut.js]] \
$lnk_path \
- [file join $oguilib git-gui.ico] \
+ [file nativename [file join $oguilib git-gui.ico]] \
$lnk_dir \
$lnk_exec] $lnk_args
}
incr argc -2
}
-set bindir [file dirname \
+set basedir [file dirname \
[file dirname \
[file dirname [info script]]]]
-set bindir [file join $bindir bin]
+set bindir [file join $basedir bin]
+set bindir "$bindir;[file join $basedir mingw bin]"
regsub -all ";" $bindir "\\;" bindir
set env(PATH) "$bindir;$env(PATH)"
unset bindir
resolve_full_httpd () {
case "$httpd" in
- *apache2*|*lighttpd*)
+ *apache2*|*lighttpd*|*httpd*)
+ # yes, *httpd* covers *lighttpd* above, but it is there for clarity
# ensure that the apache2/lighttpd command ends with "-f"
if ! echo "$httpd" | sane_grep -- '-f *$' >/dev/null 2>&1
then
httpd_only="${httpd%% *}" # cut on first space
return
;;
+ *webrick*)
+ # server is started by running via generated webrick.rb in
+ # $fqgitdir/gitweb
+ full_httpd="$fqgitdir/gitweb/webrick.rb"
+ httpd_only="${httpd%% *}" # cut on first space
+ return
+ ;;
esac
httpd_only="$(echo $httpd | cut -f1 -d' ')"
export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
webrick_conf () {
+ # webrick seems to have no way of passing arbitrary environment
+ # variables to the underlying CGI executable, so we wrap the
+ # actual gitweb.cgi using a shell script to force it
+ wrapper="$fqgitdir/gitweb/$httpd/wrapper.sh"
+ cat > "$wrapper" <<EOF
+#!/bin/sh
+# we use this shell script wrapper around the real gitweb.cgi since
+# there appears to be no other way to pass arbitrary environment variables
+# into the CGI process
+GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
+exec $root/gitweb.cgi
+EOF
+ chmod +x "$wrapper"
+
+ # This assumes _ruby_ is in the user's $PATH. that's _one_
+ # portable way to run ruby, which could be installed anywhere, really.
# generate a standalone server script in $fqgitdir/gitweb.
cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
+#!/usr/bin/env ruby
require 'webrick'
-require 'yaml'
-options = YAML::load_file(ARGV[0])
-options[:StartCallback] = proc do
- File.open(options[:PidFile],"w") do |f|
- f.puts Process.pid
- end
-end
-options[:ServerType] = WEBrick::Daemon
+require 'logger'
+options = {
+ :Port => $port,
+ :DocumentRoot => "$root",
+ :Logger => Logger.new('$fqgitdir/gitweb/error.log'),
+ :AccessLog => [
+ [ Logger.new('$fqgitdir/gitweb/access.log'),
+ WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
+ ],
+ :DirectoryIndex => ["gitweb.cgi"],
+ :CGIInterpreter => "$wrapper",
+ :StartCallback => lambda do
+ File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
+ end,
+ :ServerType => WEBrick::Daemon,
+}
+options[:BindAddress] = '127.0.0.1' if "$local" == "true"
server = WEBrick::HTTPServer.new(options)
['INT', 'TERM'].each do |signal|
trap(signal) {server.shutdown}
end
server.start
EOF
- # generate a shell script to invoke the above ruby script,
- # which assumes _ruby_ is in the user's $PATH. that's _one_
- # portable way to run ruby, which could be installed anywhere,
- # really.
- cat >"$fqgitdir/gitweb/$httpd" <<EOF
-#!/bin/sh
-exec ruby "$fqgitdir/gitweb/$httpd.rb" \$*
-EOF
- chmod +x "$fqgitdir/gitweb/$httpd"
-
- cat >"$conf" <<EOF
-:Port: $port
-:DocumentRoot: "$root"
-:DirectoryIndex: ["gitweb.cgi"]
-:PidFile: "$fqgitdir/pid"
-EOF
- test "$local" = true && echo ':BindAddress: "127.0.0.1"' >> "$conf"
+ chmod +x "$fqgitdir/gitweb/$httpd.rb"
+ # configuration is embedded in server script file, webrick.rb
+ rm -f "$conf"
}
lighttpd_conf () {
}
apache2_conf () {
- test -z "$module_path" && module_path=/usr/lib/apache2/modules
+ if test -z "$module_path"
+ then
+ test -d "/usr/lib/httpd/modules" &&
+ module_path="/usr/lib/httpd/modules"
+ test -d "/usr/lib/apache2/modules" &&
+ module_path="/usr/lib/apache2/modules"
+ fi
bind=
test x"$local" = xtrue && bind='127.0.0.1:'
echo 'text/css css' > "$fqgitdir/mime.types"
Listen $bind$port
EOF
- for mod in mime dir; do
- if test -e $module_path/mod_${mod}.so; then
+ for mod in mime dir env log_config
+ do
+ if test -e $module_path/mod_${mod}.so
+ then
echo "LoadModule ${mod}_module " \
"$module_path/mod_${mod}.so" >> "$conf"
fi
cat >> "$conf" <<EOF
LoadModule perl_module $module_path/mod_perl.so
PerlPassEnv GIT_DIR
-PerlPassEnv GIT_EXEC_DIR
+PerlPassEnv GIT_EXEC_PATH
PerlPassEnv GITWEB_CONFIG
<Location /gitweb.cgi>
SetHandler perl-script
echo "ScriptSock logs/gitweb.sock" >> "$conf"
fi
cat >> "$conf" <<EOF
+PassEnv GIT_DIR
+PassEnv GIT_EXEC_PATH
+PassEnv GITWEB_CONFIG
AddHandler cgi-script .cgi
<Location /gitweb.cgi>
Options +ExecCGI
*lighttpd*)
lighttpd_conf
;;
-*apache2*)
+*apache2*|*httpd*)
apache2_conf
;;
webrick)
last_status=0
rollup_status=0
+rerere=false
+
+files_to_merge() {
+ if test "$rerere" = true
+ then
+ git rerere status
+ else
+ git ls-files -u | sed -e 's/^[^ ]* //' | sort -u
+ fi
+}
+
if test $# -eq 0 ; then
- files=$(git ls-files -u | sed -e 's/^[^ ]* //' | sort -u)
+ cd_to_toplevel
+
+ if test -e "$GIT_DIR/MERGE_RR"
+ then
+ rerere=true
+ fi
+
+ files=$(files_to_merge)
if test -z "$files" ; then
echo "No files need merging"
exit 0
fi
- echo Merging the files: "$files"
- git ls-files -u |
- sed -e 's/^[^ ]* //' |
- sort -u |
+ printf "Merging:\n"
+ printf "$files\n"
+
+ files_to_merge |
while IFS= read i
do
if test $last_status -ne 0; then
export GIT_CHERRY_PICK_HELP
warn () {
- echo "$*" >&2
+ printf '%s\n' "$*" >&2
}
output () {
then
if test "$fast_forward" = t
then
- cat "$DOTEST"/current-commit | while read current_commit
+ while read current_commit
do
git rev-parse HEAD > "$REWRITTEN"/$current_commit
- done
+ done <"$DOTEST"/current-commit
rm "$DOTEST"/current-commit ||
die "Cannot write current commit's replacement sha1"
fi
echo "$oldsha1" >> "$REWRITTEN_PENDING"
case "$(peek_next_command)" in
- squash|s|fixup|f)
+ squash|s|fixup|f)
;;
- *)
+ *)
flush_rewritten_pending
;;
esac
do_next () {
rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
- read command sha1 rest < "$TODO"
+ read -r command sha1 rest < "$TODO"
case "$command" in
'#'*|''|noop)
mark_action_done
# skip picking commits whose parents are unchanged
skip_unnecessary_picks () {
fd=3
- while read command sha1 rest
+ while read -r command sha1 rest
do
# fd=3 means we skip the command
case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
fd=1
;;
esac
- echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
+ printf '%s\n' "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
mv -f "$TODO".new "$TODO" &&
case "$(peek_next_command)" in
test -s "$1.sq" || return
used=
- while read pick sha1 message
+ while read -r pick sha1 message
do
case " $used" in
*" $sha1 "*) continue ;;
esac
- echo "$pick $sha1 $message"
- while read squash action msg
+ printf '%s\n' "$pick $sha1 $message"
+ while read -r squash action msg
do
case "$message" in
"$msg"*)
- echo "$action $squash $action! $msg"
+ printf '%s\n' "$action $squash $action! $msg"
used="$used$squash "
;;
esac
git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
--abbrev=7 --reverse --left-right --topo-order \
$REVISIONS | \
- sed -n "s/^>//p" | while read shortsha1 rest
+ sed -n "s/^>//p" |
+ while read -r shortsha1 rest
do
if test t != "$PRESERVE_MERGES"
then
- echo "pick $shortsha1 $rest" >> "$TODO"
+ printf '%s\n' "pick $shortsha1 $rest" >> "$TODO"
else
sha1=$(git rev-parse $shortsha1)
if test -z "$REBASE_ROOT"
if test f = "$preserve"
then
touch "$REWRITTEN"/$sha1
- echo "pick $shortsha1 $rest" >> "$TODO"
+ printf '%s\n' "pick $shortsha1 $rest" >> "$TODO"
fi
fi
done
"
unset newbase
strategy=recursive
+strategy_opts=
do_merge=
dotest="$GIT_DIR"/rebase-merge
prec=4
then
export GIT_MERGE_VERBOSITY=1
fi
- git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
+ eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
rv=$?
case "$rv" in
0)
test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
die "No rebase in progress?"
+ git update-index --ignore-submodules --refresh &&
git diff-files --quiet --ignore-submodules || {
echo "You must edit all merge conflicts and then"
echo "mark them as resolved using git add"
-M|-m|--m|--me|--mer|--merg|--merge)
do_merge=t
;;
+ -X*|--strategy-option*)
+ case "$#,$1" in
+ 1,-X|1,--strategy-option)
+ usage ;;
+ *,-X|*,--strategy-option)
+ newopt="$2"
+ shift ;;
+ *,--strategy-option=*)
+ newopt="$(expr " $1" : ' --strategy-option=\(.*\)')" ;;
+ *,-X*)
+ newopt="$(expr " $1" : ' -X\(.*\)')" ;;
+ 1,*)
+ usage ;;
+ esac
+ strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$newopt")"
+ do_merge=t
+ if test -n "$strategy"
+ then
+ strategy=recursive
+ fi
+ ;;
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
--strateg=*|--strategy=*|\
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
--root)
rebase_root=t
;;
- -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff)
+ -f|--f|--fo|--for|--forc|--force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff)
force_rebase=t
;;
--rerere-autoupdate|--no-rerere-autoupdate)
if test -z "$do_merge"
then
git format-patch -k --stdout --full-index --ignore-if-in-upstream \
- $root_flag "$revisions" |
+ --no-renames $root_flag "$revisions" |
git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
move_to_original_branch
ret=$?
# Copyright (c) 2007 Lars Hjemli
dashless=$(basename "$0" | sed -e 's/-/ /')
-USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> [<path>]
+USAGE="[--quiet] add [-b branch] [-f|--force] [--reference <repository>] [--] <repository> [<path>]
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: $dashless [--quiet] init [--] [<path>...]
or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
command=
branch=
+force=
reference=
cached=
recursive=
branch=$2
shift
;;
+ -f | --force)
+ force=$1
+ ;;
-q|--quiet)
GIT_QUIET=1
;;
git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
die "'$path' already exists in the index"
+ if test -z "$force" && ! git add --dry-run --ignore-missing "$path" > /dev/null 2>&1
+ then
+ echo >&2 "The following path is ignored by one of your .gitignore files:" &&
+ echo >&2 $path &&
+ echo >&2 "Use -f if you really want to add it."
+ exit 1
+ fi
+
# perhaps the path exists and is already a git repo, else clone it
if test -e "$path"
then
) || die "Unable to checkout submodule '$path'"
fi
- git add "$path" ||
+ git add $force "$path" ||
die "Failed to add submodule '$path'"
git config -f .gitmodules submodule."$path".path "$path" &&
git config -f .gitmodules submodule."$path".url "$repo" &&
- git add .gitmodules ||
+ git add --force .gitmodules ||
die "Failed to register submodule '$path'"
}
sub cmd_dcommit {
my $head = shift;
+ command_noisy(qw/update-index --refresh/);
git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
'Cannot dcommit with a dirty index. Commit your changes first, '
. "or stash them with `git stash'.\n";
die("svn-remote.$remote: remote ref '$remote_ref' "
. "must start with 'refs/'\n")
unless $remote_ref =~ m{^refs/};
+ $local_ref = uri_decode($local_ref);
$r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
$r->{$remote}->{svm} = {} if $use_svm_props;
} elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
. "must start with 'refs/'\n")
unless $remote_ref =~ m{^refs/};
+ $local_ref = uri_decode($local_ref);
my $rs = {
t => $t,
remote => $remote,
my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from);
unless ($gs) {
my $ref_id = $old_ref_id;
- $ref_id =~ s/\@\d+$//;
+ $ref_id =~ s/\@\d+-*$//;
$ref_id .= "\@$r";
# just grow a tail if we're not unique enough :x
$ref_id .= '-' while find_ref($ref_id);
- print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
if ($u =~ s#^\Q$url\E(/|$)##) {
$p = $u;
$u = $url;
$repo_id = $self->{repo_id};
}
- $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+ while (1) {
+ # It is possible to tag two different subdirectories at
+ # the same revision. If the url for an existing ref
+ # does not match, we must either find a ref with a
+ # matching url or create a new ref by growing a tail.
+ $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+ my (undef, $max_commit) = $gs->rev_map_max(1);
+ last if (!$max_commit);
+ my ($url) = ::cmt_metadata($max_commit);
+ last if ($url eq $gs->full_url);
+ $ref_id .= '-';
+ }
+ print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
}
$gs
}
LIST_CACHE => 'FAULT',
;
}
+
+ sub unmemoize_svn_mergeinfo_functions {
+ return if not $memoized;
+ $memoized = 0;
+
+ Memoize::unmemoize 'lookup_svn_merge';
+ Memoize::unmemoize 'check_cherry_pick';
+ Memoize::unmemoize 'has_no_changes';
+ }
+}
+
+END {
+ # Force cache writeout explicitly instead of waiting for
+ # global destruction to avoid segfault in Storable:
+ # http://rt.cpan.org/Public/Bug/Display.html?id=36087
+ unmemoize_svn_mergeinfo_functions();
}
sub parents_exclude {
$self->{absent_dir} = {};
$self->{absent_file} = {};
$self->{gii} = $git_svn->tmp_index_do(sub { Git::IndexInfo->new });
+ $self->{pathnameencoding} = Git::config('svn.pathnameencoding');
$self;
}
sub git_path {
my ($self, $path) = @_;
+ if (my $enc = $self->{pathnameencoding}) {
+ require Encode;
+ Encode::from_to($path, 'UTF-8', $enc);
+ }
if ($self->{path_strip}) {
$path =~ s!$self->{path_strip}!! or
die "Failed to strip path '$path' ($self->{path_strip})\n";
sub repo_path {
my ($self, $path) = @_;
+ if (my $enc = $self->{pathnameencoding}) {
+ require Encode;
+ Encode::from_to($path, $enc, 'UTF-8');
+ }
$self->{path_prefix}.(defined $path ? $path : '');
}
valid_tool() {
case "$1" in
- firefox | iceweasel | chrome | chromium | konqueror | w3m | links | lynx | dillo | open | start)
+ firefox | iceweasel | chrome | google-chrome | chromium | konqueror | w3m | links | lynx | dillo | open | start)
;; # happy
*)
valid_custom_tool "$1" || return 1
if test -z "$browser" ; then
if test -n "$DISPLAY"; then
- browser_candidates="firefox iceweasel chrome chromium konqueror w3m links lynx dillo"
+ browser_candidates="firefox iceweasel google-chrome chrome chromium konqueror w3m links lynx dillo"
if test "$KDE_FULL_SESSION" = "true"; then
browser_candidates="konqueror $browser_candidates"
fi
test "$vers" -lt 2 && NEWTAB=''
"$browser_path" $NEWTAB "$@" &
;;
- chrome|chromium)
+ google-chrome|chrome|chromium)
# Actual command for chromium is chromium-browser.
# No need to specify newTab. It's default in chromium
eval "$browser_path" "$@" &
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
" [-p|--paginate|--no-pager] [--no-replace-objects]\n"
" [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
- " [-c name=value\n"
- " [--help] COMMAND [ARGS]";
+ " [-c name=value] [--help]\n"
+ " COMMAND [ARGS]";
const char git_more_info_string[] =
"See 'git help COMMAND' for more information on a specific command.";
alias_string = alias_lookup(alias_command);
if (alias_string) {
if (alias_string[0] == '!') {
+ commit_pager_choice();
if (*argcp > 1) {
struct strbuf buf;
const char *tmp;
int status;
+ commit_pager_choice();
+
strbuf_addf(&cmd, "git-%s", argv[0]);
/*
argv++;
argc--;
handle_options(&argv, &argc, NULL);
- commit_pager_choice();
if (argc > 0) {
if (!prefixcmp(argv[0], "--"))
argv[0] += 2;
} else {
/* The user didn't specify a command; give them help */
+ commit_pager_choice();
printf("usage: %s\n\n", git_usage_string);
list_common_cmds_help();
printf("\n%s\n", git_more_info_string);
# Leave it undefined (or set to 'undef') to turn off load checking.
our $maxload = 300;
+# configuration for 'highlight' (http://www.andre-simon.de/)
+# match by basename
+our %highlight_basename = (
+ #'Program' => 'py',
+ #'Library' => 'py',
+ 'SConstruct' => 'py', # SCons equivalent of Makefile
+ 'Makefile' => 'make',
+);
+# match by extension
+our %highlight_ext = (
+ # main extensions, defining name of syntax;
+ # see files in /usr/share/highlight/langDefs/ directory
+ map { $_ => $_ }
+ qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
+ # alternate extensions, see /etc/highlight/filetypes.conf
+ 'h' => 'c',
+ map { $_ => 'cpp' } qw(cxx c++ cc),
+ map { $_ => 'php' } qw(php3 php4),
+ map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi'
+ 'mak' => 'make',
+ map { $_ => 'xml' } qw(xhtml html htm),
+);
+
# You define site-wide feature defaults here; override them with
# $GITWEB_CONFIG as necessary.
our %feature = (
$actions{$action}->();
}
-sub run_request {
+sub reset_timer {
our $t0 = [Time::HiRes::gettimeofday()]
if defined $t0;
+ our $number_of_git_cmds = 0;
+}
+
+sub run_request {
+ reset_timer();
evaluate_uri();
evaluate_gitweb_config();
- evaluate_git_version();
check_loadavg();
# $projectroot and $projects_list might be set in gitweb config file
sub run {
evaluate_argv();
+ evaluate_git_version();
$pre_listen_hook->()
if $pre_listen_hook;
run_request();
- $pre_dispatch_hook->()
+ $post_dispatch_hook->()
if $post_dispatch_hook;
last REQUEST if ($is_last_request->());
sub esc_url {
my $str = shift;
return undef unless defined $str;
- $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg;
- $str =~ s/\+/%2B/g;
+ $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&= ]+)/CGI::escape($1)/eg;
$str =~ s/ /\+/g;
return $str;
}
sub guess_file_syntax {
my ($highlight, $mimetype, $file_name) = @_;
return undef unless ($highlight && defined $file_name);
-
- # configuration for 'highlight' (http://www.andre-simon.de/)
- # match by basename
- my %highlight_basename = (
- #'Program' => 'py',
- #'Library' => 'py',
- 'SConstruct' => 'py', # SCons equivalent of Makefile
- 'Makefile' => 'make',
- );
- # match by extension
- my %highlight_ext = (
- # main extensions, defining name of syntax;
- # see files in /usr/share/highlight/langDefs/ directory
- map { $_ => $_ }
- qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
- # alternate extensions, see /etc/highlight/filetypes.conf
- 'h' => 'c',
- map { $_ => 'cpp' } qw(cxx c++ cc),
- map { $_ => 'php' } qw(php3 php4),
- map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi'
- 'mak' => 'make',
- map { $_ => 'xml' } qw(xhtml html htm),
- );
-
my $basename = basename($file_name, '.in');
return $highlight_basename{$basename}
if exists $highlight_basename{$basename};
$paging_nav .= " ⋅ next";
}
- if ($#commitlist >= 100) {
- }
-
git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
git_print_header_div('commit', esc_html($co{'title'}), $hash);
- git_search_grep_body(\@commitlist, 0, 99, $next_link);
+ if ($page == 0 && !@commitlist) {
+ print "<p>No match.</p>\n";
+ } else {
+ git_search_grep_body(\@commitlist, 0, 99, $next_link);
+ }
}
if ($searchtype eq 'pickaxe') {
/* Internal API */
-/*
- * Output the next line for a graph.
- * This formats the next graph line into the specified strbuf. It is not
- * terminated with a newline.
- *
- * Returns 1 if the line includes the current commit, and 0 otherwise.
- * graph_next_line() will return 1 exactly once for each time
- * graph_update() is called.
- */
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb);
-
/*
* Output a padding line in the graph.
* This is similar to graph_next_line(). However, it is guaranteed to
/*
* The list of available column colors.
*/
-static char column_colors[][COLOR_MAXLEN] = {
+static const char *column_colors_ansi[] = {
GIT_COLOR_RED,
GIT_COLOR_GREEN,
GIT_COLOR_YELLOW,
GIT_COLOR_BOLD_BLUE,
GIT_COLOR_BOLD_MAGENTA,
GIT_COLOR_BOLD_CYAN,
+ GIT_COLOR_RESET,
};
-#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
+#define COLUMN_COLORS_ANSI_MAX (ARRAY_SIZE(column_colors_ansi) - 1)
+
+static const char **column_colors;
+static unsigned short column_colors_max;
-static const char *column_get_color_code(const struct column *c)
+void graph_set_column_colors(const char **colors, unsigned short colors_max)
{
- return column_colors[c->color];
+ column_colors = colors;
+ column_colors_max = colors_max;
+}
+
+static const char *column_get_color_code(unsigned short color)
+{
+ return column_colors[color];
}
static void strbuf_write_column(struct strbuf *sb, const struct column *c,
char col_char)
{
- if (c->color < COLUMN_COLORS_MAX)
- strbuf_addstr(sb, column_get_color_code(c));
+ if (c->color < column_colors_max)
+ strbuf_addstr(sb, column_get_color_code(c->color));
strbuf_addch(sb, col_char);
- if (c->color < COLUMN_COLORS_MAX)
- strbuf_addstr(sb, GIT_COLOR_RESET);
+ if (c->color < column_colors_max)
+ strbuf_addstr(sb, column_get_color_code(column_colors_max));
}
struct git_graph {
struct git_graph *graph_init(struct rev_info *opt)
{
struct git_graph *graph = xmalloc(sizeof(struct git_graph));
+
+ if (!column_colors)
+ graph_set_column_colors(column_colors_ansi,
+ COLUMN_COLORS_ANSI_MAX);
+
graph->commit = NULL;
graph->revs = opt;
graph->num_parents = 0;
* always increment it for the first commit we output.
* This way we start at 0 for the first commit.
*/
- graph->default_column_color = COLUMN_COLORS_MAX - 1;
+ graph->default_column_color = column_colors_max - 1;
/*
* Allocate a reasonably large default number of columns
static unsigned short graph_get_current_column_color(const struct git_graph *graph)
{
if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
- return COLUMN_COLORS_MAX;
+ return column_colors_max;
return graph->default_column_color;
}
static void graph_increment_column_color(struct git_graph *graph)
{
graph->default_column_color = (graph->default_column_color + 1) %
- COLUMN_COLORS_MAX;
+ column_colors_max;
}
static unsigned short graph_find_commit_color(const struct git_graph *graph,
graph_update_state(graph, GRAPH_PADDING);
}
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+int graph_next_line(struct git_graph *graph, struct strbuf *sb)
{
switch (graph->state) {
case GRAPH_PADDING:
/* A graph is a pointer to this opaque structure */
struct git_graph;
+/*
+ * Set up a custom scheme for column colors.
+ *
+ * The default column color scheme inserts ANSI color escapes to colorize
+ * the graph. The various color escapes are stored in an array of strings
+ * where each entry corresponds to a color, except for the last entry,
+ * which denotes the escape for resetting the color back to the default.
+ * When generating the graph, strings from this array are inserted before
+ * and after the various column characters.
+ *
+ * This function allows you to enable a custom array of color escapes.
+ * The 'colors_max' argument is the index of the last "reset" entry.
+ *
+ * This functions must be called BEFORE graph_init() is called.
+ */
+void graph_set_column_colors(const char **colors, unsigned short colors_max);
+
/*
* Create a new struct git_graph.
*/
*/
int graph_is_commit_finished(struct git_graph const *graph);
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf. It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
/*
* graph_show_*: helper functions for printing to stdout
#endif
#if LIBCURL_VERSION_NUM < 0x070704
-#define curl_global_cleanup() do { /* nothing */ } while(0)
+#define curl_global_cleanup() do { /* nothing */ } while (0)
#endif
#if LIBCURL_VERSION_NUM < 0x070800
-#define curl_global_init(a) do { /* nothing */ } while(0)
+#define curl_global_init(a) do { /* nothing */ } while (0)
#endif
#if (LIBCURL_VERSION_NUM < 0x070c04) || (LIBCURL_VERSION_NUM == 0x071000)
while (imap->literal_pending)
get_cmd_result(ctx, NULL);
- bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
- "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
- cmd->tag, cmd->cmd, cmd->cb.dlen);
+ if (!cmd->cb.data)
+ bufl = nfsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
+ else
+ bufl = nfsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
+ cmd->tag, cmd->cmd, cmd->cb.dlen,
+ CAP(LITERALPLUS) ? "+" : "");
+
if (Verbose) {
if (imap->num_in_progress)
printf("(%d in progress) ", imap->num_in_progress);
int gai;
char portstr[6];
- snprintf(portstr, sizeof(portstr), "%hu", srvc->port);
+ snprintf(portstr, sizeof(portstr), "%d", srvc->port);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
if (parse_commit(commit) != 0)
printf("(bad commit)\n");
else {
- const char *s;
- int len;
- for (s = commit->buffer; *s; s++)
- if (*s == '\n' && s[1] == '\n') {
- s += 2;
- break;
- }
- for (len = 0; s[len] && '\n' != s[len]; len++)
- ; /* do nothing */
- printf("%.*s\n", len, s);
+ const char *title;
+ int len = find_commit_subject(commit->buffer, &title);
+ if (len)
+ printf("%.*s\n", len, title);
}
}
}
}
if (sha_eq(common->object.sha1, merge->object.sha1)) {
- output(o, 0, "Already uptodate!");
+ output(o, 0, "Already up-to-date!");
*result = head;
return 1;
}
return 0;
}
+/*
+ * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Note: munges *name.
+ */
+static void try_remove_empty_parents(char *name)
+{
+ char *p, *q;
+ int i;
+ p = name;
+ for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
+ while (*p && *p != '/')
+ p++;
+ /* tolerate duplicate slashes; see check_ref_format() */
+ while (*p == '/')
+ p++;
+ }
+ for (q = p; *q; q++)
+ ;
+ while (1) {
+ while (q > p && *q != '/')
+ q--;
+ while (q > p && *(q-1) == '/')
+ q--;
+ if (q == p)
+ break;
+ *q = '\0';
+ if (rmdir(git_path("%s", name)))
+ break;
+ }
+}
+
/* make sure nobody touched the ref, and unlink */
static void prune_ref(struct ref_to_prune *r)
{
if (lock) {
unlink_or_warn(git_path("%s", r->name));
unlock_ref(lock);
+ try_remove_empty_parents(r->name);
}
}
size_t username_len = first_slash - username;
if (username_len == 0) {
const char *home = getenv("HOME");
+ if (!home)
+ goto return_null;
strbuf_add(&user_path, home, strlen(home));
} else {
struct passwd *pw = getpw_str(username, username_len);
int size = ondisk_ce_size(ce);
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
char *name;
+ int result;
ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
name = ondisk->name;
memcpy(name, ce->name, ce_namelen(ce));
- return ce_write(c, fd, ondisk, size);
+ result = ce_write(c, fd, ondisk, size);
+ free(ondisk);
+ return result;
}
int write_index(struct index_state *istate, int newfd)
return ret;
}
+/*
+ * People using contrib's git-new-workdir have .git/logs/refs ->
+ * /some/other/path/.git/logs/refs, and that may live on another device.
+ *
+ * IOW, to avoid cross device rename errors, the temporary renamed log must
+ * live into logs/refs.
+ */
+#define TMP_RENAMED_LOG "logs/refs/.tmp-renamed-log"
+
int rename_ref(const char *oldref, const char *newref, const char *logmsg)
{
static const char renamed_ref[] = "RENAMED-REF";
if (write_ref_sha1(lock, orig_sha1, logmsg))
return error("unable to save current sha1 in %s", renamed_ref);
- if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log")))
- return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
+ if (log && rename(git_path("logs/%s", oldref), git_path(TMP_RENAMED_LOG)))
+ return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
oldref, strerror(errno));
if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
}
retry:
- if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) {
+ if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newref))) {
if (errno==EISDIR || errno==ENOTDIR) {
/*
* rename(a, b) when b is an existing
}
goto retry;
} else {
- error("unable to move logfile tmp-renamed-log to logs/%s: %s",
+ error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
newref, strerror(errno));
goto rollback;
}
error("unable to restore logfile %s from %s: %s",
oldref, newref, strerror(errno));
if (!logmoved && log &&
- rename(git_path("tmp-renamed-log"), git_path("logs/%s", oldref)))
- error("unable to restore logfile %s from tmp-renamed-log: %s",
+ rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldref)))
+ error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s",
oldref, strerror(errno));
return 1;
rpc->len = n;
err |= post_rpc(rpc);
}
- strbuf_read(&rpc->result, client.out, 0);
close(client.in);
- close(client.out);
client.in = -1;
+ strbuf_read(&rpc->result, client.out, 0);
+
+ close(client.out);
client.out = -1;
err |= finish_command(&client);
goto out_stale;
break;
case 'D': /* we used to emit D but that was misguided. */
- goto out_stale;
- break;
case 'T': /* we used to emit T but nobody uses it. */
goto out_stale;
- break;
default:
error("unrecognized: %s", line);
break;
char path[PATH_MAX];
size_t len = strlen(suspect);
+ if (PATH_MAX <= len + strlen("/objects"))
+ die("Too long path: %.*s", 60, suspect);
strcpy(path, suspect);
if (getenv(DB_ENVIRONMENT)) {
if (access(getenv(DB_ENVIRONMENT), X_OK))
const char *gitdirenv;
const char *gitfile_dir;
int len, offset, ceil_offset, root_len;
- int current_device = 0, one_filesystem = 1;
+ dev_t current_device = 0;
+ int one_filesystem = 1;
struct stat buf;
/*
{
const unsigned char *repl = lookup_replace_object(sha1);
void *data = read_object(repl, type, size);
+ char *path;
/* die if we replaced an object with one that does not exist */
if (!data && repl != sha1)
sha1_to_hex(repl), sha1_to_hex(sha1));
/* legacy behavior is to die on corrupted objects */
- if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
- die("object %s is corrupted", sha1_to_hex(repl));
+ if (!data) {
+ if (has_loose_object(repl)) {
+ path = sha1_file_name(sha1);
+ die("loose object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
+ }
+ if (has_packed_and_bad(repl)) {
+ path = sha1_pack_name(sha1);
+ die("packed object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
+ }
+ }
if (replacement)
*replacement = repl;
return get_short_sha1(name, len, sha1, 0);
}
+/*
+ * This interprets names like ':/Initial revision of "git"' by searching
+ * through history and returning the first commit whose message starts
+ * the given regular expression.
+ *
+ * For future extension, ':/!' is reserved. If you want to match a message
+ * beginning with a '!', you have to repeat the exclamation mark.
+ */
+#define ONELINE_SEEN (1u<<20)
+
static int handle_one_ref(const char *path,
const unsigned char *sha1, int flag, void *cb_data)
{
if (object->type != OBJ_COMMIT)
return 0;
insert_by_date((struct commit *)object, list);
+ object->flags |= ONELINE_SEEN;
return 0;
}
-/*
- * This interprets names like ':/Initial revision of "git"' by searching
- * through history and returning the first commit whose message matches
- * the given regular expression.
- *
- * For future extension, ':/!' is reserved. If you want to match a message
- * beginning with a '!', you have to repeat the exclamation mark.
- */
-
-#define ONELINE_SEEN (1u<<20)
static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
{
struct commit_list *list = NULL, *backup = NULL, *l;
#include "revision.h"
#include "run-command.h"
#include "diffcore.h"
+#include "string-list.h"
+
+struct string_list config_name_for_path;
+struct string_list config_ignore_for_name;
static int add_submodule_odb(const char *path)
{
return ret;
}
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+ const char *path)
+{
+ struct string_list_item *path_option, *ignore_option;
+ path_option = unsorted_string_list_lookup(&config_name_for_path, path);
+ if (path_option) {
+ ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
+ if (ignore_option)
+ handle_ignore_submodules_arg(diffopt, ignore_option->util);
+ }
+}
+
+static int submodule_config(const char *var, const char *value, void *cb)
+{
+ if (!prefixcmp(var, "submodule."))
+ return parse_submodule_config_option(var, value);
+ return 0;
+}
+
+void gitmodules_config(void)
+{
+ const char *work_tree = get_git_work_tree();
+ if (work_tree) {
+ struct strbuf gitmodules_path = STRBUF_INIT;
+ strbuf_addstr(&gitmodules_path, work_tree);
+ strbuf_addstr(&gitmodules_path, "/.gitmodules");
+ git_config_from_file(submodule_config, gitmodules_path.buf, NULL);
+ strbuf_release(&gitmodules_path);
+ }
+}
+
+int parse_submodule_config_option(const char *var, const char *value)
+{
+ int len;
+ struct string_list_item *config;
+ struct strbuf submodname = STRBUF_INIT;
+
+ var += 10; /* Skip "submodule." */
+
+ len = strlen(var);
+ if ((len > 5) && !strcmp(var + len - 5, ".path")) {
+ strbuf_add(&submodname, var, len - 5);
+ config = unsorted_string_list_lookup(&config_name_for_path, value);
+ if (config)
+ free(config->util);
+ else
+ config = string_list_append(&config_name_for_path, xstrdup(value));
+ config->util = strbuf_detach(&submodname, NULL);
+ strbuf_release(&submodname);
+ } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
+ if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
+ strcmp(value, "all") && strcmp(value, "none")) {
+ warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
+ return 0;
+ }
+
+ strbuf_add(&submodname, var, len - 7);
+ config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
+ if (config)
+ free(config->util);
+ else
+ config = string_list_append(&config_ignore_for_name,
+ strbuf_detach(&submodname, NULL));
+ strbuf_release(&submodname);
+ config->util = xstrdup(value);
+ return 0;
+ }
+ return 0;
+}
+
void handle_ignore_submodules_arg(struct diff_options *diffopt,
const char *arg)
{
+ DIFF_OPT_CLR(diffopt, IGNORE_SUBMODULES);
+ DIFF_OPT_CLR(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+ DIFF_OPT_CLR(diffopt, IGNORE_DIRTY_SUBMODULES);
+
if (!strcmp(arg, "all"))
DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
else if (!strcmp(arg, "untracked"))
DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
else if (!strcmp(arg, "dirty"))
DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
- else
+ else if (strcmp(arg, "none"))
die("bad --ignore-submodules argument: %s", arg);
}
struct diff_options;
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+ const char *path);
+void gitmodules_config();
+int parse_submodule_config_option(const char *var, const char *value);
void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
void show_submodule_summary(FILE *f, const char *path,
unsigned char one[20], unsigned char two[20],
/trash directory*
/test-results
+/.prove
$(RM) -r 'trash directory'.* test-results
$(RM) t????/cvsroot/CVSROOT/?*
$(RM) -r valgrind/bin
+ $(RM) .prove
aggregate-results-and-cleanup: $(T)
$(MAKE) aggregate-results
$(MAKE) clean
aggregate-results:
- for f in test-results/t*-*; do \
+ for f in test-results/t*-*.counts; do \
echo "$$f"; \
done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
the tests.
*** t0000-basic.sh ***
- * ok 1: .git/objects should be empty after git-init in an empty repo.
- * ok 2: .git/objects should have 256 subdirectories.
- * ok 3: git-update-index without --add should fail adding.
+ ok 1 - .git/objects should be empty after git init in an empty repo.
+ ok 2 - .git/objects should have 3 subdirectories.
+ ok 3 - success is reported like this
...
- * ok 23: no diff after checkout and git-update-index --refresh.
- * passed all 23 test(s)
- *** t0100-environment-names.sh ***
- * ok 1: using old names should issue warnings.
- * ok 2: using old names but having new names should not issue warnings.
- ...
-
-Or you can run each test individually from command line, like
-this:
-
- $ sh ./t3001-ls-files-killed.sh
- * ok 1: git-update-index --add to add various paths.
- * ok 2: git-ls-files -k to show killed files.
- * ok 3: validate git-ls-files -k output.
- * passed all 3 test(s)
+ ok 43 - very long name in the index handled sanely
+ # fixed 1 known breakage(s)
+ # still have 1 known breakage(s)
+ # passed all remaining 42 test(s)
+ 1..43
+ *** t0001-init.sh ***
+ ok 1 - plain
+ ok 2 - plain with GIT_WORK_TREE
+ ok 3 - plain bare
+
+Since the tests all output TAP (see http://testanything.org) they can
+be run with any TAP harness. Here's an example of parallel testing
+powered by a recent version of prove(1):
+
+ $ prove --timer --jobs 15 ./t[0-9]*.sh
+ [19:17:33] ./t0005-signals.sh ................................... ok 36 ms
+ [19:17:33] ./t0022-crlf-rename.sh ............................... ok 69 ms
+ [19:17:33] ./t0024-crlf-archive.sh .............................. ok 154 ms
+ [19:17:33] ./t0004-unwritable.sh ................................ ok 289 ms
+ [19:17:33] ./t0002-gitfile.sh ................................... ok 480 ms
+ ===( 102;0 25/? 6/? 5/? 16/? 1/? 4/? 2/? 1/? 3/? 1... )===
+
+prove and other harnesses come with a lot of useful options. The
+--state option in particular is very useful:
+
+ # Repeat until no more failures
+ $ prove -j 15 --state=failed,save ./t[0-9]*.sh
+
+You can also run each test individually from command line, like this:
+
+ $ sh ./t3010-ls-files-killed-modified.sh
+ ok 1 - git update-index --add to add various paths.
+ ok 2 - git ls-files -k to show killed files.
+ ok 3 - validate git ls-files -k output.
+ ok 4 - git ls-files -m to show modified files.
+ ok 5 - validate git ls-files -m output.
+ # passed all 5 test(s)
+ 1..5
You can pass --verbose (or -v), --debug (or -d), and --immediate
(or -i) command line argument to the test, or by setting GIT_TEST_OPTS
- If the script is invoked with command line argument --help
(or -h), it shows the test_description and exits.
- - Creates an empty test directory with an empty .git/objects
- database and chdir(2) into it. This directory is 't/trash directory'
- if you must know, but I do not think you care.
+ - Creates an empty test directory with an empty .git/objects database
+ and chdir(2) into it. This directory is 't/trash
+ directory.$test_name_without_dotsh', with t/ subject to change by
+ the --root option documented above.
- Defines standard test helper functions for your scripts to
use. These functions are designed to make all scripts behave
consistently when command line arguments --verbose (or -v),
--debug (or -d), and --immediate (or -i) is given.
+Do's, don'ts & things to keep in mind
+-------------------------------------
+
+Here are a few examples of things you probably should and shouldn't do
+when writing tests.
+
+Do:
+
+ - Put all code inside test_expect_success and other assertions.
+
+ Even code that isn't a test per se, but merely some setup code
+ should be inside a test assertion.
+
+ - Chain your test assertions
+
+ Write test code like this:
+
+ git merge foo &&
+ git push bar &&
+ test ...
+
+ Instead of:
+
+ git merge hla
+ git push gh
+ test ...
+
+ That way all of the commands in your tests will succeed or fail. If
+ you must ignore the return value of something (e.g., the return
+ after unsetting a variable that was already unset is unportable) it's
+ best to indicate so explicitly with a semicolon:
+
+ unset HLAGH;
+ git merge hla &&
+ git push gh &&
+ test ...
+
+Don't:
+
+ - exit() within a <script> part.
+
+ The harness will catch this as a programming error of the test.
+ Use test_done instead if you need to stop the tests early (see
+ "Skipping tests" below).
+
+ - Break the TAP output
+
+ The raw output from your test may be interpreted by a TAP harness. TAP
+ harnesses will ignore everything they don't know about, but don't step
+ on their toes in these areas:
+
+ - Don't print lines like "$x..$y" where $x and $y are integers.
+
+ - Don't print lines that begin with "ok" or "not ok".
+
+ TAP harnesses expect a line that begins with either "ok" and "not
+ ok" to signal a test passed or failed (and our harness already
+ produces such lines), so your script shouldn't emit such lines to
+ their output.
+
+ You can glean some further possible issues from the TAP grammar
+ (see http://search.cpan.org/perldoc?TAP::Parser::Grammar#TAP_Grammar)
+ but the best indication is to just run the tests with prove(1),
+ it'll complain if anything is amiss.
+
+Keep in mind:
+
+ - Inside <script> part, the standard output and standard error
+ streams are discarded, and the test harness only reports "ok" or
+ "not ok" to the end user running the tests. Under --verbose, they
+ are shown to help debugging the tests.
+
+
+Skipping tests
+--------------
+
+If you need to skip all the remaining tests you should set skip_all
+and immediately call test_done. The string you give to skip_all will
+be used as an explanation for why the test was skipped. for instance:
+
+ if ! test_have_prereq PERL
+ then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+ fi
End with test_done
------------------
There are a handful helper functions defined in the test harness
library for your script to use.
- - test_expect_success <message> <script>
+ - test_expect_success [<prereq>] <message> <script>
- This takes two strings as parameter, and evaluates the
+ Usually takes two strings as parameter, and evaluates the
<script>. If it yields success, test is considered
successful. <message> should state what it is testing.
'git-write-tree should be able to write an empty tree.' \
'tree=$(git-write-tree)'
- - test_expect_failure <message> <script>
+ If you supply three parameters the first will be taken to be a
+ prerequisite, see the test_set_prereq and test_have_prereq
+ documentation below:
+
+ test_expect_success TTY 'git --paginate rev-list uses a pager' \
+ ' ... '
+
+ - test_expect_failure [<prereq>] <message> <script>
This is NOT the opposite of test_expect_success, but is used
to mark a test that demonstrates a known breakage. Unlike
success and "still broken" on failure. Failures from these
tests won't cause -i (immediate) to stop.
+ Like test_expect_success this function can optionally use a three
+ argument invocation with a prerequisite as the first argument.
+
+ - test_expect_code [<prereq>] <code> <message> <script>
+
+ Analogous to test_expect_success, but pass the test if it exits
+ with a given exit <code>
+
+ test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+
- test_debug <script>
This takes a single argument, <script>, and evaluates it only
Merges the given rev using the given message. Like test_commit,
creates a tag and calls test_tick before committing.
+ - test_set_prereq SOME_PREREQ
+
+ Set a test prerequisite to be used later with test_have_prereq. The
+ test-lib will set some prerequisites for you, e.g. PERL and PYTHON
+ which are derived from ./GIT-BUILD-OPTIONS (grep test_set_prereq
+ test-lib.sh for more). Others you can set yourself and use later
+ with either test_have_prereq directly, or the three argument
+ invocation of test_expect_success and test_expect_failure.
+
+ - test_have_prereq SOME PREREQ
+
+ Check if we have a prerequisite previously set with
+ test_set_prereq. The most common use of this directly is to skip
+ all the tests if we don't have some essential prerequisite:
+
+ if ! test_have_prereq PERL
+ then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+ fi
+
+ - test_external [<prereq>] <message> <external> <script>
+
+ Execute a <script> with an <external> interpreter (like perl). This
+ was added for tests like t9700-perl-git.sh which do most of their
+ work in an external test script.
+
+ test_external \
+ 'GitwebCache::*FileCache*' \
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9503/test_cache_interface.pl
+
+ If the test is outputting its own TAP you should set the
+ test_external_has_tap variable somewhere before calling the first
+ test_external* function. See t9700-perl-git.sh for an example.
+
+ # The external test will outputs its own plan
+ test_external_has_tap=1
+
+ - test_external_without_stderr [<prereq>] <message> <external> <script>
+
+ Like test_external but fail if there's any output on stderr,
+ instead of checking the exit code.
+
+ test_external_without_stderr \
+ 'Perl API' \
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
+
+ - test_must_fail <git-command>
+
+ Run a git command and ensure it fails in a controlled way. Use
+ this instead of "! <git-command>". When git-command dies due to a
+ segfault, test_must_fail diagnoses it as an error; "! <git-command>"
+ treats it as just another expected failure, which would let such a
+ bug go unnoticed.
+
+ - test_might_fail <git-command>
+
+ Similar to test_must_fail, but tolerate success, too. Use this
+ instead of "<git-command> || :" to catch failures due to segv.
+
+ - test_cmp <expected> <actual>
+
+ Check whether the content of the <actual> file matches the
+ <expected> file. This behaves like "cmp" but produces more
+ helpful output when the test is run with "-v" option.
+
+ - test_when_finished <script>
+
+ Prepend <script> to a list of commands to run to clean up
+ at the end of the current test. If some clean-up command
+ fails, the test will not pass.
+
+ Example:
+
+ test_expect_success 'branch pointing to non-commit' '
+ git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+ test_when_finished "git update-ref -d refs/heads/invalid" &&
+ ...
+ '
+
+
Tips for Writing Tests
----------------------
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping gitweb tests, perl not available'
+ skip_all='skipping gitweb tests, perl not available'
test_done
fi
perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
- say 'skipping gitweb tests, perl version is too old'
+ skip_all='skipping gitweb tests, perl version is too old'
test_done
}
if ! type cvs >/dev/null 2>&1
then
- say 'skipping cvsimport tests, cvs not found'
+ skip_all='skipping cvsimport tests, cvs not found'
test_done
fi
2.1 | 2.2*)
;;
'')
- say 'skipping cvsimport tests, cvsps not found'
+ skip_all='skipping cvsimport tests, cvsps not found'
test_done
;;
*)
- say 'skipping cvsimport tests, unsupported cvsps version'
+ skip_all='skipping cvsimport tests, unsupported cvsps version'
test_done
;;
esac
if test -n "$NO_SVN_TESTS"
then
- say 'skipping git svn tests, NO_SVN_TESTS defined'
+ skip_all='skipping git svn tests, NO_SVN_TESTS defined'
test_done
fi
if ! test_have_prereq PERL; then
- say 'skipping git svn tests, perl not available'
+ skip_all='skipping git svn tests, perl not available'
test_done
fi
GIT_DIR=$PWD/.git
GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
-PERL=${PERL:-perl}
svn >/dev/null 2>&1
if test $? -ne 1
then
- say 'skipping git svn tests, svn not found'
+ skip_all='skipping git svn tests, svn not found'
test_done
fi
svnconf=$PWD/svnconf
export svnconf
-$PERL -w -e "
+"$PERL_PATH" -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
if test $x -ne 0
then
if test $x -eq 42; then
- err='Perl SVN libraries must be >= 1.1.0'
+ skip_all='Perl SVN libraries must be >= 1.1.0'
elif test $x -eq 41; then
- err='svnadmin failed to create fsfs repository'
+ skip_all='svnadmin failed to create fsfs repository'
else
- err='Perl SVN libraries not found or unusable, skipping test'
+ skip_all='Perl SVN libraries not found or unusable'
fi
- say "$err"
test_done
fi
}
convert_to_rev_db () {
- $PERL -w -- - "$@" <<\EOF
+ "$PERL_PATH" -w -- - "$@" <<\EOF
use strict;
@ARGV == 2 or die "Usage: convert_to_rev_db <input> <output>";
open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]";
require_svnserve () {
if test -z "$SVNSERVE_PORT"
then
- say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
+ skip_all='skipping svnserve test. (set $SVNSERVE_PORT to enable)'
test_done
fi
}
if test -z "$GIT_TEST_HTTPD"
then
- say "skipping test, network testing disabled by default"
- say "(define GIT_TEST_HTTPD to enable)"
+ skip_all="Network testing disabled (define GIT_TEST_HTTPD to enable)"
test_done
fi
if ! test -x "$LIB_HTTPD_PATH"
then
- say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
+ skip_all="skipping test, no web server found at '$LIB_HTTPD_PATH'"
test_done
fi
then
if ! test $HTTPD_VERSION -ge 2
then
- say "skipping test, at least Apache version 2 is required"
+ skip_all="skipping test, at least Apache version 2 is required"
test_done
fi
if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
then
- say "Apache module directory not found. Skipping tests."
+ skip_all="Apache module directory not found. Skipping tests."
test_done
fi
>&3 2>&4
if test $? -ne 0
then
- say "skipping test, web server setup failed"
+ skip_all="skipping test, web server setup failed"
trap 'die' EXIT
test_done
fi
echo "changed" > path2 &&
git commit -a -m path2 --amend &&
- !(git push -v origin >output 2>&1) &&
+ test_must_fail git push -v origin >output 2>&1 &&
(cd "$REMOTE_REPO" &&
test $HEAD = $(git rev-parse --verify HEAD))
'
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping --patch tests, perl not available'
+ skip_all='skipping --patch tests, perl not available'
test_done
fi
test-sigchain >actual
case "$?" in
143) true ;; # POSIX w/ SIGTERM=15
+ 271) true ;; # ksh w/ SIGTERM=15
3) true ;; # Windows
*) false ;;
esac &&
check_parse() {
echo "$1 -> $2" >expect
- test_expect_${3:-success} "parse date ($1)" "
- test-date parse '$1' >actual &&
+ test_expect_${4:-success} "parse date ($1${3:+ TZ=$3})" "
+ TZ=${3:-$TZ} test-date parse '$1' >actual &&
test_cmp expect actual
"
}
check_parse 2008-02 bad
check_parse 2008-02-14 bad
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
check_approxidate() {
echo "$1 -> $2 +0000" >expect
test_expect_success \
'a/b (untracked) vs a, plus c/d case test.' \
- '! git read-tree -u -m "$treeH" "$treeM" &&
+ 'test_must_fail git read-tree -u -m "$treeH" "$treeM" &&
git ls-files --stage &&
test -f a/b'
git ls-files --stage | tee >treeMcheck.out &&
test_cmp treeM.out treeMcheck.out'
+test_expect_success '-m references the correct modified tree' '
+ echo >file-a &&
+ echo >file-b &&
+ git add file-a file-b &&
+ git commit -a -m "test for correct modified tree"
+ git branch initial-mod &&
+ echo b >file-b &&
+ git commit -a -m "B" &&
+ echo a >file-a &&
+ git add file-a &&
+ git ls-tree $(git write-tree) file-a >expect &&
+ git read-tree -m HEAD initial-mod &&
+ git ls-tree $(git write-tree) file-a >actual &&
+ test_cmp expect actual
+'
+
test_done
cd "$HERE" &&
git update-index --add one &&
case "`git ls-files`" in
- one) echo ok one ;;
+ one) echo pass one ;;
*) echo bad one; exit 1 ;;
esac &&
cd dir &&
git update-index --add two &&
case "`git ls-files`" in
- two) echo ok two ;;
+ two) echo pass two ;;
*) echo bad two; exit 1 ;;
esac &&
cd .. &&
case "`git ls-files`" in
- dir/two"$LF"one) echo ok both ;;
+ dir/two"$LF"one) echo pass both ;;
*) echo bad; exit 1 ;;
esac
'
echo a >>one &&
echo d >>dir/two &&
case "`git diff-files --name-only`" in
- dir/two"$LF"one) echo ok top ;;
+ dir/two"$LF"one) echo pass top ;;
*) echo bad top; exit 1 ;;
esac &&
# diff should not omit leading paths
cd dir &&
case "`git diff-files --name-only`" in
- dir/two"$LF"one) echo ok subdir ;;
+ dir/two"$LF"one) echo pass subdir ;;
*) echo bad subdir; exit 1 ;;
esac &&
case "`git diff-files --name-only .`" in
- dir/two) echo ok subdir limited ;;
+ dir/two) echo pass subdir limited ;;
*) echo bad subdir limited; exit 1 ;;
esac
'
git config --path path.trailingtilde "foo~" &&
test_cmp expect .git/config'
+if test "${HOME+set}"
+then
+ test_set_prereq HOMEVAR
+fi
+
cat >expect <<EOF
$HOME/
/dev/null
foo~
EOF
-test_expect_success 'get --path' '
+test_expect_success HOMEVAR 'get --path' '
git config --get --path path.home > result &&
git config --get --path path.normal >> result &&
git config --get --path path.trailingtilde >> result &&
test_cmp expect result
'
+cat >expect <<\EOF
+/dev/null
+foo~
+EOF
+
+test_expect_success 'get --path copes with unset $HOME' '
+ (
+ unset HOME;
+ test_must_fail git config --get --path path.home \
+ >result 2>msg &&
+ git config --get --path path.normal >>result &&
+ git config --get --path path.trailingtilde >>result
+ ) &&
+ grep "[Ff]ailed to expand.*~/" msg &&
+ test_cmp expect result
+'
+
rm .git/config
git config quote.leading " test"
# is a good candidate: exists on all unices, and it has permission
# anyway, so we don't create a security hole running the testsuite.
-if ! setfacl -m u:root:rwx .; then
- say "Skipping ACL tests: unable to use setfacl"
- test_done
+setfacl_out="$(setfacl -m u:root:rwx . 2>&1)"
+setfacl_ret=$?
+
+if [ $setfacl_ret != 0 ]; then
+ skip_all="Skipping ACL tests: unable to use setfacl (output: '$setfacl_out'; return code: '$setfacl_ret')"
+ test_done
fi
check_perms_and_acl () {
refname2=$(git check-ref-format --branch @{-2}) &&
test "$refname2" = master'
+test_expect_success 'check-ref-format --branch from subdir' '
+ mkdir subdir &&
+
+ T=$(git write-tree) &&
+ sha1=$(echo A | git commit-tree $T) &&
+ git update-ref refs/heads/master $sha1 &&
+ git update-ref refs/remotes/origin/master $sha1
+ git checkout master &&
+ git checkout origin/master &&
+ git checkout master &&
+ refname=$(
+ cd subdir &&
+ git check-ref-format --branch @{-1}
+ ) &&
+ test "$refname" = "$sha1"
+'
+
valid_ref_normalized() {
test_expect_success "ref name '$1' simplifies to '$2'" "
refname=\$(git check-ref-format --print '$1') &&
test_cmp expect output
'
+cat >expect <<EOF
+set -- --foo -- '--' 'arg' '--spam=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option with --' '
+ git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo -- arg --spam=ham <optionspec >output &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- 'arg' '--spam=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option without --' '
+ git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo arg --spam=ham <optionspec >output &&
+ test_cmp expect output
+'
+
test_done
}
if ! test_have_prereq POSIXPERM || ! [ -w / ]; then
- say "Dangerous test skipped. Read this test if you want to execute it"
+ skip_all="Dangerous test skipped. Read this test if you want to execute it"
test_done
fi
if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
- say "You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
+ skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
test_done
fi
if [ "$UID" = 0 ]; then
- say "No you can't run this with root"
+ skip_all="No you can't run this with root"
test_done
fi
if ! test_have_prereq SYMLINKS
then
- say "symbolic links not supported - skipping tests"
+ skip_all="symbolic links not supported - skipping tests"
test_done
fi
git checkout master &&
git config --unset core.logAllRefUpdates &&
git checkout --orphan delta &&
- ! test -f .git/logs/refs/heads/delta &&
- test_must_fail PAGER= git reflog show delta &&
+ test_must_fail git rev-parse --verify delta@{0} &&
git commit -m Delta &&
- test -f .git/logs/refs/heads/delta &&
- PAGER= git reflog show delta
+ git rev-parse --verify delta@{0}
'
test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
git checkout master &&
git config core.logAllRefUpdates false &&
git checkout --orphan epsilon &&
- ! test -f .git/logs/refs/heads/epsilon &&
- test_must_fail PAGER= git reflog show epsilon &&
+ test_must_fail git rev-parse --verify epsilon@{0} &&
git commit -m Epsilon &&
- ! test -f .git/logs/refs/heads/epsilon &&
- test_must_fail PAGER= git reflog show epsilon
+ test_must_fail git rev-parse --verify epsilon@{0}
'
test_expect_success '--orphan with -l makes reflog when core.logAllRefUpdates = false' '
git checkout master &&
git checkout -l --orphan zeta &&
- test -f .git/logs/refs/heads/zeta &&
- test_must_fail PAGER= git reflog show zeta &&
+ test_must_fail git rev-parse --verify zeta@{0} &&
git commit -m Zeta &&
- PAGER= git reflog show zeta
+ git rev-parse --verify zeta@{0}
'
test_expect_success 'giving up --orphan not committed when -l and core.logAllRefUpdates = false deletes reflog' '
git checkout master &&
git checkout -l --orphan eta &&
- test -f .git/logs/refs/heads/eta &&
- test_must_fail PAGER= git reflog show eta &&
+ test_must_fail git rev-parse --verify eta@{0} &&
git checkout master &&
- ! test -f .git/logs/refs/heads/eta &&
- test_must_fail PAGER= git reflog show eta
+ test_must_fail git rev-parse --verify eta@{0}
'
test_expect_success '--orphan is rejected with an existing name' '
--- /dev/null
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+#
+# Runs "git checkout" to switch to <branch>, testing that
+#
+# 1) we are on the specified branch, <branch>;
+# 2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+#
+# If <checkout options> is not specified, "git checkout" is run with -b.
+do_checkout() {
+ exp_branch=$1 &&
+ exp_ref="refs/heads/$exp_branch" &&
+
+ # if <sha> is not specified, use HEAD.
+ exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+ # default options for git checkout: -b
+ if [ -z "$3" ]; then
+ opts="-b"
+ else
+ opts="$3"
+ fi
+
+ git checkout $opts $exp_branch $exp_sha &&
+
+ test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+ test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty_unmergeable() {
+ ! git diff --exit-code >/dev/null
+}
+
+setup_dirty_unmergeable() {
+ echo >>file1 change2
+}
+
+test_dirty_mergeable() {
+ ! git diff --cached --exit-code >/dev/null
+}
+
+setup_dirty_mergeable() {
+ echo >file2 file2 &&
+ git add file2
+}
+
+test_expect_success 'setup' '
+ test_commit initial file1 &&
+ HEAD1=$(git rev-parse --verify HEAD) &&
+
+ test_commit change1 file1 &&
+ HEAD2=$(git rev-parse --verify HEAD) &&
+
+ git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch, set to HEAD' '
+ do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+ git checkout branch1 &&
+ git branch -D branch2 &&
+
+ do_checkout branch2 $HEAD1
+'
+
+test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_unmergeable &&
+ test_must_fail do_checkout branch2 $HEAD1 &&
+ test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '
+ # still dirty and on branch1
+ do_checkout branch2 $HEAD1 "-f -b" &&
+ test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 &&
+ test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '
+ # clean up from previous test
+ git reset --hard &&
+
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 "-f -b" &&
+ test_must_fail test_dirty_mergeable
+'
+
+test_expect_success 'checkout -b to an existing branch fails' '
+ git reset --hard HEAD &&
+
+ test_must_fail do_checkout branch2 $HEAD2
+'
+
+test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
+ git checkout branch1 &&
+
+ do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' '
+ git checkout $(git rev-parse --verify HEAD) &&
+
+ do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' '
+ git checkout branch1 &&
+
+ do_checkout branch2 $HEAD1 -B
+'
+
+test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' '
+ git checkout branch1 &&
+
+ setup_dirty_unmergeable &&
+ test_must_fail do_checkout branch2 $HEAD1 -B &&
+ test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '
+ # still dirty and on branch1
+ do_checkout branch2 $HEAD1 "-f -B" &&
+ test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '
+ git checkout branch1 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 -B &&
+ test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '
+ # clean up from previous test
+ git reset --hard &&
+
+ git checkout branch1 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 "-f -B" &&
+ test_must_fail test_dirty_mergeable
+'
+
+test_done
test_expect_success \
'the index entry must still be a symbolic link' '
case "`git ls-files --stage --cached symlink`" in
-120000" "*symlink) echo ok;;
+120000" "*symlink) echo pass;;
*) echo fail; git ls-files --stage --cached symlink; (exit 1);;
esac'
'
. ./test-lib.sh
-date >path0
-if test_have_prereq SYMLINKS
-then
- ln -s xyzzy path1
-else
- date > path1
-fi
-mkdir path2 path3 path4
-date >path2/file2
-date >path2-junk
-date >path3/file3
-date >path3-junk
-git update-index --add path3-junk path3/file3
-
-cat >expected1 <<EOF
-expected1
-expected2
-expected3
-output
-path0
-path1
-path2-junk
-path2/file2
-EOF
-sed -e 's|path2/file2|path2/|' <expected1 >expected2
-cat <expected2 >expected3
-echo path4/ >>expected2
-
-test_expect_success \
- 'git ls-files --others to show output.' \
- 'git ls-files --others >output'
-
-test_expect_success \
- 'git ls-files --others should pick up symlinks.' \
- 'test_cmp expected1 output'
+test_expect_success 'setup ' '
+ date >path0 &&
+ if test_have_prereq SYMLINKS
+ then
+ ln -s xyzzy path1
+ else
+ date >path1
+ fi &&
+ mkdir path2 path3 path4 &&
+ date >path2/file2 &&
+ date >path2-junk &&
+ date >path3/file3 &&
+ date >path3-junk &&
+ git update-index --add path3-junk path3/file3
+'
-test_expect_success \
- 'git ls-files --others --directory to show output.' \
- 'git ls-files --others --directory >output'
+test_expect_success 'setup: expected output' '
+ cat >expected1 <<-\EOF &&
+ expected1
+ expected2
+ expected3
+ output
+ path0
+ path1
+ path2-junk
+ path2/file2
+ EOF
+ sed -e "s|path2/file2|path2/|" <expected1 >expected2 &&
+ cp expected2 expected3 &&
+ echo path4/ >>expected2
+'
-test_expect_success \
- 'git ls-files --others --directory should not get confused.' \
- 'test_cmp expected2 output'
+test_expect_success 'ls-files --others' '
+ git ls-files --others >output &&
+ test_cmp expected1 output
+'
-test_expect_success \
- 'git ls-files --others --directory --no-empty-directory to show output.' \
- 'git ls-files --others --directory --no-empty-directory >output'
+test_expect_success 'ls-files --others --directory' '
+ git ls-files --others --directory >output &&
+ test_cmp expected2 output
+'
-test_expect_success \
- '--no-empty-directory hides empty directory' \
- 'test_cmp expected3 output'
+test_expect_success '--no-empty-directory hides empty directory' '
+ git ls-files --others --directory --no-empty-directory >output &&
+ test_cmp expected3 output
+'
test_done
git checkout master &&
git config --unset core.logAllRefUpdates &&
git checkout -b alpha &&
- test -f .git/logs/refs/heads/alpha &&
- PAGER= git reflog show alpha
+ git rev-parse --verify alpha@{0}
'
test_expect_success 'checkout -b does not make reflog when core.logAllRefUpdates = false' '
git checkout master &&
git config core.logAllRefUpdates false &&
git checkout -b beta &&
- ! test -f .git/logs/refs/heads/beta &&
- test_must_fail PAGER= git reflog show beta
+ test_must_fail git rev-parse --verify beta@{0}
'
test_expect_success 'checkout -b with -l makes reflog when core.logAllRefUpdates = false' '
git checkout master &&
git checkout -lb gamma &&
git config --unset core.logAllRefUpdates &&
- test -f .git/logs/refs/heads/gamma &&
- PAGER= git reflog show gamma
+ git rev-parse --verify gamma@{0}
'
test_expect_success 'avoid ambiguous track' '
! test -f .git/refs/heads/f
'
+test_expect_success 'see if git pack-refs --prune removes empty dirs' '
+ git branch r/s/t &&
+ git pack-refs --all --prune &&
+ ! test -e .git/refs/heads/r
+'
+
test_expect_success \
'git branch g should work when git branch g/h has been deleted' \
'git branch g/h &&
test -f "$p1" && cmp "$p0" "$p1" || {
# since FAT/NTFS does not allow tabs in filenames, skip this test
- say 'Your filesystem does not allow tabs in filenames, test skipped.'
+ skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
test_done
}
test_expect_success 'verify note removal with -F /dev/null' '
git log -4 > output &&
test_cmp expect-rm-F output &&
- ! git notes show
+ test_must_fail git notes show
'
test_expect_success 'do not create empty note with -m "" (setup)' '
test_expect_success 'verify non-creation of note with -m ""' '
git log -4 > output &&
test_cmp expect-rm-F output &&
- ! git notes show
+ test_must_fail git notes show
'
cat > expect-combine_m_and_F << EOF
test_expect_success 'verify note removal with "git notes remove"' '
git log -4 > output &&
test_cmp expect-rm-remove output &&
- ! git notes show HEAD^
+ test_must_fail git notes show HEAD^
'
cat > expect << EOF
git add a10 &&
test_tick &&
git commit -m 10th &&
- test_must_fail MSG="yet another note" git notes add -c deadbeef &&
+ (
+ MSG="yet another note" &&
+ export MSG &&
+ test_must_fail git notes add -c deadbeef
+ ) &&
test_must_fail git notes list HEAD
'
. ./test-lib.sh
test -z "$GIT_NOTES_TIMING_TESTS" && {
- say Skipping timing tests
+ skip_all="Skipping timing tests"
test_done
exit
}
test_expect_success 'verify that commits are gone' '
- ! git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
'
test_expect_success 'verify that notes are gone' '
- ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
'
test_expect_success 'verify that notes are gone' '
- ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
- ! git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+ test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
'
GIT_AUTHOR_EMAIL=bogus@email@address
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
-test_expect_success \
- 'prepare repository with topic branches' \
- 'git config core.logAllRefUpdates true &&
- echo First > A &&
- git update-index --add A &&
- git commit -m "Add A." &&
- git checkout -b my-topic-branch &&
- echo Second > B &&
- git update-index --add B &&
- git commit -m "Add B." &&
- git checkout -f master &&
- echo Third >> A &&
- git update-index A &&
- git commit -m "Modify A." &&
- git checkout -b side my-topic-branch &&
- echo Side >> C &&
- git add C &&
- git commit -m "Add C" &&
- git checkout -b nonlinear my-topic-branch &&
- echo Edit >> B &&
- git add B &&
- git commit -m "Modify B" &&
- git merge side &&
- git checkout -b upstream-merged-nonlinear &&
- git merge master &&
- git checkout -f my-topic-branch &&
- git tag topic
+test_expect_success 'prepare repository with topic branches' '
+ git config core.logAllRefUpdates true &&
+ echo First >A &&
+ git update-index --add A &&
+ git commit -m "Add A." &&
+ git checkout -b force-3way &&
+ echo Dummy >Y &&
+ git update-index --add Y &&
+ git commit -m "Add Y." &&
+ git checkout -b filemove &&
+ git reset --soft master &&
+ mkdir D &&
+ git mv A D/A &&
+ git commit -m "Move A." &&
+ git checkout -b my-topic-branch master &&
+ echo Second >B &&
+ git update-index --add B &&
+ git commit -m "Add B." &&
+ git checkout -f master &&
+ echo Third >>A &&
+ git update-index A &&
+ git commit -m "Modify A." &&
+ git checkout -b side my-topic-branch &&
+ echo Side >>C &&
+ git add C &&
+ git commit -m "Add C" &&
+ git checkout -b nonlinear my-topic-branch &&
+ echo Edit >>B &&
+ git add B &&
+ git commit -m "Modify B" &&
+ git merge side &&
+ git checkout -b upstream-merged-nonlinear &&
+ git merge master &&
+ git checkout -f my-topic-branch &&
+ git tag topic
'
test_expect_success 'rebase on dirty worktree' '
- echo dirty >> A &&
- test_must_fail git rebase master'
+ echo dirty >>A &&
+ test_must_fail git rebase master
+'
test_expect_success 'rebase on dirty cache' '
- git add A &&
- test_must_fail git rebase master'
+ git add A &&
+ test_must_fail git rebase master
+'
test_expect_success 'rebase against master' '
- git reset --hard HEAD &&
- git rebase master'
+ git reset --hard HEAD &&
+ git rebase master
+'
test_expect_success 'rebase against master twice' '
- git rebase master >out &&
- grep "Current branch my-topic-branch is up to date" out
+ git rebase master >out &&
+ grep "Current branch my-topic-branch is up to date" out
'
test_expect_success 'rebase against master twice with --force' '
- git rebase --force-rebase master >out &&
- grep "Current branch my-topic-branch is up to date, rebase forced" out
+ git rebase --force-rebase master >out &&
+ grep "Current branch my-topic-branch is up to date, rebase forced" out
'
test_expect_success 'rebase against master twice from another branch' '
- git checkout my-topic-branch^ &&
- git rebase master my-topic-branch >out &&
- grep "Current branch my-topic-branch is up to date" out
+ git checkout my-topic-branch^ &&
+ git rebase master my-topic-branch >out &&
+ grep "Current branch my-topic-branch is up to date" out
'
test_expect_success 'rebase fast-forward to master' '
- git checkout my-topic-branch^ &&
- git rebase my-topic-branch >out &&
- grep "Fast-forwarded HEAD to my-topic-branch" out
+ git checkout my-topic-branch^ &&
+ git rebase my-topic-branch >out &&
+ grep "Fast-forwarded HEAD to my-topic-branch" out
'
-test_expect_success \
- 'the rebase operation should not have destroyed author information' \
- '! (git log | grep "Author:" | grep "<>")'
+test_expect_success 'the rebase operation should not have destroyed author information' '
+ ! (git log | grep "Author:" | grep "<>")
+'
-test_expect_success \
- 'the rebase operation should not have destroyed author information (2)' \
- "git log -1 | grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'"
+test_expect_success 'the rebase operation should not have destroyed author information (2)' "
+ git log -1 |
+ grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'
+"
test_expect_success 'HEAD was detached during rebase' '
- test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
+ test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
'
test_expect_success 'rebase after merge master' '
- git reset --hard topic &&
- git merge master &&
- git rebase master &&
- ! (git show | grep "^Merge:")
+ git reset --hard topic &&
+ git merge master &&
+ git rebase master &&
+ ! (git show | grep "^Merge:")
'
test_expect_success 'rebase of history with merges is linearized' '
- git checkout nonlinear &&
- test 4 = $(git rev-list master.. | wc -l) &&
- git rebase master &&
- test 3 = $(git rev-list master.. | wc -l)
+ git checkout nonlinear &&
+ test 4 = $(git rev-list master.. | wc -l) &&
+ git rebase master &&
+ test 3 = $(git rev-list master.. | wc -l)
'
-test_expect_success \
- 'rebase of history with merges after upstream merge is linearized' '
- git checkout upstream-merged-nonlinear &&
- test 5 = $(git rev-list master.. | wc -l) &&
- git rebase master &&
- test 3 = $(git rev-list master.. | wc -l)
+test_expect_success 'rebase of history with merges after upstream merge is linearized' '
+ git checkout upstream-merged-nonlinear &&
+ test 5 = $(git rev-list master.. | wc -l) &&
+ git rebase master &&
+ test 3 = $(git rev-list master.. | wc -l)
'
test_expect_success 'rebase a single mode change' '
- git checkout master &&
- echo 1 > X &&
- git add X &&
- test_tick &&
- git commit -m prepare &&
- git checkout -b modechange HEAD^ &&
- echo 1 > X &&
- git add X &&
- test_chmod +x A &&
- test_tick &&
- git commit -m modechange &&
- GIT_TRACE=1 git rebase master
+ git checkout master &&
+ echo 1 >X &&
+ git add X &&
+ test_tick &&
+ git commit -m prepare &&
+ git checkout -b modechange HEAD^ &&
+ echo 1 >X &&
+ git add X &&
+ test_chmod +x A &&
+ test_tick &&
+ git commit -m modechange &&
+ GIT_TRACE=1 git rebase master
+'
+
+test_expect_success 'rebase is not broken by diff.renames' '
+ git config diff.renames copies &&
+ test_when_finished "git config --unset diff.renames" &&
+ git checkout filemove &&
+ GIT_TRACE=1 git rebase force-3way
+'
+
+test_expect_success 'setup: recover' '
+ test_might_fail git rebase --abort &&
+ git reset --hard &&
+ git checkout modechange
'
test_expect_success 'Show verbose error when HEAD could not be detached' '
- : > B &&
- test_must_fail git rebase topic 2> output.err > output.out &&
- grep "Untracked working tree file .B. would be overwritten" output.err
+ >B &&
+ test_must_fail git rebase topic 2>output.err >output.out &&
+ grep "Untracked working tree file .B. would be overwritten" output.err
'
rm -f B
test_expect_success 'dump usage when upstream arg is missing' '
- git checkout -b usage topic &&
- test_must_fail git rebase 2>error1 &&
- grep "[Uu]sage" error1 &&
- test_must_fail git rebase --abort 2>error2 &&
- grep "No rebase in progress" error2 &&
- test_must_fail git rebase --onto master 2>error3 &&
- grep "[Uu]sage" error3 &&
- ! grep "can.t shift" error3
+ git checkout -b usage topic &&
+ test_must_fail git rebase 2>error1 &&
+ grep "[Uu]sage" error1 &&
+ test_must_fail git rebase --abort 2>error2 &&
+ grep "No rebase in progress" error2 &&
+ test_must_fail git rebase --onto master 2>error3 &&
+ grep "[Uu]sage" error3 &&
+ ! grep "can.t shift" error3
'
test_expect_success 'rebase -q is quiet' '
- git checkout -b quiet topic &&
- git rebase -q master > output.out 2>&1 &&
- test ! -s output.out
+ git checkout -b quiet topic &&
+ git rebase -q master >output.out 2>&1 &&
+ test ! -s output.out
'
test_expect_success 'Rebase a commit that sprinkles CRs in' '
git rebase --merge side
'
+test_expect_success 'rebase -Xtheirs' '
+ git checkout -b conflicting master~2 &&
+ echo "AB $T" >> original &&
+ git commit -mconflicting original &&
+ git rebase -Xtheirs master &&
+ grep AB original &&
+ ! grep 11 original
+'
+
test_expect_success 'merge and rebase should match' '
git diff-tree -r test-rebase test-merge >difference &&
if test -s difference
test_cmp empty out
'
+test_expect_success 'set up commits with funny messages' '
+ git checkout -b funny A &&
+ echo >>file1 &&
+ test_tick &&
+ git commit -a -m "end with slash\\" &&
+ echo >>file1 &&
+ test_tick &&
+ git commit -a -m "something (\000) that looks like octal" &&
+ echo >>file1 &&
+ test_tick &&
+ git commit -a -m "something (\n) that looks like a newline" &&
+ echo >>file1 &&
+ test_tick &&
+ git commit -a -m "another commit"
+'
+
+test_expect_success 'rebase-i history with funny messages' '
+ git rev-list A..funny >expect &&
+ test_tick &&
+ FAKE_LINES="1 2 3 4" git rebase -i A &&
+ git rev-list A.. >actual &&
+ test_cmp expect actual
+'
+
test_done
# G2 = same changes as G
test_expect_success 'skip same-resolution merges with -p' '
git checkout H &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit L file1 23 &&
git checkout I &&
test_commit G2 file1 3 &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit J file1 23 &&
test_commit K file7 file7 &&
git rebase -i -p L &&
# G2 = different changes as G
test_expect_success 'keep different-resolution merges with -p' '
git checkout H &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit L2 file1 23 &&
git checkout I &&
test_commit G3 file1 4 &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit J2 file1 24 &&
test_commit K2 file7 file7 &&
test_must_fail git rebase -i -p L2 &&
--- /dev/null
+#!/bin/sh
+
+test_description='git rebase --continue tests'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+test_expect_success 'setup' '
+ test_commit "commit-new-file-F1" F1 1 &&
+ test_commit "commit-new-file-F2" F2 2 &&
+
+ git checkout -b topic HEAD^ &&
+ test_commit "commit-new-file-F2-on-topic-branch" F2 22 &&
+
+ git checkout master
+'
+
+test_expect_success 'interactive rebase --continue works with touched file' '
+ rm -fr .git/rebase-* &&
+ git reset --hard &&
+ git checkout master &&
+
+ FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+ test-chmtime =-60 F1 &&
+ git rebase --continue
+'
+
+test_expect_success 'non-interactive rebase --continue works with touched file' '
+ rm -fr .git/rebase-* &&
+ git reset --hard &&
+ git checkout master &&
+
+ test_must_fail git rebase --onto master master topic &&
+ echo "Resolved" >F2 &&
+ git add F2 &&
+ test-chmtime =-60 F1 &&
+ git rebase --continue
+'
+
+test_done
git checkout -b empty-branch &&
test_tick &&
- git commit --allow-empty -m "empty"
+ git commit --allow-empty -m "empty" &&
+
+ echo third >> file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit --allow-empty-message -m ""
'
test_expect_success 'cherry-pick an empty commit' '
+ git checkout master && {
+ git cherry-pick empty-branch^
+ test "$?" = 1
+ }
+'
+
+test_expect_success 'index lockfile was removed' '
+
+ test ! -f .git/index.lock
+
+'
+
+test_expect_success 'cherry-pick a commit with an empty message' '
git checkout master && {
git cherry-pick empty-branch
test "$?" = 1
then
test_set_prereq RO_DIR
else
- say 'skipping removal failure test (perhaps running as root?)'
+ skip_all='skipping removal failure test (perhaps running as root?)'
fi
chmod 775 .
rm -f test-file
chmod 755 xfoo1 &&
git add xfoo1 &&
case "`git ls-files --stage xfoo1`" in
- 100644" "*xfoo1) echo ok;;
+ 100644" "*xfoo1) echo pass;;
*) echo fail; git ls-files --stage xfoo1; (exit 1);;
esac'
ln -s foo xfoo1 &&
git add xfoo1 &&
case "`git ls-files --stage xfoo1`" in
- 120000" "*xfoo1) echo ok;;
+ 120000" "*xfoo1) echo pass;;
*) echo fail; git ls-files --stage xfoo1; (exit 1);;
esac
'
chmod 755 xfoo2 &&
git update-index --add xfoo2 &&
case "`git ls-files --stage xfoo2`" in
- 100644" "*xfoo2) echo ok;;
+ 100644" "*xfoo2) echo pass;;
*) echo fail; git ls-files --stage xfoo2; (exit 1);;
esac'
ln -s foo xfoo2 &&
git update-index --add xfoo2 &&
case "`git ls-files --stage xfoo2`" in
- 120000" "*xfoo2) echo ok;;
+ 120000" "*xfoo2) echo pass;;
*) echo fail; git ls-files --stage xfoo2; (exit 1);;
esac
'
ln -s xfoo2 xfoo3 &&
git update-index --add xfoo3 &&
case "`git ls-files --stage xfoo3`" in
- 120000" "*xfoo3) echo ok;;
+ 120000" "*xfoo3) echo pass;;
*) echo fail; git ls-files --stage xfoo3; (exit 1);;
esac'
test -z "`git diff-index HEAD -- foo`" &&
git read-tree HEAD &&
case "`git diff-index HEAD -- foo`" in
- :100644" "*"M foo") echo ok;;
+ :100644" "*"M foo") echo pass;;
*) echo fail; (exit 1);;
esac &&
git add --refresh -- foo &&
! (git ls-files | grep "non-existent")
'
+test_expect_success 'git add --dry-run of existing changed file' "
+ echo new >>track-this &&
+ git add --dry-run track-this >actual 2>&1 &&
+ echo \"add 'track-this'\" | test_cmp - actual
+"
+
+test_expect_success 'git add --dry-run of non-existing file' "
+ echo ignored-file >>.gitignore &&
+ test_must_fail git add --dry-run track-this ignored-file >actual 2>&1 &&
+ echo \"fatal: pathspec 'ignored-file' did not match any files\" | test_cmp - actual
+"
+
+cat >expect.err <<\EOF
+The following paths are ignored by one of your .gitignore files:
+ignored-file
+Use -f if you really want to add them.
+fatal: no files added
+EOF
+cat >expect.out <<\EOF
+add 'track-this'
+EOF
+
+test_expect_success 'git add --dry-run --ignore-missing of non-existing file' '
+ test_must_fail git add --dry-run --ignore-missing track-this ignored-file >actual.out 2>actual.err &&
+ test_cmp expect.out actual.out &&
+ test_cmp expect.err actual.err
+'
+
test_done
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping git add -i tests, perl not available'
+ skip_all='skipping git add -i tests, perl not available'
test_done
fi
if test "$(git config --bool core.filemode)" = false
then
- say 'skipping filemode tests (filesystem does not properly support modes)'
+ say '# skipping filemode tests (filesystem does not properly support modes)'
else
test_set_prereq FILEMODE
fi
echo foo 2>/dev/null > "Name and an${HT}HT"
test -f "Name and an${HT}HT" || {
# since FAT/NTFS does not allow tabs in filenames, skip this test
- say 'Your filesystem does not allow tabs in filenames, test skipped.'
+ skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
test_done
}
if ! test_have_prereq SYMLINKS
then
- say 'Symbolic links not supported, skipping tests.'
+ skip_all='Symbolic links not supported, skipping tests.'
test_done
fi
if ! test_have_prereq SYMLINKS
then
- say 'Symbolic links not supported, skipping tests.'
+ skip_all='Symbolic links not supported, skipping tests.'
test_done
fi
P3='pathname
with LF'
: 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" || {
- say 'Your filesystem does not allow tabs in filenames, test skipped.'
+ skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
test_done
}
if ! test_have_prereq SYMLINKS
then
- say 'Symbolic links not supported, skipping tests.'
+ skip_all='Symbolic links not supported, skipping tests.'
test_done
fi
! test -s actual4
'
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
+ git config diff.ignoreSubmodules all &&
+ git diff HEAD >actual &&
+ ! test -s actual &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config submodule.subname.ignore all &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ sed -e "1,/^@@/d" actual3 >actual3.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual3.body &&
+ git config submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git diff HEAD --ignore-submodules=none >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname &&
+ git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
+ git config diff.ignoreSubmodules dirty &&
+ git diff HEAD >actual &&
+ ! test -s actual &&
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config -f .gitmodules submodule.subname.ignore all &&
+ git config -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config -f .gitmodules submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ sed -e "1,/^@@/d" actual3 >actual3.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual3.body &&
+ git config -f .gitmodules submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ git config --unset diff.ignoreSubmodules &&
+ rm .gitmodules
+'
+
test_expect_success 'git diff HEAD with dirty submodule (index, refs match)' '
(
cd sub &&
! test -s actual4
'
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
+ git config submodule.subname.ignore all &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ ! test -s actual3 &&
+ git config submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git diff --ignore-submodules=none HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
+ git config --add -f .gitmodules submodule.subname.ignore all &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config -f .gitmodules submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ ! test -s actual3 &&
+ git config -f .gitmodules submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ rm .gitmodules
+'
+
+test_expect_success 'git diff between submodule commits' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git diff --ignore-submodules HEAD^..HEAD >actual &&
+ ! test -s actual
+'
+
+test_expect_success 'git diff between submodule commits [.git/config]' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config submodule.subname.ignore dirty &&
+ git config submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config submodule.subname.ignore all &&
+ git diff HEAD^..HEAD >actual &&
+ ! test -s actual &&
+ git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff between submodule commits [.gitmodules]' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config -f .gitmodules submodule.subname.ignore all &&
+ git diff HEAD^..HEAD >actual &&
+ ! test -s actual &&
+ git config submodule.subname.ignore dirty &&
+ git config submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ rm .gitmodules
+'
+
test_expect_success 'git diff (empty submodule dir)' '
: >empty &&
rm -rf sub/* sub/.git &&
--- /dev/null
+#!/bin/sh
+
+test_description='diff --relative tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m empty &&
+ echo content >file1 &&
+ mkdir subdir &&
+ echo other content >subdir/file2 &&
+ git add . &&
+ git commit -m one
+'
+
+check_diff() {
+expect=$1; shift
+cat >expected <<EOF
+diff --git a/$expect b/$expect
+new file mode 100644
+index 0000000..25c05ef
+--- /dev/null
++++ b/$expect
+@@ -0,0 +1 @@
++other content
+EOF
+test_expect_success "-p $*" "
+ git diff -p $* HEAD^ >actual &&
+ test_cmp expected actual
+"
+}
+
+check_stat() {
+expect=$1; shift
+cat >expected <<EOF
+ $expect | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+EOF
+test_expect_success "--stat $*" "
+ git diff --stat $* HEAD^ >actual &&
+ test_cmp expected actual
+"
+}
+
+check_raw() {
+expect=$1; shift
+cat >expected <<EOF
+:000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A $expect
+EOF
+test_expect_success "--raw $*" "
+ git diff --no-abbrev --raw $* HEAD^ >actual &&
+ test_cmp expected actual
+"
+}
+
+for type in diff stat raw; do
+ check_$type file2 --relative=subdir/
+ check_$type file2 --relative=subdir
+ check_$type dir/file2 --relative=sub
+done
+
+test_done
if ! test_have_prereq SYMLINKS
then
- say 'Symbolic links not supported, skipping tests.'
+ skip_all='Symbolic links not supported, skipping tests.'
test_done
fi
if ! test_have_prereq SYMLINKS
then
- say 'Symbolic links not supported, skipping tests.'
+ skip_all='Symbolic links not supported, skipping tests.'
test_done
fi
if ! test_have_prereq SYMLINKS
then
- say 'Symbolic links not supported, skipping tests.'
+ skip_all='Symbolic links not supported, skipping tests.'
test_done
fi
. ./test-lib.sh
-cat >msg <<EOF
-second
-
-Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
-eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
-voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
-kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
-ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
-tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
-vero eos et accusam et justo duo dolores et ea rebum.
-
- Duis autem vel eum iriure dolor in hendrerit in vulputate velit
- esse molestie consequat, vel illum dolore eu feugiat nulla facilisis
- at vero eros et accumsan et iusto odio dignissim qui blandit
- praesent luptatum zzril delenit augue duis dolore te feugait nulla
- facilisi.
-
-
-Lorem ipsum dolor sit amet,
-consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
-laoreet dolore magna aliquam erat volutpat.
-
- git
- ---
- +++
-
-Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
-lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
-dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
-dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
-dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
-feugait nulla facilisi.
-EOF
-
-cat >failmail <<EOF
-From foo@example.com Fri May 23 10:43:49 2008
-From: foo@example.com
-To: bar@example.com
-Subject: Re: [RFC/PATCH] git-foo.sh
-Date: Fri, 23 May 2008 05:23:42 +0200
-
-Sometimes we have to find out that there's nothing left.
-
-EOF
-
-cat >pine <<EOF
-From MAILER-DAEMON Fri May 23 10:43:49 2008
-Date: 23 May 2008 05:23:42 +0200
-From: Mail System Internal Data <MAILER-DAEMON@example.com>
-Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
-Message-ID: <foo-0001@example.com>
-
-This text is part of the internal format of your mail folder, and is not
-a real message. It is created automatically by the mail system software.
-If deleted, important folder data will be lost, and it will be re-created
-with the data reset to initial values.
-
-EOF
-
-echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected
+test_expect_success 'setup: messages' '
+ cat >msg <<-\EOF &&
+ second
+
+ Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
+ eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
+ voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
+ kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
+ ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+ tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+ vero eos et accusam et justo duo dolores et ea rebum.
+
+ EOF
+ q_to_tab <<-\EOF >>msg &&
+ QDuis autem vel eum iriure dolor in hendrerit in vulputate velit
+ Qesse molestie consequat, vel illum dolore eu feugiat nulla facilisis
+ Qat vero eros et accumsan et iusto odio dignissim qui blandit
+ Qpraesent luptatum zzril delenit augue duis dolore te feugait nulla
+ Qfacilisi.
+ EOF
+ cat >>msg <<-\EOF &&
+
+ Lorem ipsum dolor sit amet,
+ consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
+ laoreet dolore magna aliquam erat volutpat.
+
+ git
+ ---
+ +++
+
+ Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
+ dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
+ dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
+ dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
+ feugait nulla facilisi.
+ EOF
+
+ cat >failmail <<-\EOF &&
+ From foo@example.com Fri May 23 10:43:49 2008
+ From: foo@example.com
+ To: bar@example.com
+ Subject: Re: [RFC/PATCH] git-foo.sh
+ Date: Fri, 23 May 2008 05:23:42 +0200
+
+ Sometimes we have to find out that there'\''s nothing left.
+
+ EOF
+
+ cat >pine <<-\EOF &&
+ From MAILER-DAEMON Fri May 23 10:43:49 2008
+ Date: 23 May 2008 05:23:42 +0200
+ From: Mail System Internal Data <MAILER-DAEMON@example.com>
+ Subject: DON'\''T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
+ Message-ID: <foo-0001@example.com>
+
+ This text is part of the internal format of your mail folder, and is not
+ a real message. It is created automatically by the mail system software.
+ If deleted, important folder data will be lost, and it will be re-created
+ with the data reset to initial values.
+
+ EOF
+
+ signoff="Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+'
test_expect_success setup '
echo hello >file &&
test_tick &&
git commit -m first &&
git tag first &&
+
echo world >>file &&
git add file &&
test_tick &&
git commit -s -F msg &&
git tag second &&
+
git format-patch --stdout first >patch1 &&
{
echo "X-Fake-Field: Line One" &&
echo "X-Fake-Field: Line Three" &&
git format-patch --stdout first | sed -e "1d"
} | append_cr >patch1-crlf.eml &&
+
sed -n -e "3,\$p" msg >file &&
git add file &&
test_tick &&
git commit -m third &&
+
git format-patch --stdout first >patch2 &&
+
git checkout -b lorem &&
sed -n -e "11,\$p" msg >file &&
head -n 9 msg >>file &&
test_tick &&
git commit -a -m "moved stuff" &&
+
echo goodbye >another &&
git add another &&
test_tick &&
git commit -m "added another file" &&
- git format-patch --stdout master >lorem-move.patch
-'
-# reset time
-unset test_tick
-test_tick
+ git format-patch --stdout master >lorem-move.patch &&
+
+ git checkout -b rename &&
+ git mv file renamed &&
+ git commit -m "renamed a file" &&
+
+ git format-patch -M --stdout lorem >rename.patch &&
+
+ git reset --soft lorem^ &&
+ git commit -m "renamed a file and added another" &&
+
+ git format-patch -M --stdout lorem^ >rename-add.patch &&
+
+ # reset time
+ unset test_tick &&
+ test_tick
+'
test_expect_success 'am applies patch correctly' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am <patch1 &&
! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ 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 'am applies patch e-mail not in a mbox' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch1.eml &&
! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ 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 'am applies patch e-mail not in a mbox with CRLF' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch1-crlf.eml &&
! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ git diff --exit-code second &&
test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
-GIT_AUTHOR_NAME="Another Thor"
-GIT_AUTHOR_EMAIL="a.thor@example.com"
-GIT_COMMITTER_NAME="Co M Miter"
-GIT_COMMITTER_EMAIL="c.miter@example.com"
-export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+test_expect_success 'setup: new author and committer' '
+ GIT_AUTHOR_NAME="Another Thor" &&
+ GIT_AUTHOR_EMAIL="a.thor@example.com" &&
+ GIT_COMMITTER_NAME="Co M Miter" &&
+ GIT_COMMITTER_EMAIL="c.miter@example.com" &&
+ export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+'
compare () {
- test "$(git cat-file commit "$2" | grep "^$1 ")" = \
- "$(git cat-file commit "$3" | grep "^$1 ")"
+ a=$(git cat-file commit "$2" | grep "^$1 ") &&
+ b=$(git cat-file commit "$3" | grep "^$1 ") &&
+ test "$a" = "$b"
}
test_expect_success 'am changes committer and keeps author' '
test_tick &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch2 &&
! test -d .git/rebase-apply &&
test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
- test -z "$(git diff master..HEAD)" &&
- test -z "$(git diff master^..HEAD^)" &&
+ git diff --exit-code master..HEAD &&
+ git diff --exit-code master^..HEAD^ &&
compare author master HEAD &&
compare author master^ HEAD^ &&
test "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
'
test_expect_success 'am --signoff adds Signed-off-by: line' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout -b master2 first &&
git am --signoff <patch2 &&
+ printf "%s\n" "$signoff" >expected &&
echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >>expected &&
git cat-file commit HEAD^ | grep "Signed-off-by:" >actual &&
- test_cmp actual expected &&
+ test_cmp expected actual &&
echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
git cat-file commit HEAD | grep "Signed-off-by:" >actual &&
- test_cmp actual expected
+ test_cmp expected actual
'
test_expect_success 'am stays in branch' '
- test "refs/heads/master2" = "$(git symbolic-ref HEAD)"
+ echo refs/heads/master2 >expected &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expected actual
'
test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
git format-patch --stdout HEAD^ >patch3 &&
sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout HEAD^ &&
git am --signoff patch4 &&
- test "$(git cat-file commit HEAD | grep -c "^Signed-off-by:")" -eq 1
+ git cat-file commit HEAD >actual &&
+ test $(grep -c "^Signed-off-by:" actual) -eq 1
'
test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
- test "$(git rev-parse HEAD)" = "$(git rev-parse master2)"
+ git rev-parse HEAD >expected &&
+ git rev-parse master2 >actual &&
+ test_cmp expected actual
'
test_expect_success 'am --keep really keeps the subject' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout HEAD^ &&
git am --keep patch4 &&
! test -d .git/rebase-apply &&
- git cat-file commit HEAD |
- fgrep "Re: Re: Re: [PATCH 1/5 v2] third"
+ git cat-file commit HEAD >actual &&
+ grep "Re: Re: Re: \[PATCH 1/5 v2\] third" actual
'
test_expect_success 'am -3 falls back to 3-way merge' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout -b lorem2 master2 &&
sed -n -e "3,\$p" msg >file &&
head -n 9 msg >>file &&
git commit -m "copied stuff" &&
git am -3 lorem-move.patch &&
! test -d .git/rebase-apply &&
- test -z "$(git diff lorem)"
+ git diff --exit-code lorem
+'
+
+test_expect_success 'am can rename a file' '
+ grep "^rename from" rename.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am rename.patch &&
+ ! test -d .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file' '
+ grep "^rename from" rename.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am -3 rename.patch &&
+ ! test -d .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file after falling back to 3-way merge' '
+ grep "^rename from" rename-add.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am -3 rename-add.patch &&
+ ! test -d .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
'
test_expect_success 'am -3 -q is quiet' '
+ rm -fr .git/rebase-apply &&
+ git checkout -f lorem2 &&
git reset master2 --hard &&
sed -n -e "3,\$p" msg >file &&
head -n 9 msg >>file &&
git add file &&
test_tick &&
git commit -m "copied stuff" &&
- git am -3 -q lorem-move.patch > output.out 2>&1 &&
+ git am -3 -q lorem-move.patch >output.out 2>&1 &&
! test -s output.out
'
test_expect_success 'am pauses on conflict' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout lorem2^^ &&
test_must_fail git am lorem-move.patch &&
test -d .git/rebase-apply
'
test_expect_success 'am --skip works' '
+ echo goodbye >expected &&
git am --skip &&
! test -d .git/rebase-apply &&
- test -z "$(git diff lorem2^^ -- file)" &&
- test goodbye = "$(cat another)"
+ git diff --exit-code lorem2^^ -- file &&
+ test_cmp expected another
'
test_expect_success 'am --resolved works' '
+ echo goodbye >expected &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout lorem2^^ &&
test_must_fail git am lorem-move.patch &&
test -d .git/rebase-apply &&
git add file &&
git am --resolved &&
! test -d .git/rebase-apply &&
- test goodbye = "$(cat another)"
+ test_cmp expected another
'
test_expect_success 'am takes patches from a Pine mailbox' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
cat pine patch1 | git am &&
! test -d .git/rebase-apply &&
- test -z "$(git diff master^..HEAD)"
+ git diff --exit-code master^..HEAD
'
test_expect_success 'am fails on mail without patch' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
test_must_fail git am <failmail &&
- rm -r .git/rebase-apply/
+ git am --abort &&
+ ! test -d .git/rebase-apply
'
test_expect_success 'am fails on empty patch' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
echo "---" >>failmail &&
test_must_fail git am <failmail &&
git am --skip &&
test_expect_success 'am works from stdin in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
(
mkdir -p subdir &&
cd subdir &&
git am <../patch1
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am works from file (relative path given) in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
(
mkdir -p subdir &&
cd subdir &&
git am ../patch1
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am works from file (absolute path given) in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
P=$(pwd) &&
(
cd subdir &&
git am "$P/patch1"
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am --committer-date-is-author-date' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am --committer-date-is-author-date patch1 &&
git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
- at=$(sed -ne "/^author /s/.*> //p" head1) &&
- ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
- test "$at" = "$ct"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ sed -ne "/^committer /s/.*> //p" head1 >ct &&
+ test_cmp at ct
'
test_expect_success 'am without --committer-date-is-author-date' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am patch1 &&
git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
- at=$(sed -ne "/^author /s/.*> //p" head1) &&
- ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
- test "$at" != "$ct"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ sed -ne "/^committer /s/.*> //p" head1 >ct &&
+ ! test_cmp at ct
'
# This checks for +0000 because TZ is set to UTC and that should
# by test_tick that uses -0700 timezone; if this feature does not
# work, we will see that instead of +0000.
test_expect_success 'am --ignore-date' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am --ignore-date patch1 &&
git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
- at=$(sed -ne "/^author /s/.*> //p" head1) &&
- echo "$at" | grep "+0000"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ grep "+0000" at
'
test_expect_success 'am into an unborn branch' '
+ git rev-parse first^{tree} >expected &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
rm -fr subdir &&
- mkdir -p subdir &&
+ mkdir subdir &&
git format-patch --numbered-files -o subdir -1 first &&
(
cd subdir &&
git init &&
git am 1
) &&
- result=$(
- cd subdir && git rev-parse HEAD^{tree}
+ (
+ cd subdir &&
+ git rev-parse HEAD^{tree} >../actual
) &&
- test "z$result" = "z$(git rev-parse first^{tree})"
+ test_cmp expected actual
'
test_expect_success 'am newline in subject' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
- sed -e "s/second/second \\\n foo/" patch1 > patchnl &&
- git am < patchnl > output.out 2>&1 &&
+ sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
+ git am <patchnl >output.out 2>&1 &&
grep "^Applying: second \\\n foo$" output.out
'
test_expect_success 'am -q is quiet' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
- git am -q < patch1 > output.out 2>&1 &&
+ git am -q <patch1 >output.out 2>&1 &&
! test -s output.out
'
test_must_fail git am$with3 --skip >output &&
test "$(grep "^Applying" output)" = "Applying: 6" &&
test_cmp file-2-expect file-2 &&
- test ! -f .git/rr-cache/MERGE_RR
+ test ! -f .git/MERGE_RR
'
test_expect_success "am --abort goes back after failed am$with3" '
test_cmp expect actual &&
test_cmp file-2-expect file-2 &&
git diff-index --exit-code --cached HEAD &&
- test ! -f .git/rr-cache/MERGE_RR
+ test ! -f .git/MERGE_RR
'
done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2010 Will Palmer
+#
+
+test_description='git merge-tree'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit "initial" "initial-file" "initial"
+'
+
+test_expect_success 'file add A, !B' '
+ cat >expected <<\EXPECTED &&
+added in remote
+ their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+@@ -0,0 +1 @@
++AAA
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "add-a-not-b" "ONE" "AAA" &&
+ git merge-tree initial initial add-a-not-b >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file add !A, B' '
+ cat >expected <<\EXPECTED &&
+added in local
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "add-not-a-b" "ONE" "AAA" &&
+ git merge-tree initial add-not-a-b initial >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file add A, B (same)' '
+ cat >expected <<\EXPECTED &&
+added in both
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "add-a-b-same-A" "ONE" "AAA" &&
+ git reset --hard initial &&
+ test_commit "add-a-b-same-B" "ONE" "AAA" &&
+ git merge-tree initial add-a-b-same-A add-a-b-same-B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file add A, B (different)' '
+ cat >expected <<\EXPECTED &&
+added in both
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ their 100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+@@ -1 +1,5 @@
++<<<<<<< .our
+ AAA
++=======
++BBB
++>>>>>>> .their
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "add-a-b-diff-A" "ONE" "AAA" &&
+ git reset --hard initial &&
+ test_commit "add-a-b-diff-B" "ONE" "BBB" &&
+ git merge-tree initial add-a-b-diff-A add-a-b-diff-B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, !B' '
+ cat >expected <<\EXPECTED &&
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-not-b" "initial-file" "BBB" &&
+ git merge-tree initial change-a-not-b initial >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change !A, B' '
+ cat >expected <<\EXPECTED &&
+merged
+ result 100644 ba629238ca89489f2b350e196ca445e09d8bb834 initial-file
+ our 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+@@ -1 +1 @@
+-initial
++BBB
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-not-a-b" "initial-file" "BBB" &&
+ git merge-tree initial initial change-not-a-b >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (same)' '
+ cat >expected <<\EXPECTED &&
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-b-same-A" "initial-file" "AAA" &&
+ git reset --hard initial &&
+ test_commit "change-a-b-same-B" "initial-file" "AAA" &&
+ git merge-tree initial change-a-b-same-A change-a-b-same-B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (different)' '
+ cat >expected <<\EXPECTED &&
+changed in both
+ base 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d initial-file
+ their 100644 ba629238ca89489f2b350e196ca445e09d8bb834 initial-file
+@@ -1 +1,5 @@
++<<<<<<< .our
+ AAA
++=======
++BBB
++>>>>>>> .their
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-b-diff-A" "initial-file" "AAA" &&
+ git reset --hard initial &&
+ test_commit "change-a-b-diff-B" "initial-file" "BBB" &&
+ git merge-tree initial change-a-b-diff-A change-a-b-diff-B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (mixed)' '
+ cat >expected <<\EXPECTED &&
+changed in both
+ base 100644 f4f1f998c7776568c4ff38f516d77fef9399b5a7 ONE
+ our 100644 af14c2c3475337c73759d561ef70b59e5c731176 ONE
+ their 100644 372d761493f524d44d59bd24700c3bdf914c973c ONE
+@@ -7,7 +7,11 @@
+ AAA
+ AAA
+ AAA
++<<<<<<< .our
+ BBB
++=======
++CCC
++>>>>>>> .their
+ AAA
+ AAA
+ AAA
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-b-mix-base" "ONE" "
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA" &&
+ test_commit "change-a-b-mix-A" "ONE" \
+ "$(sed -e "1{s/AAA/BBB/;}" -e "10{s/AAA/BBB/;}" <ONE)" &&
+ git reset --hard change-a-b-mix-base &&
+ test_commit "change-a-b-mix-B" "ONE" \
+ "$(sed -e "1{s/AAA/BBB/;}" -e "10{s/AAA/CCC/;}" <ONE)" &&
+ git merge-tree change-a-b-mix-base change-a-b-mix-A change-a-b-mix-B \
+ >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file remove A, !B' '
+ cat >expected <<\EXPECTED &&
+removed in local
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "rm-a-not-b-base" "ONE" "AAA" &&
+ git rm ONE &&
+ git commit -m "rm-a-not-b" &&
+ git tag "rm-a-not-b" &&
+ git merge-tree rm-a-not-b-base rm-a-not-b rm-a-not-b-base >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file remove !A, B' '
+ cat >expected <<\EXPECTED &&
+removed in remote
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+@@ -1 +0,0 @@
+-AAA
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "rm-not-a-b-base" "ONE" "AAA" &&
+ git rm ONE &&
+ git commit -m "rm-not-a-b" &&
+ git tag "rm-not-a-b" &&
+ git merge-tree rm-a-not-b-base rm-a-not-b-base rm-a-not-b >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, remove B' '
+ cat >expected <<\EXPECTED &&
+removed in remote
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ our 100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+@@ -1 +0,0 @@
+-BBB
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-rm-b-base" "ONE" "AAA" &&
+ test_commit "change-a-rm-b-A" "ONE" "BBB" &&
+ git reset --hard change-a-rm-b-base &&
+ git rm ONE &&
+ git commit -m "change-a-rm-b-B" &&
+ git tag "change-a-rm-b-B" &&
+ git merge-tree change-a-rm-b-base change-a-rm-b-A change-a-rm-b-B \
+ >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file remove A, change B' '
+ cat >expected <<\EXPECTED &&
+removed in local
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ their 100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "rm-a-change-b-base" "ONE" "AAA" &&
+
+ git rm ONE &&
+ git commit -m "rm-a-change-b-A" &&
+ git tag "rm-a-change-b-A" &&
+ git reset --hard rm-a-change-b-base &&
+ test_commit "rm-a-change-b-B" "ONE" "BBB" &&
+ git merge-tree rm-a-change-b-base rm-a-change-b-A rm-a-change-b-B \
+ >actual &&
+ test_cmp expected actual
+'
+
+test_done
. ./test-lib.sh
-SUBSTFORMAT=%H%n
+SUBSTFORMAT='%H (%h)%n'
test_expect_exists() {
test_expect_success " $1 exists" "test -e $1"
then
test_set_prereq OFF64_T
else
- say "skipping tests concerning 64-bit offsets"
+ say "# skipping tests concerning 64-bit offsets"
fi
test_expect_success OFF64_T \
case $(uname -s) in
*MINGW*)
- say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
+ skip_all="GIT_DEBUG_SEND_PACK not supported - skipping tests"
test_done
esac
if ! test_have_prereq SYMLINKS
then
- say 'Symbolic links not supported, skipping tests.'
+ skip_all='Symbolic links not supported, skipping tests.'
test_done
fi
grep "bad tree object" output.err
'
+test_expect_success 'upload-pack error message when bad ref requested' '
+
+ printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
+ "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" >input &&
+ test_must_fail git upload-pack . <input >output 2>output.err &&
+ grep -q "not our ref" output.err &&
+ ! grep -q multi_ack_detailed output.err
+'
+
test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
printf "0032want %s\n00000009done\n0000" \
if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
then
- say "skipping test, USE_CURL_MULTI is not defined"
+ skip_all="skipping test, USE_CURL_MULTI is not defined"
test_done
fi
. ./test-lib.sh
if test -n "$NO_CURL"; then
- say 'skipping test, git built without http support'
+ skip_all='skipping test, git built without http support'
test_done
fi
# push master too; this ensures there is at least one '"'push'"' command to
# the remote helper and triggers interaction with the helper.
- !(git push -v origin +master master:retsam >output 2>&1) &&
+ test_must_fail git push -v origin +master master:retsam >output 2>&1 &&
grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output &&
. ./test-lib.sh
if test -n "$NO_CURL"; then
- say 'skipping test, git built without http support'
+ skip_all='skipping test, git built without http support'
test_done
fi
. ./test-lib.sh
if test -n "$NO_CURL"; then
- say 'skipping test, git built without http support'
+ skip_all='skipping test, git built without http support'
test_done
fi
. ./test-lib.sh
if test -n "$NO_CURL"; then
- say 'skipping test, git built without http support'
+ skip_all='skipping test, git built without http support'
test_done
fi
. ./test-lib.sh
test -z "$GIT_TEST_CLONE_2GB" &&
-say "Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" &&
+skip_all="Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" &&
test_done &&
exit
then
:
else
- say 'skipping git remote-testgit tests: requires Python 2.4 or newer'
+ skip_all='skipping git remote-testgit tests: requires Python 2.4 or newer'
test_done
fi
if ! test_have_prereq SYMLINKS
then
- say 'Symbolic links not supported, skipping tests.'
+ skip_all='Symbolic links not supported, skipping tests.'
test_done
fi
git reset --hard master && git pull -s recursive -X ours . side &&
git reset --hard master && git pull -s recursive -Xtheirs . side &&
git reset --hard master && git pull -s recursive -X theirs . side &&
- git reset --hard master && ! git pull -s recursive -X bork . side
+ git reset --hard master && test_must_fail git pull -s recursive -X bork . side
'
test_done
git bisect reset
'
+test_expect_success 'index-pack and replacements' '
+ git --no-replace-objects rev-list --objects HEAD |
+ git --no-replace-objects pack-objects test- &&
+ git index-pack test-*.pack
+'
+
#
#
test_done
test_expect_success 'use index-filter to move into a subdirectory' '
git branch directorymoved &&
git filter-branch -f --index-filter \
- "git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
+ "git ls-files -s | sed \"s- -&newsubdir/-\" |
GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved &&
- test -z "$(git diff HEAD directorymoved:newsubdir)"'
+ git diff --exit-code HEAD directorymoved:newsubdir
+'
test_expect_success 'stops when msg filter fails' '
old=$(git rev-parse HEAD) &&
# subsequent tests require gpg; check if it is available
gpg --version >/dev/null 2>/dev/null
if [ $? -eq 127 ]; then
- say "gpg not found - skipping tag signing and verification tests"
+ say "# gpg not found - skipping tag signing and verification tests"
else
# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
# the gpg version 1.0.6 didn't parse trust packets correctly, so for
if ! echo 'echo space > "$1"' > "e space.sh"
then
- say "Skipping; FS does not support spaces in filenames"
+ skip_all="Skipping; FS does not support spaces in filenames"
test_done
fi
}
test_set_prereq TTY
else
- say no usable terminal, so skipping some tests
+ say "# no usable terminal, so skipping some tests"
fi
test_expect_success 'setup' '
test_set_prereq SIMPLEPAGERTTY
fi
-test_expect_success SIMPLEPAGERTTY 'default pager is used by default' '
- unset PAGER GIT_PAGER;
- test_might_fail git config --unset core.pager &&
- rm -f default_pager_used ||
- cleanup_fail &&
+# Use this helper to make it easy for the caller of your
+# terminal-using function to specify whether it should fail.
+# If you write
+#
+# your_test() {
+# parse_args "$@"
+#
+# $test_expectation "$cmd - behaves well" "
+# ...
+# $full_command &&
+# ...
+# "
+# }
+#
+# then your test can be used like this:
+#
+# your_test expect_(success|failure) [test_must_fail] 'git foo'
+#
+parse_args() {
+ test_expectation="test_$1"
+ shift
+ if test "$1" = test_must_fail
+ then
+ full_command="test_must_fail test_terminal "
+ shift
+ else
+ full_command="test_terminal "
+ fi
+ cmd=$1
+ full_command="$full_command $1"
+}
- cat >$less <<-\EOF &&
- #!/bin/sh
- wc >default_pager_used
- EOF
- chmod +x $less &&
- (
- PATH=.:$PATH &&
- export PATH &&
- test_terminal git log
- ) &&
- test -e default_pager_used
-'
+test_default_pager() {
+ parse_args "$@"
+
+ $test_expectation SIMPLEPAGERTTY "$cmd - default pager is used by default" "
+ unset PAGER GIT_PAGER;
+ test_might_fail git config --unset core.pager &&
+ rm -f default_pager_used ||
+ cleanup_fail &&
+
+ cat >\$less <<-\EOF &&
+ #!/bin/sh
+ wc >default_pager_used
+ EOF
+ chmod +x \$less &&
+ (
+ PATH=.:\$PATH &&
+ export PATH &&
+ $full_command
+ ) &&
+ test -e default_pager_used
+ "
+}
-test_expect_success TTY 'PAGER overrides default pager' '
- unset GIT_PAGER;
- test_might_fail git config --unset core.pager &&
- rm -f PAGER_used ||
- cleanup_fail &&
+test_PAGER_overrides() {
+ parse_args "$@"
- PAGER="wc >PAGER_used" &&
- export PAGER &&
- test_terminal git log &&
- test -e PAGER_used
-'
+ $test_expectation TTY "$cmd - PAGER overrides default pager" "
+ unset GIT_PAGER;
+ test_might_fail git config --unset core.pager &&
+ rm -f PAGER_used ||
+ cleanup_fail &&
-test_expect_success TTY 'core.pager overrides PAGER' '
- unset GIT_PAGER;
- rm -f core.pager_used ||
- cleanup_fail &&
+ PAGER='wc >PAGER_used' &&
+ export PAGER &&
+ $full_command &&
+ test -e PAGER_used
+ "
+}
- PAGER=wc &&
- export PAGER &&
- git config core.pager "wc >core.pager_used" &&
- test_terminal git log &&
- test -e core.pager_used
-'
+test_core_pager_overrides() {
+ if_local_config=
+ used_if_wanted='overrides PAGER'
+ test_core_pager "$@"
+}
-test_expect_success TTY 'GIT_PAGER overrides core.pager' '
- rm -f GIT_PAGER_used ||
- cleanup_fail &&
+test_local_config_ignored() {
+ if_local_config='! '
+ used_if_wanted='is not used'
+ test_core_pager "$@"
+}
- git config core.pager wc &&
- GIT_PAGER="wc >GIT_PAGER_used" &&
- export GIT_PAGER &&
- test_terminal git log &&
- test -e GIT_PAGER_used
+test_core_pager() {
+ parse_args "$@"
+
+ $test_expectation TTY "$cmd - repository-local core.pager setting $used_if_wanted" "
+ unset GIT_PAGER;
+ rm -f core.pager_used ||
+ cleanup_fail &&
+
+ PAGER=wc &&
+ export PAGER &&
+ git config core.pager 'wc >core.pager_used' &&
+ $full_command &&
+ ${if_local_config}test -e core.pager_used
+ "
+}
+
+test_core_pager_subdir() {
+ if_local_config=
+ used_if_wanted='overrides PAGER'
+ test_pager_subdir_helper "$@"
+}
+
+test_no_local_config_subdir() {
+ if_local_config='! '
+ used_if_wanted='is not used'
+ test_pager_subdir_helper "$@"
+}
+
+test_pager_subdir_helper() {
+ parse_args "$@"
+
+ $test_expectation TTY "$cmd - core.pager $used_if_wanted from subdirectory" "
+ unset GIT_PAGER;
+ rm -f core.pager_used &&
+ rm -fr sub ||
+ cleanup_fail &&
+
+ PAGER=wc &&
+ stampname=\$(pwd)/core.pager_used &&
+ export PAGER stampname &&
+ git config core.pager 'wc >\"\$stampname\"' &&
+ mkdir sub &&
+ (
+ cd sub &&
+ $full_command
+ ) &&
+ ${if_local_config}test -e core.pager_used
+ "
+}
+
+test_GIT_PAGER_overrides() {
+ parse_args "$@"
+
+ $test_expectation TTY "$cmd - GIT_PAGER overrides core.pager" "
+ rm -f GIT_PAGER_used ||
+ cleanup_fail &&
+
+ git config core.pager wc &&
+ GIT_PAGER='wc >GIT_PAGER_used' &&
+ export GIT_PAGER &&
+ $full_command &&
+ test -e GIT_PAGER_used
+ "
+}
+
+test_doesnt_paginate() {
+ parse_args "$@"
+
+ $test_expectation TTY "no pager for '$cmd'" "
+ rm -f GIT_PAGER_used ||
+ cleanup_fail &&
+
+ GIT_PAGER='wc >GIT_PAGER_used' &&
+ export GIT_PAGER &&
+ $full_command &&
+ ! test -e GIT_PAGER_used
+ "
+}
+
+test_pager_choices() {
+ test_default_pager expect_success "$@"
+ test_PAGER_overrides expect_success "$@"
+ test_core_pager_overrides expect_success "$@"
+ test_core_pager_subdir expect_success "$@"
+ test_GIT_PAGER_overrides expect_success "$@"
+}
+
+test_expect_success 'setup: some aliases' '
+ git config alias.aliasedlog log &&
+ git config alias.true "!true"
'
+test_pager_choices 'git log'
+test_pager_choices 'git -p log'
+test_pager_choices 'git aliasedlog'
+
+test_default_pager expect_success 'git -p aliasedlog'
+test_PAGER_overrides expect_success 'git -p aliasedlog'
+test_core_pager_overrides expect_success 'git -p aliasedlog'
+test_core_pager_subdir expect_failure 'git -p aliasedlog'
+test_GIT_PAGER_overrides expect_success 'git -p aliasedlog'
+
+test_default_pager expect_success 'git -p true'
+test_PAGER_overrides expect_success 'git -p true'
+test_core_pager_overrides expect_success 'git -p true'
+test_core_pager_subdir expect_failure 'git -p true'
+test_GIT_PAGER_overrides expect_success 'git -p true'
+
+test_default_pager expect_success test_must_fail 'git -p request-pull'
+test_PAGER_overrides expect_success test_must_fail 'git -p request-pull'
+test_core_pager_overrides expect_success test_must_fail 'git -p request-pull'
+test_core_pager_subdir expect_failure test_must_fail 'git -p request-pull'
+test_GIT_PAGER_overrides expect_success test_must_fail 'git -p request-pull'
+
+test_default_pager expect_success test_must_fail 'git -p'
+test_PAGER_overrides expect_success test_must_fail 'git -p'
+test_local_config_ignored expect_failure test_must_fail 'git -p'
+test_no_local_config_subdir expect_success test_must_fail 'git -p'
+test_GIT_PAGER_overrides expect_success test_must_fail 'git -p'
+
+test_doesnt_paginate expect_failure test_must_fail 'git -p nonsense'
+
test_done
! test -d bar
'
+test_expect_success 'git clean -e' '
+ rm -fr repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+ touch 1 2 3 known &&
+ git add known &&
+ git clean -f -e 1 -e 2 &&
+ test -e 1 &&
+ test -e 2 &&
+ ! (test -e 3) &&
+ test -e known
+ )
+'
+
test_done
'
test_expect_success 'setup - repository to add submodules to' '
- git init addtest
+ git init addtest &&
+ git init addtest-ignore
'
# The 'submodule add' tests need some repository to add as a submodule.
test_cmp empty untracked
'
+test_expect_success 'submodule add to .gitignored path fails' '
+ (
+ cd addtest-ignore &&
+ cat <<-\EOF >expect &&
+ The following path is ignored by one of your .gitignore files:
+ submod
+ Use -f if you really want to add it.
+ EOF
+ # Does not use test_commit due to the ignore
+ echo "*" > .gitignore &&
+ git add --force .gitignore &&
+ git commit -m"Ignore everything" &&
+ ! git submodule add "$submodurl" submod >actual 2>&1 &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule add to .gitignored path with --force' '
+ (
+ cd addtest-ignore &&
+ git submodule add --force "$submodurl" submod
+ )
+'
+
test_expect_success 'submodule add --branch' '
echo "refs/heads/initial" >expect-head &&
cat <<-\EOF >expect-heads &&
git commit -m sub-b) &&
git add sub &&
test_tick &&
- git commit -m b
+ git commit -m b &&
git checkout -b c a &&
git merge -s ours b &&
(exit $status)
'
+(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar' && cd .. && git add sm)
+new_head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+touch .gitmodules
+
cat > expect << EOF
# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: sm
+#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: dir1/modified
#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+# > Add bar
+#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
+# .gitmodules
# dir1/untracked
# dir2/modified
# dir2/untracked
# expect
# output
# untracked
-no changes added to commit (use "git add" and/or "git commit -a")
EOF
test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' '
test_cmp expect output
'
+test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
+ git config diff.ignoreSubmodules dirty &&
+ git status >output &&
+ test_cmp expect output &&
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname &&
+ git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname
+'
+
test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
git status --ignore-submodules=dirty > output &&
test_cmp expect output
'
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
+ git config diff.ignoreSubmodules dirty &&
+ git status >output &&
+ ! test -s actual &&
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname &&
+ git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '
echo modified > sm/foo &&
git status --ignore-submodules=dirty > output &&
test_cmp expect output
'
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' '
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with modified content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
cat > expect << EOF
# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: sm
+#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
# modified: dir1/modified
# modified: sm (modified content)
#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+# > Add bar
+#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
+# .gitmodules
# dir1/untracked
# dir2/modified
# dir2/untracked
# expect
# output
# untracked
-no changes added to commit (use "git add" and/or "git commit -a")
EOF
test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
test_cmp expect output
'
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" '
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodules with modified content" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD)
cat > expect << EOF
# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: sm
+#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
# modified: dir1/modified
# modified: sm (new commits)
#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+# > Add bar
+#
# Submodules changed but not updated:
#
-# * sm $head...$head2 (1):
+# * sm $new_head...$head2 (1):
# > 2nd commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
+# .gitmodules
# dir1/untracked
# dir2/modified
# dir2/untracked
# expect
# output
# untracked
-no changes added to commit (use "git add" and/or "git commit -a")
EOF
test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
test_cmp expect output
'
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
git status --ignore-submodules=dirty > output &&
test_cmp expect output
'
+test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
cat > expect << EOF
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
+# .gitmodules
# dir1/untracked
# dir2/modified
# dir2/untracked
test_cmp expect output
'
+test_expect_failure '.gitmodules ignore=all suppresses submodule summary' '
+ git config --add -f .gitmodules submodule.subname.ignore all &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_failure '.git/config ignore=all suppresses submodule summary' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore all &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
test_done
test_when_finished "git checkout Initial" &&
echo "Empty author test" >>foo &&
test_tick &&
- ! git commit -a -m "empty author" --amend 2>err &&
+ test_must_fail git commit -a -m "empty author" --amend 2>err &&
grep "empty ident" err
'
test_when_finished "git checkout Initial" &&
echo "Missing author test" >>foo &&
test_tick &&
- ! git commit -a -m "malformed author" --amend 2>err &&
+ test_must_fail git commit -a -m "malformed author" --amend 2>err &&
grep "empty ident" err
'
test_expect_success 'will not overwrite untracked file' '
git reset --hard c1 &&
cat important > c2.c &&
- ! git merge c2 &&
+ test_must_fail git merge c2 &&
test_cmp important c2.c
'
git reset --hard c1 &&
cat important > c2.c &&
git add c2.c &&
- ! git merge c2 &&
+ test_must_fail git merge c2 &&
test_cmp important c2.c
'
cat important > c2.c &&
git add c2.c &&
rm c2.c &&
- ! git merge c2 &&
+ test_must_fail git merge c2 &&
git checkout c2.c &&
test_cmp important c2.c
'
git rm c1.c &&
git commit -m "rm c1.c" &&
cat important > c1.c &&
- ! git merge c1a &&
+ test_must_fail git merge c1a &&
test_cmp important c1.c
'
git commit -m "rm c1.c" &&
cat important > c1.c &&
git add c1.c &&
- ! git merge c1a &&
+ test_must_fail git merge c1a &&
test_cmp important c1.c
'
cat important > c1.c &&
git add c1.c &&
rm c1.c &&
- ! git merge c1a &&
+ test_must_fail git merge c1a &&
git checkout c1.c &&
test_cmp important c1.c
'
# running mergetool
test_expect_success 'setup' '
+ git config rerere.enabled true &&
echo master >file1 &&
mkdir subdir &&
echo master sub >subdir/file3 &&
cd subdir && (
test_must_fail git merge master >/dev/null 2>&1 &&
( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
- test "$(cat file3)" = "master new sub" )
+ test "$(cat file3)" = "master new sub") &&
+ cd ..
'
-# We can't merge files from parent directories when running mergetool
-# from a subdir. Is this a bug?
-#
-#test_expect_failure 'mergetool in subdir' '
-# cd subdir && (
-# ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
-# ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
-# test "$(cat ../file1)" = "master updated" &&
-# test "$(cat ../file2)" = "master new" &&
-# git commit -m "branch1 resolved with mergetool - subdir" )
-#'
+test_expect_success 'mergetool on file in parent dir' '
+ cd subdir && (
+ ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+ test "$(cat ../file1)" = "master updated" &&
+ test "$(cat ../file2)" = "master new" &&
+ git commit -m "branch1 resolved with mergetool - subdir") &&
+ cd ..
+'
+
+test_expect_success 'mergetool skips autoresolved' '
+ git checkout -b test4 branch1 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git reset --hard
+'
+
+test_expect_success 'mergetool merges all from subdir' '
+ cd subdir && (
+ git config rerere.enabled false &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt &&
+ test "$(cat ../file1)" = "master updated" &&
+ test "$(cat ../file2)" = "master new" &&
+ test "$(cat file3)" = "master new sub" &&
+ git add ../file1 ../file2 file3 &&
+ git commit -m "branch2 resolved by mergetool from subdir") &&
+ cd ..
+'
test_done
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping difftool tests, perl not available'
+ skip_all='skipping difftool tests, perl not available'
test_done
fi
test_expect_success "grep -w $L (w)" '
: >expected &&
- ! git grep -n -w -e "^w" >actual &&
+ test_must_fail git grep -n -w -e "^w" >actual &&
test_cmp expected actual
'
test_cmp empty out
'
+test_config() {
+ git config "$1" "$2" &&
+ test_when_finished "git config --unset $1"
+}
+
+test_expect_success 'copes with color settings' '
+ rm -f actual &&
+ echo grep.h >expect &&
+ test_config color.grep always &&
+ test_config color.grep.filename yellow &&
+ test_config color.grep.separator green &&
+ git grep -O'\''printf "%s\n" >actual'\'' GREP_AND &&
+ test_cmp expect actual
+'
+
test_expect_success 'run from subdir' '
rm -f actual &&
echo grep.c >expect &&
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping git send-email tests, perl not available'
+ skip_all='skipping git send-email tests, perl not available'
test_done
fi
# Exit immediately to prevent hang if a no-confirm test fails
check_no_confirm () {
test -f no_confirm_okay || {
- say 'No confirm test failed; skipping remaining tests to prevent hanging'
+ skip_all='confirm test failed; skipping remaining tests to prevent hanging'
test_done
}
}
test_set_prereq UTF8
;;
*)
- say "UTF-8 locale not set, some tests skipped ($GIT_SVN_LC_ALL)"
+ say "# UTF-8 locale not set, some tests skipped ($GIT_SVN_LC_ALL)"
;;
esac
test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
"
+test_expect_success 'dcommit should not fail with a touched file' '
+ test_commit "commit-new-file-foo2" foo2 &&
+ test-chmtime =-60 foo &&
+ git svn dcommit
+'
+
+test_expect_success 'rebase should not fail with a touched file' '
+ test-chmtime =-60 foo &&
+ git svn rebase
+'
+
test_expect_success 'able to set-tree to a subdirectory' "
echo cba > d &&
git update-index d &&
svn_cmd cp -m "trailing .lock" "$svnrepo/pr ject/trunk" \
"$svnrepo/pr ject/branches/trailing_dotlock.lock" &&
svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \
- "$svnrepo/pr ject/branches/not-a@{0}reflog" &&
+ "$svnrepo/pr ject/branches/not-a%40{0}reflog" &&
start_httpd
'
test_expect_success 'test clone with funky branch names' '
git svn clone -s "$svnrepo/pr ject" project &&
- cd project &&
+ (
+ cd project &&
git rev-parse "refs/remotes/fun%20plugin" &&
git rev-parse "refs/remotes/more%20fun%20plugin!" &&
git rev-parse "refs/remotes/$scary_ref" &&
git rev-parse "refs/remotes/%2Eleading_dot" &&
git rev-parse "refs/remotes/trailing_dot%2E" &&
git rev-parse "refs/remotes/trailing_dotlock%2Elock" &&
- git rev-parse "refs/remotes/not-a%40{0}reflog" &&
- cd ..
+ git rev-parse "refs/remotes/not-a%40{0}reflog"
+ )
'
test_expect_success 'test dcommit to funky branch' "
- cd project &&
- git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
- echo hello >> foo &&
- git commit -m 'hello' -- foo &&
- git svn dcommit &&
- cd ..
+ (
+ cd project &&
+ git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
+ echo hello >> foo &&
+ git commit -m 'hello' -- foo &&
+ git svn dcommit
+ )
"
test_expect_success 'test dcommit to scary branch' '
- cd project &&
- git reset --hard "refs/remotes/$scary_ref" &&
- echo urls are scary >> foo &&
- git commit -m "eep" -- foo &&
- git svn dcommit &&
- cd ..
+ (
+ cd project &&
+ git reset --hard "refs/remotes/$scary_ref" &&
+ echo urls are scary >> foo &&
+ git commit -m "eep" -- foo &&
+ git svn dcommit
+ )
'
test_expect_success 'test dcommit to trailing_dotlock branch' '
- cd project &&
- git reset --hard "refs/remotes/trailing_dotlock%2Elock" &&
- echo who names branches like this anyway? >> foo &&
- git commit -m "bar" -- foo &&
- git svn dcommit &&
- cd ..
+ (
+ cd project &&
+ git reset --hard "refs/remotes/trailing_dotlock%2Elock" &&
+ echo who names branches like this anyway? >> foo &&
+ git commit -m "bar" -- foo &&
+ git svn dcommit
+ )
'
stop_httpd
1.[456].*)
;;
*)
- say "skipping svn-info test (SVN version: $v not supported)"
+ skip_all="skipping svn-info test (SVN version: $v not supported)"
test_done
;;
esac
then
test_set_prereq UTF8
else
- say "UTF-8 locale not available, some tests are skipped"
+ say "# UTF-8 locale not available, some tests are skipped"
fi
compare_svn_head_with () {
'
test_expect_success 'start import with incomplete authors file' '
- ! git svn clone --authors-file=svn-authors "$svnrepo" x
+ test_must_fail git svn clone --authors-file=svn-authors "$svnrepo" x
'
test_expect_success 'imported 2 revisions successfully' '
'
test_expect_success 'fetch fails on ee' '
- ( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+ ( cd aa-work && test_must_fail git svn fetch --authors-file=../svn-authors )
'
tmp_config_get () {
(
cd $H &&
git config --unset i18n.commitencoding &&
- ! git svn dcommit
+ test_must_fail git svn dcommit
)
'
done
test_expect_success 'fetch fails on modified hidden file' '
( cd g &&
git svn find-rev refs/remotes/git-svn > ../expect &&
- ! git svn fetch 2> ../errors &&
+ test_must_fail git svn fetch 2> ../errors &&
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
fgrep "not found in commit" errors &&
test_cmp expect expect2
gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz
'
else
- say "Perl Compress::Zlib unavailable, skipping gunzip test"
+ say "# Perl Compress::Zlib unavailable, skipping gunzip test"
fi
test_expect_success 'git svn gc does not change unhandled.log files' '
--- /dev/null
+#!/bin/sh
+
+test_description='git svn fetch deleted tag'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+ mkdir -p import/trunk/subdir &&
+ mkdir -p import/branches &&
+ mkdir -p import/tags &&
+ echo "base" >import/trunk/subdir/file &&
+ svn_cmd import -m "import for git svn" import "$svnrepo" &&
+ rm -rf import &&
+
+ svn_cmd mkdir --parents -m "create mybranch directory" "$svnrepo/branches/mybranch" &&
+ svn_cmd cp -m "create branch mybranch" "$svnrepo/trunk" "$svnrepo/branches/mybranch/trunk" &&
+
+ svn_cmd co "$svnrepo/trunk" svn_project &&
+ (cd svn_project &&
+ echo "trunk change" >>subdir/file &&
+ svn_cmd ci -m "trunk change" subdir/file &&
+
+ svn_cmd switch "$svnrepo/branches/mybranch/trunk" &&
+ echo "branch change" >>subdir/file &&
+ svn_cmd ci -m "branch change" subdir/file
+ ) &&
+
+ svn_cmd cp -m "create mytag attempt 1" -r5 "$svnrepo/trunk/subdir" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag attempt 1" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag attempt 2" -r5 "$svnrepo/branches/mybranch/trunk/subdir" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with checksum error' '
+ git svn init --stdlayout "$svnrepo" git_project &&
+ cd git_project &&
+ git svn fetch &&
+
+ git diff --exit-code mybranch:trunk/subdir/file tags/mytag:file &&
+ git diff --exit-code master:subdir/file tags/mytag^:file
+'
+
+test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git svn fetch deleted tag 2'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+ mkdir -p import/branches &&
+ mkdir -p import/tags &&
+ mkdir -p import/trunk/subdir1 &&
+ mkdir -p import/trunk/subdir2 &&
+ mkdir -p import/trunk/subdir3 &&
+ echo "file1" >import/trunk/subdir1/file &&
+ echo "file2" >import/trunk/subdir2/file &&
+ echo "file3" >import/trunk/subdir3/file &&
+ svn_cmd import -m "import for git svn" import "$svnrepo" &&
+ rm -rf import &&
+
+ svn_cmd co "$svnrepo/trunk" svn_project &&
+ (cd svn_project &&
+ echo "change1" >>subdir1/file &&
+ echo "change2" >>subdir2/file &&
+ echo "change3" >>subdir3/file &&
+ svn_cmd ci -m "change" .
+ ) &&
+
+ svn_cmd cp -m "create mytag 1" -r2 "$svnrepo/trunk/subdir1" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag 1" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag 2" -r2 "$svnrepo/trunk/subdir2" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag 2" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag 3" -r2 "$svnrepo/trunk/subdir3" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with no checksum error' '
+ git svn init --stdlayout "$svnrepo" git_project &&
+ cd git_project &&
+ git svn fetch &&
+
+ git diff --exit-code master:subdir3/file tags/mytag:file &&
+ git diff --exit-code master:subdir2/file tags/mytag^:file &&
+ git diff --exit-code master:subdir1/file tags/mytag^^:file
+'
+
+test_done
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping git cvsexportcommit tests, perl not available'
+ skip_all='skipping git cvsexportcommit tests, perl not available'
test_done
fi
cvs >/dev/null 2>&1
if test $? -ne 1
then
- say 'skipping git cvsexportcommit tests, cvs not found'
+ skip_all='skipping git cvsexportcommit tests, cvs not found'
test_done
fi
test `git rev-parse --verify master:file2` \
= `git rev-parse --verify verify--import-marks:copy-of-file2`'
+test_tick
+mt=$(git hash-object --stdin < /dev/null)
+: >input.blob
+: >marks.exp
+: >tree.exp
+
+cat >input.commit <<EOF
+commit refs/heads/verify--dump-marks
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+test the sparse array dumping routines with exponentially growing marks
+COMMIT
+EOF
+
+i=0
+l=4
+m=6
+n=7
+while test "$i" -lt 27; do
+ cat >>input.blob <<EOF
+blob
+mark :$l
+data 0
+blob
+mark :$m
+data 0
+blob
+mark :$n
+data 0
+EOF
+ echo "M 100644 :$l l$i" >>input.commit
+ echo "M 100644 :$m m$i" >>input.commit
+ echo "M 100644 :$n n$i" >>input.commit
+
+ echo ":$l $mt" >>marks.exp
+ echo ":$m $mt" >>marks.exp
+ echo ":$n $mt" >>marks.exp
+
+ printf "100644 blob $mt\tl$i\n" >>tree.exp
+ printf "100644 blob $mt\tm$i\n" >>tree.exp
+ printf "100644 blob $mt\tn$i\n" >>tree.exp
+
+ l=$(($l + $l))
+ m=$(($m + $m))
+ n=$(($l + $n))
+
+ i=$((1 + $i))
+done
+
+sort tree.exp > tree.exp_s
+
+test_expect_success 'A: export marks with large values' '
+ cat input.blob input.commit | git fast-import --export-marks=marks.large &&
+ git ls-tree refs/heads/verify--dump-marks >tree.out &&
+ test_cmp tree.exp_s tree.out &&
+ test_cmp marks.exp marks.large'
+
###
### series B
###
'git fast-import <input &&
test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
+test_expect_success \
+ 'N: copy directory by id' \
+ 'cat >expect <<-\EOF &&
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+ compare_diff_raw expect actual'
+
+test_expect_success \
+ 'N: modify copied tree' \
+ 'cat >expect <<-\EOF &&
+ :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N5
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3
+
+ commit refs/heads/N5
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ modify directory copy
+ COMMIT
+
+ M 644 inline file3/file5
+ data <<EOF
+ $file5_data
+ EOF
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
+ compare_diff_raw expect actual'
+
###
### series O
###
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping git cvsserver tests, perl not available'
+ skip_all='skipping git cvsserver tests, perl not available'
test_done
fi
cvs >/dev/null 2>&1
if test $? -ne 1
then
- say 'skipping git-cvsserver tests, cvs not found'
+ skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
- say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
+ skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
}
cvs >/dev/null 2>&1
if test $? -ne 1
then
- say 'skipping git-cvsserver tests, cvs not found'
+ skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi
if ! test_have_prereq PERL
then
- say 'skipping git-cvsserver tests, perl not available'
+ skip_all='skipping git-cvsserver tests, perl not available'
test_done
fi
"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
- say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
+ skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done
}
. ./lib-cvs.sh
if ! test_have_prereq PERL; then
- say 'skipping git cvsimport tests, perl not available'
+ skip_all='skipping git cvsimport tests, perl not available'
test_done
fi
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping perl interface tests, perl not available'
+ skip_all='skipping perl interface tests, perl not available'
test_done
fi
"$PERL_PATH" -MTest::More -e 0 2>/dev/null || {
- say "Perl Test::More unavailable, skipping test"
+ skip_all="Perl Test::More unavailable, skipping test"
test_done
}
git config --add test.int 2k
'
+# The external test will outputs its own plan
+test_external_has_tap=1
+
test_external_without_stderr \
'Perl API' \
"$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
use Test::More qw(no_plan);
+BEGIN {
+ # t9700-perl-git.sh kicks off our testing, so we have to go from
+ # there.
+ Test::More->builder->current_test(1);
+ Test::More->builder->no_ending(1);
+}
+
use Cwd;
use File::Basename;
like($last_commit, qr/^[0-9a-fA-F]{40}$/, 'rev-parse returned hash');
my $dir_commit = $r2->command_oneline('log', '-n1', '--pretty=format:%H', '.');
isnt($last_commit, $dir_commit, 'log . does not show last commit');
+
+printf "1..%d\n", Test::More->builder->current_test;
+
+my $is_passing = eval { Test::More->is_passing };
+exit($is_passing ? 0 : 1) unless $@ =~ /Can't locate object method/;
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
verbose=t; shift ;;
-q|--q|--qu|--qui|--quie|--quiet)
- quiet=t; shift ;;
+ # Ignore --quiet under a TAP::Harness. Saying how many tests
+ # passed without the ok/not ok details is always an error.
+ test -z "$HARNESS_ACTIVE" && quiet=t; shift ;;
--with-dashes)
with_dashes=t; shift ;;
--no-color)
color=; shift ;;
- --no-python)
- # noop now...
- shift ;;
--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
valgrind=t; verbose=t; shift ;;
--tee)
*) test -n "$quiet" && return;;
esac
shift
- printf "* %s" "$*"
+ printf "%s" "$*"
tput sgr0
echo
)
say_color() {
test -z "$1" && test -n "$quiet" && return
shift
- echo "* $*"
+ echo "$*"
}
fi
test_broken=0
test_success=0
+test_external_has_tap=0
+
die () {
code=$?
if test -n "$GIT_EXIT_OK"
tr Q '\015'
}
+q_to_tab () {
+ tr Q '\011'
+}
+
append_cr () {
sed -e 's/$/Q/' | tr Q '\015'
}
test_ok_ () {
test_success=$(($test_success + 1))
- say_color "" " ok $test_count: $@"
+ say_color "" "ok $test_count - $@"
}
test_failure_ () {
test_failure=$(($test_failure + 1))
- say_color error "FAIL $test_count: $1"
+ say_color error "not ok - $test_count $1"
shift
- echo "$@" | sed -e 's/^/ /'
+ echo "$@" | sed -e 's/^/# /'
test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
}
test_known_broken_ok_ () {
test_fixed=$(($test_fixed+1))
- say_color "" " FIXED $test_count: $@"
+ say_color "" "ok $test_count - $@ # TODO known breakage"
}
test_known_broken_failure_ () {
test_broken=$(($test_broken+1))
- say_color skip " still broken $test_count: $@"
+ say_color skip "not ok $test_count - $@ # TODO known breakage"
}
test_debug () {
eval >&3 2>&4 "$1"
eval_ret=$?
eval >&3 2>&4 "$test_cleanup"
+ if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
+ echo ""
+ fi
return 0
}
case $this_test.$test_count in
$skp)
to_skip=t
+ break
esac
done
if test -z "$to_skip" && test -n "$prereq" &&
case "$to_skip" in
t)
say_color skip >&3 "skipping test: $@"
- say_color skip "skip $test_count: $1"
+ say_color skip "ok $test_count # skip $1"
: true
;;
*)
# test_external runs external test scripts that provide continuous
# test output about their progress, and succeeds/fails on
# zero/non-zero exit code. It outputs the test output on stdout even
-# in non-verbose mode, and announces the external script with "* run
+# in non-verbose mode, and announces the external script with "# run
# <n>: ..." before running it. When providing relative paths, keep in
# mind that all scripts run in "trash directory".
# Usage: test_external description command arguments...
then
# Announce the script to reduce confusion about the
# test output that follows.
- say_color "" " run $test_count: $descr ($*)"
+ say_color "" "# run $test_count: $descr ($*)"
# Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
# to be able to use them in script
export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
"$@" 2>&4
if [ "$?" = 0 ]
then
- test_ok_ "$descr"
+ if test $test_external_has_tap -eq 0; then
+ test_ok_ "$descr"
+ else
+ say_color "" "# test_external test $descr was ok"
+ test_success=$(($test_success + 1))
+ fi
else
- test_failure_ "$descr" "$@"
+ if test $test_external_has_tap -eq 0; then
+ test_failure_ "$descr" "$@"
+ else
+ say_color error "# test_external test $descr failed: $@"
+ test_failure=$(($test_failure + 1))
+ fi
fi
fi
}
[ -f "$stderr" ] || error "Internal error: $stderr disappeared."
descr="no stderr: $1"
shift
- say >&3 "expecting no stderr from previous command"
+ say >&3 "# expecting no stderr from previous command"
if [ ! -s "$stderr" ]; then
rm "$stderr"
- test_ok_ "$descr"
+
+ if test $test_external_has_tap -eq 0; then
+ test_ok_ "$descr"
+ else
+ say_color "" "# test_external_without_stderr test $descr was ok"
+ test_success=$(($test_success + 1))
+ fi
else
if [ "$verbose" = t ]; then
- output=`echo; echo Stderr is:; cat "$stderr"`
+ output=`echo; echo "# Stderr is:"; cat "$stderr"`
else
output=
fi
# rm first in case test_failure exits.
rm "$stderr"
- test_failure_ "$descr" "$@" "$output"
+ if test $test_external_has_tap -eq 0; then
+ test_failure_ "$descr" "$@" "$output"
+ else
+ say_color error "# test_external_without_stderr test $descr failed: $@: $output"
+ test_failure=$(($test_failure + 1))
+ fi
fi
}
GIT_EXIT_OK=t
test_results_dir="$TEST_DIRECTORY/test-results"
mkdir -p "$test_results_dir"
- test_results_path="$test_results_dir/${0%.sh}-$$"
+ test_results_path="$test_results_dir/${0%.sh}-$$.counts"
echo "total $test_count" >> $test_results_path
echo "success $test_success" >> $test_results_path
if test "$test_fixed" != 0
then
- say_color pass "fixed $test_fixed known breakage(s)"
+ say_color pass "# fixed $test_fixed known breakage(s)"
fi
if test "$test_broken" != 0
then
- say_color error "still have $test_broken known breakage(s)"
+ say_color error "# still have $test_broken known breakage(s)"
msg="remaining $(($test_count-$test_broken)) test(s)"
else
msg="$test_count test(s)"
fi
case "$test_failure" in
0)
- say_color pass "passed all $msg"
+ # Maybe print SKIP message
+ [ -z "$skip_all" ] || skip_all=" # SKIP $skip_all"
+
+ if test $test_external_has_tap -eq 0; then
+ say_color pass "# passed all $msg"
+ say "1..$test_count$skip_all"
+ fi
test -d "$remove_trash" &&
cd "$(dirname "$remove_trash")" &&
exit 0 ;;
*)
- say_color error "failed $test_failure among $msg"
+ if test $test_external_has_tap -eq 0; then
+ say_color error "# failed $test_failure among $msg"
+ say "1..$test_count"
+ fi
+
exit 1 ;;
esac
this_test=${this_test%%-*}
for skp in $GIT_SKIP_TESTS
do
- to_skip=
- for skp in $GIT_SKIP_TESTS
- do
- case "$this_test" in
- $skp)
- to_skip=t
- esac
- done
- case "$to_skip" in
- t)
+ case "$this_test" in
+ $skp)
say_color skip >&3 "skipping test $this_test altogether"
- say_color skip "skip all tests in $this_test"
+ skip_all="skip all tests in $this_test"
test_done
esac
done
{
for (; *argv; argv++) {
char result[100];
- time_t t;
+ unsigned long t;
+ int tz;
result[0] = 0;
parse_date(*argv, result, sizeof(result));
- t = strtoul(result, NULL, 0);
- printf("%s -> %s\n", *argv,
- t ? show_date(t, 0, DATE_ISO8601) : "bad");
+ if (sscanf(result, "%lu %d", &t, &tz) == 2)
+ printf("%s -> %s\n",
+ *argv, show_date(t, tz, DATE_ISO8601));
+ else
+ printf("%s -> bad\n", *argv);
}
}
{
int i, ret, bottom;
struct tree_desc t[MAX_UNPACK_TREES];
+ void *buf[MAX_UNPACK_TREES];
struct traverse_info newinfo;
struct name_entry *p;
const unsigned char *sha1 = NULL;
if (dirmask & 1)
sha1 = names[i].sha1;
- fill_tree_descriptor(t+i, sha1);
+ buf[i] = fill_tree_descriptor(t+i, sha1);
}
bottom = switch_cache_bottom(&newinfo);
ret = traverse_trees(n, t, &newinfo);
restore_cache_bottom(&newinfo, bottom);
+
+ for (i = 0; i < n; i++)
+ free(buf[i]);
+
return ret;
}
fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
}
-static int do_rev_list(int in, int out, void *create_full_pack)
+static int do_rev_list(int in, int out, void *user_data)
{
int i;
struct rev_info revs;
if (use_thin_pack)
revs.edge_hint = 1;
- if (create_full_pack) {
- const char *args[] = {"rev-list", "--all", NULL};
- setup_revisions(2, args, &revs, NULL);
- } else {
- for (i = 0; i < want_obj.nr; i++) {
- struct object *o = want_obj.objects[i].item;
- /* why??? */
- o->flags &= ~UNINTERESTING;
- add_pending_object(&revs, o, NULL);
- }
- for (i = 0; i < have_obj.nr; i++) {
- struct object *o = have_obj.objects[i].item;
- o->flags |= UNINTERESTING;
- add_pending_object(&revs, o, NULL);
- }
- setup_revisions(0, NULL, &revs, NULL);
+ for (i = 0; i < want_obj.nr; i++) {
+ struct object *o = want_obj.objects[i].item;
+ /* why??? */
+ o->flags &= ~UNINTERESTING;
+ add_pending_object(&revs, o, NULL);
+ }
+ for (i = 0; i < have_obj.nr; i++) {
+ struct object *o = have_obj.objects[i].item;
+ o->flags |= UNINTERESTING;
+ add_pending_object(&revs, o, NULL);
}
+ setup_revisions(0, NULL, &revs, NULL);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(revs.commits, &revs, show_edge);
*/
o = lookup_object(sha1_buf);
if (!o || !(o->flags & OUR_REF))
- die("git upload-pack: not our ref %s", line+5);
+ die("git upload-pack: not our ref %s",
+ sha1_to_hex(sha1_buf));
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
add_object_array(o, NULL, &want_obj);
DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
if (!s->show_untracked_files)
DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
- if (s->ignore_submodule_arg)
+ if (s->ignore_submodule_arg) {
+ DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+ }
rev.diffopt.format_callback = wt_status_collect_changed_cb;
rev.diffopt.format_callback_data = s;
rev.prune_data = s->pathspec;
opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
setup_revisions(0, NULL, &rev, &opt);
- if (s->ignore_submodule_arg)
+ if (s->ignore_submodule_arg) {
+ DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+ }
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = wt_status_collect_updated_cb;
{
int i1, i2;
+ if (s1 == s2 && !memcmp(l1, l2, s1))
+ return 1;
if (!(flags & XDF_WHITESPACE_FLAGS))
- return s1 == s2 && !memcmp(l1, l2, s1);
+ return 0;
i1 = 0;
i2 = 0;