Merge branch 'by/blame-doc-m-c' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 16 Jun 2010 23:23:51 +0000 (16:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Jun 2010 23:23:51 +0000 (16:23 -0700)
* by/blame-doc-m-c:
blame-options.txt: Add default value for `-M/-C` options.

78 files changed:
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-checkout.txt
Documentation/git-commit.txt
Documentation/git-mailinfo.txt
Documentation/git-rebase.txt
Documentation/git-send-email.txt
Documentation/git-shortlog.txt
Documentation/gitdiffcore.txt
Documentation/pretty-formats.txt
Documentation/pretty-options.txt
GIT-VERSION-GEN
Makefile
attr.c
builtin/blame.c
builtin/commit.c
builtin/describe.c
builtin/for-each-ref.c
builtin/grep.c
builtin/index-pack.c
builtin/init-db.c
builtin/notes.c
builtin/pack-objects.c
builtin/revert.c
builtin/shortlog.c
builtin/show-branch.c
cache.h
compat/mingw.c
compat/mingw.h
compat/win32/pthread.h
compat/win32mmap.c
contrib/completion/git-completion.bash
contrib/hooks/post-receive-email
diff-no-index.c
dir.c
git-add--interactive.perl
git-am.sh
git-compat-util.h
git-send-email.perl
git-stash.sh
http-walker.c
http.c
http.h
object.c
pack-check.c
pack.h
pretty.c
remote-curl.c
remote.c
run-command.c
setup.c
sha1_file.c
shortlog.h
t/README
t/t0000-basic.sh
t/t0001-init.sh
t/t0003-attributes.sh
t/t1501-worktree.sh
t/t3903-stash.sh
t/t4201-shortlog.sh
t/t5516-fetch-push.sh
t/t5541-http-push.sh
t/t5550-http-fetch.sh
t/t5704-bundle.sh
t/t5705-clone-2gb.sh
t/t6006-rev-list-format.sh
t/t6120-describe.sh
t/t7006-pager.sh
t/t7502-commit.sh
t/t7508-status.sh
t/test-lib.sh
tag.c
tag.h
thread-utils.c
thread-utils.h
wrapper.c
wt-status.c
index abc65de9464144a7bac38756c01ab315ab6922eb..eb53e0636e3c3bab06e88ce3371945f5602c5756 100644 (file)
@@ -41,6 +41,7 @@ Checklist (and a short version for the impatient):
          maintainer (gitster@pobox.com) if (and only if) the patch
          is ready for inclusion. If you use git-send-email(1),
          please test it first by sending email to yourself.
+       - see below for instructions specific to your mailer
 
 Long version:
 
@@ -53,6 +54,34 @@ But the patch submission requirements are a lot more relaxed
 here on the technical/contents front, because the core GIT is
 thousand times smaller ;-).  So here is only the relevant bits.
 
+(0) Decide what to base your work on.
+
+In general, always base your work on the oldest branch that your
+change is relevant to.
+
+ - A bugfix should be based on 'maint' in general. If the bug is not
+   present in 'maint', base it on 'master'. For a bug that's not yet
+   in 'master', find the topic that introduces the regression, and
+   base your work on the tip of the topic.
+
+ - A new feature should be based on 'master' in general. If the new
+   feature depends on a topic that is in 'pu', but not in 'master',
+   base your work on the tip of that topic.
+
+ - Corrections and enhancements to a topic not yet in 'master' should
+   be based on the tip of that topic. If the topic has not been merged
+   to 'next', it's alright to add a note to squash minor corrections
+   into the series.
+
+ - In the exceptional case that a new feature depends on several topics
+   not in 'master', start working on 'next' or 'pu' privately and send
+   out patches for discussion. Before the final merge, you may have to
+   wait until some of the dependent topics graduate to 'master', and
+   rebase your work.
+
+To find the tip of a topic branch, run "git log --first-parent
+master..pu" and look for the merge commit. The second parent of this
+commit is the tip of the topic branch.
 
 (1) Make separate commits for logically separate changes.
 
@@ -170,17 +199,16 @@ patch, format it as "multipart/signed", not a text/plain message
 that starts with '-----BEGIN PGP SIGNED MESSAGE-----'.  That is
 not a text/plain, it's something else.
 
-Note that your maintainer does not necessarily read everything
-on the git mailing list.  If your patch is for discussion first,
-send it "To:" the mailing list, and optionally "cc:" him.  If it
-is trivially correct or after the list reached a consensus, send
-it "To:" the maintainer and optionally "cc:" the list for
-inclusion.
-
-Also note that your maintainer does not actively involve himself in
-maintaining what are in contrib/ hierarchy.  When you send fixes and
-enhancements to them, do not forget to "cc: " the person who primarily
-worked on that hierarchy in contrib/.
+Unless your patch is a very trivial and an obviously correct one,
+first send it with "To:" set to the mailing list, with "cc:" listing
+people who are involved in the area you are touching (the output from
+"git blame $path" and "git shortlog --no-merges $path" would help to
+identify them), to solicit comments and reviews.  After the list
+reached a consensus that it is a good idea to apply the patch, re-send
+it with "To:" set to the maintainer and optionally "cc:" the list for
+inclusion.  Do not forget to add trailers such as "Acked-by:",
+"Reviewed-by:" and "Tested-by:" after your "Signed-off-by:" line as
+necessary.
 
 
 (4) Sign your work
@@ -519,9 +547,27 @@ Gmail
 
 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 any IMAP email client to connect to the google imap server, and forward
+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.
 
