aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.7.1.1.txt67
-rw-r--r--Documentation/SubmittingPatches49
-rw-r--r--Documentation/blame-options.txt10
-rw-r--r--Documentation/config.txt17
-rw-r--r--Documentation/diff-options.txt7
-rw-r--r--Documentation/git-add.txt6
-rw-r--r--Documentation/git-checkout.txt54
-rw-r--r--Documentation/git-log.txt42
-rw-r--r--Documentation/git-mailinfo.txt8
-rw-r--r--Documentation/git-merge.txt7
-rw-r--r--Documentation/git-notes.txt177
-rw-r--r--Documentation/git-send-email.txt7
-rw-r--r--Documentation/git-shortlog.txt15
-rw-r--r--Documentation/git-update-index.txt2
-rw-r--r--Documentation/gitignore.txt14
-rw-r--r--Makefile14
l---------RelNotes2
-rw-r--r--attr.c38
-rw-r--r--builtin.h1
-rw-r--r--builtin/apply.c18
-rw-r--r--builtin/blame.c2
-rw-r--r--builtin/clone.c3
-rw-r--r--builtin/commit.c29
-rw-r--r--builtin/describe.c49
-rw-r--r--builtin/fmt-merge-msg.c76
-rw-r--r--builtin/grep.c2
-rw-r--r--builtin/index-pack.c89
-rw-r--r--builtin/ls-files.c75
-rw-r--r--builtin/merge-file.c2
-rw-r--r--builtin/merge-tree.c2
-rw-r--r--builtin/merge.c20
-rw-r--r--builtin/notes.c2
-rw-r--r--builtin/pack-objects.c13
-rw-r--r--builtin/rerere.c2
-rw-r--r--builtin/shortlog.c3
-rw-r--r--cache.h3
-rw-r--r--combine-diff.c2
-rw-r--r--compat/win32/pthread.h8
-rw-r--r--config.mak.in1
-rw-r--r--configure.ac6
-rw-r--r--date.c3
-rw-r--r--diff.c92
-rw-r--r--exec_cmd.c2
-rwxr-xr-xgit-add--interactive.perl6
-rwxr-xr-xgit-am.sh2
-rw-r--r--git-compat-util.h18
-rwxr-xr-xgit-pull.sh7
-rwxr-xr-xgit-send-email.perl48
-rwxr-xr-xgit-stash.sh2
-rw-r--r--gitweb/Makefile2
-rw-r--r--http-walker.c2
-rw-r--r--http.c159
-rw-r--r--http.h5
-rw-r--r--merge-file.c2
-rw-r--r--merge-recursive.c2
-rw-r--r--object.c4
-rw-r--r--pack-check.c15
-rw-r--r--pack.h1
-rw-r--r--pretty.c7
-rw-r--r--quote.c89
-rw-r--r--quote.h5
-rw-r--r--remote-curl.c16
-rw-r--r--sha1_file.c42
-rw-r--r--shortlog.h1
-rw-r--r--t/README6
-rw-r--r--t/lib-t6000.sh (renamed from t/t6000lib.sh)0
-rwxr-xr-xt/t0003-attributes.sh15
-rwxr-xr-xt/t2007-checkout-symlink.sh6
-rwxr-xr-xt/t2106-update-index-assume-unchanged.sh24
-rwxr-xr-xt/t3307-notes-man.sh38
-rwxr-xr-xt/t3903-stash.sh150
-rwxr-xr-xt/t4015-diff-whitespace.sh37
-rwxr-xr-xt/t4043-diff-rename-binary.sh45
-rwxr-xr-xt/t4124-apply-ws-rule.sh12
-rwxr-xr-xt/t4134-apply-submodule.sh38
-rwxr-xr-xt/t4201-shortlog.sh116
-rwxr-xr-xt/t5541-http-push.sh29
-rwxr-xr-xt/t5550-http-fetch.sh37
-rwxr-xr-xt/t5704-bundle.sh16
-rwxr-xr-xt/t6002-rev-list-bisect.sh2
-rwxr-xr-xt/t6003-rev-list-topo-order.sh2
-rwxr-xr-xt/t6006-rev-list-format.sh31
-rwxr-xr-xt/t6101-rev-parse-parents.sh2
-rwxr-xr-xt/t6120-describe.sh8
-rwxr-xr-xt/t7005-editor.sh2
-rwxr-xr-xt/t7006-pager.sh151
-rwxr-xr-xt/t7010-setup.sh12
-rwxr-xr-xt/t7508-status.sh70
-rwxr-xr-xt/t7509-commit.sh46
-rwxr-xr-xt/t7604-merge-custom-message.sh24
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh20
-rw-r--r--t/test-lib.sh16
-rw-r--r--tag.c71
-rw-r--r--tag.h2
-rw-r--r--thread-utils.c16
-rw-r--r--thread-utils.h1
-rw-r--r--unpack-trees.c14
-rw-r--r--userdiff.c4
-rw-r--r--wrapper.c42
-rw-r--r--wt-status.c21
100 files changed, 2006 insertions, 596 deletions
diff --git a/Documentation/RelNotes-1.7.1.1.txt b/Documentation/RelNotes-1.7.1.1.txt
new file mode 100644
index 000000000..e0386a43f
--- /dev/null
+++ b/Documentation/RelNotes-1.7.1.1.txt
@@ -0,0 +1,67 @@
+Git v1.7.1.1 Release Notes (draft)
+==================================
+
+Fixes since v1.7.1
+------------------
+
+ * Authentication over http transport can now be made lazily, in that the
+ request can first go to a URL without username, get a 401 response and
+ then the client will ask for the username to use.
+
+ * We used to mistakenly think "../work" is a subdirectory of the current
+ directory when we are in "../work-xyz".
+
+ * The attribute mechanism now allows an entry that uses an attribute
+ macro that set/unset one attribute, immediately followed by an
+ overriding setting; this makes attribute macros much easier to use.
+
+ * We didn't recognize timezone "Z" as a synonym for "UTC" (75b37e70).
+
+ * "git am -3" did not show diagnosis when the patch in the message was corrupt.
+
+ * After "git apply --whitespace=fix" removed trailing blank lines in an
+ patch in a patch series, it failed to apply later patches that depend
+ on the presense of such blank lines.
+
+ * "git bundle --stdin" segfaulted.
+
+ * "git checkout" and "git rebase" overwrote paths that are marked "assume
+ unchanged".
+
+ * "git describe" did not tie-break tags that point at the same commit
+ correctly; newer ones are preferred by paying attention to the
+ tagger date now.
+
+ * "git diff" used to tell underlying xdiff machinery to work very hard to
+ minimize the output, but this often was spending too many extra cycles
+ for very little gain.
+
+ * "git diff --color" did not paint extended diff headers per line
+ (i.e. the coloring escape sequence didn't end at the end of line),
+ which confused "less -R".
+
+ * "git fetch" over HTTP verifies the downloaded packfiles more robustly.
+
+ * The memory usage by "git index-pack" (run during "git fetch" and "git
+ push") got leaner.
+
+ * "GIT_DIR=foo.git git init --bare bar.git" created foo.git instead of bar.git.
+
+ * "git log --abbrev=$num --format='%h' ignored --abbrev=$num.
+
+ * "git pull" accepted "--dry-run", gave it to underlying "git fetch" but
+ ignored the option itself, resulting in a bogus attempt to merge
+ unrelated commit.
+
+ * "git send-email" lacked a way to specify the domainname used in the
+ EHLO/HELO exchange, causing rejected connection from picky servers.
+ It learned --smtp-domain option to solve this issue.
+
+ * "git stash" incorrectly lost paths in the working tree that were
+ previously removed from the index.
+
+ * "git status" stopped refreshing the index by mistake in 1.7.1.
+
+ * "git status" showed excess "hints" even when advice.statusHints is set to false.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 84248daa5..eb53e0636 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -54,6 +54,34 @@ But the patch submission requirements are a lot more relaxed
here on the technical/contents front, because the core GIT is
thousand times smaller ;-). So here is only the relevant bits.
+(0) Decide what to base your work on.
+
+In general, always base your work on the oldest branch that your
+change is relevant to.
+
+ - A bugfix should be based on 'maint' in general. If the bug is not
+ present in 'maint', base it on 'master'. For a bug that's not yet
+ in 'master', find the topic that introduces the regression, and
+ base your work on the tip of the topic.
+
+ - A new feature should be based on 'master' in general. If the new
+ feature depends on a topic that is in 'pu', but not in 'master',
+ base your work on the tip of that topic.
+
+ - Corrections and enhancements to a topic not yet in 'master' should
+ be based on the tip of that topic. If the topic has not been merged
+ to 'next', it's alright to add a note to squash minor corrections
+ into the series.
+
+ - In the exceptional case that a new feature depends on several topics
+ not in 'master', start working on 'next' or 'pu' privately and send
+ out patches for discussion. Before the final merge, you may have to
+ wait until some of the dependent topics graduate to 'master', and
+ rebase your work.
+
+To find the tip of a topic branch, run "git log --first-parent
+master..pu" and look for the merge commit. The second parent of this
+commit is the tip of the topic branch.
(1) Make separate commits for logically separate changes.
@@ -171,17 +199,16 @@ patch, format it as "multipart/signed", not a text/plain message
that starts with '-----BEGIN PGP SIGNED MESSAGE-----'. That is
not a text/plain, it's something else.
-Note that your maintainer does not necessarily read everything
-on the git mailing list. If your patch is for discussion first,
-send it "To:" the mailing list, and optionally "cc:" him. If it
-is trivially correct or after the list reached a consensus, send
-it "To:" the maintainer and optionally "cc:" the list for
-inclusion.
-
-Also note that your maintainer does not actively involve himself in
-maintaining what are in contrib/ hierarchy. When you send fixes and
-enhancements to them, do not forget to "cc: " the person who primarily
-worked on that hierarchy in contrib/.
+Unless your patch is a very trivial and an obviously correct one,
+first send it with "To:" set to the mailing list, with "cc:" listing
+people who are involved in the area you are touching (the output from
+"git blame $path" and "git shortlog --no-merges $path" would help to
+identify them), to solicit comments and reviews. After the list
+reached a consensus that it is a good idea to apply the patch, re-send
+it with "To:" set to the maintainer and optionally "cc:" the list for
+inclusion. Do not forget to add trailers such as "Acked-by:",
+"Reviewed-by:" and "Tested-by:" after your "Signed-off-by:" line as
+necessary.
(4) Sign your work
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index d8205691c..16e3c6857 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -90,9 +90,9 @@ of lines before or after the line given by <start>.
running extra passes of inspection.
+
<num> is optional but it is the lower bound on the number of
-alphanumeric characters that git must detect as moving
+alphanumeric characters that git must detect as moving/copying
within a file for it to associate those lines with the parent
-commit.
+commit. The default value is 20.
-C|<num>|::
In addition to `-M`, detect lines moved or copied from other
@@ -105,9 +105,11 @@ commit.
looks for copies from other files in any commit.
+
<num> is optional but it is the lower bound on the number of
-alphanumeric characters that git must detect as moving
+alphanumeric characters that git must detect as moving/copying
between files for it to associate those lines with the parent
-commit.
+commit. And the default value is 40. If there are more than one
+`-C` options given, the <num> argument of the last `-C` will
+take effect.
-h::
--help::
diff --git a/Documentation/config.txt b/Documentation/config.txt
index bcf370575..ae174c99d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -518,18 +518,12 @@ check that makes sure that existing object files will not get overwritten.
core.notesRef::
When showing commit messages, also show notes which are stored in
- the given ref. This ref is expected to contain files named
- after the full SHA-1 of the commit they annotate. The ref
- must be fully qualified.
+ the given ref. The ref must be fully qualified. If the given
+ ref does not exist, it is not an error but means that no
+ notes should be printed.
+
-If such a file exists in the given ref, the referenced blob is read, and
-appended to the commit message, separated by a "Notes (<refname>):"
-line (shortened to "Notes:" in the case of "refs/notes/commits"). If the
-given ref itself does not exist, it is not an error, but means that no
-notes should be printed.
-+
-This setting defaults to "refs/notes/commits", and can be overridden by
-the `GIT_NOTES_REF` environment variable.
+This setting defaults to "refs/notes/commits", and it can be overridden by
+the 'GIT_NOTES_REF' environment variable. See linkgit:git-notes[1].
core.sparseCheckout::
Enable "sparse checkout" feature. See section "Sparse checkout" in
@@ -1642,6 +1636,7 @@ sendemail.smtppass::
sendemail.suppresscc::
sendemail.suppressfrom::
sendemail.to::
+sendemail.smtpdomain::
sendemail.smtpserver::
sendemail.smtpserverport::
sendemail.smtpuser::
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index c9c6c2b1c..3070dddfe 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -177,7 +177,14 @@ endif::git-format-patch[]
Break complete rewrite changes into pairs of delete and create.
-M::
+ifndef::git-log[]
Detect renames.
+endif::git-log[]
+ifdef::git-log[]
+ If generating diffs, detect and report renames for each commit.
+ For following files across renames while traversing history, see
+ `--follow`.
+endif::git-log[]
-C::
Detect copies as well as renames. See also `--find-copies-harder`.
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 51cbeb703..74741a42f 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -266,9 +266,9 @@ patch::
y - stage this hunk
n - do not stage this hunk
- q - quit, do not stage this hunk nor any of the remaining ones
- a - stage this and all the remaining hunks in the file
- d - do not stage this hunk nor any of the remaining hunks in the file
+ q - quit; do not stage this hunk nor any of the remaining ones
+ a - stage this hunk and all later hunks in the file
+ d - do not stage this hunk nor any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index a3a87fa7f..7146288ad 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -15,33 +15,41 @@ SYNOPSIS
DESCRIPTION
-----------
-
-When <paths> are not given, this command switches branches by
-updating the index, working tree, and HEAD to reflect the specified
+Updates files in the working tree to match the version in the index
+or the specified tree. If no paths are given, 'git checkout' will
+also update `HEAD` to set the specified branch as the current
branch.
-If `-b` is given, a new branch is created and checked out, as if
-linkgit:git-branch[1] were called; in this case you can
-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.
-
-When <paths> or --patch are given, this command does *not* switch
-branches. It updates the named paths in the working tree from
-the index file, or from a named <tree-ish> (most often a commit). In
-this case, the `-b` and `--track` options are meaningless and giving
-either of them results in an error. The <tree-ish> argument can be
-used to specify a specific tree-ish (i.e. commit, tag or tree)
-to update the index for the given paths before updating the
-working tree.
-
-The index may contain unmerged entries after a failed merge. By
-default, if you try to check out such an entry from the index, the
+'git checkout' [<branch>]::
+'git checkout' -b <new branch> [<start point>]::
+
+ This form switches branches by updating the index, working
+ tree, and HEAD to reflect the specified branch.
++
+If `-b` is given, a new branch is created as if linkgit:git-branch[1]
+were called and then checked out; in this case you can
+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.
+
+'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::
+
+ When <paths> or `--patch` are given, 'git checkout' *not* switch
+ branches. It updates the named paths in the working tree from
+ the index file or from a named <tree-ish> (most often a commit). In
+ this case, the `-b` and `--track` options are meaningless and giving
+ either of them results in an error. The <tree-ish> argument can be
+ used to specify a specific tree-ish (i.e. commit, tag or tree)
+ to update the index for the given paths before updating the
+ working tree.
++
+The index may contain unmerged entries because of a previous failed merge.
+By default, if you try to check out such an entry from the index, the
checkout operation will fail and nothing will be checked out.
-Using -f will ignore these unmerged entries. The contents from a
+Using `-f` will ignore these unmerged entries. The contents from a
specific side of the merge can be checked out of the index by
-using --ours or --theirs. With -m, changes made to the working tree
-file can be discarded to recreate the original conflicted merge result.
+using `--ours` or `--theirs`. With `-m`, changes made to the working tree
+file can be discarded to re-create the original conflicted merge result.
OPTIONS
-------
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index fb184ba18..d7f6a9cc3 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -132,6 +132,48 @@ Discussion
include::i18n.txt[]
+Configuration
+-------------
+
+See linkgit:git-config[1] for core variables and linkgit:git-diff[1]
+for settings related to diff generation.
+
+format.pretty::
+ Default for the `--format` option. (See "PRETTY FORMATS" above.)
+ Defaults to "medium".
+
+i18n.logOutputEncoding::
+ Encoding to use when displaying logs. (See "Discussion", above.)
+ Defaults to the value of `i18n.commitEncoding` if set, UTF-8
+ otherwise.
+
+log.date::
+ Default format for human-readable dates. (Compare the
+ `--date` option.) Defaults to "default", which means to write
+ dates like `Sat May 8 19:35:34 2010 -0500`.
+
+log.showroot::
+ If `false`, 'git log' and related commands will not treat the
+ initial commit as a big creation event. Any root commits in
+ `git log -p` output would be shown without a diff attached.
+ The default is `true`.
+
+mailmap.file::
+ See linkgit:git-shortlog[1].
+
+notes.displayRef::
+ Which refs, in addition to the default set by `core.notesRef`
+ or 'GIT_NOTES_REF', to read notes from when showing commit
+ messages with the 'log' family of commands. See
+ linkgit:git-notes[1].
++
+May be an unabbreviated ref name or a glob and may be specified
+multiple times. A warning will be issued for refs that do not exist,
+but a glob that does not match any refs is silently ignored.
++
+This setting can be disabled by the `--no-standard-notes` option,
+overridden by the 'GIT_NOTES_DISPLAY_REF' environment variable,
+and supplemented by the `--show-notes` option.
Author
------
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index e3d58cbac..3ea5aad56 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -40,16 +40,16 @@ OPTIONS
-u::
The commit log message, author name and author email are
taken from the e-mail, and after minimally decoding MIME
- transfer encoding, re-coded in UTF-8 by transliterating
+ transfer encoding, re-coded in the charset specified by
+ i18n.commitencoding (defaulting to UTF-8) by transliterating
them. This used to be optional but now it is the default.
+
Note that the patch is always used as-is without charset
conversion, even with this flag.
--encoding=<encoding>::
- Similar to -u but if the local convention is different
- from what is specified by i18n.commitencoding, this flag
- can be used to override it.
+ Similar to -u. But when re-coding, the charset specified here is
+ used instead of the one specified by i18n.commitencoding or UTF-8.
-n::
Disable all charset re-coding of the metadata.
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index c2325ef90..84043cc5b 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -58,7 +58,12 @@ include::merge-options.txt[]
-m <msg>::
Set the commit message to be used for the merge commit (in
- case one is created). The 'git fmt-merge-msg' command can be
+ case one is created).
+
+ If `--log` is specified, a shortlog of the commits being merged
+ will be appended to the specified message.
+
+ The 'git fmt-merge-msg' command can be
used to give a good default for automated 'git merge'
invocations.
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index 4e5113b83..de63ef074 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -3,7 +3,7 @@ git-notes(1)
NAME
----
-git-notes - Add/inspect object notes
+git-notes - Add or inspect object notes
SYNOPSIS
--------
@@ -20,24 +20,26 @@ SYNOPSIS
DESCRIPTION
-----------
-This command allows you to add/remove notes to/from objects, without
-changing the objects themselves.
+Adds, removes, or reads notes attached to objects, without touching
+the objects themselves.
-A typical use of notes is to extend a commit message without having
-to change the commit itself. Such commit notes can be shown by `git log`
-along with the original commit message. To discern these notes from the
+By default, notes are saved to and read from `refs/notes/commits`, but
+this default can be overridden. See the OPTIONS, CONFIGURATION, and
+ENVIRONMENT sections below. If this ref does not exist, it will be
+quietly created when it is first needed to store a note.
+
+A typical use of notes is to supplement a commit message without
+changing the commit itself. Notes can be shown by 'git log' along with
+the original commit message. To distinguish these notes from the
message stored in the commit object, the notes are indented like the
message, after an unindented line saying "Notes (<refname>):" (or
-"Notes:" for the default setting).
+"Notes:" for `refs/notes/commits`).
-This command always manipulates the notes specified in "core.notesRef"
-(see linkgit:git-config[1]), which can be overridden by GIT_NOTES_REF.
-To change which notes are shown by 'git-log', see the
-"notes.displayRef" configuration.
+To change which notes are shown by 'git log', see the
+"notes.displayRef" configuration in linkgit:git-log[1].
-See the description of "notes.rewrite.<command>" in
-linkgit:git-config[1] for a way of carrying your notes across commands
-that rewrite commits.
+See the "notes.rewrite.<command>" configuration for a way to carry
+notes across commands that rewrite commits.
SUBCOMMANDS
@@ -101,15 +103,20 @@ OPTIONS
Use the given note message (instead of prompting).
If multiple `-m` options are given, their values
are concatenated as separate paragraphs.
+ Lines starting with `#` and empty lines other than a
+ single line between paragraphs will be stripped out.
-F <file>::
--file=<file>::
Take the note message from the given file. Use '-' to
read the note message from the standard input.
+ Lines starting with `#` and empty lines other than a
+ single line between paragraphs will be stripped out.
-C <object>::
--reuse-message=<object>::
- Reuse the note message from the given note object.
+ Take the note message from the given blob object (for
+ example, another note).
-c <object>::
--reedit-message=<object>::
@@ -117,22 +124,144 @@ OPTIONS
the user can further edit the note message.
--ref <ref>::
- Manipulate the notes tree in <ref>. This overrides both
- GIT_NOTES_REF and the "core.notesRef" configuration. The ref
+ Manipulate the notes tree in <ref>. This overrides
+ 'GIT_NOTES_REF' and the "core.notesRef" configuration. The ref
is taken to be in `refs/notes/` if it is not qualified.
-NOTES
------
+DISCUSSION
+----------
+
+Commit notes are blobs containing extra information about an object
+(usually information to supplement a commit's message). These blobs
+are taken from notes refs. A notes ref is usually a branch which
+contains "files" whose paths are the object names for the objects
+they describe, with some directory separators included for performance
+reasons footnote:[Permitted pathnames have the form
+'ab'`/`'cd'`/`'ef'`/`'...'`/`'abcdef...': a sequence of directory
+names of two hexadecimal digits each followed by a filename with the
+rest of the object ID.].
Every notes change creates a new commit at the specified notes ref.
You can therefore inspect the history of the notes by invoking, e.g.,
-`git log -p notes/commits`.
+`git log -p notes/commits`. Currently the commit message only records
+which operation triggered the update, and the commit authorship is
+determined according to the usual rules (see linkgit:git-commit[1]).
+These details may change in the future.
+
+It is also permitted for a notes ref to point directly to a tree
+object, in which case the history of the notes can be read with
+`git log -p -g <refname>`.
+
+
+EXAMPLES
+--------
+
+You can use notes to add annotations with information that was not
+available at the time a commit was written.
+
+------------
+$ git notes add -m 'Tested-by: Johannes Sixt <j6t@kdbg.org>' 72a144e2
+$ git show -s 72a144e
+[...]
+ Signed-off-by: Junio C Hamano <gitster@pobox.com>
+
+Notes:
+ Tested-by: Johannes Sixt <j6t@kdbg.org>
+------------
+
+In principle, a note is a regular Git blob, and any kind of
+(non-)format is accepted. You can binary-safely create notes from
+arbitrary files using 'git hash-object':
+
+------------
+$ cc *.c
+$ blob=$(git hash-object -w a.out)
+$ git notes --ref=built add -C "$blob" HEAD
+------------
+
+Of course, it doesn't make much sense to display non-text-format notes
+with 'git log', so if you use such notes, you'll probably need to write
+some special-purpose tools to do something useful with them.
+
+
+CONFIGURATION
+-------------
+
+core.notesRef::
+ Notes ref to read and manipulate instead of
+ `refs/notes/commits`. Must be an unabbreviated ref name.
+ This setting can be overridden through the environment and
+ command line.
-Currently the commit message only records which operation triggered
-the update, and the commit authorship is determined according to the
-usual rules (see linkgit:git-commit[1]). These details may change in
-the future.
+notes.displayRef::
+ Which ref (or refs, if a glob or specified more than once), in
+ addition to the default set by `core.notesRef` or
+ 'GIT_NOTES_REF', to read notes from when showing commit
+ messages with the 'git log' family of commands.
+ This setting can be overridden on the command line or by the
+ 'GIT_NOTES_DISPLAY_REF' environment variable.
+ See linkgit:git-log[1].
+
+notes.rewrite.<command>::
+ When rewriting commits with <command> (currently `amend` or
+ `rebase`), if this variable is `false`, git will not copy
+ notes from the original to the rewritten commit. Defaults to
+ `true`. See also "`notes.rewriteRef`" below.
++
+This setting can be overridden by the 'GIT_NOTES_REWRITE_REF'
+environment variable.
+
+notes.rewriteMode::
+ When copying notes during a rewrite, what to do if the target
+ commit already has a note. Must be one of `overwrite`,
+ `concatenate`, and `ignore`. Defaults to `concatenate`.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
+environment variable.
+
+notes.rewriteRef::
+ When copying notes during a rewrite, specifies the (fully
+ qualified) ref whose notes should be copied. May be a glob,
+ in which case notes in all matching refs will be copied. You
+ may also specify this configuration several times.
++
+Does not have a default value; you must configure this variable to
+enable note rewriting.
++
+Can be overridden with the 'GIT_NOTES_REWRITE_REF' environment variable.
+
+
+ENVIRONMENT
+-----------
+
+'GIT_NOTES_REF'::
+ Which ref to manipulate notes from, instead of `refs/notes/commits`.
+ This overrides the `core.notesRef` setting.
+
+'GIT_NOTES_DISPLAY_REF'::
+ Colon-delimited list of refs or globs indicating which refs,
+ in addition to the default from `core.notesRef` or
+ 'GIT_NOTES_REF', to read notes from when showing commit
+ messages.
+ This overrides the `notes.displayRef` setting.
++
+A warning will be issued for refs that do not exist, but a glob that
+does not match any refs is silently ignored.
+
+'GIT_NOTES_REWRITE_MODE'::
+ When copying notes during a rewrite, what to do if the target
+ commit already has a note.
+ Must be one of `overwrite`, `concatenate`, and `ignore`.
+ This overrides the `core.rewriteMode` setting.
+
+'GIT_NOTES_REWRITE_REF'::
+ When rewriting commits, which notes to copy from the original
+ to the rewritten commit. Must be a colon-delimited list of
+ refs or globs.
++
+If not set in the environment, the list of notes to copy depends
+on the `notes.rewrite.<command>` and `notes.rewriteRef` settings.
Author
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 3dfdc7cca..12622fc49 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -119,6 +119,13 @@ Sending
value reverts to plain SMTP. Default is the value of
'sendemail.smtpencryption'.
+--smtp-domain=<FQDN>::
+ Specifies the Fully Qualified Domain Name (FQDN) used in the
+ HELO/EHLO command to the SMTP server. Some servers require the
+ FQDN to match your IP address. If not set, git send-email attempts
+ to determine your FQDN automatically. Default is the value of
+ 'sendemail.smtpdomain'.
+
--smtp-pass[=<password>]::
Password for SMTP-AUTH. The argument is optional: If no
argument is specified, then the empty string is used as
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index dfd4d0c22..bc1ac7749 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
git log --pretty=short | 'git shortlog' [-h] [-n] [-s] [-e] [-w]
-'git shortlog' [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] [<committish>...]
+'git shortlog' [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] <commit>...
DESCRIPTION
-----------
@@ -19,6 +19,11 @@ the first line of the commit message will be shown.
Additionally, "[PATCH]" will be stripped from the commit description.
+If no revisions are passed on the command line and either standard input
+is not a terminal or there is no current branch, 'git shortlog' will
+output a summary of the log read from standard input, without
+reference to the current repository.
+
OPTIONS
-------
@@ -39,6 +44,14 @@ OPTIONS
--email::
Show the email address of each author.
+--format[='<format>']::
+ Instead of the commit subject, use some other information to
+ describe each commit. '<format>' can be any string accepted
+ by the `--format` option of 'git log', such as '{asterisk} [%h] %s'.
+ (See the "PRETTY FORMATS" section of linkgit:git-log[1].)
+
+ Each pretty-printed commit will be rewrapped before it is shown.
+
-w[<width>[,<indent1>[,<indent2>]]]::
Linewrap the output by wrapping each line at `width`. The first
line of each entry is indented by `indent1` spaces, and the second
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 68dc1879f..765d4b312 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -93,8 +93,6 @@ OPTIONS
This option can be also used as a coarse file-level mechanism
to ignore uncommitted changes in tracked files (akin to what
`.gitignore` does for untracked files).
-You should remember that an explicit 'git add' operation will
-still cause the file to be refreshed from the working tree.
Git will fail (gracefully) in case it needs to modify this file
in the index e.g. when merging in a commit;
thus, in case the assumed-untracked file is changed upstream,
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index 98c459dc8..e10fa88b8 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -83,16 +83,20 @@ Patterns have the following format:
- If the pattern does not contain a slash '/', git treats it as
a shell glob pattern and checks for a match against the
- pathname without leading directories.
+ pathname relative to the location of the `.gitignore` file
+ (relative to the toplevel of the work tree if not from a
+ `.gitignore` file).
- Otherwise, git treats the pattern as a shell glob suitable
for consumption by fnmatch(3) with the FNM_PATHNAME flag:
wildcards in the pattern will not match a / in the pathname.
For example, "Documentation/\*.html" matches
- "Documentation/git.html" but not
- "Documentation/ppc/ppc.html". A leading slash matches the
- beginning of the pathname; for example, "/*.c" matches
- "cat-file.c" but not "mozilla-sha1/sha1.c".
+ "Documentation/git.html" but not "Documentation/ppc/ppc.html"
+ or "tools/perf/Documentation/perf.html".
+
+ - A leading slash matches the beginning of the pathname.
+ For example, "/*.c" matches "cat-file.c" but not
+ "mozilla-sha1/sha1.c".
An example:
diff --git a/Makefile b/Makefile
index 61ad6688a..b18768e0a 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,9 @@ all::
# Define EXPATDIR=/foo/bar if your expat header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
#
+# Define HAVE_PATHS_H if you have paths.h and want to use the default PATH
+# it specifies.
+#
# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
#
# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
@@ -735,10 +738,12 @@ EXTLIBS =
ifeq ($(uname_S),Linux)
NO_STRLCPY = YesPlease
NO_MKSTEMPS = YesPlease
+ HAVE_PATHS_H = YesPlease
endif
ifeq ($(uname_S),GNU/kFreeBSD)
NO_STRLCPY = YesPlease
NO_MKSTEMPS = YesPlease
+ HAVE_PATHS_H = YesPlease
endif
ifeq ($(uname_S),UnixWare)
CC = cc
@@ -867,6 +872,7 @@ ifeq ($(uname_S),FreeBSD)
NO_STRTOUMAX = YesPlease
endif
PYTHON_PATH = /usr/local/bin/python
+ HAVE_PATHS_H = YesPlease
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
@@ -875,6 +881,7 @@ ifeq ($(uname_S),OpenBSD)
NEEDS_LIBICONV = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
+ HAVE_PATHS_H = YesPlease
endif
ifeq ($(uname_S),NetBSD)
ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
@@ -884,8 +891,10 @@ ifeq ($(uname_S),NetBSD)
BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
USE_ST_TIMESPEC = YesPlease
NO_MKSTEMPS = YesPlease
+ HAVE_PATHS_H = YesPlease
endif
ifeq ($(uname_S),AIX)
+ DEFAULT_PAGER = more
NO_STRCASESTR=YesPlease
NO_MEMMEM = YesPlease
NO_MKDTEMP = YesPlease
@@ -904,6 +913,7 @@ ifeq ($(uname_S),GNU)
# GNU/Hurd
NO_STRLCPY=YesPlease
NO_MKSTEMPS = YesPlease
+ HAVE_PATHS_H = YesPlease
endif
ifeq ($(uname_S),IRIX)
NO_SETENV = YesPlease
@@ -1353,6 +1363,10 @@ else
LIB_OBJS += thread-utils.o
endif
+ifdef HAVE_PATHS_H
+ BASIC_CFLAGS += -DHAVE_PATHS_H
+endif
+
ifdef DIR_HAS_BSD_GROUP_SEMANTICS
COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
endif
diff --git a/RelNotes b/RelNotes
index 00e77229d..136c1b6af 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.7.1.txt \ No newline at end of file
+Documentation/RelNotes-1.7.1.1.txt \ No newline at end of file
diff --git a/attr.c b/attr.c
index f5346ed32..7467baf2d 100644
--- a/attr.c
+++ b/attr.c
@@ -594,20 +594,25 @@ static int path_matches(const char *pathname, int pathlen,
return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
}
+static int macroexpand_one(int attr_nr, int rem);
+
static int fill_one(const char *what, struct match_attr *a, int rem)
{
struct git_attr_check *check = check_all_attr;
int i;
- for (i = 0; 0 < rem && i < a->num_attr; i++) {
+ for (i = a->num_attr - 1; 0 < rem && 0 <= i; i--) {
struct git_attr *attr = a->state[i].attr;
const char **n = &(check[attr->attr_nr].value);
const char *v = a->state[i].setto;
if (*n == ATTR__UNKNOWN) {
- debug_set(what, a->u.pattern, attr, v);
+ debug_set(what,
+ a->is_macro ? a->u.attr->name : a->u.pattern,
+ attr, v);
*n = v;
rem--;
+ rem = macroexpand_one(attr->attr_nr, rem);
}
}
return rem;
@@ -629,19 +634,27 @@ static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
return rem;
}
-static int macroexpand(struct attr_stack *stk, int rem)
+static int macroexpand_one(int attr_nr, int rem)
{
+ struct attr_stack *stk;
+ struct match_attr *a = NULL;
int i;
- struct git_attr_check *check = check_all_attr;
- for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) {
- struct match_attr *a = stk->attrs[i];
- if (!a->is_macro)
- continue;
- if (check[a->u.attr->attr_nr].value != ATTR__TRUE)
- continue;
+ if (check_all_attr[attr_nr].value != ATTR__TRUE)
+ return rem;
+
+ for (stk = attr_stack; !a && stk; stk = stk->prev)
+ for (i = stk->num_matches - 1; !a && 0 <= i; i--) {
+ struct match_attr *ma = stk->attrs[i];
+ if (!ma->is_macro)
+ continue;
+ if (ma->u.attr->attr_nr == attr_nr)
+ a = ma;
+ }
+
+ if (a)
rem = fill_one("expand", a, rem);
- }
+
return rem;
}
@@ -666,9 +679,6 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
rem = fill(path, pathlen, stk, rem);
- for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
- rem = macroexpand(stk, rem);
-
for (i = 0; i < num; i++) {
const char *value = check_all_attr[check[i].attr->attr_nr].value;
if (value == ATTR__UNKNOWN)
diff --git a/builtin.h b/builtin.h
index 464588b29..322901f33 100644
--- a/builtin.h
+++ b/builtin.h
@@ -16,6 +16,7 @@ extern const char *help_unknown_cmd(const char *cmd);
extern void prune_packed_objects(int);
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
struct strbuf *out);
+extern int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out);
extern int commit_tree(const char *msg, unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author);
diff --git a/builtin/apply.c b/builtin/apply.c
index 771c972c5..59bbcdb13 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1864,13 +1864,13 @@ static int match_fragment(struct image *img,
if (match_end && (preimage->nr + try_lno != img->nr))
return 0;
} else if (ws_error_action == correct_ws_error &&
- (ws_rule & WS_BLANK_AT_EOF) && match_end) {
+ (ws_rule & WS_BLANK_AT_EOF)) {
/*
- * This hunk that matches at the end extends beyond
- * the end of img, and we are removing blank lines
- * at the end of the file. This many lines from the
- * beginning of the preimage must match with img, and
- * the remainder of the preimage must be blank.
+ * This hunk extends beyond the end of img, and we are
+ * removing blank lines at the end of the file. This
+ * many lines from the beginning of the preimage must
+ * match with img, and the remainder of the preimage
+ * must be blank.
*/
preimage_limit = img->nr - try_lno;
} else {
@@ -3141,11 +3141,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
die("unable to remove %s from index", patch->old_name);
}
if (!cached) {
- if (S_ISGITLINK(patch->old_mode)) {
- if (rmdir(patch->old_name))
- warning("unable to remove submodule %s",
- patch->old_name);
- } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
+ if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
remove_path(patch->old_name);
}
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 4dd4c3f49..8506286dd 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -39,7 +39,7 @@ static int show_root;
static int reverse;
static int blank_boundary;
static int incremental;
-static int xdl_opts = XDF_NEED_MINIMAL;
+static int xdl_opts;
static enum date_mode blame_date_mode = DATE_ISO8601;
static size_t blame_date_width;
diff --git a/builtin/clone.c b/builtin/clone.c
index 0bedde41f..3a3625b2a 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -464,7 +464,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
set_git_dir(make_absolute_path(git_dir));
if (0 <= option_verbosity)
- printf("Cloning into %s...\n", get_git_dir());
+ printf("Cloning into %s%s...\n",
+ option_bare ? "bare repository " : "", dir);
init_db(option_template, INIT_DB_QUIET);
/*
diff --git a/builtin/commit.c b/builtin/commit.c
index c5ab683d5..278dcdfa6 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -455,15 +455,21 @@ static void determine_author_info(void)
if (!a)
die("invalid commit: %s", use_message);
- lb = strstr(a + 8, " <");
- rb = strstr(a + 8, "> ");
- eol = strchr(a + 8, '\n');
- if (!lb || !rb || !eol)
+ lb = strchrnul(a + strlen("\nauthor "), '<');
+ rb = strchrnul(lb, '>');
+ eol = strchrnul(rb, '\n');
+ if (!*lb || !*rb || !*eol)
die("invalid commit: %s", use_message);
- name = xstrndup(a + 8, lb - (a + 8));
- email = xstrndup(lb + 2, rb - (lb + 2));
- date = xstrndup(rb + 2, eol - (rb + 2));
+ if (lb == a + strlen("\nauthor "))
+ /* \nauthor <foo@example.com> */
+ name = xcalloc(1, 1);
+ else
+ name = xmemdupz(a + strlen("\nauthor "),
+ (lb - strlen(" ") -
+ (a + strlen("\nauthor "))));
+ email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
+ date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
}
if (force_author) {
@@ -1017,6 +1023,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
int cmd_status(int argc, const char **argv, const char *prefix)
{
struct wt_status s;
+ int fd;
unsigned char sha1[20];
static struct option builtin_status_options[] = {
OPT__VERBOSE(&verbose),
@@ -1050,6 +1057,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
read_cache_preload(s.pathspec);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
+
+ fd = hold_locked_index(&index_lock, 0);
+ if (0 <= fd) {
+ if (!write_cache(fd, active_cache, active_nr))
+ commit_locked_index(&index_lock);
+ rollback_lock_file(&index_lock);
+ }
+
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
s.in_merge = in_merge;
wt_status_collect(&s);
diff --git a/builtin/describe.c b/builtin/describe.c
index 71be2a936..43caff2ff 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -35,7 +35,8 @@ static const char *diff_index_args[] = {
struct commit_name {
struct tag *tag;
- int prio; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned name_checked:1;
unsigned char sha1[20];
char path[FLEX_ARRAY]; /* more */
};
@@ -43,18 +44,53 @@ static const char *prio_names[] = {
"head", "lightweight", "annotated",
};
+static int replace_name(struct commit_name *e,
+ int prio,
+ const unsigned char *sha1,
+ struct tag **tag)
+{
+ if (!e || e->prio < prio)
+ return 1;
+
+ if (e->prio == 2 && prio == 2) {
+ /* Multiple annotated tags point to the same commit.
+ * Select one to keep based upon their tagger date.
+ */
+ struct tag *t;
+
+ if (!e->tag) {
+ t = lookup_tag(e->sha1);
+ if (!t || parse_tag(t))
+ return 1;
+ e->tag = t;
+ }
+
+ t = lookup_tag(sha1);
+ if (!t || parse_tag(t))
+ return 0;
+ *tag = t;
+
+ if (e->tag->date < t->date)
+ return 1;
+ }
+
+ return 0;
+}
+
static void add_to_known_names(const char *path,
struct commit *commit,
int prio,
const unsigned char *sha1)
{
struct commit_name *e = commit->util;
- if (!e || e->prio < prio) {
+ struct tag *tag = NULL;
+ if (replace_name(e, prio, sha1, &tag)) {
size_t len = strlen(path)+1;
free(e);
e = xmalloc(sizeof(struct commit_name) + len);
- e->tag = NULL;
+ e->tag = tag;
e->prio = prio;
+ e->name_checked = 0;
hashcpy(e->sha1, sha1);
memcpy(e->path, path, len);
commit->util = e;
@@ -165,10 +201,15 @@ static void display_name(struct commit_name *n)
{
if (n->prio == 2 && !n->tag) {
n->tag = lookup_tag(n->sha1);
- if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
+ if (!n->tag || parse_tag(n->tag))
die("annotated tag %s not available", n->path);
+ }
+ if (n->tag && !n->name_checked) {
+ if (!n->tag->tag)
+ die("annotated tag %s has no embedded name", n->path);
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
+ n->name_checked = 1;
}
if (n->tag)
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 379a03131..44204257c 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -202,35 +202,10 @@ static void shortlog(const char *name, unsigned char *sha1,
string_list_clear(&subjects, 0);
}
-int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
- int limit = 20, i = 0, pos = 0;
+static void do_fmt_merge_msg_title(struct strbuf *out,
+ const char *current_branch) {
+ int i = 0;
char *sep = "";
- unsigned char head_sha1[20];
- const char *current_branch;
-
- /* get current branch */
- current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
- if (!current_branch)
- die("No current branch");
- if (!prefixcmp(current_branch, "refs/heads/"))
- current_branch += 11;
-
- /* get a line */
- while (pos < in->len) {
- int len;
- char *newline, *p = in->buf + pos;
-
- newline = strchr(p, '\n');
- len = newline ? newline - p : strlen(p);
- pos += len + !!newline;
- i++;
- p[len] = 0;
- if (handle_line(p))
- die ("Error in line %d: %.*s", i, len, p);
- }
-
- if (!srcs.nr)
- return 0;
strbuf_addstr(out, "Merge ");
for (i = 0; i < srcs.nr; i++) {
@@ -278,6 +253,40 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
strbuf_addch(out, '\n');
else
strbuf_addf(out, " into %s\n", current_branch);
+}
+
+static int do_fmt_merge_msg(int merge_title, int merge_summary,
+ struct strbuf *in, struct strbuf *out) {
+ int limit = 20, i = 0, pos = 0;
+ unsigned char head_sha1[20];
+ const char *current_branch;
+
+ /* get current branch */
+ current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+ if (!current_branch)
+ die("No current branch");
+ if (!prefixcmp(current_branch, "refs/heads/"))
+ current_branch += 11;
+
+ /* get a line */
+ while (pos < in->len) {
+ int len;
+ char *newline, *p = in->buf + pos;
+
+ newline = strchr(p, '\n');
+ len = newline ? newline - p : strlen(p);
+ pos += len + !!newline;
+ i++;
+ p[len] = 0;
+ if (handle_line(p))
+ die ("Error in line %d: %.*s", i, len, p);
+ }
+
+ if (!srcs.nr)
+ return 0;
+
+ if (merge_title)
+ do_fmt_merge_msg_title(out, current_branch);
if (merge_summary) {
struct commit *head;
@@ -289,6 +298,9 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
rev.ignore_merges = 1;
rev.limited = 1;
+ if (suffixcmp(out->buf, "\n"))
+ strbuf_addch(out, '\n');
+
for (i = 0; i < origins.nr; i++)
shortlog(origins.items[i].string, origins.items[i].util,
head, &rev, limit, out);
@@ -296,6 +308,14 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
return 0;
}
+int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
+ return do_fmt_merge_msg(1, merge_summary, in, out);
+}
+
+int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
+ return do_fmt_merge_msg(0, 1, in, out);
+}
+
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{
const char *inpath = NULL;
diff --git a/builtin/grep.c b/builtin/grep.c
index 8e928e217..b194ea3ce 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -17,8 +17,8 @@
#include "dir.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static char const * const grep_usage[] = {
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 03d0cd2f4..a89ae831d 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -266,26 +266,23 @@ static void unlink_base_data(struct base_data *c)
static void *unpack_entry_data(unsigned long offset, unsigned long size)
{
+ int status;
z_stream stream;
void *buf = xmalloc(size);
memset(&stream, 0, sizeof(stream));
+ git_inflate_init(&stream);
stream.next_out = buf;
stream.avail_out = size;
- stream.next_in = fill(1);
- stream.avail_in = input_len;
- git_inflate_init(&stream);
- for (;;) {
- int ret = git_inflate(&stream, 0);
- use(input_len - stream.avail_in);
- if (stream.total_out == size && ret == Z_STREAM_END)
- break;
- if (ret != Z_OK)
- bad_object(offset, "inflate returned %d", ret);
+ do {
stream.next_in = fill(1);
stream.avail_in = input_len;
- }
+ status = git_inflate(&stream, 0);
+ use(input_len - stream.avail_in);
+ } while (status == Z_OK);
+ if (stream.total_out != size || status != Z_STREAM_END)
+ bad_object(offset, "inflate returned %d", status);
git_inflate_end(&stream);
return buf;
}
@@ -359,34 +356,38 @@ static void *get_data_from_pack(struct object_entry *obj)
{
off_t from = obj[0].idx.offset + obj[0].hdr_size;
unsigned long len = obj[1].idx.offset - from;
- unsigned long rdy = 0;
- unsigned char *src, *data;
+ unsigned char *data, *inbuf;
z_stream stream;
- int st;
+ int status;
- src = xmalloc(len);
- data = src;
- do {
- ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
- if (n < 0)
- die_errno("cannot pread pack file");
- if (!n)
- die("premature end of pack file, %lu bytes missing",
- len - rdy);
- rdy += n;
- } while (rdy < len);
data = xmalloc(obj->size);
+ inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
+
memset(&stream, 0, sizeof(stream));
+ git_inflate_init(&stream);
stream.next_out = data;
stream.avail_out = obj->size;
- stream.next_in = src;
- stream.avail_in = len;
- git_inflate_init(&stream);
- while ((st = git_inflate(&stream, Z_FINISH)) == Z_OK);
- git_inflate_end(&stream);
- if (st != Z_STREAM_END || stream.total_out != obj->size)
+
+ do {
+ ssize_t n = (len < 64*1024) ? len : 64*1024;
+ n = pread(pack_fd, inbuf, n, from);
+ if (n < 0)
+ die_errno("cannot pread pack file");
+ if (!n)
+ die("premature end of pack file, %lu bytes missing", len);
+ from += n;
+ len -= n;
+ stream.next_in = inbuf;
+ stream.avail_in = n;
+ status = git_inflate(&stream, 0);
+ } while (len && status == Z_OK && !stream.avail_in);
+
+ /* This has been inflated OK when first encountered, so... */
+ if (status != Z_STREAM_END || stream.total_out != obj->size)
die("serious inflate inconsistency");
- free(src);
+
+ git_inflate_end(&stream);
+ free(inbuf);
return data;
}
@@ -668,25 +669,25 @@ static void parse_pack_objects(unsigned char *sha1)
static int write_compressed(struct sha1file *f, void *in, unsigned int size)
{
z_stream stream;
- unsigned long maxsize;
- void *out;
+ int status;
+ unsigned char outbuf[4096];
memset(&stream, 0, sizeof(stream));
deflateInit(&stream, zlib_compression_level);
- maxsize = deflateBound(&stream, size);
- out = xmalloc(maxsize);
-
- /* Compress it */
stream.next_in = in;
stream.avail_in = size;
- stream.next_out = out;
- stream.avail_out = maxsize;
- while (deflate(&stream, Z_FINISH) == Z_OK);
- deflateEnd(&stream);
+ do {
+ stream.next_out = outbuf;
+ stream.avail_out = sizeof(outbuf);
+ status = deflate(&stream, Z_FINISH);
+ sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out);
+ } while (status == Z_OK);
+
+ if (status != Z_STREAM_END)
+ die("unable to deflate appended object (%d)", status);
size = stream.total_out;
- sha1write(f, out, size);
- free(out);
+ deflateEnd(&stream);
return size;
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index c0fbcdcf4..080404769 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -26,8 +26,9 @@ static int show_killed;
static int show_valid_bit;
static int line_terminator = '\n';
+static const char *prefix;
+static int max_prefix_len;
static int prefix_len;
-static int prefix_offset;
static const char **pathspec;
static int error_unmatch;
static char *ps_matched;
@@ -43,10 +44,15 @@ static const char *tag_modified = "";
static const char *tag_skip_worktree = "";
static const char *tag_resolve_undo = "";
+static void write_name(const char* name, size_t len)
+{
+ write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
+ line_terminator);
+}
+
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
- int len = prefix_len;
- int offset = prefix_offset;
+ int len = max_prefix_len;
if (len >= ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
@@ -55,7 +61,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
return;
fputs(tag, stdout);
- write_name_quoted(ent->name + offset, stdout, line_terminator);
+ write_name(ent->name, ent->len);
}
static void show_other_files(struct dir_struct *dir)
@@ -121,8 +127,7 @@ static void show_killed_files(struct dir_struct *dir)
static void show_ce_entry(const char *tag, struct cache_entry *ce)
{
- int len = prefix_len;
- int offset = prefix_offset;
+ int len = max_prefix_len;
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
@@ -156,20 +161,19 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
find_unique_abbrev(ce->sha1,abbrev),
ce_stage(ce));
}
- write_name_quoted(ce->name + offset, stdout, line_terminator);
+ write_name(ce->name, ce_namelen(ce));
}
static int show_one_ru(struct string_list_item *item, void *cbdata)
{
- int offset = prefix_offset;
const char *path = item->string;
struct resolve_undo_info *ui = item->util;
int i, len;
len = strlen(path);
- if (len < prefix_len)
+ if (len < max_prefix_len)
return 0; /* outside of the prefix */
- if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
+ if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
return 0; /* uninterested */
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
@@ -177,19 +181,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
find_unique_abbrev(ui->sha1[i], abbrev),
i + 1);
- write_name_quoted(path + offset, stdout, line_terminator);
+ write_name(path, len);
}
return 0;
}
-static void show_ru_info(const char *prefix)
+static void show_ru_info(void)
{
if (!the_index.resolve_undo)
return;
for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
}
-static void show_files(struct dir_struct *dir, const char *prefix)
+static void show_files(struct dir_struct *dir)
{
int i;
@@ -243,7 +247,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
*/
static void prune_cache(const char *prefix)
{
- int pos = cache_name_pos(prefix, prefix_len);
+ int pos = cache_name_pos(prefix, max_prefix_len);
unsigned int first, last;
if (pos < 0)
@@ -256,7 +260,7 @@ static void prune_cache(const char *prefix)
while (last > first) {
int next = (last + first) >> 1;
struct cache_entry *ce = active_cache[next];
- if (!strncmp(ce->name, prefix, prefix_len)) {
+ if (!strncmp(ce->name, prefix, max_prefix_len)) {
first = next+1;
continue;
}
@@ -265,11 +269,16 @@ static void prune_cache(const char *prefix)
active_nr = last;
}
-static const char *verify_pathspec(const char *prefix)
+static const char *pathspec_prefix(const char *prefix)
{
const char **p, *n, *prev;
unsigned long max;
+ if (!pathspec) {
+ max_prefix_len = prefix ? strlen(prefix) : 0;
+ return prefix;
+ }
+
prev = NULL;
max = PATH_MAX;
for (p = pathspec; (n = *p) != NULL; p++) {
@@ -291,10 +300,7 @@ static const char *verify_pathspec(const char *prefix)
}
}
- if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
- die("git ls-files: cannot generate relative filenames containing '..'");
-
- prefix_len = max;
+ max_prefix_len = max;
return max ? xmemdupz(prev, max) : NULL;
}
@@ -374,7 +380,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
}
}
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
{
/*
* Make sure all pathspec matched; otherwise it is an error.
@@ -404,7 +410,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
continue;
error("pathspec '%s' did not match any file(s) known to git.",
- pathspec[num] + prefix_offset);
+ pathspec[num] + prefix_len);
errors++;
}
return errors;
@@ -456,9 +462,10 @@ static int option_parse_exclude_standard(const struct option *opt,
return 0;
}
-int cmd_ls_files(int argc, const char **argv, const char *prefix)
+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
{
int require_work_tree = 0, show_tag = 0;
+ const char *max_prefix;
struct dir_struct dir;
struct option builtin_ls_files_options[] = {
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -504,7 +511,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
"add the standard git exclusions",
PARSE_OPT_NOARG, option_parse_exclude_standard },
- { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
+ { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
"make the output relative to the project top directory",
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
@@ -516,8 +523,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
};
memset(&dir, 0, sizeof(dir));
+ prefix = cmd_prefix;
if (prefix)
- prefix_offset = strlen(prefix);
+ prefix_len = strlen(prefix);
git_config(git_default_config, NULL);
if (read_cache() < 0)
@@ -555,9 +563,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
if (pathspec)
strip_trailing_slash_from_submodules();
- /* Verify that the pathspec matches the prefix */
- if (pathspec)
- prefix = verify_pathspec(prefix);
+ /* Find common prefix for all pathspec's */
+ max_prefix = pathspec_prefix(prefix);
/* Treat unmatching pathspec elements as errors */
if (pathspec && error_unmatch) {
@@ -575,8 +582,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
show_killed | show_modified | show_resolve_undo))
show_cached = 1;
- if (prefix)
- prune_cache(prefix);
+ if (max_prefix)
+ prune_cache(max_prefix);
if (with_tree) {
/*
* Basic sanity check; show-stages and show-unmerged
@@ -584,15 +591,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
*/
if (show_stage || show_unmerged)
die("ls-files --with-tree is incompatible with -s or -u");
- overlay_tree_on_cache(with_tree, prefix);
+ overlay_tree_on_cache(with_tree, max_prefix);
}
- show_files(&dir, prefix);
+ show_files(&dir);
if (show_resolve_undo)
- show_ru_info(prefix);
+ show_ru_info();
if (ps_matched) {
int bad;
- bad = report_path_error(ps_matched, pathspec, prefix_offset);
+ bad = report_path_error(ps_matched, pathspec, prefix_len);
if (bad)
fprintf(stderr, "Did you forget to 'git add'?\n");
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 610849a65..b8e9e5ba0 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -25,7 +25,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
const char *names[3] = { NULL, NULL, NULL };
mmfile_t mmfs[3];
mmbuffer_t result = {NULL, 0};
- xmparam_t xmp = {{XDF_NEED_MINIMAL}};
+ xmparam_t xmp = {{0}};
int ret = 0, i = 0, to_stdout = 0;
int quiet = 0;
int nongit;
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index a4a4f2ce4..fc00d794d 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -106,7 +106,7 @@ static void show_diff(struct merge_list *entry)
xdemitconf_t xecfg;
xdemitcb_t ecb;
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
ecb.outf = show_outf;
diff --git a/builtin/merge.c b/builtin/merge.c
index c04306684..cae1cbee2 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -973,7 +973,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
reset_hard(remote_head->sha1, 0);
return 0;
} else {
- struct strbuf msg = STRBUF_INIT;
+ struct strbuf merge_names = STRBUF_INIT;
/* We are invoked directly as the first-class UI. */
head_arg = "HEAD";
@@ -986,13 +986,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* codepath so we discard the error in this
* loop.
*/
- if (!have_message) {
- for (i = 0; i < argc; i++)
- merge_name(argv[i], &msg);
- fmt_merge_msg(option_log, &msg, &merge_msg);
- if (merge_msg.len)
- strbuf_setlen(&merge_msg, merge_msg.len-1);
- }
+ for (i = 0; i < argc; i++)
+ merge_name(argv[i], &merge_names);
+
+ if (have_message && option_log)
+ fmt_merge_msg_shortlog(&merge_names, &merge_msg);
+ else if (!have_message)
+ fmt_merge_msg(option_log, &merge_names, &merge_msg);
+
+
+ if (!(have_message && !option_log) && merge_msg.len)
+ strbuf_setlen(&merge_msg, merge_msg.len-1);
}
if (head_invalid || !argc)
diff --git a/builtin/notes.c b/builtin/notes.c
index 52b72fca6..26617546c 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -416,7 +416,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd)
{
struct strbuf buf = STRBUF_INIT;
struct notes_rewrite_cfg *c = NULL;
- struct notes_tree *t;
+ struct notes_tree *t = NULL;
int ret = 0;
if (rewrite_cmd) {
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 97802585e..214d7ef2b 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -18,8 +18,8 @@
#include "refs.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static const char pack_usage[] =
@@ -1522,6 +1522,13 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
#ifndef NO_PTHREADS
+static void try_to_free_from_threads(size_t size)
+{
+ read_lock();
+ release_pack_memory(size, -1);
+ read_unlock();
+}
+
/*
* The main thread waits on the condition that (at least) one of the workers
* has stopped working (which is indicated in the .working member of
@@ -1552,14 +1559,16 @@ static pthread_cond_t progress_cond;
*/
static void init_threaded_search(void)
{
- pthread_mutex_init(&read_mutex, NULL);
+ init_recursive_mutex(&read_mutex);
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
+ set_try_to_free_routine(try_to_free_from_threads);
}
static void cleanup_threaded_search(void)
{
+ set_try_to_free_routine(NULL);
pthread_cond_destroy(&progress_cond);
pthread_mutex_destroy(&read_mutex);
pthread_mutex_destroy(&cache_mutex);
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 34f9acee9..0048f9ef7 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -89,7 +89,7 @@ static int diff_two(const char *file1, const char *label1,
printf("--- a/%s\n+++ b/%s\n", label1, label2);
fflush(stdout);
memset(&xpp, 0, sizeof(xpp));
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
ecb.outf = outf;
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 06320f528..508950280 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -162,7 +162,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
sha1_to_hex(commit->object.sha1));
if (log->user_format) {
struct pretty_print_context ctx = {0};
- ctx.abbrev = DEFAULT_ABBREV;
+ ctx.abbrev = log->abbrev;
ctx.subject = "";
ctx.after_subject = "";
ctx.date_mode = DATE_NORMAL;
@@ -290,6 +290,7 @@ parse_done:
}
log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
+ log.abbrev = rev.abbrev;
/* assume HEAD if from a tty */
if (!nongit && !rev.pending.nr && isatty(0))
diff --git a/cache.h b/cache.h
index 5eb0573bc..0d101e491 100644
--- a/cache.h
+++ b/cache.h
@@ -905,7 +905,7 @@ struct extra_have_objects {
extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
extern int server_supports(const char *feature);
-extern struct packed_git *parse_pack_index(unsigned char *sha1);
+extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
extern void prepare_packed_git(void);
extern void reprepare_packed_git(void);
@@ -916,6 +916,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
extern void pack_report(void);
extern int open_pack_index(struct packed_git *);
+extern void close_pack_index(struct packed_git *);
extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
extern void close_pack_windows(struct packed_git *);
extern void unuse_pack(struct pack_window **);
diff --git a/combine-diff.c b/combine-diff.c
index 7557136c8..655fa89d8 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -220,7 +220,7 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
parent_file.ptr = grab_blob(parent, mode, &sz);
parent_file.size = sz;
memset(&xpp, 0, sizeof(xpp));
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
memset(&state, 0, sizeof(state));
state.nmask = nmask;
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index c72f100f4..a45f8d66d 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -18,11 +18,17 @@
*/
#define pthread_mutex_t CRITICAL_SECTION
-#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
+#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0)
#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
#define pthread_mutex_lock EnterCriticalSection
#define pthread_mutex_unlock LeaveCriticalSection
+typedef int pthread_mutexattr_t;
+#define pthread_mutexattr_init(a) (*(a) = 0)
+#define pthread_mutexattr_destroy(a) do {} while (0)
+#define pthread_mutexattr_settype(a, t) 0
+#define PTHREAD_MUTEX_RECURSIVE 0
+
/*
* Implement simple condition variable for Windows threads, based on ACE
* implementation.
diff --git a/config.mak.in b/config.mak.in
index 6008ac9f1..0d4b64d07 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -31,6 +31,7 @@ NO_OPENSSL=@NO_OPENSSL@
NO_CURL=@NO_CURL@
NO_EXPAT=@NO_EXPAT@
NO_LIBGEN_H=@NO_LIBGEN_H@
+HAVE_PATHS_H=@HAVE_PATHS_H@
NEEDS_LIBICONV=@NEEDS_LIBICONV@
NEEDS_SOCKET=@NEEDS_SOCKET@
NEEDS_RESOLV=@NEEDS_RESOLV@
diff --git a/configure.ac b/configure.ac
index f4d7372ef..71038fcf1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -724,6 +724,12 @@ AC_CHECK_HEADER([libgen.h],
[NO_LIBGEN_H=YesPlease])
AC_SUBST(NO_LIBGEN_H)
#
+# Define HAVE_PATHS_H if you have paths.h.
+AC_CHECK_HEADER([paths.h],
+[HAVE_PATHS_H=YesPlease],
+[HAVE_PATHS_H=])
+AC_SUBST(HAVE_PATHS_H)
+#
# Define NO_STRCASESTR if you don't have strcasestr.
GIT_CHECK_FUNC(strcasestr,
[NO_STRCASESTR=],
diff --git a/date.c b/date.c
index 002aa3c8d..6bae49ca3 100644
--- a/date.c
+++ b/date.c
@@ -229,6 +229,7 @@ static const struct {
{ "GMT", 0, 0, }, /* Greenwich Mean */
{ "UTC", 0, 0, }, /* Universal (Coordinated) */
+ { "Z", 0, 0, }, /* Zulu, alias for UTC */
{ "WET", 0, 0, }, /* Western European */
{ "BST", 0, 1, }, /* British Summer */
@@ -305,7 +306,7 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)
for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
int match = match_string(date, timezone_names[i].name);
- if (match >= 3) {
+ if (match >= 3 || match == strlen(timezone_names[i].name)) {
int off = timezone_names[i].offset;
/* This is bogus, but we like summer */
diff --git a/diff.c b/diff.c
index e40c1271d..57e1212fb 100644
--- a/diff.c
+++ b/diff.c
@@ -717,7 +717,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
memset(&xecfg, 0, sizeof(xecfg));
diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
/* as only the hunk header will be parsed, we need a 0-context */
xecfg.ctxlen = 0;
xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
@@ -1600,6 +1600,7 @@ static void builtin_diff(const char *name_a,
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
+ int must_show_header,
struct diff_options *o,
int complete_rewrite)
{
@@ -1649,21 +1650,24 @@ static void builtin_diff(const char *name_a,
if (lbl[0][0] == '/') {
/* /dev/null */
strbuf_addf(&header, "%snew file mode %06o%s\n", set, two->mode, reset);
- if (xfrm_msg && xfrm_msg[0])
- strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+ if (xfrm_msg)
+ strbuf_addstr(&header, xfrm_msg);
+ must_show_header = 1;
}
else if (lbl[1][0] == '/') {
strbuf_addf(&header, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
- if (xfrm_msg && xfrm_msg[0])
- strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+ if (xfrm_msg)
+ strbuf_addstr(&header, xfrm_msg);
+ must_show_header = 1;
}
else {
if (one->mode != two->mode) {
strbuf_addf(&header, "%sold mode %06o%s\n", set, one->mode, reset);
strbuf_addf(&header, "%snew mode %06o%s\n", set, two->mode, reset);
+ must_show_header = 1;
}
- if (xfrm_msg && xfrm_msg[0])
- strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
+ if (xfrm_msg)
+ strbuf_addstr(&header, xfrm_msg);
/*
* we do not run diff between different kind
@@ -1691,8 +1695,11 @@ static void builtin_diff(const char *name_a,
(diff_filespec_is_binary(two) && !textconv_two) )) {
/* Quite common confusing case */
if (mf1.size == mf2.size &&
- !memcmp(mf1.ptr, mf2.ptr, mf1.size))
+ !memcmp(mf1.ptr, mf2.ptr, mf1.size)) {
+ if (must_show_header)
+ fprintf(o->file, "%s", header.buf);
goto free_ab_and_return;
+ }
fprintf(o->file, "%s", header.buf);
strbuf_reset(&header);
if (DIFF_OPT_TST(o, BINARY))
@@ -1710,7 +1717,7 @@ static void builtin_diff(const char *name_a,
struct emit_callback ecbdata;
const struct userdiff_funcname *pe;
- if (!DIFF_XDL_TST(o, WHITESPACE_FLAGS)) {
+ if (!DIFF_XDL_TST(o, WHITESPACE_FLAGS) || must_show_header) {
fprintf(o->file, "%s", header.buf);
strbuf_reset(&header);
}
@@ -1745,7 +1752,7 @@ static void builtin_diff(const char *name_a,
check_blank_at_eof(&mf1, &mf2, &ecbdata);
ecbdata.file = o->file;
ecbdata.header = header.len ? &header : NULL;
- xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
+ xpp.flags = o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.interhunkctxlen = o->interhunkcontext;
xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1834,7 +1841,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
- xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
+ xpp.flags = o->xdl_opts;
xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
&xpp, &xecfg);
}
@@ -1882,7 +1889,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 1; /* at least one context line */
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
&xpp, &xecfg);
@@ -2319,36 +2326,43 @@ static void fill_metainfo(struct strbuf *msg,
struct diff_filespec *one,
struct diff_filespec *two,
struct diff_options *o,
- struct diff_filepair *p)
+ struct diff_filepair *p,
+ int *must_show_header,
+ int use_color)
{
+ const char *set = diff_get_color(use_color, DIFF_METAINFO);
+ const char *reset = diff_get_color(use_color, DIFF_RESET);
+
+ *must_show_header = 1;
strbuf_init(msg, PATH_MAX * 2 + 300);
switch (p->status) {
case DIFF_STATUS_COPIED:
- strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
- strbuf_addstr(msg, "\ncopy from ");
+ strbuf_addf(msg, "%ssimilarity index %d%%",
+ set, similarity_index(p));
+ strbuf_addf(msg, "%s\n%scopy from ", reset, set);
quote_c_style(name, msg, NULL, 0);
- strbuf_addstr(msg, "\ncopy to ");
+ strbuf_addf(msg, "%s\n%scopy to ", reset, set);
quote_c_style(other, msg, NULL, 0);
- strbuf_addch(msg, '\n');
+ strbuf_addf(msg, "%s\n", reset);
break;
case DIFF_STATUS_RENAMED:
- strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
- strbuf_addstr(msg, "\nrename from ");
+ strbuf_addf(msg, "%ssimilarity index %d%%",
+ set, similarity_index(p));
+ strbuf_addf(msg, "%s\n%srename from ", reset, set);
quote_c_style(name, msg, NULL, 0);
- strbuf_addstr(msg, "\nrename to ");
+ strbuf_addf(msg, "%s\n%srename to ", reset, set);
quote_c_style(other, msg, NULL, 0);
- strbuf_addch(msg, '\n');
+ strbuf_addf(msg, "%s\n", reset);
break;
case DIFF_STATUS_MODIFIED:
if (p->score) {
- strbuf_addf(msg, "dissimilarity index %d%%\n",
- similarity_index(p));
+ strbuf_addf(msg, "%sdissimilarity index %d%%%s\n",
+ set, similarity_index(p), reset);
break;
}
/* fallthru */
default:
- /* nothing */
- ;
+ *must_show_header = 0;
}
if (one && two && hashcmp(one->sha1, two->sha1)) {
int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
@@ -2359,15 +2373,13 @@ static void fill_metainfo(struct strbuf *msg,
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
abbrev = 40;
}
- strbuf_addf(msg, "index %.*s..%.*s",
+ strbuf_addf(msg, "%sindex %.*s..%.*s", set,
abbrev, sha1_to_hex(one->sha1),
abbrev, sha1_to_hex(two->sha1));
if (one->mode == two->mode)
strbuf_addf(msg, " %06o", one->mode);
- strbuf_addch(msg, '\n');
+ strbuf_addf(msg, "%s\n", reset);
}
- if (msg->len)
- strbuf_setlen(msg, msg->len - 1);
}
static void run_diff_cmd(const char *pgm,
@@ -2382,11 +2394,7 @@ static void run_diff_cmd(const char *pgm,
{
const char *xfrm_msg = NULL;
int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
-
- if (msg) {
- fill_metainfo(msg, name, other, one, two, o, p);
- xfrm_msg = msg->len ? msg->buf : NULL;
- }
+ int must_show_header = 0;
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
pgm = NULL;
@@ -2396,6 +2404,17 @@ static void run_diff_cmd(const char *pgm,
pgm = drv->external;
}
+ if (msg) {
+ /*
+ * don't use colors when the header is intended for an
+ * external diff driver
+ */
+ fill_metainfo(msg, name, other, one, two, o, p,
+ &must_show_header,
+ DIFF_OPT_TST(o, COLOR_DIFF) && !pgm);
+ xfrm_msg = msg->len ? msg->buf : NULL;
+ }
+
if (pgm) {
run_external_diff(pgm, name, other, one, two, xfrm_msg,
complete_rewrite);
@@ -2403,7 +2422,8 @@ static void run_diff_cmd(const char *pgm,
}
if (one && two)
builtin_diff(name, other ? other : name,
- one, two, xfrm_msg, o, complete_rewrite);
+ one, two, xfrm_msg, must_show_header,
+ o, complete_rewrite);
else
fprintf(o->file, "* Unmerged path %s\n", name);
}
@@ -3436,7 +3456,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
len2, p->two->path);
git_SHA1_Update(&ctx, buffer, len1);
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_FUNCNAMES;
xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
diff --git a/exec_cmd.c b/exec_cmd.c
index b2c07c70c..bf225706e 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -107,7 +107,7 @@ void setup_path(void)
if (old_path)
strbuf_addstr(&new_path, old_path);
else
- strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
+ strbuf_addstr(&new_path, _PATH_DEFPATH);
setenv("PATH", new_path.buf, 1);
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 21f1330a5..27fc79347 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1111,9 +1111,9 @@ sub help_patch_cmd {
print colored $help_color, <<EOF ;
y - $verb this hunk$target
n - do not $verb this hunk$target
-q - quit, do not $verb this hunk nor any of the remaining ones
-a - $verb this and all the remaining hunks in the file
-d - do not $verb this hunk nor any of the remaining hunks in the file
+q - quit; do not $verb this hunk nor any of the remaining ones
+a - $verb this hunk and all later hunks in the file
+d - do not $verb this hunk nor any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
diff --git a/git-am.sh b/git-am.sh
index 105607554..87ffae252 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -740,7 +740,7 @@ do
;;
esac
- if test $apply_status = 1 && test "$threeway" = t
+ if test $apply_status != 0 && test "$threeway" = t
then
if (fall_back_3way)
then
diff --git a/git-compat-util.h b/git-compat-util.h
index b96912b5d..c0198dde4 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -164,6 +164,13 @@ extern char *gitbasename(char *);
#define PATH_SEP ':'
#endif
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifndef _PATH_DEFPATH
+#define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
+#endif
+
#ifndef STRIP_EXTENSION
#define STRIP_EXTENSION ""
#endif
@@ -356,6 +363,8 @@ static inline void *gitmempcpy(void *dest, const void *src, size_t n)
extern void release_pack_memory(size_t, int);
+extern void set_try_to_free_routine(void (*routine)(size_t));
+
extern char *xstrdup(const char *str);
extern void *xmalloc(size_t size);
extern void *xmallocz(size_t size);
@@ -479,5 +488,14 @@ void git_qsort(void *base, size_t nmemb, size_t size,
* Always returns the return value of unlink(2).
*/
int unlink_or_warn(const char *path);
+/*
+ * Likewise for rmdir(2).
+ */
+int rmdir_or_warn(const char *path);
+/*
+ * Calls the correct function out of {unlink,rmdir}_or_warn based on
+ * the supplied file mode.
+ */
+int remove_or_warn(unsigned int mode, const char *path);
#endif
diff --git a/git-pull.sh b/git-pull.sh
index 1a4729f7b..a09a44ec4 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -43,6 +43,7 @@ merge_args=
curr_branch=$(git symbolic-ref -q HEAD)
curr_branch_short="${curr_branch#refs/heads/}"
rebase=$(git config --bool branch.$curr_branch_short.rebase)
+dry_run=
while :
do
case "$1" in
@@ -104,6 +105,9 @@ do
--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
rebase=false
;;
+ --d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
+ dry_run=--dry-run
+ ;;
-h|--h|--he|--hel|--help)
usage
;;
@@ -216,7 +220,8 @@ test true = "$rebase" && {
done
}
orig_head=$(git rev-parse -q --verify HEAD)
-git fetch $verbosity $progress --update-head-ok "$@" || exit 1
+git fetch $verbosity $progress $dry_run --update-head-ok "$@" || exit 1
+test -z "$dry_run" || exit 0
curr_head=$(git rev-parse -q --verify HEAD)
if test -n "$orig_head" && test "$curr_head" != "$orig_head"
diff --git a/git-send-email.perl b/git-send-email.perl
index ce569a9c8..111c98122 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -132,8 +132,6 @@ my $have_email_valid = eval { require Email::Valid; 1 };
my $have_mail_address = eval { require Mail::Address; 1 };
my $smtp;
my $auth;
-my $mail_domain_default = "localhost.localdomain";
-my $mail_domain;
sub unique_email_list(@);
sub cleanup_compose_files();
@@ -190,7 +188,7 @@ sub do_edit {
# Variables with corresponding config settings
my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
-my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
+my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts, $smtp_domain);
my ($validate, $confirm);
my (@suppress_cc);
@@ -212,6 +210,7 @@ my %config_settings = (
"smtpserverport" => \$smtp_server_port,
"smtpuser" => \$smtp_authuser,
"smtppass" => \$smtp_authpass,
+ "smtpdomain" => \$smtp_domain,
"to" => \@to,
"cc" => \@initial_cc,
"cccmd" => \$cc_cmd,
@@ -283,7 +282,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
"smtp-ssl" => sub { $smtp_encryption = 'ssl' },
"smtp-encryption=s" => \$smtp_encryption,
"smtp-debug:i" => \$debug_net_smtp,
- "smtp-domain:s" => \$mail_domain,
+ "smtp-domain:s" => \$smtp_domain,
"identity=s" => \$identity,
"annotate" => \$annotate,
"compose" => \$compose,
@@ -761,8 +760,7 @@ sub extract_valid_address {
# We'll setup a template for the message id, using the "from" address:
my ($message_id_stamp, $message_id_serial);
-sub make_message_id
-{
+sub make_message_id {
my $uniq;
if (!defined $message_id_stamp) {
$message_id_stamp = sprintf("%s-%s", time, $$);
@@ -817,8 +815,7 @@ sub is_rfc2047_quoted {
}
# use the simplest quoting being able to handle the recipient
-sub sanitize_address
-{
+sub sanitize_address {
my ($recipient) = @_;
my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
@@ -863,21 +860,23 @@ sub sanitize_address
# This maildomain*() code is based on ideas in Perl library Test::Reporter
# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()
-sub maildomain_net
-{
+sub valid_fqdn {
+ my $domain = shift;
+ return !($^O eq 'darwin' && $domain =~ /\.local$/) && $domain =~ /\./;
+}
+
+sub maildomain_net {
my $maildomain;
if (eval { require Net::Domain; 1 }) {
my $domain = Net::Domain::domainname();
- $maildomain = $domain
- unless $^O eq 'darwin' && $domain =~ /\.local$/;
+ $maildomain = $domain if valid_fqdn($domain);
}
return $maildomain;
}
-sub maildomain_mta
-{
+sub maildomain_mta {
my $maildomain;
if (eval { require Net::SMTP; 1 }) {
@@ -887,8 +886,7 @@ sub maildomain_mta
my $domain = $smtp->domain;
$smtp->quit;
- $maildomain = $domain
- unless $^O eq 'darwin' && $domain =~ /\.local$/;
+ $maildomain = $domain if valid_fqdn($domain);
last if $maildomain;
}
@@ -898,17 +896,15 @@ sub maildomain_mta
return $maildomain;
}
-sub maildomain
-{
- return maildomain_net() || maildomain_mta() || $mail_domain_default;
+sub maildomain {
+ return maildomain_net() || maildomain_mta() || 'localhost.localdomain';
}
# Returns 1 if the message was sent, and 0 otherwise.
# In actuality, the whole program dies when there
# is an error sending a message.
-sub send_message
-{
+sub send_message {
my @recipients = unique_email_list(@to);
@cc = (grep { my $cc = extract_valid_address($_);
not grep { $cc eq $_ } @recipients
@@ -1005,18 +1001,18 @@ X-Mailer: git-send-email $gitversion
if ($smtp_encryption eq 'ssl') {
$smtp_server_port ||= 465; # ssmtp
require Net::SMTP::SSL;
- $mail_domain ||= maildomain();
+ $smtp_domain ||= maildomain();
$smtp ||= Net::SMTP::SSL->new($smtp_server,
- Hello => $mail_domain,
+ Hello => $smtp_domain,
Port => $smtp_server_port);
}
else {
require Net::SMTP;
- $mail_domain ||= maildomain();
+ $smtp_domain ||= maildomain();
$smtp ||= Net::SMTP->new((defined $smtp_server_port)
? "$smtp_server:$smtp_server_port"
: $smtp_server,
- Hello => $mail_domain,
+ Hello => $smtp_domain,
Debug => $debug_net_smtp);
if ($smtp_encryption eq 'tls' && $smtp) {
require Net::SMTP::SSL;
@@ -1039,7 +1035,7 @@ X-Mailer: git-send-email $gitversion
die "Unable to initialize SMTP properly. Check config and use --smtp-debug. ",
"VALUES: server=$smtp_server ",
"encryption=$smtp_encryption ",
- "maildomain=$mail_domain",
+ "hello=$smtp_domain",
defined $smtp_server_port ? "port=$smtp_server_port" : "";
}
diff --git a/git-stash.sh b/git-stash.sh
index 59db3dc38..0f858d334 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -86,7 +86,7 @@ create_stash () {
GIT_INDEX_FILE="$TMP-index" &&
export GIT_INDEX_FILE &&
git read-tree -m $i_tree &&
- git add -u &&
+ git diff --name-only -z HEAD | git update-index -z --add --remove --stdin &&
git write-tree &&
rm -f "$TMP-index"
) ) ||
diff --git a/gitweb/Makefile b/gitweb/Makefile
index f2e1d92fb..e7dd25277 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -91,7 +91,7 @@ ifdef CSSMIN
GITWEB_CSS = gitweb.min.css
all:: gitweb.min.css
gitweb.min.css: gitweb.css GITWEB-BUILD-OPTIONS
- $(QUIET_GEN)$(CSSMIN) <$ >$@
+ $(QUIET_GEN)$(CSSMIN) <$< >$@
endif
GITWEB_REPLACE = \
diff --git a/http-walker.c b/http-walker.c
index ef99ae647..8ca76d050 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -510,7 +510,7 @@ static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned c
ret = error("File %s has bad hash", hex);
} else if (req->rename < 0) {
ret = error("unable to write sha1 filename %s",
- req->filename);
+ sha1_file_name(req->sha1));
}
release_http_object_request(req);
diff --git a/http.c b/http.c
index 4814217c6..1320c50e3 100644
--- a/http.c
+++ b/http.c
@@ -1,6 +1,7 @@
#include "http.h"
#include "pack.h"
#include "sideband.h"
+#include "run-command.h"
int data_received;
int active_requests;
@@ -720,7 +721,7 @@ static inline int hex(int v)
return 'A' + v - 10;
}
-static void end_url_with_slash(struct strbuf *buf, const char *url)
+void end_url_with_slash(struct strbuf *buf, const char *url)
{
strbuf_addstr(buf, url);
if (buf->len && buf->buf[buf->len - 1] != '/')
@@ -815,7 +816,21 @@ static int http_request(const char *url, void *result, int target, int options)
ret = HTTP_OK;
else if (missing_target(&results))
ret = HTTP_MISSING_TARGET;
- else
+ else if (results.http_code == 401) {
+ if (user_name) {
+ ret = HTTP_NOAUTH;
+ } else {
+ /*
+ * git_getpass is needed here because its very likely stdin/stdout are
+ * pipes to our parent process. So we instead need to use /dev/tty,
+ * but that is non-portable. Using git_getpass() can at least be stubbed
+ * on other platforms with a different implementation if/when necessary.
+ */
+ user_name = xstrdup(git_getpass("Username: "));
+ init_curl_http_auth(slot->curl);
+ ret = HTTP_REAUTH;
+ }
+ } else
ret = HTTP_ERROR;
} else {
error("Unable to start HTTP request for %s", url);
@@ -831,7 +846,11 @@ static int http_request(const char *url, void *result, int target, int options)
int http_get_strbuf(const char *url, struct strbuf *result, int options)
{
- return http_request(url, result, HTTP_REQUEST_STRBUF, options);
+ int http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
+ if (http_ret == HTTP_REAUTH) {
+ http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
+ }
+ return http_ret;
}
/*
@@ -896,47 +915,67 @@ int http_fetch_ref(const char *base, struct ref *ref)
}
/* Helpers for fetching packs */
-static int fetch_pack_index(unsigned char *sha1, const char *base_url)
+static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
{
- int ret = 0;
- char *hex = xstrdup(sha1_to_hex(sha1));
- char *filename;
- char *url = NULL;
+ char *url, *tmp;
struct strbuf buf = STRBUF_INIT;
- if (has_pack_index(sha1)) {
- ret = 0;
- goto cleanup;
- }
-
if (http_is_verbose)
- fprintf(stderr, "Getting index for pack %s\n", hex);
+ fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
end_url_with_slash(&buf, base_url);
- strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
+ strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
url = strbuf_detach(&buf, NULL);
- filename = sha1_pack_index_name(sha1);
- if (http_get_file(url, filename, 0) != HTTP_OK)
- ret = error("Unable to get pack index %s\n", url);
+ strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
+ tmp = strbuf_detach(&buf, NULL);
+
+ if (http_get_file(url, tmp, 0) != HTTP_OK) {
+ error("Unable to get pack index %s\n", url);
+ free(tmp);
+ tmp = NULL;
+ }
-cleanup:
- free(hex);
free(url);
- return ret;
+ return tmp;
}
static int fetch_and_setup_pack_index(struct packed_git **packs_head,
unsigned char *sha1, const char *base_url)
{
struct packed_git *new_pack;
+ char *tmp_idx = NULL;
+ int ret;
- if (fetch_pack_index(sha1, base_url))
+ if (has_pack_index(sha1)) {
+ new_pack = parse_pack_index(sha1, NULL);
+ if (!new_pack)
+ return -1; /* parse_pack_index() already issued error message */
+ goto add_pack;
+ }
+
+ tmp_idx = fetch_pack_index(sha1, base_url);
+ if (!tmp_idx)
return -1;
- new_pack = parse_pack_index(sha1);
- if (!new_pack)
+ new_pack = parse_pack_index(sha1, tmp_idx);
+ if (!new_pack) {
+ unlink(tmp_idx);
+ free(tmp_idx);
+
return -1; /* parse_pack_index() already issued error message */
+ }
+
+ ret = verify_pack_index(new_pack);
+ if (!ret) {
+ close_pack_index(new_pack);
+ ret = move_temp_to_file(tmp_idx, sha1_pack_index_name(sha1));
+ }
+ free(tmp_idx);
+ if (ret)
+ return -1;
+
+add_pack:
new_pack->next = *packs_head;
*packs_head = new_pack;
return 0;
@@ -1000,37 +1039,62 @@ void release_http_pack_request(struct http_pack_request *preq)
int finish_http_pack_request(struct http_pack_request *preq)
{
- int ret;
struct packed_git **lst;
+ struct packed_git *p = preq->target;
+ char *tmp_idx;
+ struct child_process ip;
+ const char *ip_argv[8];
- preq->target->pack_size = ftell(preq->packfile);
-
- if (preq->packfile != NULL) {
- fclose(preq->packfile);
- preq->packfile = NULL;
- preq->slot->local = NULL;
- }
+ close_pack_index(p);
- ret = move_temp_to_file(preq->tmpfile, preq->filename);
- if (ret)
- return ret;
+ fclose(preq->packfile);
+ preq->packfile = NULL;
+ preq->slot->local = NULL;
lst = preq->lst;
- while (*lst != preq->target)
+ while (*lst != p)
lst = &((*lst)->next);
*lst = (*lst)->next;
- if (verify_pack(preq->target))
+ tmp_idx = xstrdup(preq->tmpfile);
+ strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
+ ".idx.temp");
+
+ ip_argv[0] = "index-pack";
+ ip_argv[1] = "-o";
+ ip_argv[2] = tmp_idx;
+ ip_argv[3] = preq->tmpfile;
+ ip_argv[4] = NULL;
+
+ memset(&ip, 0, sizeof(ip));
+ ip.argv = ip_argv;
+ ip.git_cmd = 1;
+ ip.no_stdin = 1;
+ ip.no_stdout = 1;
+
+ if (run_command(&ip)) {
+ unlink(preq->tmpfile);
+ unlink(tmp_idx);
+ free(tmp_idx);
+ return -1;
+ }
+
+ unlink(sha1_pack_index_name(p->sha1));
+
+ if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1))
+ || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
+ free(tmp_idx);
return -1;
- install_packed_git(preq->target);
+ }
+ install_packed_git(p);
+ free(tmp_idx);
return 0;
}
struct http_pack_request *new_http_pack_request(
struct packed_git *target, const char *base_url)
{
- char *filename;
long prev_posn = 0;
char range[RANGE_HEADER_SIZE];
struct strbuf buf = STRBUF_INIT;
@@ -1045,9 +1109,8 @@ struct http_pack_request *new_http_pack_request(
sha1_to_hex(target->sha1));
preq->url = strbuf_detach(&buf, NULL);
- filename = sha1_pack_name(target->sha1);
- snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
- snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
+ snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
+ sha1_pack_name(target->sha1));
preq->packfile = fopen(preq->tmpfile, "a");
if (!preq->packfile) {
error("Unable to open local file %s for pack",
@@ -1082,7 +1145,6 @@ struct http_pack_request *new_http_pack_request(
return preq;
abort:
- free(filename);
free(preq->url);
free(preq);
return NULL;
@@ -1137,7 +1199,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
freq->localfile = -1;
filename = sha1_file_name(sha1);
- snprintf(freq->filename, sizeof(freq->filename), "%s", filename);
snprintf(freq->tmpfile, sizeof(freq->tmpfile),
"%s.temp", filename);
@@ -1166,8 +1227,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
}
if (freq->localfile < 0) {
- error("Couldn't create temporary file %s for %s: %s",
- freq->tmpfile, freq->filename, strerror(errno));
+ error("Couldn't create temporary file %s: %s",
+ freq->tmpfile, strerror(errno));
goto abort;
}
@@ -1214,8 +1275,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
prev_posn = 0;
lseek(freq->localfile, 0, SEEK_SET);
if (ftruncate(freq->localfile, 0) < 0) {
- error("Couldn't truncate temporary file %s for %s: %s",
- freq->tmpfile, freq->filename, strerror(errno));
+ error("Couldn't truncate temporary file %s: %s",
+ freq->tmpfile, strerror(errno));
goto abort;
}
}
@@ -1291,7 +1352,7 @@ int finish_http_object_request(struct http_object_request *freq)
return -1;
}
freq->rename =
- move_temp_to_file(freq->tmpfile, freq->filename);
+ move_temp_to_file(freq->tmpfile, sha1_file_name(freq->sha1));
return freq->rename;
}
diff --git a/http.h b/http.h
index 5c9441c10..a0b590159 100644
--- a/http.h
+++ b/http.h
@@ -117,6 +117,7 @@ extern void append_remote_object_url(struct strbuf *buf, const char *url,
int only_two_digit_prefix);
extern char *get_remote_object_url(const char *url, const char *hex,
int only_two_digit_prefix);
+extern void end_url_with_slash(struct strbuf *buf, const char *url);
/* Options for http_request_*() */
#define HTTP_NO_CACHE 1
@@ -126,6 +127,8 @@ extern char *get_remote_object_url(const char *url, const char *hex,
#define HTTP_MISSING_TARGET 1
#define HTTP_ERROR 2
#define HTTP_START_FAILED 3
+#define HTTP_REAUTH 4
+#define HTTP_NOAUTH 5
/*
* Requests an url and stores the result in a strbuf.
@@ -152,7 +155,6 @@ struct http_pack_request
struct packed_git *target;
struct packed_git **lst;
FILE *packfile;
- char filename[PATH_MAX];
char tmpfile[PATH_MAX];
struct curl_slist *range_header;
struct active_request_slot *slot;
@@ -167,7 +169,6 @@ extern void release_http_pack_request(struct http_pack_request *preq);
struct http_object_request
{
char *url;
- char filename[PATH_MAX];
char tmpfile[PATH_MAX];
int localfile;
CURLcode curl_result;
diff --git a/merge-file.c b/merge-file.c
index c336c93c0..db4d0d50d 100644
--- a/merge-file.c
+++ b/merge-file.c
@@ -66,7 +66,7 @@ static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
xdemitcb_t ecb;
memset(&xpp, 0, sizeof(xpp));
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_COMMON;
diff --git a/merge-recursive.c b/merge-recursive.c
index 917397ca7..206c10363 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -409,7 +409,7 @@ static int remove_file(struct merge_options *o, int clean,
return -1;
}
if (update_working_directory) {
- if (remove_path(path) && errno != ENOENT)
+ if (remove_path(path))
return -1;
}
return 0;
diff --git a/object.c b/object.c
index 3ca92c4c4..277b3ddba 100644
--- a/object.c
+++ b/object.c
@@ -252,10 +252,10 @@ void add_object_array_with_mode(struct object *obj, const char *name, struct obj
void object_array_remove_duplicates(struct object_array *array)
{
- int ref, src, dst;
+ unsigned int ref, src, dst;
struct object_array_entry *objects = array->objects;
- for (ref = 0; ref < array->nr - 1; ref++) {
+ for (ref = 0; ref + 1 < array->nr; ref++) {
for (src = ref + 1, dst = src;
src < array->nr;
src++) {
diff --git a/pack-check.c b/pack-check.c
index 166ca703c..395fb9527 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -133,14 +133,13 @@ static int verify_packfile(struct packed_git *p,
return err;
}
-int verify_pack(struct packed_git *p)
+int verify_pack_index(struct packed_git *p)
{
off_t index_size;
const unsigned char *index_base;
git_SHA_CTX ctx;
unsigned char sha1[20];
int err = 0;
- struct pack_window *w_curs = NULL;
if (open_pack_index(p))
return error("packfile %s index not opened", p->pack_name);
@@ -154,8 +153,18 @@ int verify_pack(struct packed_git *p)
if (hashcmp(sha1, index_base + index_size - 20))
err = error("Packfile index for %s SHA1 mismatch",
p->pack_name);
+ return err;
+}
+
+int verify_pack(struct packed_git *p)
+{
+ int err = 0;
+ struct pack_window *w_curs = NULL;
+
+ err |= verify_pack_index(p);
+ if (!p->index_data)
+ return -1;
- /* Verify pack file */
err |= verify_packfile(p, &w_curs);
unuse_pack(&w_curs);
diff --git a/pack.h b/pack.h
index d268c014c..bb275762b 100644
--- a/pack.h
+++ b/pack.h
@@ -57,6 +57,7 @@ struct pack_idx_entry {
extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
+extern int verify_pack_index(struct packed_git *);
extern int verify_pack(struct packed_git *);
extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
extern char *index_pack_lockfile(int fd);
diff --git a/pretty.c b/pretty.c
index 7cb3a2af5..2777d3022 100644
--- a/pretty.c
+++ b/pretty.c
@@ -716,7 +716,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
if (add_again(sb, &c->abbrev_commit_hash))
return 1;
strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
- DEFAULT_ABBREV));
+ c->pretty_ctx->abbrev));
c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
return 1;
case 'T': /* tree hash */
@@ -726,7 +726,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
if (add_again(sb, &c->abbrev_tree_hash))
return 1;
strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
- DEFAULT_ABBREV));
+ c->pretty_ctx->abbrev));
c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
return 1;
case 'P': /* parent hashes */
@@ -743,7 +743,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
if (p != commit->parents)
strbuf_addch(sb, ' ');
strbuf_addstr(sb, find_unique_abbrev(
- p->item->object.sha1, DEFAULT_ABBREV));
+ p->item->object.sha1,
+ c->pretty_ctx->abbrev));
}
c->abbrev_parent_hashes.len = sb->len -
c->abbrev_parent_hashes.off;
diff --git a/quote.c b/quote.c
index fc9343572..63d3b0181 100644
--- a/quote.c
+++ b/quote.c
@@ -295,42 +295,75 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen,
fputc(terminator, fp);
}
-/* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
- struct strbuf *out, const char *prefix)
+static const char *path_relative(const char *in, int len,
+ struct strbuf *sb, const char *prefix,
+ int prefix_len);
+
+void write_name_quoted_relative(const char *name, size_t len,
+ const char *prefix, size_t prefix_len,
+ FILE *fp, int terminator)
{
- int needquote;
+ struct strbuf sb = STRBUF_INIT;
+
+ name = path_relative(name, len, &sb, prefix, prefix_len);
+ write_name_quoted(name, fp, terminator);
+
+ strbuf_release(&sb);
+}
+
+/*
+ * Give path as relative to prefix.
+ *
+ * The strbuf may or may not be used, so do not assume it contains the
+ * returned path.
+ */
+static const char *path_relative(const char *in, int len,
+ struct strbuf *sb, const char *prefix,
+ int prefix_len)
+{
+ int off, i;
if (len < 0)
len = strlen(in);
+ if (prefix && prefix_len < 0)
+ prefix_len = strlen(prefix);
+
+ off = 0;
+ i = 0;
+ while (i < prefix_len && i < len && prefix[i] == in[i]) {
+ if (prefix[i] == '/')
+ off = i + 1;
+ i++;
+ }
+ in += off;
+ len -= off;
+
+ if (i >= prefix_len)
+ return in;
- /* "../" prefix itself does not need quoting, but "in" might. */
- needquote = next_quote_pos(in, len) < len;
- strbuf_setlen(out, 0);
- strbuf_grow(out, len);
-
- if (needquote)
- strbuf_addch(out, '"');
- if (prefix) {
- int off = 0;
- while (prefix[off] && off < len && prefix[off] == in[off])
- if (prefix[off] == '/') {
- prefix += off + 1;
- in += off + 1;
- len -= off + 1;
- off = 0;
- } else
- off++;
-
- for (; *prefix; prefix++)
- if (*prefix == '/')
- strbuf_addstr(out, "../");
+ strbuf_reset(sb);
+ strbuf_grow(sb, len);
+
+ while (i < prefix_len) {
+ if (prefix[i] == '/')
+ strbuf_addstr(sb, "../");
+ i++;
}
+ strbuf_add(sb, in, len);
+
+ return sb->buf;
+}
- quote_c_style_counted (in, len, out, NULL, 1);
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+ struct strbuf *out, const char *prefix)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char *rel = path_relative(in, len, &sb, prefix, -1);
+ strbuf_reset(out);
+ quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
+ strbuf_release(&sb);
- if (needquote)
- strbuf_addch(out, '"');
if (!out->len)
strbuf_addstr(out, "./");
diff --git a/quote.h b/quote.h
index f83eb233c..38003bff5 100644
--- a/quote.h
+++ b/quote.h
@@ -54,9 +54,12 @@ extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
extern void write_name_quoted(const char *name, FILE *, int terminator);
extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
const char *name, FILE *, int terminator);
+extern void write_name_quoted_relative(const char *name, size_t len,
+ const char *prefix, size_t prefix_len,
+ FILE *fp, int terminator);
/* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
+extern char *quote_path_relative(const char *in, int len,
struct strbuf *out, const char *prefix);
/* quoting as a string literal for other languages */
diff --git a/remote-curl.c b/remote-curl.c
index b76bfcb3d..24fbb9a9b 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -9,7 +9,7 @@
#include "sideband.h"
static struct remote *remote;
-static const char *url;
+static const char *url; /* always ends with a trailing slash */
struct options {
int verbosity;
@@ -101,7 +101,7 @@ static struct discovery* discover_refs(const char *service)
return last;
free_discovery(last);
- strbuf_addf(&buffer, "%s/info/refs", url);
+ strbuf_addf(&buffer, "%sinfo/refs", url);
if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
is_http = 1;
if (!strchr(url, '?'))
@@ -120,7 +120,7 @@ static struct discovery* discover_refs(const char *service)
strbuf_reset(&buffer);
proto_git_candidate = 0;
- strbuf_addf(&buffer, "%s/info/refs", url);
+ strbuf_addf(&buffer, "%sinfo/refs", url);
refs_url = strbuf_detach(&buffer, NULL);
http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
@@ -132,6 +132,8 @@ static struct discovery* discover_refs(const char *service)
case HTTP_MISSING_TARGET:
die("%s not found: did you run git update-server-info on the"
" server?", refs_url);
+ case HTTP_NOAUTH:
+ die("Authentication failed");
default:
http_error(refs_url, http_ret);
die("HTTP request failed");
@@ -509,7 +511,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
rpc->out = client.out;
strbuf_init(&rpc->result, 0);
- strbuf_addf(&buf, "%s/%s", url, svc);
+ strbuf_addf(&buf, "%s%s", url, svc);
rpc->service_url = strbuf_detach(&buf, NULL);
strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
@@ -798,11 +800,13 @@ int main(int argc, const char **argv)
remote = remote_get(argv[1]);
if (argc > 2) {
- url = argv[2];
+ end_url_with_slash(&buf, argv[2]);
} else {
- url = remote->url[0];
+ end_url_with_slash(&buf, remote->url[0]);
}
+ url = strbuf_detach(&buf, NULL);
+
http_init(remote);
do {
diff --git a/sha1_file.c b/sha1_file.c
index 1b551e460..72de38909 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -102,20 +102,22 @@ static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
*/
char *sha1_file_name(const unsigned char *sha1)
{
- static char *name, *base;
+ static char buf[PATH_MAX];
+ const char *objdir;
+ int len;
- if (!base) {
- const char *sha1_file_directory = get_object_directory();
- int len = strlen(sha1_file_directory);
- base = xmalloc(len + 60);
- memcpy(base, sha1_file_directory, len);
- memset(base+len, 0, 60);
- base[len] = '/';
- base[len+3] = '/';
- name = base + len + 1;
- }
- fill_sha1_path(name, sha1);
- return base;
+ objdir = get_object_directory();
+ len = strlen(objdir);
+
+ /* '/' + sha1(2) + '/' + sha1(38) + '\0' */
+ if (len + 43 > PATH_MAX)
+ die("insanely long object directory %s", objdir);
+ memcpy(buf, objdir, len);
+ buf[len] = '/';
+ buf[len+3] = '/';
+ buf[len+42] = '\0';
+ fill_sha1_path(buf + len + 1, sha1);
+ return buf;
}
static char *sha1_get_pack_name(const unsigned char *sha1,
@@ -599,6 +601,14 @@ void unuse_pack(struct pack_window **w_cursor)
}
}
+void close_pack_index(struct packed_git *p)
+{
+ if (p->index_data) {
+ munmap((void *)p->index_data, p->index_size);
+ p->index_data = NULL;
+ }
+}
+
/*
* This is used by git-repack in case a newly created pack happens to
* contain the same set of objects as an existing one. In that case
@@ -620,8 +630,7 @@ void free_pack_by_name(const char *pack_name)
close_pack_windows(p);
if (p->pack_fd != -1)
close(p->pack_fd);
- if (p->index_data)
- munmap((void *)p->index_data, p->index_size);
+ close_pack_index(p);
free(p->bad_object_sha1);
*pp = p->next;
free(p);
@@ -831,9 +840,8 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
return p;
}
-struct packed_git *parse_pack_index(unsigned char *sha1)
+struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
{
- const char *idx_path = sha1_pack_index_name(sha1);
const char *path = sha1_pack_name(sha1);
struct packed_git *p = alloc_packed_git(strlen(path) + 1);
diff --git a/shortlog.h b/shortlog.h
index bc02cc29e..de4f86fb9 100644
--- a/shortlog.h
+++ b/shortlog.h
@@ -12,6 +12,7 @@ struct shortlog {
int in1;
int in2;
int user_format;
+ int abbrev;
char *common_repo_prefix;
int email;
diff --git a/t/README b/t/README
index dcd3ebb5f..0e4e8d886 100644
--- a/t/README
+++ b/t/README
@@ -84,6 +84,12 @@ appropriately before running "make".
implied by other options like --valgrind and
GIT_TEST_INSTALLED.
+--root=<directory>::
+ Create "trash" directories used to store all temporary data during
+ testing under <directory>, instead of the t/ directory.
+ Using this option with a RAM-based filesystem (such as tmpfs)
+ can massively speed up the test suite.
+
You can also set the GIT_TEST_INSTALLED environment variable to
the bindir of an existing git installation to test that installation.
You still need to have built this git sandbox, from which various
diff --git a/t/t6000lib.sh b/t/lib-t6000.sh
index 985d517a1..985d517a1 100644
--- a/t/t6000lib.sh
+++ b/t/lib-t6000.sh
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 1c77192eb..53bd7fcc4 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -20,8 +20,12 @@ test_expect_success 'setup' '
mkdir -p a/b/d a/c &&
(
+ echo "[attr]notest !test"
echo "f test=f"
echo "a/i test=a/i"
+ echo "onoff test -test"
+ echo "offon -test test"
+ echo "no notest"
) >.gitattributes &&
(
echo "g test=a/g" &&
@@ -30,6 +34,7 @@ test_expect_success 'setup' '
(
echo "h test=a/b/h" &&
echo "d/* test=a/b/d/*"
+ echo "d/yes notest"
) >a/b/.gitattributes
'
@@ -44,6 +49,11 @@ test_expect_success 'attribute test' '
attr_check b/g unspecified &&
attr_check a/b/h a/b/h &&
attr_check a/b/d/g "a/b/d/*"
+ attr_check onoff unset
+ attr_check offon set
+ attr_check no unspecified
+ attr_check a/b/d/no "a/b/d/*"
+ attr_check a/b/d/yes unspecified
'
@@ -58,6 +68,11 @@ a/b/g: test: a/b/g
b/g: test: unspecified
a/b/h: test: a/b/h
a/b/d/g: test: a/b/d/*
+onoff: test: unset
+offon: test: set
+no: test: unspecified
+a/b/d/no: test: a/b/d/*
+a/b/d/yes: test: unspecified
EOF
sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index 20f33436d..27e2127af 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -44,8 +44,10 @@ test_expect_success 'switch from symlink to dir' '
'
-rm -fr frotz xyzzy nitfol &&
-git checkout -f master || exit
+test_expect_success 'Remove temporary directories & switch to master' '
+ rm -fr frotz xyzzy nitfol &&
+ git checkout -f master
+'
test_expect_success 'switch from dir to symlink' '
diff --git a/t/t2106-update-index-assume-unchanged.sh b/t/t2106-update-index-assume-unchanged.sh
new file mode 100755
index 000000000..99d858c6b
--- /dev/null
+++ b/t/t2106-update-index-assume-unchanged.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='git update-index --assume-unchanged test.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' \
+ ': >file &&
+ git add file &&
+ git commit -m initial &&
+ git branch other &&
+ echo upstream >file &&
+ git add file &&
+ git commit -m upstream'
+
+test_expect_success 'do not switch branches with dirty file' \
+ 'git reset --hard &&
+ git checkout other &&
+ echo dirt >file &&
+ git update-index --assume-unchanged file &&
+ test_must_fail git checkout master'
+
+test_done
diff --git a/t/t3307-notes-man.sh b/t/t3307-notes-man.sh
new file mode 100755
index 000000000..3269f2eeb
--- /dev/null
+++ b/t/t3307-notes-man.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='Examples from the git-notes man page
+
+Make sure the manual is not full of lies.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit A &&
+ test_commit B &&
+ test_commit C
+'
+
+test_expect_success 'example 1: notes to add an Acked-by line' '
+ cat <<-\EOF >expect &&
+ B
+
+ Notes:
+ Acked-by: A C Ker <acker@example.com>
+ EOF
+ git notes add -m "Acked-by: A C Ker <acker@example.com>" B &&
+ git show -s B^{commit} >log &&
+ tail -n 4 log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'example 2: binary notes' '
+ cp "$TEST_DIRECTORY"/test4012.png .
+ git checkout B &&
+ blob=$(git hash-object -w test4012.png) &&
+ git notes --ref=logo add -C "$blob" &&
+ git notes --ref=logo copy B C &&
+ git notes --ref=logo show C >actual &&
+ test_cmp test4012.png actual
+'
+
+test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 476e5ec03..8fe14ccc5 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -228,4 +228,154 @@ test_expect_success 'stash --invalid-option' '
test bar,bar2 = $(cat file),$(cat file2)
'
+test_expect_success 'stash an added file' '
+ git reset --hard &&
+ echo new >file3 &&
+ git add file3 &&
+ git stash save "added file" &&
+ ! test -r file3 &&
+ git stash apply &&
+ test new = "$(cat file3)"
+'
+
+test_expect_success 'stash rm then recreate' '
+ git reset --hard &&
+ git rm file &&
+ echo bar7 >file &&
+ git stash save "rm then recreate" &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ test bar7 = "$(cat file)"
+'
+
+test_expect_success 'stash rm and ignore' '
+ git reset --hard &&
+ git rm file &&
+ echo file >.gitignore &&
+ git stash save "rm and ignore" &&
+ test bar = "$(cat file)" &&
+ test file = "$(cat .gitignore)"
+ git stash apply &&
+ ! test -r file &&
+ test file = "$(cat .gitignore)"
+'
+
+test_expect_success 'stash rm and ignore (stage .gitignore)' '
+ git reset --hard &&
+ git rm file &&
+ echo file >.gitignore &&
+ git add .gitignore &&
+ git stash save "rm and ignore (stage .gitignore)" &&
+ test bar = "$(cat file)" &&
+ ! test -r .gitignore
+ git stash apply &&
+ ! test -r file &&
+ test file = "$(cat .gitignore)"
+'
+
+test_expect_success SYMLINKS 'stash file to symlink' '
+ git reset --hard &&
+ rm file &&
+ ln -s file2 file &&
+ git stash save "file to symlink" &&
+ test -f file &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
+ git reset --hard &&
+ git rm file &&
+ ln -s file2 file &&
+ git stash save "file to symlink (stage rm)" &&
+ test -f file &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
+ git reset --hard &&
+ rm file &&
+ ln -s file2 file &&
+ git add file &&
+ git stash save "file to symlink (full stage)" &&
+ test -f file &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+# This test creates a commit with a symlink used for the following tests
+
+test_expect_success SYMLINKS 'stash symlink to file' '
+ git reset --hard &&
+ ln -s file filelink &&
+ git add filelink &&
+ git commit -m "Add symlink" &&
+ rm filelink &&
+ cp file filelink &&
+ git stash save "symlink to file" &&
+ test -h filelink &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ git stash apply &&
+ ! test -h filelink &&
+ test bar = "$(cat file)"
+'
+
+test_expect_success SYMLINKS 'stash symlink to file (stage rm)' '
+ git reset --hard &&
+ git rm filelink &&
+ cp file filelink &&
+ git stash save "symlink to file (stage rm)" &&
+ test -h filelink &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ git stash apply &&
+ ! test -h filelink &&
+ test bar = "$(cat file)"
+'
+
+test_expect_success SYMLINKS 'stash symlink to file (full stage)' '
+ git reset --hard &&
+ rm filelink &&
+ cp file filelink &&
+ git add filelink &&
+ git stash save "symlink to file (full stage)" &&
+ test -h filelink &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ git stash apply &&
+ ! test -h filelink &&
+ test bar = "$(cat file)"
+'
+
+test_expect_failure 'stash directory to file' '
+ git reset --hard &&
+ mkdir dir &&
+ echo foo >dir/file &&
+ git add dir/file &&
+ git commit -m "Add file in dir" &&
+ rm -fr dir &&
+ echo bar >dir &&
+ git stash save "directory to file" &&
+ test -d dir &&
+ test foo = "$(cat dir/file)" &&
+ test_must_fail git stash apply &&
+ test bar = "$(cat dir)" &&
+ git reset --soft HEAD^
+'
+
+test_expect_failure 'stash file to directory' '
+ git reset --hard &&
+ rm file &&
+ mkdir file &&
+ echo foo >file/file &&
+ git stash save "file to directory" &&
+ test -f file &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ test -f file/file &&
+ test foo = "$(cat file/file)"
+'
+
test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 90f334237..7e78851a1 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -396,6 +396,43 @@ test_expect_success 'whitespace-only changes not reported' '
test_cmp expect actual
'
+cat <<EOF >expect
+diff --git a/x b/z
+similarity index NUM%
+rename from x
+rename to z
+index 380c32a..a97b785 100644
+EOF
+test_expect_success 'whitespace-only changes reported across renames' '
+ git reset --hard &&
+ for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+ git add x &&
+ git commit -m "base" &&
+ sed -e "5s/^/ /" x >z &&
+ git rm x &&
+ git add z &&
+ git diff -w -M --cached |
+ sed -e "/^similarity index /s/[0-9][0-9]*/NUM/" >actual &&
+ test_cmp expect actual
+'
+
+cat >expected <<\EOF
+diff --git a/empty b/void
+similarity index 100%
+rename from empty
+rename to void
+EOF
+
+test_expect_success 'rename empty' '
+ git reset --hard &&
+ >empty &&
+ git add empty &&
+ git commit -m empty &&
+ git mv empty void &&
+ git diff -w --cached -M >current &&
+ test_cmp expected current
+'
+
test_expect_success 'combined diff with autocrlf conversion' '
git reset --hard &&
diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh
new file mode 100755
index 000000000..06012811a
--- /dev/null
+++ b/t/t4043-diff-rename-binary.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jakub Narebski, Christian Couder
+#
+
+test_description='Move a binary file'
+
+. ./test-lib.sh
+
+
+test_expect_success 'prepare repository' '
+ git init &&
+ echo foo > foo &&
+ echo "barQ" | q_to_nul > bar &&
+ git add . &&
+ git commit -m "Initial commit"
+'
+
+test_expect_success 'move the files into a "sub" directory' '
+ mkdir sub &&
+ git mv bar foo sub/ &&
+ git commit -m "Moved to sub/"
+'
+
+cat > expected <<\EOF
+ bar => sub/bar | Bin 5 -> 5 bytes
+ foo => sub/foo | 0
+ 2 files changed, 0 insertions(+), 0 deletions(-)
+
+diff --git a/bar b/sub/bar
+similarity index 100%
+rename from bar
+rename to sub/bar
+diff --git a/foo b/sub/foo
+similarity index 100%
+rename from foo
+rename to sub/foo
+EOF
+
+test_expect_success 'git show -C -C report renames' '
+ git show -C -C --raw --binary --stat | tail -n 12 > current &&
+ test_cmp expected current
+'
+
+test_done
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index fb9ad247b..451d75e3f 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -325,6 +325,18 @@ test_expect_success 'two missing blank lines at end with --whitespace=fix' '
test_cmp one expect
'
+test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
+ { echo a; echo; } >one &&
+ git add one &&
+ { echo b; echo a; echo; } >one &&
+ cp one expect &&
+ git diff -- one >patch &&
+ echo a >one &&
+ test_must_fail git apply patch &&
+ git apply --whitespace=fix patch &&
+ test_cmp one expect
+'
+
test_expect_success 'shrink file with tons of missing blanks at end of file' '
{ echo a; echo b; echo c; } >one &&
cp one no-blank-lines &&
diff --git a/t/t4134-apply-submodule.sh b/t/t4134-apply-submodule.sh
new file mode 100755
index 000000000..1b82f93cf
--- /dev/null
+++ b/t/t4134-apply-submodule.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Peter Collingbourne
+#
+
+test_description='git apply submodule tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ cat > create-sm.patch <<EOF
+diff --git a/dir/sm b/dir/sm
+new file mode 160000
+index 0000000..0123456
+--- /dev/null
++++ b/dir/sm
+@@ -0,0 +1 @@
++Subproject commit 0123456789abcdef0123456789abcdef01234567
+EOF
+ cat > remove-sm.patch <<EOF
+diff --git a/dir/sm b/dir/sm
+deleted file mode 160000
+index 0123456..0000000
+--- a/dir/sm
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit 0123456789abcdef0123456789abcdef01234567
+EOF
+'
+
+test_expect_success 'removing a submodule also removes all leading subdirectories' '
+ git apply --index create-sm.patch &&
+ test -d dir/sm &&
+ git apply --index remove-sm.patch &&
+ test \! -d dir
+'
+
+test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index a01e55bf6..cdb70b4b3 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -8,30 +8,93 @@ test_description='git shortlog
. ./test-lib.sh
-echo 1 > a1
-git add a1
-tree=$(git write-tree)
-commit=$( (echo "Test"; echo) | git commit-tree $tree )
-git update-ref HEAD $commit
+test_expect_success 'setup' '
+ echo 1 >a1 &&
+ git add a1 &&
+ tree=$(git write-tree) &&
+ commit=$(printf "%s\n" "Test" "" | git commit-tree "$tree") &&
+ git update-ref HEAD "$commit" &&
+
+ echo 2 >a1 &&
+ git commit --quiet -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1 &&
+
+ # test if the wrapping is still valid
+ # when replacing all is by treble clefs.
+ echo 3 >a1 &&
+ git commit --quiet -m "$(
+ echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" |
+ sed "s/i/1234/g" |
+ tr 1234 "\360\235\204\236")" a1 &&
+
+ # now fsck up the utf8
+ git config i18n.commitencoding non-utf-8 &&
+ echo 4 >a1 &&
+ git commit --quiet -m "$(
+ echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" |
+ sed "s/i/1234/g" |
+ tr 1234 "\370\235\204\236")" a1 &&
+
+ echo 5 >a1 &&
+ git commit --quiet -m "a 12 34 56 78" a1
+
+ echo 6 >a1 &&
+ git commit --quiet -m "Commit by someone else" \
+ --author="Someone else <not!me>" a1 &&
+
+ cat >expect.template <<-\EOF
+ A U Thor (5):
+ SUBJECT
+ SUBJECT
+ SUBJECT
+ SUBJECT
+ SUBJECT
+
+ Someone else (1):
+ SUBJECT
+
+ EOF
+'
-echo 2 > a1
-git commit --quiet -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1
+fuzz() {
+ file=$1 &&
+ sed "
+ s/$_x40/OBJECT_NAME/g
+ s/$_x05/OBJID/g
+ s/^ \{6\}[CTa].*/ SUBJECT/g
+ s/^ \{8\}[^ ].*/ CONTINUATION/g
+ " <"$file" >"$file.fuzzy" &&
+ sed "/CONTINUATION/ d" <"$file.fuzzy"
+}
-# test if the wrapping is still valid when replacing all i's by treble clefs.
-echo 3 > a1
-git commit --quiet -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\360\235\204\236')" a1
+test_expect_success 'default output format' '
+ git shortlog HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect.template log.predictable
+'
-# now fsck up the utf8
-git config i18n.commitencoding non-utf-8
-echo 4 > a1
-git commit --quiet -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\370\235\204\236')" a1
+test_expect_success 'pretty format' '
+ sed s/SUBJECT/OBJECT_NAME/ expect.template >expect &&
+ git shortlog --format="%H" HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect log.predictable
+'
-echo 5 > a1
-git commit --quiet -m "a 12 34 56 78" a1
+test_expect_success '--abbrev' '
+ sed s/SUBJECT/OBJID/ expect.template >expect &&
+ git shortlog --format="%h" --abbrev=5 HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect log.predictable
+'
-git shortlog -w HEAD > out
+test_expect_success 'output from user-defined format is re-wrapped' '
+ sed "s/SUBJECT/two lines/" expect.template >expect &&
+ git shortlog --format="two%nlines" HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect log.predictable
+'
-cat > expect << EOF
+test_expect_success 'shortlog wrapping' '
+ cat >expect <<\EOF &&
A U Thor (5):
Test
This is a very, very long first line for the commit message to see if
@@ -43,14 +106,19 @@ A U Thor (5):
a 12 34
56 78
-EOF
-
-test_expect_success 'shortlog wrapping' 'test_cmp expect out'
+Someone else (1):
+ Commit by someone else
-git log HEAD > log
-GIT_DIR=non-existing git shortlog -w < log > out
+EOF
+ git shortlog -w HEAD >out &&
+ test_cmp expect out
+'
-test_expect_success 'shortlog from non-git directory' 'test_cmp expect out'
+test_expect_success 'shortlog from non-git directory' '
+ git log HEAD >log &&
+ GIT_DIR=non-existing git shortlog -w <log >out &&
+ test_cmp expect out
+'
iconvfromutf8toiso88591() {
printf "%s" "$*" | iconv -f UTF-8 -t ISO8859-1
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
index 795dc2bcd..17e1bdc5a 100755
--- a/t/t5541-http-push.sh
+++ b/t/t5541-http-push.sh
@@ -34,8 +34,34 @@ test_expect_success 'setup remote repository' '
mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
'
-test_expect_success 'clone remote repository' '
+cat >exp <<EOF
+GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'no empty path components' '
+ # In the URL, add a trailing slash, and see if git appends yet another
+ # slash.
cd "$ROOT_PATH" &&
+ git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
+
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+
+ # Clear the log, so that it does not affect the "used receive-pack
+ # service" test which reads the log too.
+ #
+ # We do this before the actual comparison to ensure the log is cleared.
+ echo > "$HTTPD_ROOT_PATH"/access.log &&
+
+ test_cmp exp act
+'
+
+test_expect_success 'clone remote repository' '
+ rm -rf test_repo_clone &&
git clone $HTTPD_URL/smart/test_repo.git test_repo_clone
'
@@ -68,6 +94,7 @@ test_expect_success 'create and delete remote branch' '
'
cat >exp <<EOF
+
GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
index 8cfce969b..fc675b50a 100755
--- a/t/t5550-http-fetch.sh
+++ b/t/t5550-http-fetch.sh
@@ -55,12 +55,43 @@ test_expect_success 'http remote detects correct HEAD' '
test_expect_success 'fetch packed objects' '
cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
- cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
- git --bare repack &&
- git --bare prune-packed &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+ git --bare repack &&
+ git --bare prune-packed
+ ) &&
git clone $HTTPD_URL/dumb/repo_pack.git
'
+test_expect_success 'fetch notices corrupt pack' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+ p=`ls objects/pack/pack-*.pack` &&
+ chmod u+w $p &&
+ printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+ ) &&
+ mkdir repo_bad1.git &&
+ (cd repo_bad1.git &&
+ git --bare init &&
+ test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad1.git &&
+ test 0 = `ls objects/pack/pack-*.pack | wc -l`
+ )
+'
+
+test_expect_success 'fetch notices corrupt idx' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+ p=`ls objects/pack/pack-*.idx` &&
+ chmod u+w $p &&
+ printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+ ) &&
+ mkdir repo_bad2.git &&
+ (cd repo_bad2.git &&
+ git --bare init &&
+ test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad2.git &&
+ test 0 = `ls objects/pack | wc -l`
+ )
+'
+
test_expect_success 'did not use upload-pack service' '
grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
: >exp
diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh
index a8f4419e6..ddc3dc52f 100755
--- a/t/t5704-bundle.sh
+++ b/t/t5704-bundle.sh
@@ -30,4 +30,20 @@ test_expect_success 'tags can be excluded by rev-list options' '
'
+test_expect_failure 'bundle --stdin' '
+
+ echo master | git bundle create stdin-bundle.bdl --stdin &&
+ git ls-remote stdin-bundle.bdl >output &&
+ grep master output
+
+'
+
+test_expect_failure 'bundle --stdin <rev-list options>' '
+
+ echo master | git bundle create hybrid-bundle.bdl --stdin tag &&
+ git ls-remote hybrid-bundle.bdl >output &&
+ grep master output
+
+'
+
test_done
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b4e8fbaa5..fb07536a0 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -5,7 +5,7 @@
test_description='Tests git rev-list --bisect functionality'
. ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
# usage: test_bisection max-diff bisect-option head ^prune...
#
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 2c73f2da7..e4c52b021 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -6,7 +6,7 @@
test_description='Tests git rev-list --topo-order functionality'
. ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
list_duplicates()
{
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index a49b7c572..e8fde5c19 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -191,6 +191,31 @@ test_expect_success 'add LF before non-empty (2)' '
grep "^$" actual
'
+test_expect_success '--abbrev' '
+ echo SHORT SHORT SHORT >expect2 &&
+ echo LONG LONG LONG >expect3 &&
+ git log -1 --format="%h %h %h" HEAD >actual1 &&
+ git log -1 --abbrev=5 --format="%h %h %h" HEAD >actual2 &&
+ git log -1 --abbrev=5 --format="%H %H %H" HEAD >actual3 &&
+ sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual2 >fuzzy2 &&
+ sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual3 >fuzzy3 &&
+ test_cmp expect2 fuzzy2 &&
+ test_cmp expect3 fuzzy3 &&
+ ! test_cmp actual1 actual2
+'
+
+test_expect_success '%H is not affected by --abbrev-commit' '
+ git log -1 --format=%H --abbrev-commit --abbrev=20 HEAD >actual &&
+ len=$(wc -c <actual) &&
+ test $len = 41
+'
+
+test_expect_success '%h is not affected by --abbrev-commit' '
+ git log -1 --format=%h --abbrev-commit --abbrev=20 HEAD >actual &&
+ len=$(wc -c <actual) &&
+ test $len = 21
+'
+
test_expect_success '"%h %gD: %gs" is same as git-reflog' '
git reflog >expect &&
git log -g --format="%h %gD: %gs" >actual &&
@@ -203,6 +228,12 @@ test_expect_success '"%h %gD: %gs" is same as git-reflog (with date)' '
test_cmp expect actual
'
+test_expect_success '"%h %gD: %gs" is same as git-reflog (with --abbrev)' '
+ git reflog --abbrev=13 --date=raw >expect &&
+ git log -g --abbrev=13 --format="%h %gD: %gs" --date=raw >actual &&
+ test_cmp expect actual
+'
+
test_expect_success '%gd shortens ref name' '
echo "master@{0}" >expect.gd-short &&
git log -g -1 --format=%gd refs/heads/master >actual.gd-short &&
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index f105fab98..e673c25e9 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -6,7 +6,7 @@
test_description='Test git rev-parse with different parent options'
. ./test-lib.sh
-. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
date >path0
git update-index --add path0
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 065deadc2..876d1ab74 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -8,7 +8,7 @@ test_description='test describe
o----o----o----o----o----. /
\ A c /
.------------o---o---o
- D e
+ D,R e
'
. ./test-lib.sh
@@ -68,6 +68,8 @@ test_expect_success setup '
echo D >another && git add another && git commit -m D &&
test_tick &&
git tag -a -m D D &&
+ test_tick &&
+ git tag -a -m R R &&
test_tick &&
echo DD >another && git commit -a -m another &&
@@ -89,10 +91,10 @@ test_expect_success setup '
check_describe A-* HEAD
check_describe A-* HEAD^
-check_describe D-* HEAD^^
+check_describe R-* HEAD^^
check_describe A-* HEAD^^2
check_describe B HEAD^^2^
-check_describe D-* HEAD^^^
+check_describe R-* HEAD^^^
check_describe c-* --tags HEAD
check_describe c-* --tags HEAD^
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 5257f4d26..6355698c6 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -13,7 +13,7 @@ test_expect_success 'determine default editor' '
'
-if ! expr "$vi" : '^[a-z]*$' >/dev/null
+if ! expr "$vi" : '[a-z]*$' >/dev/null
then
vi=
fi
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index d9202d5af..0f6b36783 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -4,17 +4,24 @@ test_description='Test automatic use of a pager.'
. ./test-lib.sh
-rm -f stdout_is_tty
+cleanup_fail() {
+ echo >&2 cleanup failed
+ (exit 1)
+}
+
test_expect_success 'set up terminal for tests' '
+ rm -f stdout_is_tty ||
+ cleanup_fail &&
+
if test -t 1
then
- : > stdout_is_tty
+ >stdout_is_tty
elif
test_have_prereq PERL &&
"$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl \
sh -c "test -t 1"
then
- : > test_terminal_works
+ >test_terminal_works
fi
'
@@ -32,53 +39,68 @@ else
say no usable terminal, so skipping some tests
fi
-unset GIT_PAGER GIT_PAGER_IN_USE
-git config --unset core.pager
-PAGER='cat > paginated.out'
-export PAGER
-
test_expect_success 'setup' '
+ unset GIT_PAGER GIT_PAGER_IN_USE &&
+ test_might_fail git config --unset core.pager &&
+
+ PAGER="cat >paginated.out" &&
+ export PAGER &&
+
test_commit initial
'
-rm -f paginated.out
test_expect_success TTY 'some commands use a pager' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
test_terminal git log &&
test -e paginated.out
'
-rm -f paginated.out
test_expect_success TTY 'some commands do not use a pager' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
test_terminal git rev-list HEAD &&
! test -e paginated.out
'
-rm -f paginated.out
test_expect_success 'no pager when stdout is a pipe' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
git log | cat &&
! test -e paginated.out
'
-rm -f paginated.out
test_expect_success 'no pager when stdout is a regular file' '
- git log > file &&
+ rm -f paginated.out ||
+ cleanup_fail &&
+
+ git log >file &&
! test -e paginated.out
'
-rm -f paginated.out
test_expect_success TTY 'git --paginate rev-list uses a pager' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
test_terminal git --paginate rev-list HEAD &&
test -e paginated.out
'
-rm -f file paginated.out
test_expect_success 'no pager even with --paginate when stdout is a pipe' '
+ rm -f file paginated.out ||
+ cleanup_fail &&
+
git --paginate log | cat &&
! test -e paginated.out
'
-rm -f paginated.out
test_expect_success TTY 'no pager with --no-pager' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
test_terminal git --no-pager log &&
! test -e paginated.out
'
@@ -86,88 +108,119 @@ test_expect_success TTY 'no pager with --no-pager' '
# A colored commit log will begin with an appropriate ANSI escape
# for the first color; the text "commit" comes later.
colorful() {
- read firstline < $1
- ! expr "$firstline" : "^[a-zA-Z]" >/dev/null
+ read firstline <$1
+ ! expr "$firstline" : "[a-zA-Z]" >/dev/null
}
-rm -f colorful.log colorless.log
test_expect_success 'tests can detect color' '
- git log --no-color > colorless.log &&
- git log --color > colorful.log &&
+ rm -f colorful.log colorless.log ||
+ cleanup_fail &&
+
+ git log --no-color >colorless.log &&
+ git log --color >colorful.log &&
! colorful colorless.log &&
colorful colorful.log
'
-rm -f colorless.log
-git config color.ui auto
test_expect_success 'no color when stdout is a regular file' '
- git log > colorless.log &&
+ rm -f colorless.log &&
+ git config color.ui auto ||
+ cleanup_fail &&
+
+ git log >colorless.log &&
! colorful colorless.log
'
-rm -f paginated.out
-git config color.ui auto
test_expect_success TTY 'color when writing to a pager' '
- TERM=vt100 test_terminal git log &&
+ rm -f paginated.out &&
+ git config color.ui auto ||
+ cleanup_fail &&
+
+ (
+ TERM=vt100 &&
+ export TERM &&
+ test_terminal git log
+ ) &&
colorful paginated.out
'
-rm -f colorful.log
-git config color.ui auto
test_expect_success 'color when writing to a file intended for a pager' '
- TERM=vt100 GIT_PAGER_IN_USE=true git log > colorful.log &&
+ rm -f colorful.log &&
+ git config color.ui auto ||
+ cleanup_fail &&
+
+ (
+ TERM=vt100 &&
+ GIT_PAGER_IN_USE=true &&
+ export TERM GIT_PAGER_IN_USE &&
+ git log >colorful.log
+ ) &&
colorful colorful.log
'
-unset PAGER GIT_PAGER
-git config --unset core.pager
test_expect_success 'determine default pager' '
+ unset PAGER GIT_PAGER &&
+ test_might_fail git config --unset core.pager ||
+ cleanup_fail &&
+
less=$(git var GIT_PAGER) &&
test -n "$less"
'
-if expr "$less" : '^[a-z]*$' > /dev/null && test_have_prereq TTY
+if expr "$less" : '[a-z][a-z]*$' >/dev/null && test_have_prereq TTY
then
test_set_prereq SIMPLEPAGER
fi
-unset PAGER GIT_PAGER
-git config --unset core.pager
-rm -f default_pager_used
test_expect_success SIMPLEPAGER 'default pager is used by default' '
- cat > $less <<-EOF &&
- #!$SHELL_PATH
- wc > default_pager_used
+ 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 test_terminal git log &&
+ (
+ PATH=.:$PATH &&
+ export PATH &&
+ test_terminal git log
+ ) &&
test -e default_pager_used
'
-unset GIT_PAGER
-git config --unset core.pager
-rm -f PAGER_used
test_expect_success TTY 'PAGER overrides default pager' '
- PAGER="wc > PAGER_used" &&
+ unset GIT_PAGER &&
+ test_might_fail git config --unset core.pager &&
+ rm -f PAGER_used ||
+ cleanup_fail &&
+
+ PAGER="wc >PAGER_used" &&
export PAGER &&
test_terminal git log &&
test -e PAGER_used
'
-unset GIT_PAGER
-rm -f core.pager_used
test_expect_success TTY 'core.pager overrides PAGER' '
+ unset GIT_PAGER &&
+ rm -f core.pager_used ||
+ cleanup_fail &&
+
PAGER=wc &&
export PAGER &&
- git config core.pager "wc > core.pager_used" &&
+ git config core.pager "wc >core.pager_used" &&
test_terminal git log &&
test -e core.pager_used
'
-rm -f GIT_PAGER_used
test_expect_success TTY 'GIT_PAGER overrides core.pager' '
+ rm -f GIT_PAGER_used ||
+ cleanup_fail &&
+
git config core.pager wc &&
- GIT_PAGER="wc > GIT_PAGER_used" &&
+ GIT_PAGER="wc >GIT_PAGER_used" &&
export GIT_PAGER &&
test_terminal git log &&
test -e GIT_PAGER_used
diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh
index d8a7c7985..0335a9a15 100755
--- a/t/t7010-setup.sh
+++ b/t/t7010-setup.sh
@@ -103,14 +103,10 @@ test_expect_success 'git ls-files (relative #3)' '
git add a &&
(
cd a/b &&
- if git ls-files "../e/f"
- then
- echo Gaah, should have failed
- exit 1
- else
- : happy
- fi
- )
+ git ls-files "../e/f"
+ ) >current &&
+ echo ../e/f >expect &&
+ test_cmp expect current
'
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 556d0faa7..008d5711b 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -69,6 +69,34 @@ test_expect_success 'status (2)' '
'
cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+# new file: dir2/added
+#
+# Changed but not updated:
+# modified: dir1/modified
+#
+# Untracked files:
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+EOF
+
+git config advice.statusHints false
+
+test_expect_success 'status (advice.statusHints false)' '
+
+ git status >output &&
+ test_cmp expect output
+
+'
+
+git config --unset advice.statusHints
+
+cat >expect <<\EOF
M dir1/modified
A dir2/added
?? dir1/untracked
@@ -115,6 +143,23 @@ test_expect_success 'status (status.showUntrackedFiles no)' '
test_cmp expect output
'
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# new file: dir2/added
+#
+# Changed but not updated:
+# modified: dir1/modified
+#
+# Untracked files not listed
+EOF
+git config advice.statusHints false
+test_expect_success 'status -uno (advice.statusHints false)' '
+ git status -uno >output &&
+ test_cmp expect output
+'
+git config --unset advice.statusHints
+
cat >expect << EOF
M dir1/modified
A dir2/added
@@ -496,6 +541,16 @@ test_expect_success 'dry-run of partial commit excluding new file in index' '
test_cmp expect output
'
+cat >expect <<EOF
+:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M dir1/modified
+EOF
+test_expect_success 'status refreshes the index' '
+ touch dir2/added &&
+ git status &&
+ git diff-files >output &&
+ test_cmp expect output
+'
+
test_expect_success 'setup status submodule summary' '
test_create_repo sm && (
cd sm &&
@@ -693,4 +748,19 @@ test_expect_success 'commit --dry-run submodule summary (--amend)' '
test_cmp expect output
'
+test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
+ (
+ chmod a-w .git &&
+ # make dir1/tracked stat-dirty
+ >dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked &&
+ git status -s >output &&
+ ! grep dir1/tracked output &&
+ # make sure "status" succeeded without writing index out
+ git diff-files | grep dir1/tracked
+ )
+ status=$?
+ chmod 775 .git
+ (exit $status)
+'
+
test_done
diff --git a/t/t7509-commit.sh b/t/t7509-commit.sh
index d52c060b0..3ea33db6c 100755
--- a/t/t7509-commit.sh
+++ b/t/t7509-commit.sh
@@ -83,6 +83,52 @@ test_expect_success '--amend option copies authorship' '
test_cmp expect actual
'
+sha1_file() {
+ echo "$*" | sed "s#..#.git/objects/&/#"
+}
+remove_object() {
+ rm -f $(sha1_file "$*")
+}
+no_reflog() {
+ cp .git/config .git/config.saved &&
+ echo "[core] logallrefupdates = false" >>.git/config &&
+ test_when_finished "mv -f .git/config.saved .git/config" &&
+
+ if test -e .git/logs
+ then
+ mv .git/logs . &&
+ test_when_finished "mv logs .git/"
+ fi
+}
+
+test_expect_success '--amend option with empty author' '
+ git cat-file commit Initial >tmp &&
+ sed "s/author [^<]* </author </" tmp >empty-author &&
+ no_reflog &&
+ sha=$(git hash-object -t commit -w empty-author) &&
+ test_when_finished "remove_object $sha" &&
+ git checkout $sha &&
+ test_when_finished "git checkout Initial" &&
+ echo "Empty author test" >>foo &&
+ test_tick &&
+ ! git commit -a -m "empty author" --amend 2>err &&
+ grep "empty ident" err
+'
+
+test_expect_success '--amend option with missing author' '
+ git cat-file commit Initial >tmp &&
+ sed "s/author [^<]* </author </" tmp >malformed &&
+ no_reflog &&
+ sha=$(git hash-object -t commit -w malformed) &&
+ test_when_finished "remove_object $sha" &&
+ git checkout $sha &&
+ test_when_finished "git checkout Initial" &&
+ echo "Missing author test" >>foo &&
+ test_tick &&
+ ! git commit -a -m "malformed author" --amend 2>err &&
+ grep "empty ident" err
+'
+
test_expect_success '--reset-author makes the commit ours even with --amend option' '
git checkout Initial &&
echo "Test 6" >>foo &&
diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh
index 269cfdf26..9114785ef 100755
--- a/t/t7604-merge-custom-message.sh
+++ b/t/t7604-merge-custom-message.sh
@@ -6,6 +6,15 @@ Testing merge when using a custom message for the merge commit.'
. ./test-lib.sh
+create_merge_msgs() {
+ echo >exp.subject "custom message"
+
+ cp exp.subject exp.log &&
+ echo >>exp.log "" &&
+ echo >>exp.log "* commit 'c2':" &&
+ echo >>exp.log " c2"
+}
+
test_expect_success 'setup' '
echo c0 > c0.c &&
git add c0.c &&
@@ -19,16 +28,23 @@ test_expect_success 'setup' '
echo c2 > c2.c &&
git add c2.c &&
git commit -m c2 &&
- git tag c2
+ git tag c2 &&
+ create_merge_msgs
'
test_expect_success 'merge c2 with a custom message' '
git reset --hard c1 &&
- echo >expected "custom message" &&
- git merge -m "custom message" c2 &&
+ git merge -m "$(cat exp.subject)" c2 &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+ test_cmp exp.subject actual
+'
+
+test_expect_success 'merge --log appends to custom message' '
+ git reset --hard c1 &&
+ git merge --log -m "$(cat exp.subject)" c2 &&
git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
- test_cmp expected actual
+ test_cmp exp.log actual
'
test_done
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index b9224bdb2..1e9a2eb12 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -14,10 +14,22 @@ compare_git_head_with () {
test_cmp current "$1"
}
+a_utf8_locale=$(locale -a | sed -n '/\.[uU][tT][fF]-*8$/{
+ p
+ q
+}')
+
+if test -n "$a_utf8_locale"
+then
+ test_set_prereq UTF8
+else
+ say "UTF-8 locale not available, some tests are skipped"
+fi
+
compare_svn_head_with () {
# extract just the log message and strip out committer info.
# don't use --limit here since svn 1.1.x doesn't have it,
- LC_ALL=en_US.UTF-8 svn log `git svn info --url` | perl -w -e '
+ LC_ALL="$a_utf8_locale" svn log `git svn info --url` | perl -w -e '
use bytes;
$/ = ("-"x72) . "\n";
my @x = <STDIN>;
@@ -69,12 +81,6 @@ do
'
done
-if locale -a |grep -q en_US.utf8; then
- test_set_prereq UTF8
-else
- say "UTF-8 locale not available, test skipped"
-fi
-
test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' '
(
cd ISO8859-1 &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 7422bba47..9bfa14be7 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -530,6 +530,22 @@ test_must_fail () {
test $? -gt 0 -a $? -le 129 -o $? -gt 192
}
+# Similar to test_must_fail, but tolerates success, too. This is
+# meant to be used in contexts like:
+#
+# test_expect_success 'some command works without configuration' '
+# test_might_fail git config --unset all.configuration &&
+# do something
+# '
+#
+# Writing "git config --unset all.configuration || :" would be wrong,
+# because we want to notice if it fails due to segv.
+
+test_might_fail () {
+ "$@"
+ test $? -ge 0 -a $? -le 129 -o $? -gt 192
+}
+
# test_cmp is a helper function to compare actual and expected output.
# You can use it like:
#
diff --git a/tag.c b/tag.c
index 4470d2bf7..85607c219 100644
--- a/tag.c
+++ b/tag.c
@@ -36,43 +36,50 @@ struct tag *lookup_tag(const unsigned char *sha1)
return (struct tag *) obj;
}
+static unsigned long parse_tag_date(const char *buf, const char *tail)
+{
+ const char *dateptr;
+
+ while (buf < tail && *buf++ != '>')
+ /* nada */;
+ if (buf >= tail)
+ return 0;
+ dateptr = buf;
+ while (buf < tail && *buf++ != '\n')
+ /* nada */;
+ if (buf >= tail)
+ return 0;
+ /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
+ return strtoul(dateptr, NULL, 10);
+}
+
int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
{
- int typelen, taglen;
unsigned char sha1[20];
- const char *type_line, *tag_line, *sig_line;
char type[20];
- const char *start = data;
+ const char *bufptr = data;
+ const char *tail = bufptr + size;
+ const char *nl;
- if (item->object.parsed)
- return 0;
- item->object.parsed = 1;
+ if (item->object.parsed)
+ return 0;
+ item->object.parsed = 1;
if (size < 64)
return -1;
- if (memcmp("object ", data, 7) || get_sha1_hex((char *) data + 7, sha1))
+ if (memcmp("object ", bufptr, 7) || get_sha1_hex(bufptr + 7, sha1) || bufptr[47] != '\n')
return -1;
+ bufptr += 48; /* "object " + sha1 + "\n" */
- type_line = (char *) data + 48;
- if (memcmp("\ntype ", type_line-1, 6))
+ if (prefixcmp(bufptr, "type "))
return -1;
-
- tag_line = memchr(type_line, '\n', size - (type_line - start));
- if (!tag_line || memcmp("tag ", ++tag_line, 4))
+ bufptr += 5;
+ nl = memchr(bufptr, '\n', tail - bufptr);
+ if (!nl || sizeof(type) <= (nl - bufptr))
return -1;
-
- sig_line = memchr(tag_line, '\n', size - (tag_line - start));
- if (!sig_line)
- return -1;
- sig_line++;
-
- typelen = tag_line - type_line - strlen("type \n");
- if (typelen >= 20)
- return -1;
- memcpy(type, type_line + 5, typelen);
- type[typelen] = '\0';
- taglen = sig_line - tag_line - strlen("tag \n");
- item->tag = xmemdupz(tag_line + 4, taglen);
+ strncpy(type, bufptr, nl - bufptr);
+ type[nl - bufptr] = '\0';
+ bufptr = nl + 1;
if (!strcmp(type, blob_type)) {
item->tagged = &lookup_blob(sha1)->object;
@@ -87,6 +94,20 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
item->tagged = NULL;
}
+ if (prefixcmp(bufptr, "tag "))
+ return -1;
+ bufptr += 4;
+ nl = memchr(bufptr, '\n', tail - bufptr);
+ if (!nl)
+ return -1;
+ item->tag = xmemdupz(bufptr, nl - bufptr);
+ bufptr = nl + 1;
+
+ if (!prefixcmp(bufptr, "tagger "))
+ item->date = parse_tag_date(bufptr, tail);
+ else
+ item->date = 0;
+
return 0;
}
diff --git a/tag.h b/tag.h
index 7a0cb0070..47662724a 100644
--- a/tag.h
+++ b/tag.h
@@ -9,7 +9,7 @@ struct tag {
struct object object;
struct object *tagged;
char *tag;
- char *signature; /* not actually implemented */
+ unsigned long date;
};
extern struct tag *lookup_tag(const unsigned char *sha1);
diff --git a/thread-utils.c b/thread-utils.c
index 4f9c829c2..589f838f8 100644
--- a/thread-utils.c
+++ b/thread-utils.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include <pthread.h>
#if defined(hpux) || defined(__hpux) || defined(_hpux)
# include <sys/pstat.h>
@@ -43,3 +44,18 @@ int online_cpus(void)
return 1;
}
+
+int init_recursive_mutex(pthread_mutex_t *m)
+{
+ pthread_mutexattr_t a;
+ int ret;
+
+ ret = pthread_mutexattr_init(&a);
+ if (!ret) {
+ ret = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
+ if (!ret)
+ ret = pthread_mutex_init(m, &a);
+ pthread_mutexattr_destroy(&a);
+ }
+ return ret;
+}
diff --git a/thread-utils.h b/thread-utils.h
index cce4b77bd..1727a0333 100644
--- a/thread-utils.h
+++ b/thread-utils.h
@@ -2,5 +2,6 @@
#define THREAD_COMPAT_H
extern int online_cpus(void);
+extern int init_recursive_mutex(pthread_mutex_t*);
#endif /* THREAD_COMPAT_H */
diff --git a/unpack-trees.c b/unpack-trees.c
index 75f54cac9..490cd5f6f 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -67,16 +67,8 @@ static void unlink_entry(struct cache_entry *ce)
{
if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
return;
- if (S_ISGITLINK(ce->ce_mode)) {
- if (rmdir(ce->name)) {
- warning("unable to rmdir %s: %s",
- ce->name, strerror(errno));
- return;
- }
- }
- else
- if (unlink_or_warn(ce->name))
- return;
+ if (remove_or_warn(ce->ce_mode, ce->name))
+ return;
schedule_dir_for_removal(ce->name, ce_namelen(ce));
}
@@ -862,7 +854,7 @@ static int verify_uptodate_1(struct cache_entry *ce,
{
struct stat st;
- if (o->index_only || (!ce_skip_worktree(ce) && (o->reset || ce_uptodate(ce))))
+ if (o->index_only || (!((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) && (o->reset || ce_uptodate(ce))))
return 0;
if (!lstat(ce->name, &st)) {
diff --git a/userdiff.c b/userdiff.c
index df992490d..38563daa3 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -44,7 +44,9 @@ PATTERNS("pascal",
"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
"|<>|<=|>=|:=|\\.\\."
"|[^[:space:]]|[\x80-\xff]+"),
-PATTERNS("php", "^[\t ]*((function|class).*)",
+PATTERNS("php",
+ "^[\t ]*(((public|protected|private|static)[\t ]+)*function.*)$\n"
+ "^[\t ]*(class.*)$",
/* -- */
"[a-zA-Z_][a-zA-Z0-9_]*"
"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
diff --git a/wrapper.c b/wrapper.c
index 9c71b2124..58201b6bc 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -3,11 +3,23 @@
*/
#include "cache.h"
+static void try_to_free_builtin(size_t size)
+{
+ release_pack_memory(size, -1);
+}
+
+static void (*try_to_free_routine)(size_t size) = try_to_free_builtin;
+
+void set_try_to_free_routine(void (*routine)(size_t))
+{
+ try_to_free_routine = (routine) ? routine : try_to_free_builtin;
+}
+
char *xstrdup(const char *str)
{
char *ret = strdup(str);
if (!ret) {
- release_pack_memory(strlen(str) + 1, -1);
+ try_to_free_routine(strlen(str) + 1);
ret = strdup(str);
if (!ret)
die("Out of memory, strdup failed");
@@ -21,7 +33,7 @@ void *xmalloc(size_t size)
if (!ret && !size)
ret = malloc(1);
if (!ret) {
- release_pack_memory(size, -1);
+ try_to_free_routine(size);
ret = malloc(size);
if (!ret && !size)
ret = malloc(1);
@@ -67,7 +79,7 @@ void *xrealloc(void *ptr, size_t size)
if (!ret && !size)
ret = realloc(ptr, 1);
if (!ret) {
- release_pack_memory(size, -1);
+ try_to_free_routine(size);
ret = realloc(ptr, size);
if (!ret && !size)
ret = realloc(ptr, 1);
@@ -83,7 +95,7 @@ void *xcalloc(size_t nmemb, size_t size)
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
if (!ret) {
- release_pack_memory(nmemb * size, -1);
+ try_to_free_routine(nmemb * size);
ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
@@ -311,18 +323,30 @@ int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
}
-int unlink_or_warn(const char *file)
+static int warn_if_unremovable(const char *op, const char *file, int rc)
{
- int rc = unlink(file);
-
if (rc < 0) {
int err = errno;
if (ENOENT != err) {
- warning("unable to unlink %s: %s",
- file, strerror(errno));
+ warning("unable to %s %s: %s",
+ op, file, strerror(errno));
errno = err;
}
}
return rc;
}
+int unlink_or_warn(const char *file)
+{
+ return warn_if_unremovable("unlink", file, unlink(file));
+}
+
+int rmdir_or_warn(const char *file)
+{
+ return warn_if_unremovable("rmdir", file, rmdir(file));
+}
+
+int remove_or_warn(unsigned int mode, const char *file)
+{
+ return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
+}
diff --git a/wt-status.c b/wt-status.c
index 8ca59a2d2..d44486c82 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -625,7 +625,9 @@ void wt_status_print(struct wt_status *s)
if (s->show_untracked_files)
wt_status_print_untracked(s);
else if (s->commitable)
- fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
+ fprintf(s->fp, "# Untracked files not listed%s\n",
+ advice_status_hints
+ ? " (use -u option to show untracked files)" : "");
if (s->verbose)
wt_status_print_verbose(s);
@@ -635,15 +637,22 @@ void wt_status_print(struct wt_status *s)
else if (s->nowarn)
; /* nothing */
else if (s->workdir_dirty)
- printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
+ printf("no changes added to commit%s\n",
+ advice_status_hints
+ ? " (use \"git add\" and/or \"git commit -a\")" : "");
else if (s->untracked.nr)
- printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
+ printf("nothing added to commit but untracked files present%s\n",
+ advice_status_hints
+ ? " (use \"git add\" to track)" : "");
else if (s->is_initial)
- printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
+ printf("nothing to commit%s\n", advice_status_hints
+ ? " (create/copy files and use \"git add\" to track)" : "");
else if (!s->show_untracked_files)
- printf("nothing to commit (use -u to show untracked files)\n");
+ printf("nothing to commit%s\n", advice_status_hints
+ ? " (use -u to show untracked files)" : "");
else
- printf("nothing to commit (working directory clean)\n");
+ printf("nothing to commit%s\n", advice_status_hints
+ ? " (working directory clean)" : "");
}
}