--- /dev/null
+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.
+
+ * "git am -3" did not show diagnosis when the patch in the message was corrupt.
+
+ * "git bundle --stdin" segfaulted.
+
+ * "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 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 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.
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.
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
GMail does not appear to have any way to turn off line wrapping in the web
interface, so this will mangle any emails that you send. You can however
-use "git send e-mail" and send your patches through the GMail SMTP server, or
+use "git send-email" and send your patches through the GMail SMTP server, or
use any IMAP email client to connect to the google IMAP server and forward
the emails through that.
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
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::
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
sendemail.suppresscc::
sendemail.suppressfrom::
sendemail.to::
+sendemail.smtpdomain::
sendemail.smtpserver::
sendemail.smtpserverport::
sendemail.smtpuser::
This variable can be overridden with the -u|--untracked-files option
of linkgit:git-status[1] and linkgit:git-commit[1].
+status.submodulesummary::
+ Defaults to false.
+ If this is set to a non zero number or true (identical to -1 or an
+ unlimited number), the submodule summary will be enabled and a
+ summary of commits for modified submodules will be shown (see
+ --summary-limit option of linkgit:git-submodule[1]).
+
tar.umask::
This variable can be used to restrict the permission bits of
tar archive entries. The default is 0002, which turns off the
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
As a special case, the `"@\{-N\}"` syntax for the N-th last branch
checks out the branch (instead of detaching). You may also specify
`-` which is synonymous with `"@\{-1\}"`.
++
+As a further special case, you may use `"A...B"` as a shortcut for the
+merge base of `A` and `B` if there is exactly one merge base. You can
+leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
<new_branch>::
Name for the new branch.
read the message from the standard input.
--author=<author>::
- Override the author name used in the commit. You can use the
- standard `A U Thor <author@example.com>` format. Otherwise,
- an existing commit that matches the given string and its author
- name is used.
+ Override the commit author. Specify an explicit author using the
+ standard `A U Thor <author@example.com>` format. Otherwise <author>
+ is assumed to be a pattern and is used to search for an existing
+ commit by that author (i.e. rev-list --all -i --author=<author>);
+ the commit author is then copied from the first such commit found.
--date=<date>::
Override the author date used in the commit.
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
------
-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.
NAME
----
-git-notes - Add/inspect object notes
+git-notes - Add or inspect object notes
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
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>::
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
--onto option is not specified, the starting point is
<upstream>. May be any valid commit, and not just an
existing branch name.
++
+As a special case, you may use "A...B" as a shortcut for the
+merge base of A and B if there is exactly one merge base. You can
+leave out at most one of A and B, in which case it defaults to HEAD.
<upstream>::
Upstream branch to compare against. May be any valid commit,
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
--------
[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
-----------
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
-------
--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
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,
true parent commits, without taking grafts nor history
simplification into account.
-* 'format:'
+* 'format:<string>'
+
-The 'format:' format allows you to specify which information
+The 'format:<string>' format allows you to specify which information
you want to show. It works a little bit like printf format,
with the notable exception that you get a newline with '%n'
instead of '\n'.
Pretty-print the contents of the commit logs in a given format,
where '<format>' can be one of 'oneline', 'short', 'medium',
- 'full', 'fuller', 'email', 'raw' and 'format:<string>'.
- When omitted, the format defaults to 'medium'.
+ 'full', 'fuller', 'email', 'raw' and 'format:<string>'. See
+ the "PRETTY FORMATS" section for some additional details for each
+ format. When omitted, the format defaults to 'medium'.
+
Note: you can specify the default pretty format in the repository
configuration (see linkgit:git-config[1]).
# 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
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
NO_STRTOUMAX = YesPlease
endif
PYTHON_PATH = /usr/local/bin/python
+ HAVE_PATHS_H = YesPlease
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
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)
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
# GNU/Hurd
NO_STRLCPY=YesPlease
NO_MKSTEMPS = YesPlease
+ HAVE_PATHS_H = YesPlease
endif
ifeq ($(uname_S),IRIX)
NO_SETENV = YesPlease
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
-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
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;
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;
}
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)
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 {
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;
strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
printf("%s%c%d %d %d\n",
hex,
- ent->guilty ? ' ' : '*', // purely for debugging
+ ent->guilty ? ' ' : '*', /* purely for debugging */
ent->s_lno + 1,
ent->lno + 1,
ent->num_lines);
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),
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);
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 */
};
"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;
{
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)
grab_person("committer", val, deref, obj, buf, sz);
break;
case OBJ_TREE:
- // grab_tree_values(val, deref, obj, buf, sz);
+ /* grab_tree_values(val, deref, obj, buf, sz); */
break;
case OBJ_BLOB:
- // grab_blob_values(val, deref, obj, buf, sz);
+ /* grab_blob_values(val, deref, obj, buf, sz); */
break;
default:
die("Eh? Object of type %d?", obj->type);
#include "dir.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static char const * const grep_usage[] = {
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;
}
{
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;
}
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;
}
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;
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;
{
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) {
#include "refs.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static const char pack_usage[] =
#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
*/
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);
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;
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;
}
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))
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);
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 **);
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;
*/
#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.
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@
[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=],
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,
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);
}
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);
}
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);
}
- 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
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;
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);
}
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);
struct diff_filespec *one,
struct diff_filespec *two,
struct diff_options *o,
- struct diff_filepair *p)
+ struct diff_filepair *p,
+ int use_color)
{
+ const char *set = diff_get_color(use_color, DIFF_METAINFO);
+ const char *reset = diff_get_color(use_color, DIFF_RESET);
+
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 */
(!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,
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;
- }
-
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
pgm = NULL;
else {
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,
+ 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);
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,
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);
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
;;
esac
- if test $apply_status = 1 && test "$threeway" = t
+ if test $apply_status != 0 && test "$threeway" = t
then
if (fall_back_3way)
then
# define _XOPEN_SOURCE 500
# endif
#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
- !defined(_M_UNIX) && !defined(sgi) && !defined(__DragonFly__)
+ !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__)
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
#endif
#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
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);
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
--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
;;
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"
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();
# 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);
"smtpserverport" => \$smtp_server_port,
"smtpuser" => \$smtp_authuser,
"smtppass" => \$smtp_authpass,
+ "smtpdomain" => \$smtp_domain,
"to" => \@to,
"cc" => \@initial_cc,
"cccmd" => \$cc_cmd,
"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,
# 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, $$);
}
# 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*(<.*)/);
# 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 }) {
my $domain = $smtp->domain;
$smtp->quit;
- $maildomain = $domain
- unless $^O eq 'darwin' && $domain =~ /\.local$/;
+ $maildomain = $domain if valid_fqdn($domain);
last if $maildomain;
}
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
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;
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" : "";
}
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"
) ) ||
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 = \
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);
#include "http.h"
#include "pack.h"
#include "sideband.h"
+#include "run-command.h"
int data_received;
int active_requests;
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] != '/')
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);
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;
}
/*
}
/* 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;
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;
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",
return preq;
abort:
- free(filename);
free(preq->url);
free(preq);
return NULL;
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);
}
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;
}
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;
}
}
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;
}
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
#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.
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;
struct http_object_request
{
char *url;
- char filename[PATH_MAX];
char tmpfile[PATH_MAX];
int localfile;
CURLcode curl_result;
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;
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++) {
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);
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);
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);
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 */
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 */
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;
#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;
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, '?'))
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);
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");
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);
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 {
unsigned char sha1[20];
const char *head_ref;
int flag;
- if (default_remote_name) // did this already
+ if (default_remote_name) /* did this already */
return;
default_remote_name = xstrdup("origin");
current_branch = NULL;
return check_repository_format_gently(NULL);
}
+/*
+ * Returns the "prefix", a path to the current working directory
+ * relative to the work tree root, or NULL, if the current working
+ * directory is not a strict subdirectory of the work tree root. The
+ * prefix always ends with a '/' character.
+ */
const char *setup_git_directory(void)
{
const char *retval = setup_git_directory_gently(NULL);
*/
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,
}
}
+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
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);
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);
int in1;
int in2;
int user_format;
+ int abbrev;
char *common_repo_prefix;
int email;
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
--- /dev/null
+: included from 6002 and others
+
+[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+
+:> sed.script
+
+# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
+tag()
+{
+ _tag=$1
+ [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
+ cat .git/refs/tags/$_tag
+}
+
+# Generate a commit using the text specified to make it unique and the tree
+# named by the tag specified.
+unique_commit()
+{
+ _text=$1
+ _tree=$2
+ shift 2
+ echo $_text | git commit-tree $(tag $_tree) "$@"
+}
+
+# Save the output of a command into the tag specified. Prepend
+# a substitution script for the tag onto the front of sed.script
+save_tag()
+{
+ _tag=$1
+ [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+ shift 1
+ "$@" >.git/refs/tags/$_tag
+
+ echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
+ cat sed.script >> sed.script.tmp
+ rm sed.script
+ mv sed.script.tmp sed.script
+}
+
+# Replace unhelpful sha1 hashses with their symbolic equivalents
+entag()
+{
+ sed -f sed.script
+}
+
+# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
+# tag to a specified value. Restore the original value on return.
+as_author()
+{
+ _author=$1
+ shift 1
+ _save=$GIT_AUTHOR_EMAIL
+
+ GIT_AUTHOR_EMAIL="$_author"
+ export GIT_AUTHOR_EMAIL
+ "$@"
+ if test -z "$_save"
+ then
+ unset GIT_AUTHOR_EMAIL
+ else
+ GIT_AUTHOR_EMAIL="$_save"
+ export GIT_AUTHOR_EMAIL
+ fi
+}
+
+commit_date()
+{
+ _commit=$1
+ git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+}
+
+on_committer_date()
+{
+ _date=$1
+ shift 1
+ GIT_COMMITTER_DATE="$_date"
+ export GIT_COMMITTER_DATE
+ "$@"
+ unset GIT_COMMITTER_DATE
+}
+
+# Execute a command and suppress any error output.
+hide_error()
+{
+ "$@" 2>/dev/null
+}
+
+check_output()
+{
+ _name=$1
+ shift 1
+ if eval "$*" | entag > $_name.actual
+ then
+ diff $_name.expected $_name.actual
+ else
+ return 1;
+ fi
+}
+
+# Turn a reasonable test description into a reasonable test name.
+# All alphanums translated into -'s which are then compressed and stripped
+# from front and back.
+name_from_description()
+{
+ perl -pe '
+ s/[^A-Za-z0-9.]/-/g;
+ s/-+/-/g;
+ s/-$//;
+ s/^-//;
+ y/A-Z/a-z/;
+ '
+}
+
+
+# Execute the test described by the first argument, by eval'ing
+# command line specified in the 2nd argument. Check the status code
+# is zero and that the output matches the stream read from
+# stdin.
+test_output_expect_success()
+{
+ _description=$1
+ _test=$2
+ [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
+ _name=$(echo $_description | name_from_description)
+ cat > $_name.expected
+ test_expect_success "$_description" "check_output $_name \"$_test\""
+}
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" &&
(
echo "h test=a/b/h" &&
echo "d/* test=a/b/d/*"
+ echo "d/yes notest"
) >a/b/.gitattributes
'
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
'
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 &&
'
-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' '
--- /dev/null
+#!/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
--- /dev/null
+#!/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
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
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 &&
. ./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
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
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
'
'
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
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
'
+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
+++ /dev/null
-: included from 6002 and others
-
-[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
-
-:> sed.script
-
-# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
-tag()
-{
- _tag=$1
- [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
- cat .git/refs/tags/$_tag
-}
-
-# Generate a commit using the text specified to make it unique and the tree
-# named by the tag specified.
-unique_commit()
-{
- _text=$1
- _tree=$2
- shift 2
- echo $_text | git commit-tree $(tag $_tree) "$@"
-}
-
-# Save the output of a command into the tag specified. Prepend
-# a substitution script for the tag onto the front of sed.script
-save_tag()
-{
- _tag=$1
- [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
- shift 1
- "$@" >.git/refs/tags/$_tag
-
- echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
- cat sed.script >> sed.script.tmp
- rm sed.script
- mv sed.script.tmp sed.script
-}
-
-# Replace unhelpful sha1 hashses with their symbolic equivalents
-entag()
-{
- sed -f sed.script
-}
-
-# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
-# tag to a specified value. Restore the original value on return.
-as_author()
-{
- _author=$1
- shift 1
- _save=$GIT_AUTHOR_EMAIL
-
- GIT_AUTHOR_EMAIL="$_author"
- export GIT_AUTHOR_EMAIL
- "$@"
- if test -z "$_save"
- then
- unset GIT_AUTHOR_EMAIL
- else
- GIT_AUTHOR_EMAIL="$_save"
- export GIT_AUTHOR_EMAIL
- fi
-}
-
-commit_date()
-{
- _commit=$1
- git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
-}
-
-on_committer_date()
-{
- _date=$1
- shift 1
- GIT_COMMITTER_DATE="$_date"
- export GIT_COMMITTER_DATE
- "$@"
- unset GIT_COMMITTER_DATE
-}
-
-# Execute a command and suppress any error output.
-hide_error()
-{
- "$@" 2>/dev/null
-}
-
-check_output()
-{
- _name=$1
- shift 1
- if eval "$*" | entag > $_name.actual
- then
- diff $_name.expected $_name.actual
- else
- return 1;
- fi
-}
-
-# Turn a reasonable test description into a reasonable test name.
-# All alphanums translated into -'s which are then compressed and stripped
-# from front and back.
-name_from_description()
-{
- perl -pe '
- s/[^A-Za-z0-9.]/-/g;
- s/-+/-/g;
- s/-$//;
- s/^-//;
- y/A-Z/a-z/;
- '
-}
-
-
-# Execute the test described by the first argument, by eval'ing
-# command line specified in the 2nd argument. Check the status code
-# is zero and that the output matches the stream read from
-# stdin.
-test_output_expect_success()
-{
- _description=$1
- _test=$2
- [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
- _name=$(echo $_description | name_from_description)
- cat > $_name.expected
- test_expect_success "$_description" "check_output $_name \"$_test\""
-}
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...
#
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()
{
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 &&
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 &&
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
o----o----o----o----o----. /
\ A c /
.------------o---o---o
- D e
+ D,R e
'
. ./test-lib.sh
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 &&
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^
. ./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
'
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
'
# A colored commit log will begin with an appropriate ANSI escape
# for the first color; the text "commit" comes later.
colorful() {
- read firstline < $1
+ 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
'
+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
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
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 &&
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
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:
#
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;
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;
}
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);
#include "cache.h"
+#include <pthread.h>
#if defined(hpux) || defined(__hpux) || defined(_hpux)
# include <sys/pstat.h>
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;
+}
#define THREAD_COMPAT_H
extern int online_cpus(void);
+extern int init_recursive_mutex(pthread_mutex_t*);
#endif /* THREAD_COMPAT_H */
{
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)) {
"|[-+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]+"
*/
#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");
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);
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);
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);
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);
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)" : "");
}
}