+To use "git send-email" and send your patches through the GMail SMTP server,
+edit ~/.gitconfig to specify your account settings:
+
+[sendemail]
+       smtpencryption = tls
+       smtpserver = smtp.gmail.com
+       smtpuser = user@gmail.com
+       smtppass = p4ssw0rd
+       smtpserverport = 587
+
+Once your commits are ready to be sent to the mailing list, run the
+following commands:
+
+  $ git format-patch --cover-letter -M origin/master -o outgoing/
+  $ edit outgoing/0000-*
+  $ git send-email outgoing/*
+
 To submit using the IMAP interface, first, edit your ~/.gitconfig to specify your
 account settings:
 
@@ -537,8 +583,7 @@ You might need to instead use: folder = "[Google Mail]/Drafts" if you get an err
 that the "Folder doesn't exist".
 
 Once your commits are ready to be sent to the mailing list, run the
-following command to send the patch emails to your Gmail Drafts
-folder.
+following commands:
 
   $ git format-patch --cover-letter -M --stdout origin/master | git imap-send
 
@@ -546,19 +591,3 @@ Just make sure to disable line wrapping in the email client (GMail web
 interface will line wrap no matter what, so you need to use a real
 IMAP client).
 
-Alternatively, you can use "git send-email" and send your patches
-through the GMail SMTP server.  edit ~/.gitconfig to specify your
-account settings:
-
-[sendemail]
-       smtpencryption = tls
-       smtpserver = smtp.gmail.com
-       smtpuser = user@gmail.com
-       smtppass = p4ssw0rd
-       smtpserverport = 587
-
-Once your commits are ready to be sent to the mailing list, run the
-following commands:
-
-  $ git format-patch --cover-letter -M origin/master -o outgoing/
-  $ git send-email outgoing/*
index 92f851e797ca820f6f6126391eafee0d4eec7e26..fc4037a7d8e82c7328aae152b71705f62f1e69f7 100644 (file)
@@ -1359,10 +1359,6 @@ notes.rewrite.<command>::
        automatically copies your notes from the original to the
        rewritten commit.  Defaults to `true`, but see
        "notes.rewriteRef" below.
-+
-This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
-environment variable, which must be a colon separated list of refs or
-globs.
 
 notes.rewriteMode::
        When copying notes during a rewrite (see the
@@ -1382,6 +1378,10 @@ notes.rewriteRef::
 +
 Does not have a default value; you must configure this variable to
 enable note rewriting.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
+environment variable, which must be a colon separated list of refs or
+globs.
 
 pack.window::
        The size of the window used by linkgit:git-pack-objects[1] when no
@@ -1516,7 +1516,7 @@ receive.denyDeletes::
        the ref. Use this to prevent such a ref deletion via a push.
 
 receive.denyCurrentBranch::
-       If set to true or "refuse", receive-pack will deny a ref update
+       If set to true or "refuse", git-receive-pack will deny a ref update
        to the currently checked out branch of a non-bare repository.
        Such a push is potentially dangerous because it brings the HEAD
        out of sync with the index and working tree. If set to "warn",
@@ -1642,6 +1642,7 @@ sendemail.smtppass::
 sendemail.suppresscc::
 sendemail.suppressfrom::
 sendemail.to::
+sendemail.smtpdomain::
 sendemail.smtpserver::
 sendemail.smtpserverport::
 sendemail.smtpuser::
@@ -1681,6 +1682,13 @@ If this variable is not specified, it defaults to 'normal'.
 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
index 51cbeb7032865599317fd9d7d36a9c9e2f8cc0c5..74741a42f409a7af5c60584a0d3b7a0be851811c 100644 (file)
@@ -266,9 +266,9 @@ patch::
 
        y - stage this hunk
        n - do not stage this hunk
-       q - quit, do not stage this hunk nor any of the remaining ones
-       a - stage this and all the remaining hunks in the file
-       d - do not stage this hunk nor any of the remaining hunks in the file
+       q - quit; do not stage this hunk nor any of the remaining ones
+       a - stage this hunk and all later hunks in the file
+       d - do not stage this hunk nor any of the later hunks in the file
        g - select a hunk to go to
        / - search for a hunk matching the given regex
        j - leave this hunk undecided, see next undecided hunk
index 37c1810e3fc8424868333a22094107e99764fc37..a3a87fa7fd29973e15afb93382646ae6f89d0f61 100644 (file)
@@ -136,6 +136,10 @@ edits from your current working tree.
 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.
index 64fb458b4533eeda9c583ac926025495b260cf9b..69eb86e4506411b0d8029f849fd4ebff47166d05 100644 (file)
@@ -95,10 +95,11 @@ OPTIONS
        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.
index e3d58cbac3f162cc01d8731485f220fd70fed17b..3ea5aad56c5e1cec7fc2ca01ba9b5ea6badebe2f 100644 (file)
@@ -40,16 +40,16 @@ OPTIONS
 -u::
        The commit log message, author name and author email are
        taken from the e-mail, and after minimally decoding MIME
-       transfer encoding, re-coded in UTF-8 by transliterating
+       transfer encoding, re-coded in the charset specified by
+       i18n.commitencoding (defaulting to UTF-8) by transliterating
        them.  This used to be optional but now it is the default.
 +
 Note that the patch is always used as-is without charset
 conversion, even with this flag.
 
 --encoding=<encoding>::
-       Similar to -u but if the local convention is different
-       from what is specified by i18n.commitencoding, this flag
-       can be used to override it.
+       Similar to -u.  But when re-coding, the charset specified here is
+       used instead of the one specified by i18n.commitencoding or UTF-8.
 
 -n::
        Disable all charset re-coding of the metadata.
index 0d07b1b2077c62f1199c5ee96790e5a7121b4f00..50ba2e469f48f1bc16c3f6e3b6f6451a9e29637a 100644 (file)
@@ -206,6 +206,10 @@ OPTIONS
        --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,
@@ -295,6 +299,7 @@ link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for details).
 --ignore-date::
        These flags are passed to 'git am' to easily change the dates
        of the rebased commits (see linkgit:git-am[1]).
+       Incompatible with the --interactive option.
 
 -i::
 --interactive::
index 3dfdc7cca6cf27be47bf610da5c84a99d1df7926..12622fc49a0825fa8423c46ddddc06ff89f292d4 100644 (file)
@@ -119,6 +119,13 @@ Sending
        value reverts to plain SMTP.  Default is the value of
        'sendemail.smtpencryption'.
 
+--smtp-domain=<FQDN>::
+       Specifies the Fully Qualified Domain Name (FQDN) used in the
+       HELO/EHLO command to the SMTP server.  Some servers require the
+       FQDN to match your IP address.  If not set, git send-email attempts
+       to determine your FQDN automatically.  Default is the value of
+       'sendemail.smtpdomain'.
+
 --smtp-pass[=<password>]::
        Password for SMTP-AUTH. The argument is optional: If no
        argument is specified, then the empty string is used as
index dfd4d0c2233df09b64a10d1ad31a36afaa01ec7c..bc1ac77495347967c941298e2da38f76d5d124c6 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 git log --pretty=short | 'git shortlog' [-h] [-n] [-s] [-e] [-w]
-'git shortlog' [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] [<committish>...]
+'git shortlog' [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] <commit>...
 
 DESCRIPTION
 -----------
@@ -19,6 +19,11 @@ the first line of the commit message will be shown.
 
 Additionally, "[PATCH]" will be stripped from the commit description.
 
+If no revisions are passed on the command line and either standard input
+is not a terminal or there is no current branch, 'git shortlog' will
+output a summary of the log read from standard input, without
+reference to the current repository.
+
 OPTIONS
 -------
 
@@ -39,6 +44,14 @@ OPTIONS
 --email::
        Show the email address of each author.
 
+--format[='<format>']::
+       Instead of the commit subject, use some other information to
+       describe each commit.  '<format>' can be any string accepted
+       by the `--format` option of 'git log', such as '{asterisk} [%h] %s'.
+       (See the "PRETTY FORMATS" section of linkgit:git-log[1].)
+
+       Each pretty-printed commit will be rewrapped before it is shown.
+
 -w[<width>[,<indent1>[,<indent2>]]]::
        Linewrap the output by wrapping each line at `width`.  The first
        line of each entry is indented by `indent1` spaces, and the second
index 9de8caf5d11445f6db7193f7b7a4151c97befb29..5d91a7e5b3a40cbf41f03a81799c162775c36607 100644 (file)
@@ -227,8 +227,8 @@ changes that touch a specified string, and is controlled by the
 commands.
 
 When diffcore-pickaxe is in use, it checks if there are
-filepairs whose "original" side has the specified string and
-whose "result" side does not.  Such a filepair represents "the
+filepairs whose "result" side has the specified string and
+whose "origin" side does not.  Such a filepair represents "the
 string appeared in this changeset".  It also checks for the
 opposite case that loses the specified string.
 
index 1686a54d22a746036b997d6eb8d5b85ca1d79c5d..c85a52c0cc27a187abbdea17cd25cec51b808064 100644 (file)
@@ -76,9 +76,9 @@ displayed in full, regardless of whether --abbrev or
 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'.
index af6d2b995a050007ae088dec8e2c3474e9f06ad5..d78e121c76ef12b2d72fbdf1558fa967dfdba85b 100644 (file)
@@ -3,8 +3,9 @@
 
        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]).
index 0ad39484eceab30360e1f46c6057a873ca2c26f1..82fd72652b6d35837b8786005024263ac61630dd 100755 (executable)
@@ -12,7 +12,7 @@ if test -f version
 then
        VN=$(cat version) || VN="$DEF_VER"
 elif test -d .git -o -f .git &&
-       VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
+       VN=$(git describe --match "v[0-9]*" --abbrev=4 HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
        v[0-9]*)
index 910f4713ef1491278500573bab2d00ee1925abb8..ed4c733c623fc23fc324efe1fa6e58cef63961e9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -886,6 +886,7 @@ ifeq ($(uname_S),NetBSD)
        NO_MKSTEMPS = YesPlease
 endif
 ifeq ($(uname_S),AIX)
+       DEFAULT_PAGER = more
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
        NO_MKDTEMP = YesPlease
@@ -1996,12 +1997,13 @@ endif
                ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
          done; } && \
-       { for p in $(REMOTE_CURL_ALIASES); do \
+       { test x"$(REMOTE_CURL_ALIASES)" = x || \
+               { for p in $(REMOTE_CURL_ALIASES); do \
                $(RM) "$$execdir/$$p" && \
                ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
                ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
-         done; } && \
+         done; } ; } && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
 install-doc:
@@ -2098,7 +2100,7 @@ clean:
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
        $(MAKE) -C Documentation/ clean
 ifndef NO_PERL
-       $(RM) gitweb/gitweb.cgi gitweb/gitweb.min.*
+       $(MAKE) -C gitweb clean
        $(MAKE) -C perl clean
 endif
 ifndef NO_PYTHON
diff --git a/attr.c b/attr.c
index f5346ed32a1b5caf908021805214fd97e033eb27..7467baf2d6c81f94a7d043dcde13d463b3b46272 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -594,20 +594,25 @@ static int path_matches(const char *pathname, int pathlen,
        return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
 }
 
+static int macroexpand_one(int attr_nr, int rem);
+
 static int fill_one(const char *what, struct match_attr *a, int rem)
 {
        struct git_attr_check *check = check_all_attr;
        int i;
 
-       for (i = 0; 0 < rem && i < a->num_attr; i++) {
+       for (i = a->num_attr - 1; 0 < rem && 0 <= i; i--) {
                struct git_attr *attr = a->state[i].attr;
                const char **n = &(check[attr->attr_nr].value);
                const char *v = a->state[i].setto;
 
                if (*n == ATTR__UNKNOWN) {
-                       debug_set(what, a->u.pattern, attr, v);
+                       debug_set(what,
+                                 a->is_macro ? a->u.attr->name : a->u.pattern,
+                                 attr, v);
                        *n = v;
                        rem--;
+                       rem = macroexpand_one(attr->attr_nr, rem);
                }
        }
        return rem;
@@ -629,19 +634,27 @@ static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
        return rem;
 }
 
-static int macroexpand(struct attr_stack *stk, int rem)
+static int macroexpand_one(int attr_nr, int rem)
 {
+       struct attr_stack *stk;
+       struct match_attr *a = NULL;
        int i;
-       struct git_attr_check *check = check_all_attr;
 
-       for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) {
-               struct match_attr *a = stk->attrs[i];
-               if (!a->is_macro)
-                       continue;
-               if (check[a->u.attr->attr_nr].value != ATTR__TRUE)
-                       continue;
+       if (check_all_attr[attr_nr].value != ATTR__TRUE)
+               return rem;
+
+       for (stk = attr_stack; !a && stk; stk = stk->prev)
+               for (i = stk->num_matches - 1; !a && 0 <= i; i--) {
+                       struct match_attr *ma = stk->attrs[i];
+                       if (!ma->is_macro)
+                               continue;
+                       if (ma->u.attr->attr_nr == attr_nr)
+                               a = ma;
+               }
+
+       if (a)
                rem = fill_one("expand", a, rem);
-       }
+
        return rem;
 }
 
@@ -666,9 +679,6 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
        for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
                rem = fill(path, pathlen, stk, rem);
 
-       for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
-               rem = macroexpand(stk, rem);
-
        for (i = 0; i < num; i++) {
                const char *value = check_all_attr[check[i].attr->attr_nr].value;
                if (value == ATTR__UNKNOWN)
index fc1586350f94ae48e7e48a51818517b465e7a40d..4dd4c3f494042c91e2fa481314d2d8d6d61d9c43 100644 (file)
@@ -1589,7 +1589,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
        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);
index c5ab683d5b66d5ad85f53d13d6df71e29cd9234d..3c14ade9dddd06001d9c6d5fab23a9a6b7ca2580 100644 (file)
@@ -1017,6 +1017,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
 int cmd_status(int argc, const char **argv, const char *prefix)
 {
        struct wt_status s;
+       int fd;
        unsigned char sha1[20];
        static struct option builtin_status_options[] = {
                OPT__VERBOSE(&verbose),
@@ -1050,6 +1051,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        read_cache_preload(s.pathspec);
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
+
+       fd = hold_locked_index(&index_lock, 0);
+       if (0 <= fd) {
+               if (!write_cache(fd, active_cache, active_nr))
+                       commit_locked_index(&index_lock);
+               rollback_lock_file(&index_lock);
+       }
+
        s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
        s.in_merge = in_merge;
        wt_status_collect(&s);
index 71be2a9364748668996696f6c74057dba43315b5..43caff2ffe185df6ab224b93b4fd9ce3d82de2d0 100644 (file)
@@ -35,7 +35,8 @@ static const char *diff_index_args[] = {
 
 struct commit_name {
        struct tag *tag;
-       int prio; /* annotated tag = 2, tag = 1, head = 0 */
+       unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+       unsigned name_checked:1;
        unsigned char sha1[20];
        char path[FLEX_ARRAY]; /* more */
 };
@@ -43,18 +44,53 @@ static const char *prio_names[] = {
        "head", "lightweight", "annotated",
 };
 
+static int replace_name(struct commit_name *e,
+                              int prio,
+                              const unsigned char *sha1,
+                              struct tag **tag)
+{
+       if (!e || e->prio < prio)
+               return 1;
+
+       if (e->prio == 2 && prio == 2) {
+               /* Multiple annotated tags point to the same commit.
+                * Select one to keep based upon their tagger date.
+                */
+               struct tag *t;
+
+               if (!e->tag) {
+                       t = lookup_tag(e->sha1);
+                       if (!t || parse_tag(t))
+                               return 1;
+                       e->tag = t;
+               }
+
+               t = lookup_tag(sha1);
+               if (!t || parse_tag(t))
+                       return 0;
+               *tag = t;
+
+               if (e->tag->date < t->date)
+                       return 1;
+       }
+
+       return 0;
+}
+
 static void add_to_known_names(const char *path,
                               struct commit *commit,
                               int prio,
                               const unsigned char *sha1)
 {
        struct commit_name *e = commit->util;
-       if (!e || e->prio < prio) {
+       struct tag *tag = NULL;
+       if (replace_name(e, prio, sha1, &tag)) {
                size_t len = strlen(path)+1;
                free(e);
                e = xmalloc(sizeof(struct commit_name) + len);
-               e->tag = NULL;
+               e->tag = tag;
                e->prio = prio;
+               e->name_checked = 0;
                hashcpy(e->sha1, sha1);
                memcpy(e->path, path, len);
                commit->util = e;
@@ -165,10 +201,15 @@ static void display_name(struct commit_name *n)
 {
        if (n->prio == 2 && !n->tag) {
                n->tag = lookup_tag(n->sha1);
-               if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
+               if (!n->tag || parse_tag(n->tag))
                        die("annotated tag %s not available", n->path);
+       }
+       if (n->tag && !n->name_checked) {
+               if (!n->tag->tag)
+                       die("annotated tag %s has no embedded name", n->path);
                if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
                        warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
+               n->name_checked = 1;
        }
 
        if (n->tag)
index 62be1bbfd6659f9dfac73a17acd1e2d5322dac66..7f5011f75ef9eb549b33861d9546f3d18a94c256 100644 (file)
@@ -549,10 +549,10 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
                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);
index 8e928e217041a159f4a962f0883d740aa84536d7..b194ea3cea531f3a6c81d6d27b5b3028641ba9ce 100644 (file)
@@ -17,8 +17,8 @@
 #include "dir.h"
 
 #ifndef NO_PTHREADS
-#include "thread-utils.h"
 #include <pthread.h>
+#include "thread-utils.h"
 #endif
 
 static char const * const grep_usage[] = {
index 03d0cd2f47016b59994a75c11403061fcad8a165..a89ae831dd6251d7332e06470273d30fd9cb31eb 100644 (file)
@@ -266,26 +266,23 @@ static void unlink_base_data(struct base_data *c)
 
 static void *unpack_entry_data(unsigned long offset, unsigned long size)
 {
+       int status;
        z_stream stream;
        void *buf = xmalloc(size);
 
        memset(&stream, 0, sizeof(stream));
+       git_inflate_init(&stream);
        stream.next_out = buf;
        stream.avail_out = size;
-       stream.next_in = fill(1);
-       stream.avail_in = input_len;
-       git_inflate_init(&stream);
 
-       for (;;) {
-               int ret = git_inflate(&stream, 0);
-               use(input_len - stream.avail_in);
-               if (stream.total_out == size && ret == Z_STREAM_END)
-                       break;
-               if (ret != Z_OK)
-                       bad_object(offset, "inflate returned %d", ret);
+       do {
                stream.next_in = fill(1);
                stream.avail_in = input_len;
-       }
+               status = git_inflate(&stream, 0);
+               use(input_len - stream.avail_in);
+       } while (status == Z_OK);
+       if (stream.total_out != size || status != Z_STREAM_END)
+               bad_object(offset, "inflate returned %d", status);
        git_inflate_end(&stream);
        return buf;
 }
@@ -359,34 +356,38 @@ static void *get_data_from_pack(struct object_entry *obj)
 {
        off_t from = obj[0].idx.offset + obj[0].hdr_size;
        unsigned long len = obj[1].idx.offset - from;
-       unsigned long rdy = 0;
-       unsigned char *src, *data;
+       unsigned char *data, *inbuf;
        z_stream stream;
-       int st;
+       int status;
 
-       src = xmalloc(len);
-       data = src;
-       do {
-               ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
-               if (n < 0)
-                       die_errno("cannot pread pack file");
-               if (!n)
-                       die("premature end of pack file, %lu bytes missing",
-                           len - rdy);
-               rdy += n;
-       } while (rdy < len);
        data = xmalloc(obj->size);
+       inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
+
        memset(&stream, 0, sizeof(stream));
+       git_inflate_init(&stream);
        stream.next_out = data;
        stream.avail_out = obj->size;
-       stream.next_in = src;
-       stream.avail_in = len;
-       git_inflate_init(&stream);
-       while ((st = git_inflate(&stream, Z_FINISH)) == Z_OK);
-       git_inflate_end(&stream);
-       if (st != Z_STREAM_END || stream.total_out != obj->size)
+
+       do {
+               ssize_t n = (len < 64*1024) ? len : 64*1024;
+               n = pread(pack_fd, inbuf, n, from);
+               if (n < 0)
+                       die_errno("cannot pread pack file");
+               if (!n)
+                       die("premature end of pack file, %lu bytes missing", len);
+               from += n;
+               len -= n;
+               stream.next_in = inbuf;
+               stream.avail_in = n;
+               status = git_inflate(&stream, 0);
+       } while (len && status == Z_OK && !stream.avail_in);
+
+       /* This has been inflated OK when first encountered, so... */
+       if (status != Z_STREAM_END || stream.total_out != obj->size)
                die("serious inflate inconsistency");
-       free(src);
+
+       git_inflate_end(&stream);
+       free(inbuf);
        return data;
 }
 
@@ -668,25 +669,25 @@ static void parse_pack_objects(unsigned char *sha1)
 static int write_compressed(struct sha1file *f, void *in, unsigned int size)
 {
        z_stream stream;
-       unsigned long maxsize;
-       void *out;
+       int status;
+       unsigned char outbuf[4096];
 
        memset(&stream, 0, sizeof(stream));
        deflateInit(&stream, zlib_compression_level);
-       maxsize = deflateBound(&stream, size);
-       out = xmalloc(maxsize);
-
-       /* Compress it */
        stream.next_in = in;
        stream.avail_in = size;
-       stream.next_out = out;
-       stream.avail_out = maxsize;
-       while (deflate(&stream, Z_FINISH) == Z_OK);
-       deflateEnd(&stream);
 
+       do {
+               stream.next_out = outbuf;
+               stream.avail_out = sizeof(outbuf);
+               status = deflate(&stream, Z_FINISH);
+               sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out);
+       } while (status == Z_OK);
+
+       if (status != Z_STREAM_END)
+               die("unable to deflate appended object (%d)", status);
        size = stream.total_out;
-       sha1write(f, out, size);
-       free(out);
+       deflateEnd(&stream);
        return size;
 }
 
index edc40ff5748fbd68b64f382c251c6b030cf88803..0271285fad6ad532a6133838f7188498476fd77b 100644 (file)
@@ -463,7 +463,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                static char git_dir[PATH_MAX+1];
 
                setenv(GIT_DIR_ENVIRONMENT,
-                       getcwd(git_dir, sizeof(git_dir)), 0);
+                       getcwd(git_dir, sizeof(git_dir)), argc > 0);
        }
 
        if (init_shared_repository != -1)
index 52b72fca687d42dc09d1d79a8e76584a55c0c546..26617546c8cadac493f06473d02f27f320713b28 100644 (file)
@@ -416,7 +416,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 {
        struct strbuf buf = STRBUF_INIT;
        struct notes_rewrite_cfg *c = NULL;
-       struct notes_tree *t;
+       struct notes_tree *t = NULL;
        int ret = 0;
 
        if (rewrite_cmd) {
index 97802585ea3ac69ac6ed2e7995605bdcae84558e..214d7ef2b12d41e8d70539fe4a4b16fde71a780a 100644 (file)
@@ -18,8 +18,8 @@
 #include "refs.h"
 
 #ifndef NO_PTHREADS
-#include "thread-utils.h"
 #include <pthread.h>
+#include "thread-utils.h"
 #endif
 
 static const char pack_usage[] =
@@ -1522,6 +1522,13 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
 
 #ifndef NO_PTHREADS
 
+static void try_to_free_from_threads(size_t size)
+{
+       read_lock();
+       release_pack_memory(size, -1);
+       read_unlock();
+}
+
 /*
  * The main thread waits on the condition that (at least) one of the workers
  * has stopped working (which is indicated in the .working member of
@@ -1552,14 +1559,16 @@ static pthread_cond_t progress_cond;
  */
 static void init_threaded_search(void)
 {
-       pthread_mutex_init(&read_mutex, NULL);
+       init_recursive_mutex(&read_mutex);
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
+       set_try_to_free_routine(try_to_free_from_threads);
 }
 
 static void cleanup_threaded_search(void)
 {
+       set_try_to_free_routine(NULL);
        pthread_cond_destroy(&progress_cond);
        pthread_mutex_destroy(&read_mutex);
        pthread_mutex_destroy(&cache_mutex);
index 778a56eb512edde3aac6c2a4d668cc73ab3d8460..7d68ef714eee5011d82952ca1829016c90827f61 100644 (file)
@@ -109,8 +109,13 @@ static int get_message(const char *raw_message, struct commit_message *out)
                encoding = "UTF-8";
        if (!git_commit_encoding)
                git_commit_encoding = "UTF-8";
-       if ((out->reencoded_message = reencode_string(raw_message,
-                                       git_commit_encoding, encoding)))
+
+       out->reencoded_message = NULL;
+       out->message = raw_message;
+       if (strcmp(encoding, git_commit_encoding))
+               out->reencoded_message = reencode_string(raw_message,
+                                       git_commit_encoding, encoding);
+       if (out->reencoded_message)
                out->message = out->reencoded_message;
 
        abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
index 06320f5285988365b8340e110427866e68536b47..5089502800d5f477f47b6cd6499d278097f11e67 100644 (file)
@@ -162,7 +162,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                    sha1_to_hex(commit->object.sha1));
        if (log->user_format) {
                struct pretty_print_context ctx = {0};
-               ctx.abbrev = DEFAULT_ABBREV;
+               ctx.abbrev = log->abbrev;
                ctx.subject = "";
                ctx.after_subject = "";
                ctx.date_mode = DATE_NORMAL;
@@ -290,6 +290,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
        }
 
        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))
index e20fcf3e935dfafb4e30f24990aa974c8b2f5927..e8719aa9e9f47c30b697332925fcdd206fdfd55c 100644 (file)
@@ -313,7 +313,8 @@ static void show_one_commit(struct commit *commit, int no_name)
                }
                else
                        printf("[%s] ",
-                              find_unique_abbrev(commit->object.sha1, 7));
+                              find_unique_abbrev(commit->object.sha1,
+                                                 DEFAULT_ABBREV));
        }
        puts(pretty_str);
        strbuf_release(&pretty);
diff --git a/cache.h b/cache.h
index 5eb0573bcc81050cc06a304f346fe5f41ebe242e..0d101e4913f380a0eda0b3bf9e1d8b76e34c6fd8 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -905,7 +905,7 @@ struct extra_have_objects {
 extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
 extern int server_supports(const char *feature);
 
-extern struct packed_git *parse_pack_index(unsigned char *sha1);
+extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
 extern void prepare_packed_git(void);
 extern void reprepare_packed_git(void);
@@ -916,6 +916,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 
 extern void pack_report(void);
 extern int open_pack_index(struct packed_git *);
+extern void close_pack_index(struct packed_git *);
 extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
 extern void close_pack_windows(struct packed_git *);
 extern void unuse_pack(struct pack_window **);
index f90a114b021d32c2ee1976bf51a82e87a81ea1cb..9a8e3365827d303c6513475726113a3952fe0040 100644 (file)
@@ -140,6 +140,23 @@ int mingw_open (const char *filename, int oflags, ...)
        return fd;
 }
 
+#undef write
+ssize_t mingw_write(int fd, const void *buf, size_t count)
+{
+       /*
+        * While write() calls to a file on a local disk are translated
+        * into WriteFile() calls with a maximum size of 64KB on Windows
+        * XP and 256KB on Vista, no such cap is placed on writes to
+        * files over the network on Windows XP.  Unfortunately, there
+        * seems to be a limit of 32MB-28KB on X64 and 64MB-32KB on x86;
+        * bigger writes fail on Windows XP.
+        * So we cap to a nice 31MB here to avoid write failures over
+        * the net without changing the number of WriteFile() calls in
+        * the local case.
+        */
+       return write(fd, buf, min(count, 31 * 1024 * 1024));
+}
+
 #undef fopen
 FILE *mingw_fopen (const char *filename, const char *otype)
 {
index 7c2ab64cb4eb7532c2495383ab0e7eefb329bcf9..0e3e74304138ab2f279c74599ee934ade72ae37d 100644 (file)
@@ -170,6 +170,9 @@ int link(const char *oldpath, const char *newpath);
 int mingw_open (const char *filename, int oflags, ...);
 #define open mingw_open
 
+ssize_t mingw_write(int fd, const void *buf, size_t count);
+#define write mingw_write
+
 FILE *mingw_fopen (const char *filename, const char *otype);
 #define fopen mingw_fopen
 
index c72f100f40ce2ab9ae7abead730ed00c2a461fbf..a45f8d66df8d1e452d9392945bf12a74c32bbca9 100644 (file)
  */
 #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.
index 1c5a14922f255af2c3b0e75e06925b748d3d7684..b58aa69fa0609dad7f591024f9da31dfa58496fb 100644 (file)
@@ -4,19 +4,19 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
 {
        HANDLE hmap;
        void *temp;
-       size_t len;
+       off_t len;
        struct stat st;
        uint64_t o = offset;
        uint32_t l = o & 0xFFFFFFFF;
        uint32_t h = (o >> 32) & 0xFFFFFFFF;
 
        if (!fstat(fd, &st))
-               len = xsize_t(st.st_size);
+               len = st.st_size;
        else
                die("mmap: could not determine filesize");
 
        if ((length + offset) > len)
-               length = len - offset;
+               length = xsize_t(len - offset);
 
        if (!(flags & MAP_PRIVATE))
                die("Invalid usage of mmap when built with USE_WIN32_MMAP");
index 545bd4b38368e3c2a3958133bbeef6a19e831fff..57245a8c01fa3aba4f9e3f2bc258b40f38f446c0 100755 (executable)
@@ -797,6 +797,7 @@ _git_branch ()
                __gitcomp "
                        --color --no-color --verbose --abbrev= --no-abbrev
                        --track --no-track --contains --merged --no-merged
+                       --set-upstream
                        "
                ;;
        *)
index 58a35c82870c54f844fd1154a82237891655f38f..30ae63d74da065a31cced0b161708680f39c04c0 100755 (executable)
 # possible for the email to be from someone other than the person doing the
 # push.
 #
+# To help with debugging and use on pre-v1.5.1 git servers, this script will
+# also obey the interface of hooks/update, taking its arguments on the
+# command line.  Unfortunately, hooks/update is called once for each ref.
+# To avoid firing one email per ref, this script just prints its output to
+# the screen when used in this mode.  The output can then be redirected if
+# wanted.
+#
 # Config
 # ------
 # hooks.mailinglist
index aae8e7accc1ff955bd76c62b379b37f343f61cc4..4cd9dacbe8e98d1420ebe1a217b0f927d24d125a 100644 (file)
@@ -150,16 +150,14 @@ static int queue_diff(struct diff_options *o,
 
 static int path_outside_repo(const char *path)
 {
-       /*
-        * We have already done setup_git_directory_gently() so we
-        * know we are inside a git work tree already.
-        */
        const char *work_tree;
        size_t len;
 
        if (!is_absolute_path(path))
                return 0;
        work_tree = get_git_work_tree();
+       if (!work_tree)
+               return 1;
        len = strlen(work_tree);
        if (strncmp(path, work_tree, len) ||
            (path[len] != '\0' && path[len] != '/'))
diff --git a/dir.c b/dir.c
index cb83332a261f97026f9c6273afb162245944dec7..5615f33af187f381f8c2dfe7ab53910fe165fd59 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -958,9 +958,14 @@ char *get_relative_cwd(char *buffer, int size, const char *dir)
        }
        if (*dir)
                return NULL;
-       if (*cwd == '/')
+       switch (*cwd) {
+       case '\0':
+               return cwd;
+       case '/':
                return cwd + 1;
-       return cwd;
+       default:
+               return NULL;
+       }
 }
 
 int is_inside_dir(const char *dir)
index 21f1330a5bf26c955051ce9cf263289d90c7667b..27fc79347af428dd39daa5b550814cc6cf980510 100755 (executable)
@@ -1111,9 +1111,9 @@ sub help_patch_cmd {
        print colored $help_color, <<EOF ;
 y - $verb this hunk$target
 n - do not $verb this hunk$target
-q - quit, do not $verb this hunk nor any of the remaining ones
-a - $verb this and all the remaining hunks in the file
-d - do not $verb this hunk nor any of the remaining hunks in the file
+q - quit; do not $verb this hunk nor any of the remaining ones
+a - $verb this hunk and all later hunks in the file
+d - do not $verb this hunk nor any of the later hunks in the file
 g - select a hunk to go to
 / - search for a hunk matching the given regex
 j - leave this hunk undecided, see next undecided hunk
index 1056075545ad3e5e42626d167c91c1deada6186b..87ffae252b3f2ff88646d142ea9c2dfb38a28953 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -740,7 +740,7 @@ do
                ;;
        esac
 
-       if test $apply_status = 1 && test "$threeway" = t
+       if test $apply_status != 0 && test "$threeway" = t
        then
                if (fall_back_3way)
                then
index 7e62b552700a580646dbb6a8921c40761d6b57a2..c4f4e15036577bc0c55d19cef26fd0ce26b233ad 100644 (file)
@@ -56,7 +56,7 @@
 # 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
@@ -356,6 +356,8 @@ static inline void *gitmempcpy(void *dest, const void *src, size_t n)
 
 extern void release_pack_memory(size_t, int);
 
+extern void set_try_to_free_routine(void (*routine)(size_t));
+
 extern char *xstrdup(const char *str);
 extern void *xmalloc(size_t size);
 extern void *xmallocz(size_t size);
index ce569a9c8f964b3cdc8920325bc817141035c2c3..111c981229bf2c0bc6afa4a22db011b68d93fdfa 100755 (executable)
@@ -132,8 +132,6 @@ sub format_2822_time {
 my $have_mail_address = eval { require Mail::Address; 1 };
 my $smtp;
 my $auth;
-my $mail_domain_default = "localhost.localdomain";
-my $mail_domain;
 
 sub unique_email_list(@);
 sub cleanup_compose_files();
@@ -190,7 +188,7 @@ sub do_edit {
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
 my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
-my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
+my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts, $smtp_domain);
 my ($validate, $confirm);
 my (@suppress_cc);
 
@@ -212,6 +210,7 @@ sub do_edit {
     "smtpserverport" => \$smtp_server_port,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
+       "smtpdomain" => \$smtp_domain,
     "to" => \@to,
     "cc" => \@initial_cc,
     "cccmd" => \$cc_cmd,
@@ -283,7 +282,7 @@ sub signal_handler {
                    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
                    "smtp-encryption=s" => \$smtp_encryption,
                    "smtp-debug:i" => \$debug_net_smtp,
-                   "smtp-domain:s" => \$mail_domain,
+                   "smtp-domain:s" => \$smtp_domain,
                    "identity=s" => \$identity,
                    "annotate" => \$annotate,
                    "compose" => \$compose,
@@ -761,8 +760,7 @@ sub extract_valid_address {
 # We'll setup a template for the message id, using the "from" address:
 
 my ($message_id_stamp, $message_id_serial);
-sub make_message_id
-{
+sub make_message_id {
        my $uniq;
        if (!defined $message_id_stamp) {
                $message_id_stamp = sprintf("%s-%s", time, $$);
@@ -817,8 +815,7 @@ sub is_rfc2047_quoted {
 }
 
 # use the simplest quoting being able to handle the recipient
-sub sanitize_address
-{
+sub sanitize_address {
        my ($recipient) = @_;
        my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
 
@@ -863,21 +860,23 @@ sub sanitize_address
 # This maildomain*() code is based on ideas in Perl library Test::Reporter
 # /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()
 
-sub maildomain_net
-{
+sub valid_fqdn {
+       my $domain = shift;
+       return !($^O eq 'darwin' && $domain =~ /\.local$/) && $domain =~ /\./;
+}
+
+sub maildomain_net {
        my $maildomain;
 
        if (eval { require Net::Domain; 1 }) {
                my $domain = Net::Domain::domainname();
-               $maildomain = $domain
-                       unless $^O eq 'darwin' && $domain =~ /\.local$/;
+               $maildomain = $domain if valid_fqdn($domain);
        }
 
        return $maildomain;
 }
 
-sub maildomain_mta
-{
+sub maildomain_mta {
        my $maildomain;
 
        if (eval { require Net::SMTP; 1 }) {
@@ -887,8 +886,7 @@ sub maildomain_mta
                                my $domain = $smtp->domain;
                                $smtp->quit;
 
-                               $maildomain = $domain
-                                       unless $^O eq 'darwin' && $domain =~ /\.local$/;
+                               $maildomain = $domain if valid_fqdn($domain);
 
                                last if $maildomain;
                        }
@@ -898,17 +896,15 @@ sub maildomain_mta
        return $maildomain;
 }
 
-sub maildomain
-{
-       return maildomain_net() || maildomain_mta() || $mail_domain_default;
+sub maildomain {
+       return maildomain_net() || maildomain_mta() || 'localhost.localdomain';
 }
 
 # Returns 1 if the message was sent, and 0 otherwise.
 # In actuality, the whole program dies when there
 # is an error sending a message.
 
-sub send_message
-{
+sub send_message {
        my @recipients = unique_email_list(@to);
        @cc = (grep { my $cc = extract_valid_address($_);
                      not grep { $cc eq $_ } @recipients
@@ -1005,18 +1001,18 @@ sub send_message
                if ($smtp_encryption eq 'ssl') {
                        $smtp_server_port ||= 465; # ssmtp
                        require Net::SMTP::SSL;
-                       $mail_domain ||= maildomain();
+                       $smtp_domain ||= maildomain();
                        $smtp ||= Net::SMTP::SSL->new($smtp_server,
-                                                     Hello => $mail_domain,
+                                                     Hello => $smtp_domain,
                                                      Port => $smtp_server_port);
                }
                else {
                        require Net::SMTP;
-                       $mail_domain ||= maildomain();
+                       $smtp_domain ||= maildomain();
                        $smtp ||= Net::SMTP->new((defined $smtp_server_port)
                                                 ? "$smtp_server:$smtp_server_port"
                                                 : $smtp_server,
-                                                Hello => $mail_domain,
+                                                Hello => $smtp_domain,
                                                 Debug => $debug_net_smtp);
                        if ($smtp_encryption eq 'tls' && $smtp) {
                                require Net::SMTP::SSL;
@@ -1039,7 +1035,7 @@ sub send_message
                        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" : "";
                }
 
index 59db3dc38e72fda88d521171a174c08b919677a9..0f858d334c756f9cd4028b4950a3912d0f6182dd 100755 (executable)
@@ -86,7 +86,7 @@ create_stash () {
                        GIT_INDEX_FILE="$TMP-index" &&
                        export GIT_INDEX_FILE &&
                        git read-tree -m $i_tree &&
-                       git add -u &&
+                       git diff --name-only -z HEAD | git update-index -z --add --remove --stdin &&
                        git write-tree &&
                        rm -f "$TMP-index"
                ) ) ||
index ef99ae647ae02995495c71455eef785bdeca1789..8ca76d0507bdc1d95283e1f5fee3f88180cdce26 100644 (file)
@@ -510,7 +510,7 @@ static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned c
                ret = error("File %s has bad hash", hex);
        } else if (req->rename < 0) {
                ret = error("unable to write sha1 filename %s",
-                           req->filename);
+                           sha1_file_name(req->sha1));
        }
 
        release_http_object_request(req);
diff --git a/http.c b/http.c
index 4814217c6401faa1fd8f13f0288758f57b5e3755..901e5972970e672f2524d70d11d4c3bb74ce98c5 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,6 +1,7 @@
 #include "http.h"
 #include "pack.h"
 #include "sideband.h"
+#include "run-command.h"
 
 int data_received;
 int active_requests;
@@ -720,7 +721,7 @@ static inline int hex(int v)
                return 'A' + v - 10;
 }
 
-static void end_url_with_slash(struct strbuf *buf, const char *url)
+void end_url_with_slash(struct strbuf *buf, const char *url)
 {
        strbuf_addstr(buf, url);
        if (buf->len && buf->buf[buf->len - 1] != '/')
@@ -896,47 +897,67 @@ int http_fetch_ref(const char *base, struct ref *ref)
 }
 
 /* Helpers for fetching packs */
-static int fetch_pack_index(unsigned char *sha1, const char *base_url)
+static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
 {
-       int ret = 0;
-       char *hex = xstrdup(sha1_to_hex(sha1));
-       char *filename;
-       char *url = NULL;
+       char *url, *tmp;
        struct strbuf buf = STRBUF_INIT;
 
-       if (has_pack_index(sha1)) {
-               ret = 0;
-               goto cleanup;
-       }
-
        if (http_is_verbose)
-               fprintf(stderr, "Getting index for pack %s\n", hex);
+               fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
 
        end_url_with_slash(&buf, base_url);
-       strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
+       strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
        url = strbuf_detach(&buf, NULL);
 
-       filename = sha1_pack_index_name(sha1);
-       if (http_get_file(url, filename, 0) != HTTP_OK)
-               ret = error("Unable to get pack index %s\n", url);
+       strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
+       tmp = strbuf_detach(&buf, NULL);
+
+       if (http_get_file(url, tmp, 0) != HTTP_OK) {
+               error("Unable to get pack index %s\n", url);
+               free(tmp);
+               tmp = NULL;
+       }
 
-cleanup:
-       free(hex);
        free(url);
-       return ret;
+       return tmp;
 }
 
 static int fetch_and_setup_pack_index(struct packed_git **packs_head,
        unsigned char *sha1, const char *base_url)
 {
        struct packed_git *new_pack;
+       char *tmp_idx = NULL;
+       int ret;
 
-       if (fetch_pack_index(sha1, base_url))
+       if (has_pack_index(sha1)) {
+               new_pack = parse_pack_index(sha1, NULL);
+               if (!new_pack)
+                       return -1; /* parse_pack_index() already issued error message */
+               goto add_pack;
+       }
+
+       tmp_idx = fetch_pack_index(sha1, base_url);
+       if (!tmp_idx)
                return -1;
 
-       new_pack = parse_pack_index(sha1);
-       if (!new_pack)
+       new_pack = parse_pack_index(sha1, tmp_idx);
+       if (!new_pack) {
+               unlink(tmp_idx);
+               free(tmp_idx);
+
                return -1; /* parse_pack_index() already issued error message */
+       }
+
+       ret = verify_pack_index(new_pack);
+       if (!ret) {
+               close_pack_index(new_pack);
+               ret = move_temp_to_file(tmp_idx, sha1_pack_index_name(sha1));
+       }
+       free(tmp_idx);
+       if (ret)
+               return -1;
+
+add_pack:
        new_pack->next = *packs_head;
        *packs_head = new_pack;
        return 0;
@@ -1000,37 +1021,62 @@ void release_http_pack_request(struct http_pack_request *preq)
 
 int finish_http_pack_request(struct http_pack_request *preq)
 {
-       int ret;
        struct packed_git **lst;
+       struct packed_git *p = preq->target;
+       char *tmp_idx;
+       struct child_process ip;
+       const char *ip_argv[8];
 
-       preq->target->pack_size = ftell(preq->packfile);
+       close_pack_index(p);
 
-       if (preq->packfile != NULL) {
-               fclose(preq->packfile);
-               preq->packfile = NULL;
-               preq->slot->local = NULL;
-       }
-
-       ret = move_temp_to_file(preq->tmpfile, preq->filename);
-       if (ret)
-               return ret;
+       fclose(preq->packfile);
+       preq->packfile = NULL;
+       preq->slot->local = NULL;
 
        lst = preq->lst;
-       while (*lst != preq->target)
+       while (*lst != p)
                lst = &((*lst)->next);
        *lst = (*lst)->next;
 
-       if (verify_pack(preq->target))
+       tmp_idx = xstrdup(preq->tmpfile);
+       strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
+              ".idx.temp");
+
+       ip_argv[0] = "index-pack";
+       ip_argv[1] = "-o";
+       ip_argv[2] = tmp_idx;
+       ip_argv[3] = preq->tmpfile;
+       ip_argv[4] = NULL;
+
+       memset(&ip, 0, sizeof(ip));
+       ip.argv = ip_argv;
+       ip.git_cmd = 1;
+       ip.no_stdin = 1;
+       ip.no_stdout = 1;
+
+       if (run_command(&ip)) {
+               unlink(preq->tmpfile);
+               unlink(tmp_idx);
+               free(tmp_idx);
+               return -1;
+       }
+
+       unlink(sha1_pack_index_name(p->sha1));
+
+       if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1))
+        || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
+               free(tmp_idx);
                return -1;
-       install_packed_git(preq->target);
+       }
 
+       install_packed_git(p);
+       free(tmp_idx);
        return 0;
 }
 
 struct http_pack_request *new_http_pack_request(
        struct packed_git *target, const char *base_url)
 {
-       char *filename;
        long prev_posn = 0;
        char range[RANGE_HEADER_SIZE];
        struct strbuf buf = STRBUF_INIT;
@@ -1045,9 +1091,8 @@ struct http_pack_request *new_http_pack_request(
                sha1_to_hex(target->sha1));
        preq->url = strbuf_detach(&buf, NULL);
 
-       filename = sha1_pack_name(target->sha1);
-       snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
-       snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
+       snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
+               sha1_pack_name(target->sha1));
        preq->packfile = fopen(preq->tmpfile, "a");
        if (!preq->packfile) {
                error("Unable to open local file %s for pack",
@@ -1082,7 +1127,6 @@ struct http_pack_request *new_http_pack_request(
        return preq;
 
 abort:
-       free(filename);
        free(preq->url);
        free(preq);
        return NULL;
@@ -1137,7 +1181,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
        freq->localfile = -1;
 
        filename = sha1_file_name(sha1);
-       snprintf(freq->filename, sizeof(freq->filename), "%s", filename);
        snprintf(freq->tmpfile, sizeof(freq->tmpfile),
                 "%s.temp", filename);
 
@@ -1166,8 +1209,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
        }
 
        if (freq->localfile < 0) {
-               error("Couldn't create temporary file %s for %s: %s",
-                     freq->tmpfile, freq->filename, strerror(errno));
+               error("Couldn't create temporary file %s: %s",
+                     freq->tmpfile, strerror(errno));
                goto abort;
        }
 
@@ -1214,8 +1257,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
                        prev_posn = 0;
                        lseek(freq->localfile, 0, SEEK_SET);
                        if (ftruncate(freq->localfile, 0) < 0) {
-                               error("Couldn't truncate temporary file %s for %s: %s",
-                                         freq->tmpfile, freq->filename, strerror(errno));
+                               error("Couldn't truncate temporary file %s: %s",
+                                         freq->tmpfile, strerror(errno));
                                goto abort;
                        }
                }
@@ -1291,7 +1334,7 @@ int finish_http_object_request(struct http_object_request *freq)
                return -1;
        }
        freq->rename =
-               move_temp_to_file(freq->tmpfile, freq->filename);
+               move_temp_to_file(freq->tmpfile, sha1_file_name(freq->sha1));
 
        return freq->rename;
 }
diff --git a/http.h b/http.h
index 5c9441c10ce708be426afe7424d63dcbb68a49e2..b431b8bd5b6d694dd01fc92b2ffcca41655d82bf 100644 (file)
--- a/http.h
+++ b/http.h
@@ -117,6 +117,7 @@ extern void append_remote_object_url(struct strbuf *buf, const char *url,
                                     int only_two_digit_prefix);
 extern char *get_remote_object_url(const char *url, const char *hex,
                                   int only_two_digit_prefix);
+extern void end_url_with_slash(struct strbuf *buf, const char *url);
 
 /* Options for http_request_*() */
 #define HTTP_NO_CACHE          1
@@ -152,7 +153,6 @@ struct http_pack_request
        struct packed_git *target;
        struct packed_git **lst;
        FILE *packfile;
-       char filename[PATH_MAX];
        char tmpfile[PATH_MAX];
        struct curl_slist *range_header;
        struct active_request_slot *slot;
@@ -167,7 +167,6 @@ extern void release_http_pack_request(struct http_pack_request *preq);
 struct http_object_request
 {
        char *url;
-       char filename[PATH_MAX];
        char tmpfile[PATH_MAX];
        int localfile;
        CURLcode curl_result;
index 3ca92c4c4def46af10556dbe9b3f48774b9a4a35..277b3ddba7dc5387cd97cb35c23d3358727898be 100644 (file)
--- a/object.c
+++ b/object.c
@@ -252,10 +252,10 @@ void add_object_array_with_mode(struct object *obj, const char *name, struct obj
 
 void object_array_remove_duplicates(struct object_array *array)
 {
-       int ref, src, dst;
+       unsigned int ref, src, dst;
        struct object_array_entry *objects = array->objects;
 
-       for (ref = 0; ref < array->nr - 1; ref++) {
+       for (ref = 0; ref + 1 < array->nr; ref++) {
                for (src = ref + 1, dst = src;
                     src < array->nr;
                     src++) {
index 166ca703c10face0d4961da6ceee7a149ebcfac4..395fb9527a3bc6dd8ca648233911a1c35604440d 100644 (file)
@@ -133,14 +133,13 @@ static int verify_packfile(struct packed_git *p,
        return err;
 }
 
-int verify_pack(struct packed_git *p)
+int verify_pack_index(struct packed_git *p)
 {
        off_t index_size;
        const unsigned char *index_base;
        git_SHA_CTX ctx;
        unsigned char sha1[20];
        int err = 0;
-       struct pack_window *w_curs = NULL;
 
        if (open_pack_index(p))
                return error("packfile %s index not opened", p->pack_name);
@@ -154,8 +153,18 @@ int verify_pack(struct packed_git *p)
        if (hashcmp(sha1, index_base + index_size - 20))
                err = error("Packfile index for %s SHA1 mismatch",
                            p->pack_name);
+       return err;
+}
+
+int verify_pack(struct packed_git *p)
+{
+       int err = 0;
+       struct pack_window *w_curs = NULL;
+
+       err |= verify_pack_index(p);
+       if (!p->index_data)
+               return -1;
 
-       /* Verify pack file */
        err |= verify_packfile(p, &w_curs);
        unuse_pack(&w_curs);
 
diff --git a/pack.h b/pack.h
index d268c014c9eb7040bd65125b13d68edce670274b..bb275762b7eb6f473f333ae40780821e383db20b 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -57,6 +57,7 @@ struct pack_idx_entry {
 
 extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
 extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
+extern int verify_pack_index(struct packed_git *);
 extern int verify_pack(struct packed_git *);
 extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
 extern char *index_pack_lockfile(int fd);
index 7cb3a2af508bb5667cd74304f72b50766c749990..2777d30224618fb0ec823961c3c35ede1ac4e2e5 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -716,7 +716,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                if (add_again(sb, &c->abbrev_commit_hash))
                        return 1;
                strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
-                                                    DEFAULT_ABBREV));
+                                                    c->pretty_ctx->abbrev));
                c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
                return 1;
        case 'T':               /* tree hash */
@@ -726,7 +726,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                if (add_again(sb, &c->abbrev_tree_hash))
                        return 1;
                strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
-                                                    DEFAULT_ABBREV));
+                                                    c->pretty_ctx->abbrev));
                c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
                return 1;
        case 'P':               /* parent hashes */
@@ -743,7 +743,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
                        strbuf_addstr(sb, find_unique_abbrev(
-                                       p->item->object.sha1, DEFAULT_ABBREV));
+                                       p->item->object.sha1,
+                                       c->pretty_ctx->abbrev));
                }
                c->abbrev_parent_hashes.len = sb->len -
                                              c->abbrev_parent_hashes.off;
index b76bfcb3d3cdbbee2e3279a6696c7d6b526176d7..9c7fcf29566caa3813bd91a22fc78a00cec05335 100644 (file)
@@ -9,7 +9,7 @@
 #include "sideband.h"
 
 static struct remote *remote;
-static const char *url;
+static const char *url; /* always ends with a trailing slash */
 
 struct options {
        int verbosity;
@@ -101,7 +101,7 @@ static struct discovery* discover_refs(const char *service)
                return last;
        free_discovery(last);
 
-       strbuf_addf(&buffer, "%s/info/refs", url);
+       strbuf_addf(&buffer, "%sinfo/refs", url);
        if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
                is_http = 1;
                if (!strchr(url, '?'))
@@ -120,7 +120,7 @@ static struct discovery* discover_refs(const char *service)
                strbuf_reset(&buffer);
 
                proto_git_candidate = 0;
-               strbuf_addf(&buffer, "%s/info/refs", url);
+               strbuf_addf(&buffer, "%sinfo/refs", url);
                refs_url = strbuf_detach(&buffer, NULL);
 
                http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
@@ -509,7 +509,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
        rpc->out = client.out;
        strbuf_init(&rpc->result, 0);
 
-       strbuf_addf(&buf, "%s/%s", url, svc);
+       strbuf_addf(&buf, "%s%s", url, svc);
        rpc->service_url = strbuf_detach(&buf, NULL);
 
        strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
@@ -798,11 +798,13 @@ int main(int argc, const char **argv)
        remote = remote_get(argv[1]);
 
        if (argc > 2) {
-               url = argv[2];
+               end_url_with_slash(&buf, argv[2]);
        } else {
-               url = remote->url[0];
+               end_url_with_slash(&buf, remote->url[0]);
        }
 
+       url = strbuf_detach(&buf, NULL);
+
        http_init(remote);
 
        do {
index c70181cdc621b27ed02aba17b3e4f7ab64518e9f..26ce56046dd14fec4ae59c7e7df3c2072178c51d 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -476,7 +476,7 @@ static void read_config(void)
        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;
index eb5c57562909c1e505c2a7688bad6dbeea4b96a0..c7793f50fbe0a43495c2b2d36a47c0b5aac37483 100644 (file)
@@ -383,6 +383,8 @@ int start_command(struct child_process *cmd)
                        close(cmd->out);
                if (need_err)
                        close_pair(fderr);
+               else if (cmd->err)
+                       close(cmd->err);
                errno = failed_errno;
                return -1;
        }
diff --git a/setup.c b/setup.c
index 5716d90b57574d045114f4aaad1bdf36fd79ed89..0e4cfe603f1afe515365c9daf1ad8406de8e7fa8 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -519,6 +519,12 @@ int check_repository_format(void)
        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);
index ff65328006404fd0d113f2cd4c4d52377b4cc8cf..1efa9154eda4e1ec064d627dfb21c876c914f4d7 100644 (file)
@@ -599,6 +599,14 @@ void unuse_pack(struct pack_window **w_cursor)
        }
 }
 
+void close_pack_index(struct packed_git *p)
+{
+       if (p->index_data) {
+               munmap((void *)p->index_data, p->index_size);
+               p->index_data = NULL;
+       }
+}
+
 /*
  * This is used by git-repack in case a newly created pack happens to
  * contain the same set of objects as an existing one.  In that case
@@ -620,8 +628,7 @@ void free_pack_by_name(const char *pack_name)
                        close_pack_windows(p);
                        if (p->pack_fd != -1)
                                close(p->pack_fd);
-                       if (p->index_data)
-                               munmap((void *)p->index_data, p->index_size);
+                       close_pack_index(p);
                        free(p->bad_object_sha1);
                        *pp = p->next;
                        free(p);
@@ -831,9 +838,8 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
        return p;
 }
 
-struct packed_git *parse_pack_index(unsigned char *sha1)
+struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
 {
-       const char *idx_path = sha1_pack_index_name(sha1);
        const char *path = sha1_pack_name(sha1);
        struct packed_git *p = alloc_packed_git(strlen(path) + 1);
 
@@ -2448,6 +2454,8 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
                else
                        ret = -1;
                strbuf_release(&sbuf);
+       } else if (!size) {
+               ret = index_mem(sha1, NULL, size, write_object, type, path);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
@@ -2456,12 +2464,11 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
                else
                        ret = error("short read %s", strerror(errno));
                free(buf);
-       } else if (size) {
+       } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
                ret = index_mem(sha1, buf, size, write_object, type, path);
                munmap(buf, size);
-       } else
-               ret = index_mem(sha1, NULL, size, write_object, type, path);
+       }
        close(fd);
        return ret;
 }
index bc02cc29ef0d5f640ab390614def995f30fe4691..de4f86fb970e15491f44dfe38b7d7d6fdc3be9ad 100644 (file)
@@ -12,6 +12,7 @@ struct shortlog {
        int in1;
        int in2;
        int user_format;
+       int abbrev;
 
        char *common_repo_prefix;
        int email;
index dcd3ebb5f2dcdbf15ca0e4a043b45cd2fc36cbb5..0e4e8d8862c96383a6f6f22a1b6bb01044925620 100644 (file)
--- a/t/README
+++ b/t/README
@@ -84,6 +84,12 @@ appropriately before running "make".
        implied by other options like --valgrind and
        GIT_TEST_INSTALLED.
 
+--root=<directory>::
+       Create "trash" directories used to store all temporary data during
+       testing under <directory>, instead of the t/ directory.
+       Using this option with a RAM-based filesystem (such as tmpfs)
+       can massively speed up the test suite.
+
 You can also set the GIT_TEST_INSTALLED environment variable to
 the bindir of an existing git installation to test that installation.
 You still need to have built this git sandbox, from which various
index f4ca4fc85c6b52a2ba919528284f2b668e6bd3d2..3ec9cbef2c88f65e5fb254d10cc551c6c4062c88 100755 (executable)
@@ -73,6 +73,27 @@ then
        exit 1
 fi
 
+clean=no
+test_expect_success 'tests clean up after themselves' '
+    test_when_finished clean=yes
+'
+
+cleaner=no
+test_expect_code 1 'tests clean up even after a failure' '
+    test_when_finished cleaner=yes &&
+    (exit 1)
+'
+
+if test $clean$cleaner != yesyes
+then
+       say "bug in test framework: cleanup commands do not work reliably"
+       exit 1
+fi
+
+test_expect_code 2 'failure to clean up causes the test to fail' '
+    test_when_finished "(exit 2)"
+'
+
 ################################################################
 # Basics of the basics
 
index 675773479a8c6a1791ae01eb47654f4433c30ee3..7c0a698b92696ff2bbb91e65e0a42e87f9163a1d 100755 (executable)
@@ -310,4 +310,18 @@ test_expect_success POSIXPERM 'init notices EPERM' '
        )
 '
 
+test_expect_success 'init creates a new bare directory with global --bare' '
+       rm -rf newdir &&
+       git --bare init newdir &&
+       test -d newdir/refs
+'
+
+test_expect_success 'init prefers command line to GIT_DIR' '
+       rm -rf newdir &&
+       mkdir otherdir &&
+       GIT_DIR=otherdir git --bare init newdir &&
+       test -d newdir/refs &&
+       ! test -d otherdir/refs
+'
+
 test_done
index 1c77192eb318d007689089eaf42f4f939c2f9ee4..53bd7fcc4abbf6cb7db73b43d5dde477f5115f90 100755 (executable)
@@ -20,8 +20,12 @@ test_expect_success 'setup' '
 
        mkdir -p a/b/d a/c &&
        (
+               echo "[attr]notest !test"
                echo "f test=f"
                echo "a/i test=a/i"
+               echo "onoff test -test"
+               echo "offon -test test"
+               echo "no notest"
        ) >.gitattributes &&
        (
                echo "g test=a/g" &&
@@ -30,6 +34,7 @@ test_expect_success 'setup' '
        (
                echo "h test=a/b/h" &&
                echo "d/* test=a/b/d/*"
+               echo "d/yes notest"
        ) >a/b/.gitattributes
 
 '
@@ -44,6 +49,11 @@ test_expect_success 'attribute test' '
        attr_check b/g unspecified &&
        attr_check a/b/h a/b/h &&
        attr_check a/b/d/g "a/b/d/*"
+       attr_check onoff unset
+       attr_check offon set
+       attr_check no unspecified
+       attr_check a/b/d/no "a/b/d/*"
+       attr_check a/b/d/yes unspecified
 
 '
 
@@ -58,6 +68,11 @@ a/b/g: test: a/b/g
 b/g: test: unspecified
 a/b/h: test: a/b/h
 a/b/d/g: test: a/b/d/*
+onoff: test: unset
+offon: test: set
+no: test: unspecified
+a/b/d/no: test: a/b/d/*
+a/b/d/yes: test: unspecified
 EOF
 
        sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
index 9df301211c7f03e4a9edb0ccb9b1a7a648f97d8c..bd8b60732b06365fa22585eb8e97a3b1e64fdd05 100755 (executable)
@@ -30,6 +30,7 @@ test_rev_parse() {
 
 EMPTY_TREE=$(git write-tree)
 mkdir -p work/sub/dir || exit 1
+mkdir -p work2 || exit 1
 mv .git repo.git || exit 1
 
 say "core.worktree = relative path"
@@ -54,7 +55,9 @@ GIT_DIR=$(pwd)/repo.git
 GIT_CONFIG=$GIT_DIR/config
 git config core.worktree "$(pwd)/work"
 test_rev_parse 'outside'      false false false
-cd work || exit 1
+cd work2
+test_rev_parse 'outside2'     false false false
+cd ../work || exit 1
 test_rev_parse 'inside'       false false true ''
 cd sub/dir || exit 1
 test_rev_parse 'subdirectory' false false true sub/dir/
@@ -67,7 +70,9 @@ git config core.worktree non-existent
 GIT_WORK_TREE=work
 export GIT_WORK_TREE
 test_rev_parse 'outside'      false false false
-cd work || exit 1
+cd work2
+test_rev_parse 'outside'      false false false
+cd ../work || exit 1
 GIT_WORK_TREE=.
 test_rev_parse 'inside'       false false true ''
 cd sub/dir || exit 1
@@ -76,6 +81,7 @@ test_rev_parse 'subdirectory' false false true sub/dir/
 cd ../../.. || exit 1
 
 mv work repo.git/work
+mv work2 repo.git/work2
 
 say "GIT_WORK_TREE=absolute path, work tree below git dir"
 GIT_DIR=$(pwd)/repo.git
@@ -86,6 +92,8 @@ cd repo.git || exit 1
 test_rev_parse 'in repo.git'              false true  false
 cd objects || exit 1
 test_rev_parse 'in repo.git/objects'      false true  false
+cd ../work2 || exit 1
+test_rev_parse 'in repo.git/work2'      false true  false
 cd ../work || exit 1
 test_rev_parse 'in repo.git/work'         false true true ''
 cd sub/dir || exit 1
index 476e5ec038f3c9fd2cad4607c3819f54dabd04ad..8fe14ccc5444df95960bee88e5b79eb68156572b 100755 (executable)
@@ -228,4 +228,154 @@ test_expect_success 'stash --invalid-option' '
        test bar,bar2 = $(cat file),$(cat file2)
 '
 
+test_expect_success 'stash an added file' '
+       git reset --hard &&
+       echo new >file3 &&
+       git add file3 &&
+       git stash save "added file" &&
+       ! test -r file3 &&
+       git stash apply &&
+       test new = "$(cat file3)"
+'
+
+test_expect_success 'stash rm then recreate' '
+       git reset --hard &&
+       git rm file &&
+       echo bar7 >file &&
+       git stash save "rm then recreate" &&
+       test bar = "$(cat file)" &&
+       git stash apply &&
+       test bar7 = "$(cat file)"
+'
+
+test_expect_success 'stash rm and ignore' '
+       git reset --hard &&
+       git rm file &&
+       echo file >.gitignore &&
+       git stash save "rm and ignore" &&
+       test bar = "$(cat file)" &&
+       test file = "$(cat .gitignore)"
+       git stash apply &&
+       ! test -r file &&
+       test file = "$(cat .gitignore)"
+'
+
+test_expect_success 'stash rm and ignore (stage .gitignore)' '
+       git reset --hard &&
+       git rm file &&
+       echo file >.gitignore &&
+       git add .gitignore &&
+       git stash save "rm and ignore (stage .gitignore)" &&
+       test bar = "$(cat file)" &&
+       ! test -r .gitignore
+       git stash apply &&
+       ! test -r file &&
+       test file = "$(cat .gitignore)"
+'
+
+test_expect_success SYMLINKS 'stash file to symlink' '
+       git reset --hard &&
+       rm file &&
+       ln -s file2 file &&
+       git stash save "file to symlink" &&
+       test -f file &&
+       test bar = "$(cat file)" &&
+       git stash apply &&
+       case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
+       git reset --hard &&
+       git rm file &&
+       ln -s file2 file &&
+       git stash save "file to symlink (stage rm)" &&
+       test -f file &&
+       test bar = "$(cat file)" &&
+       git stash apply &&
+       case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
+       git reset --hard &&
+       rm file &&
+       ln -s file2 file &&
+       git add file &&
+       git stash save "file to symlink (full stage)" &&
+       test -f file &&
+       test bar = "$(cat file)" &&
+       git stash apply &&
+       case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+# This test creates a commit with a symlink used for the following tests
+
+test_expect_success SYMLINKS 'stash symlink to file' '
+       git reset --hard &&
+       ln -s file filelink &&
+       git add filelink &&
+       git commit -m "Add symlink" &&
+       rm filelink &&
+       cp file filelink &&
+       git stash save "symlink to file" &&
+       test -h filelink &&
+       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+       git stash apply &&
+       ! test -h filelink &&
+       test bar = "$(cat file)"
+'
+
+test_expect_success SYMLINKS 'stash symlink to file (stage rm)' '
+       git reset --hard &&
+       git rm filelink &&
+       cp file filelink &&
+       git stash save "symlink to file (stage rm)" &&
+       test -h filelink &&
+       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+       git stash apply &&
+       ! test -h filelink &&
+       test bar = "$(cat file)"
+'
+
+test_expect_success SYMLINKS 'stash symlink to file (full stage)' '
+       git reset --hard &&
+       rm filelink &&
+       cp file filelink &&
+       git add filelink &&
+       git stash save "symlink to file (full stage)" &&
+       test -h filelink &&
+       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+       git stash apply &&
+       ! test -h filelink &&
+       test bar = "$(cat file)"
+'
+
+test_expect_failure 'stash directory to file' '
+       git reset --hard &&
+       mkdir dir &&
+       echo foo >dir/file &&
+       git add dir/file &&
+       git commit -m "Add file in dir" &&
+       rm -fr dir &&
+       echo bar >dir &&
+       git stash save "directory to file" &&
+       test -d dir &&
+       test foo = "$(cat dir/file)" &&
+       test_must_fail git stash apply &&
+       test bar = "$(cat dir)" &&
+       git reset --soft HEAD^
+'
+
+test_expect_failure 'stash file to directory' '
+       git reset --hard &&
+       rm file &&
+       mkdir file &&
+       echo foo >file/file &&
+       git stash save "file to directory" &&
+       test -f file &&
+       test bar = "$(cat file)" &&
+       git stash apply &&
+       test -f file/file &&
+       test foo = "$(cat file/file)"
+'
+
 test_done
index a01e55bf6b96246c33332e5112bcb3d6583402ac..cdb70b4b3356eeb45bb6e5ac62d1f82eb6b3ccdc 100755 (executable)
@@ -8,30 +8,93 @@ test_description='git shortlog
 
 . ./test-lib.sh
 
-echo 1 > a1
-git add a1
-tree=$(git write-tree)
-commit=$( (echo "Test"; echo) | git commit-tree $tree )
-git update-ref HEAD $commit
+test_expect_success 'setup' '
+       echo 1 >a1 &&
+       git add a1 &&
+       tree=$(git write-tree) &&
+       commit=$(printf "%s\n" "Test" "" | git commit-tree "$tree") &&
+       git update-ref HEAD "$commit" &&
+
+       echo 2 >a1 &&
+       git commit --quiet -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1 &&
+
+       # test if the wrapping is still valid
+       # when replacing all is by treble clefs.
+       echo 3 >a1 &&
+       git commit --quiet -m "$(
+               echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" |
+               sed "s/i/1234/g" |
+               tr 1234 "\360\235\204\236")" a1 &&
+
+       # now fsck up the utf8
+       git config i18n.commitencoding non-utf-8 &&
+       echo 4 >a1 &&
+       git commit --quiet -m "$(
+               echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" |
+               sed "s/i/1234/g" |
+               tr 1234 "\370\235\204\236")" a1 &&
+
+       echo 5 >a1 &&
+       git commit --quiet -m "a                                                                12      34      56      78" a1
+
+       echo 6 >a1 &&
+       git commit --quiet -m "Commit by someone else" \
+               --author="Someone else <not!me>" a1 &&
+
+       cat >expect.template <<-\EOF
+       A U Thor (5):
+             SUBJECT
+             SUBJECT
+             SUBJECT
+             SUBJECT
+             SUBJECT
+
+       Someone else (1):
+             SUBJECT
+
+       EOF
+'
 
-echo 2 > a1
-git commit --quiet -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1
+fuzz() {
+       file=$1 &&
+       sed "
+                       s/$_x40/OBJECT_NAME/g
+                       s/$_x05/OBJID/g
+                       s/^ \{6\}[CTa].*/      SUBJECT/g
+                       s/^ \{8\}[^ ].*/        CONTINUATION/g
+               " <"$file" >"$file.fuzzy" &&
+       sed "/CONTINUATION/ d" <"$file.fuzzy"
+}
 
-# test if the wrapping is still valid when replacing all i's by treble clefs.
-echo 3 > a1
-git commit --quiet -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\360\235\204\236')" a1
+test_expect_success 'default output format' '
+       git shortlog HEAD >log &&
+       fuzz log >log.predictable &&
+       test_cmp expect.template log.predictable
+'
 
-# now fsck up the utf8
-git config i18n.commitencoding non-utf-8
-echo 4 > a1
-git commit --quiet -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\370\235\204\236')" a1
+test_expect_success 'pretty format' '
+       sed s/SUBJECT/OBJECT_NAME/ expect.template >expect &&
+       git shortlog --format="%H" HEAD >log &&
+       fuzz log >log.predictable &&
+       test_cmp expect log.predictable
+'
 
-echo 5 > a1
-git commit --quiet -m "a                                                               12      34      56      78" a1
+test_expect_success '--abbrev' '
+       sed s/SUBJECT/OBJID/ expect.template >expect &&
+       git shortlog --format="%h" --abbrev=5 HEAD >log &&
+       fuzz log >log.predictable &&
+       test_cmp expect log.predictable
+'
 
-git shortlog -w HEAD > out
+test_expect_success 'output from user-defined format is re-wrapped' '
+       sed "s/SUBJECT/two lines/" expect.template >expect &&
+       git shortlog --format="two%nlines" HEAD >log &&
+       fuzz log >log.predictable &&
+       test_cmp expect log.predictable
+'
 
-cat > expect << EOF
+test_expect_success 'shortlog wrapping' '
+       cat >expect <<\EOF &&
 A U Thor (5):
       Test
       This is a very, very long first line for the commit message to see if
@@ -43,14 +106,19 @@ A U Thor (5):
       a                                                                12      34
          56    78
 
-EOF
-
-test_expect_success 'shortlog wrapping' 'test_cmp expect out'
+Someone else (1):
+      Commit by someone else
 
-git log HEAD > log
-GIT_DIR=non-existing git shortlog -w < log > out
+EOF
+       git shortlog -w HEAD >out &&
+       test_cmp expect out
+'
 
-test_expect_success 'shortlog from non-git directory' 'test_cmp expect out'
+test_expect_success 'shortlog from non-git directory' '
+       git log HEAD >log &&
+       GIT_DIR=non-existing git shortlog -w <log >out &&
+       test_cmp expect out
+'
 
 iconvfromutf8toiso88591() {
        printf "%s" "$*" | iconv -f UTF-8 -t ISO8859-1
index 2de98e6561607b87bceef66c20ad9055d04878c3..6a37a4d993df3fa4958a719fdfdb82f0dd2de623 100755 (executable)
@@ -528,7 +528,7 @@ test_expect_success 'push does not update local refs on failure' '
        mk_test heads/master &&
        mk_child child &&
        mkdir testrepo/.git/hooks &&
-       echo exit 1 >testrepo/.git/hooks/pre-receive &&
+       echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
        chmod +x testrepo/.git/hooks/pre-receive &&
        (cd child &&
                git pull .. master
index 795dc2bcdf98e582dd2f05d901b791ab4225ce3b..17e1bdc5a81ce4802f5074ddb9c1996c68fc97be 100755 (executable)
@@ -34,8 +34,34 @@ test_expect_success 'setup remote repository' '
        mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
 '
 
-test_expect_success 'clone remote repository' '
+cat >exp <<EOF
+GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'no empty path components' '
+       # In the URL, add a trailing slash, and see if git appends yet another
+       # slash.
        cd "$ROOT_PATH" &&
+       git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
+
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+
+       # Clear the log, so that it does not affect the "used receive-pack
+       # service" test which reads the log too.
+       #
+       # We do this before the actual comparison to ensure the log is cleared.
+       echo > "$HTTPD_ROOT_PATH"/access.log &&
+
+       test_cmp exp act
+'
+
+test_expect_success 'clone remote repository' '
+       rm -rf test_repo_clone &&
        git clone $HTTPD_URL/smart/test_repo.git test_repo_clone
 '
 
@@ -68,6 +94,7 @@ test_expect_success 'create and delete remote branch' '
 '
 
 cat >exp <<EOF
+
 GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
 POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
 GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
index 8cfce969bcdac6e2091e635dad9c58ca616e5c3b..fc675b50adfa945724cd7d17dbb6034df53426d3 100755 (executable)
@@ -55,12 +55,43 @@ test_expect_success 'http remote detects correct HEAD' '
 
 test_expect_success 'fetch packed objects' '
        cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
-       cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
-       git --bare repack &&
-       git --bare prune-packed &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+        git --bare repack &&
+        git --bare prune-packed
+       ) &&
        git clone $HTTPD_URL/dumb/repo_pack.git
 '
 
+test_expect_success 'fetch notices corrupt pack' '
+       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+        p=`ls objects/pack/pack-*.pack` &&
+        chmod u+w $p &&
+        printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+       ) &&
+       mkdir repo_bad1.git &&
+       (cd repo_bad1.git &&
+        git --bare init &&
+        test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad1.git &&
+        test 0 = `ls objects/pack/pack-*.pack | wc -l`
+       )
+'
+
+test_expect_success 'fetch notices corrupt idx' '
+       cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+        p=`ls objects/pack/pack-*.idx` &&
+        chmod u+w $p &&
+        printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+       ) &&
+       mkdir repo_bad2.git &&
+       (cd repo_bad2.git &&
+        git --bare init &&
+        test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad2.git &&
+        test 0 = `ls objects/pack | wc -l`
+       )
+'
+
 test_expect_success 'did not use upload-pack service' '
        grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
        : >exp
index a8f4419e610c4329cefc556684a7212f1405e104..ddc3dc52f497d05e20cf4034d544df6d08632935 100755 (executable)
@@ -30,4 +30,20 @@ test_expect_success 'tags can be excluded by rev-list options' '
 
 '
 
+test_expect_failure 'bundle --stdin' '
+
+       echo master | git bundle create stdin-bundle.bdl --stdin &&
+       git ls-remote stdin-bundle.bdl >output &&
+       grep master output
+
+'
+
+test_expect_failure 'bundle --stdin <rev-list options>' '
+
+       echo master | git bundle create hybrid-bundle.bdl --stdin tag &&
+       git ls-remote hybrid-bundle.bdl >output &&
+       grep master output
+
+'
+
 test_done
index adfaae8c5b453835eeeac3e3794950971e6dd6d8..8afbdd4de2146be763f7454af66a15986490fe60 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success 'setup' '
 
        git config pack.compression 0 &&
        git config pack.depth 0 &&
-       blobsize=$((20*1024*1024)) &&
+       blobsize=$((100*1024*1024)) &&
        blobcount=$((2*1024*1024*1024/$blobsize+1)) &&
        i=1 &&
        (while test $i -le $blobcount
@@ -36,9 +36,15 @@ test_expect_success 'setup' '
 
 '
 
-test_expect_success 'clone' '
+test_expect_success 'clone - bare' '
 
-       git clone --bare --no-hardlinks . clone
+       git clone --bare --no-hardlinks . clone-bare
+
+'
+
+test_expect_success 'clone - with worktree, file:// protocol' '
+
+       git clone file://. clone-wt
 
 '
 
index a49b7c5722e6cb675f771c31e4eec87262388548..e8fde5c19c16f3fa2305ed319edb3289f4f758b9 100755 (executable)
@@ -191,6 +191,31 @@ test_expect_success 'add LF before non-empty (2)' '
        grep "^$" actual
 '
 
+test_expect_success '--abbrev' '
+       echo SHORT SHORT SHORT >expect2 &&
+       echo LONG LONG LONG >expect3 &&
+       git log -1 --format="%h %h %h" HEAD >actual1 &&
+       git log -1 --abbrev=5 --format="%h %h %h" HEAD >actual2 &&
+       git log -1 --abbrev=5 --format="%H %H %H" HEAD >actual3 &&
+       sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual2 >fuzzy2 &&
+       sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual3 >fuzzy3 &&
+       test_cmp expect2 fuzzy2 &&
+       test_cmp expect3 fuzzy3 &&
+       ! test_cmp actual1 actual2
+'
+
+test_expect_success '%H is not affected by --abbrev-commit' '
+       git log -1 --format=%H --abbrev-commit --abbrev=20 HEAD >actual &&
+       len=$(wc -c <actual) &&
+       test $len = 41
+'
+
+test_expect_success '%h is not affected by --abbrev-commit' '
+       git log -1 --format=%h --abbrev-commit --abbrev=20 HEAD >actual &&
+       len=$(wc -c <actual) &&
+       test $len = 21
+'
+
 test_expect_success '"%h %gD: %gs" is same as git-reflog' '
        git reflog >expect &&
        git log -g --format="%h %gD: %gs" >actual &&
@@ -203,6 +228,12 @@ test_expect_success '"%h %gD: %gs" is same as git-reflog (with date)' '
        test_cmp expect actual
 '
 
+test_expect_success '"%h %gD: %gs" is same as git-reflog (with --abbrev)' '
+       git reflog --abbrev=13 --date=raw >expect &&
+       git log -g --abbrev=13 --format="%h %gD: %gs" --date=raw >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '%gd shortens ref name' '
        echo "master@{0}" >expect.gd-short &&
        git log -g -1 --format=%gd refs/heads/master >actual.gd-short &&
index 065deadc29eb3838f391ce758c4b188249dc87f9..876d1ab7430c2054ddc69bd3712ff769de52e774 100755 (executable)
@@ -8,7 +8,7 @@ test_description='test describe
  o----o----o----o----o----.    /
        \        A    c        /
         .------------o---o---o
-                     D   e
+                   D,R   e
 '
 . ./test-lib.sh
 
@@ -68,6 +68,8 @@ test_expect_success setup '
        echo D >another && git add another && git commit -m D &&
        test_tick &&
        git tag -a -m D D &&
+       test_tick &&
+       git tag -a -m R R &&
 
        test_tick &&
        echo DD >another && git commit -a -m another &&
@@ -89,10 +91,10 @@ test_expect_success setup '
 
 check_describe A-* HEAD
 check_describe A-* HEAD^
-check_describe D-* HEAD^^
+check_describe R-* HEAD^^
 check_describe A-* HEAD^^2
 check_describe B HEAD^^2^
-check_describe D-* HEAD^^^
+check_describe R-* HEAD^^^
 
 check_describe c-* --tags HEAD
 check_describe c-* --tags HEAD^
index d9202d5af5a179ca0b77d6509e05cbe6bf092ccc..3bc7a2a796bdb97702eaaf92e26b189cc4204c90 100755 (executable)
@@ -4,17 +4,24 @@ test_description='Test automatic use of a pager.'
 
 . ./test-lib.sh
 
-rm -f stdout_is_tty
+cleanup_fail() {
+       echo >&2 cleanup failed
+       (exit 1)
+}
+
 test_expect_success 'set up terminal for tests' '
+       rm -f stdout_is_tty ||
+       cleanup_fail &&
+
        if test -t 1
        then
-               : > stdout_is_tty
+               >stdout_is_tty
        elif
                test_have_prereq PERL &&
                "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl \
                        sh -c "test -t 1"
        then
-               : > test_terminal_works
+               >test_terminal_works
        fi
 '
 
@@ -32,53 +39,68 @@ else
        say no usable terminal, so skipping some tests
 fi
 
-unset GIT_PAGER GIT_PAGER_IN_USE
-git config --unset core.pager
-PAGER='cat > paginated.out'
-export PAGER
-
 test_expect_success 'setup' '
+       unset GIT_PAGER GIT_PAGER_IN_USE &&
+       test_might_fail git config --unset core.pager &&
+
+       PAGER="cat >paginated.out" &&
+       export PAGER &&
+
        test_commit initial
 '
 
-rm -f paginated.out
 test_expect_success TTY 'some commands use a pager' '
+       rm -f paginated.out ||
+       cleanup_fail &&
+
        test_terminal git log &&
        test -e paginated.out
 '
 
-rm -f paginated.out
 test_expect_success TTY 'some commands do not use a pager' '
+       rm -f paginated.out ||
+       cleanup_fail &&
+
        test_terminal git rev-list HEAD &&
        ! test -e paginated.out
 '
 
-rm -f paginated.out
 test_expect_success 'no pager when stdout is a pipe' '
+       rm -f paginated.out ||
+       cleanup_fail &&
+
        git log | cat &&
        ! test -e paginated.out
 '
 
-rm -f paginated.out
 test_expect_success 'no pager when stdout is a regular file' '
-       git log > file &&
+       rm -f paginated.out ||
+       cleanup_fail &&
+
+       git log >file &&
        ! test -e paginated.out
 '
 
-rm -f paginated.out
 test_expect_success TTY 'git --paginate rev-list uses a pager' '
+       rm -f paginated.out ||
+       cleanup_fail &&
+
        test_terminal git --paginate rev-list HEAD &&
        test -e paginated.out
 '
 
-rm -f file paginated.out
 test_expect_success 'no pager even with --paginate when stdout is a pipe' '
+       rm -f file paginated.out ||
+       cleanup_fail &&
+
        git --paginate log | cat &&
        ! test -e paginated.out
 '
 
-rm -f paginated.out
 test_expect_success TTY 'no pager with --no-pager' '
+       rm -f paginated.out ||
+       cleanup_fail &&
+
        test_terminal git --no-pager log &&
        ! test -e paginated.out
 '
@@ -86,88 +108,119 @@ test_expect_success TTY 'no pager with --no-pager' '
 # A colored commit log will begin with an appropriate ANSI escape
 # for the first color; the text "commit" comes later.
 colorful() {
-       read firstline < $1
+       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
index 844fb43c6db1ae4e9b8a3cda6156af359e9f639e..95044668ee182fc2c1091fba7680e0576f7854bd 100755 (executable)
@@ -35,7 +35,7 @@ test_expect_success 'partial' '
 
 '
 
-test_expect_success 'partial modification in a subdirecotry' '
+test_expect_success 'partial modification in a subdirectory' '
 
        test_tick &&
        git commit -m "partial commit to subdirectory" not &&
index 556d0faa77e027c8a18e213088fa6bbc5d7e7af5..008d5711b818a315136290c141c830cdf5af80a0 100755 (executable)
@@ -68,6 +68,34 @@ test_expect_success 'status (2)' '
 
 '
 
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+#      new file:   dir2/added
+#
+# Changed but not updated:
+#      modified:   dir1/modified
+#
+# Untracked files:
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+
+git config advice.statusHints false
+
+test_expect_success 'status (advice.statusHints false)' '
+
+       git status >output &&
+       test_cmp expect output
+
+'
+
+git config --unset advice.statusHints
+
 cat >expect <<\EOF
  M dir1/modified
 A  dir2/added
@@ -115,6 +143,23 @@ test_expect_success 'status (status.showUntrackedFiles no)' '
        test_cmp expect output
 '
 
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#      new file:   dir2/added
+#
+# Changed but not updated:
+#      modified:   dir1/modified
+#
+# Untracked files not listed
+EOF
+git config advice.statusHints false
+test_expect_success 'status -uno (advice.statusHints false)' '
+       git status -uno >output &&
+       test_cmp expect output
+'
+git config --unset advice.statusHints
+
 cat >expect << EOF
  M dir1/modified
 A  dir2/added
@@ -496,6 +541,16 @@ test_expect_success 'dry-run of partial commit excluding new file in index' '
        test_cmp expect output
 '
 
+cat >expect <<EOF
+:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M     dir1/modified
+EOF
+test_expect_success 'status refreshes the index' '
+       touch dir2/added &&
+       git status &&
+       git diff-files >output &&
+       test_cmp expect output
+'
+
 test_expect_success 'setup status submodule summary' '
        test_create_repo sm && (
                cd sm &&
@@ -693,4 +748,19 @@ test_expect_success 'commit --dry-run submodule summary (--amend)' '
        test_cmp expect output
 '
 
+test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
+       (
+               chmod a-w .git &&
+               # make dir1/tracked stat-dirty
+               >dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked &&
+               git status -s >output &&
+               ! grep dir1/tracked output &&
+               # make sure "status" succeeded without writing index out
+               git diff-files | grep dir1/tracked
+       )
+       status=$?
+       chmod 775 .git
+       (exit $status)
+'
+
 test_done
index acce3d06a8cba094d6eac88516dc316c8084e736..9bfa14be7f1c3935013d04318eeaecc68b4ef88c 100644 (file)
@@ -366,8 +366,9 @@ test_debug () {
 }
 
 test_run_ () {
-       test_cleanup='eval_ret=$?'
+       test_cleanup=:
        eval >&3 2>&4 "$1"
+       eval_ret=$?
        eval >&3 2>&4 "$test_cleanup"
        return 0
 }
@@ -529,6 +530,22 @@ test_must_fail () {
        test $? -gt 0 -a $? -le 129 -o $? -gt 192
 }
 
+# Similar to test_must_fail, but tolerates success, too.  This is
+# meant to be used in contexts like:
+#
+#      test_expect_success 'some command works without configuration' '
+#              test_might_fail git config --unset all.configuration &&
+#              do something
+#      '
+#
+# Writing "git config --unset all.configuration || :" would be wrong,
+# because we want to notice if it fails due to segv.
+
+test_might_fail () {
+       "$@"
+       test $? -ge 0 -a $? -le 129 -o $? -gt 192
+}
+
 # test_cmp is a helper function to compare actual and expected output.
 # You can use it like:
 #
@@ -567,8 +584,8 @@ test_cmp() {
 # the test to pass.
 
 test_when_finished () {
-       test_cleanup="eval_ret=\$?; { $*
-               } && (exit \"\$eval_ret\"); $test_cleanup"
+       test_cleanup="{ $*
+               } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
 }
 
 # Most tests can use the created repository, but some may need to create more.
diff --git a/tag.c b/tag.c
index 4470d2bf78e1fbb00d00e487f41daa4373cf48e1..85607c219e25d63b0d1f3344649104c60f5b96e2 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -36,43 +36,50 @@ struct tag *lookup_tag(const unsigned char *sha1)
         return (struct tag *) obj;
 }
 
+static unsigned long parse_tag_date(const char *buf, const char *tail)
+{
+       const char *dateptr;
+
+       while (buf < tail && *buf++ != '>')
+               /* nada */;
+       if (buf >= tail)
+               return 0;
+       dateptr = buf;
+       while (buf < tail && *buf++ != '\n')
+               /* nada */;
+       if (buf >= tail)
+               return 0;
+       /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
+       return strtoul(dateptr, NULL, 10);
+}
+
 int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
 {
-       int typelen, taglen;
        unsigned char sha1[20];
-       const char *type_line, *tag_line, *sig_line;
        char type[20];
-       const char *start = data;
+       const char *bufptr = data;
+       const char *tail = bufptr + size;
+       const char *nl;
 
-        if (item->object.parsed)
-                return 0;
-        item->object.parsed = 1;
+       if (item->object.parsed)
+               return 0;
+       item->object.parsed = 1;
 
        if (size < 64)
                return -1;
-       if (memcmp("object ", data, 7) || get_sha1_hex((char *) data + 7, sha1))
+       if (memcmp("object ", bufptr, 7) || get_sha1_hex(bufptr + 7, sha1) || bufptr[47] != '\n')
                return -1;
+       bufptr += 48; /* "object " + sha1 + "\n" */
 
-       type_line = (char *) data + 48;
-       if (memcmp("\ntype ", type_line-1, 6))
+       if (prefixcmp(bufptr, "type "))
                return -1;
-
-       tag_line = memchr(type_line, '\n', size - (type_line - start));
-       if (!tag_line || memcmp("tag ", ++tag_line, 4))
+       bufptr += 5;
+       nl = memchr(bufptr, '\n', tail - bufptr);
+       if (!nl || sizeof(type) <= (nl - bufptr))
                return -1;
-
-       sig_line = memchr(tag_line, '\n', size - (tag_line - start));
-       if (!sig_line)
-               return -1;
-       sig_line++;
-
-       typelen = tag_line - type_line - strlen("type \n");
-       if (typelen >= 20)
-               return -1;
-       memcpy(type, type_line + 5, typelen);
-       type[typelen] = '\0';
-       taglen = sig_line - tag_line - strlen("tag \n");
-       item->tag = xmemdupz(tag_line + 4, taglen);
+       strncpy(type, bufptr, nl - bufptr);
+       type[nl - bufptr] = '\0';
+       bufptr = nl + 1;
 
        if (!strcmp(type, blob_type)) {
                item->tagged = &lookup_blob(sha1)->object;
@@ -87,6 +94,20 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
                item->tagged = NULL;
        }
 
+       if (prefixcmp(bufptr, "tag "))
+               return -1;
+       bufptr += 4;
+       nl = memchr(bufptr, '\n', tail - bufptr);
+       if (!nl)
+               return -1;
+       item->tag = xmemdupz(bufptr, nl - bufptr);
+       bufptr = nl + 1;
+
+       if (!prefixcmp(bufptr, "tagger "))
+               item->date = parse_tag_date(bufptr, tail);
+       else
+               item->date = 0;
+
        return 0;
 }
 
diff --git a/tag.h b/tag.h
index 7a0cb0070d46ba8c49d71029dc0704188805ea62..47662724a6d7d07eeeacd5c8528d94d750ecf878 100644 (file)
--- a/tag.h
+++ b/tag.h
@@ -9,7 +9,7 @@ struct tag {
        struct object object;
        struct object *tagged;
        char *tag;
-       char *signature; /* not actually implemented */
+       unsigned long date;
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
index 4f9c829c2df319e386b6b5d4f5c23818cc21c979..589f838f82b568195232ea81346d0049261b86b1 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include <pthread.h>
 
 #if defined(hpux) || defined(__hpux) || defined(_hpux)
 #  include <sys/pstat.h>
@@ -43,3 +44,18 @@ int online_cpus(void)
 
        return 1;
 }
+
+int init_recursive_mutex(pthread_mutex_t *m)
+{
+       pthread_mutexattr_t a;
+       int ret;
+
+       ret = pthread_mutexattr_init(&a);
+       if (!ret) {
+               ret = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
+               if (!ret)
+                       ret = pthread_mutex_init(m, &a);
+               pthread_mutexattr_destroy(&a);
+       }
+       return ret;
+}
index cce4b77bd6452e2ec589d8c0dc0e8156352dd67b..1727a03333b80f2a01ca029162a4b511480a86e8 100644 (file)
@@ -2,5 +2,6 @@
 #define THREAD_COMPAT_H
 
 extern int online_cpus(void);
+extern int init_recursive_mutex(pthread_mutex_t*);
 
 #endif /* THREAD_COMPAT_H */
index 9c71b21242773f52ca560d7e5e5e52123674d334..62edb57338ccd1ae56b86649ec07d88c251576da 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -3,11 +3,23 @@
  */
 #include "cache.h"
 
+static void try_to_free_builtin(size_t size)
+{
+       release_pack_memory(size, -1);
+}
+
+static void (*try_to_free_routine)(size_t size) = try_to_free_builtin;
+
+void set_try_to_free_routine(void (*routine)(size_t))
+{
+       try_to_free_routine = (routine) ? routine : try_to_free_builtin;
+}
+
 char *xstrdup(const char *str)
 {
        char *ret = strdup(str);
        if (!ret) {
-               release_pack_memory(strlen(str) + 1, -1);
+               try_to_free_routine(strlen(str) + 1);
                ret = strdup(str);
                if (!ret)
                        die("Out of memory, strdup failed");
@@ -21,7 +33,7 @@ void *xmalloc(size_t size)
        if (!ret && !size)
                ret = malloc(1);
        if (!ret) {
-               release_pack_memory(size, -1);
+               try_to_free_routine(size);
                ret = malloc(size);
                if (!ret && !size)
                        ret = malloc(1);
@@ -67,7 +79,7 @@ void *xrealloc(void *ptr, size_t size)
        if (!ret && !size)
                ret = realloc(ptr, 1);
        if (!ret) {
-               release_pack_memory(size, -1);
+               try_to_free_routine(size);
                ret = realloc(ptr, size);
                if (!ret && !size)
                        ret = realloc(ptr, 1);
@@ -83,7 +95,7 @@ void *xcalloc(size_t nmemb, size_t size)
        if (!ret && (!nmemb || !size))
                ret = calloc(1, 1);
        if (!ret) {
-               release_pack_memory(nmemb * size, -1);
+               try_to_free_routine(nmemb * size);
                ret = calloc(nmemb, size);
                if (!ret && (!nmemb || !size))
                        ret = calloc(1, 1);
index 8ca59a2d2abec0c9c4629cd4ae7340fcd79a0c7e..d44486c826ee46d258e88c5013d038243aad2756 100644 (file)
@@ -625,7 +625,9 @@ void wt_status_print(struct wt_status *s)
        if (s->show_untracked_files)
                wt_status_print_untracked(s);
        else if (s->commitable)
-                fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
+               fprintf(s->fp, "# Untracked files not listed%s\n",
+                       advice_status_hints
+                       ? " (use -u option to show untracked files)" : "");
 
        if (s->verbose)
                wt_status_print_verbose(s);
@@ -635,15 +637,22 @@ void wt_status_print(struct wt_status *s)
                else if (s->nowarn)
                        ; /* nothing */
                else if (s->workdir_dirty)
-                       printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
+                       printf("no changes added to commit%s\n",
+                               advice_status_hints
+                               ? " (use \"git add\" and/or \"git commit -a\")" : "");
                else if (s->untracked.nr)
-                       printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
+                       printf("nothing added to commit but untracked files present%s\n",
+                               advice_status_hints
+                               ? " (use \"git add\" to track)" : "");
                else if (s->is_initial)
-                       printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
+                       printf("nothing to commit%s\n", advice_status_hints
+                               ? " (create/copy files and use \"git add\" to track)" : "");
                else if (!s->show_untracked_files)
-                       printf("nothing to commit (use -u to show untracked files)\n");
+                       printf("nothing to commit%s\n", advice_status_hints
+                               ? " (use -u to show untracked files)" : "");
                else
-                       printf("nothing to commit (working directory clean)\n");
+                       printf("nothing to commit%s\n", advice_status_hints
+                               ? " (working directory clean)" : "");
        }
 }