Merge branch 'js/hp-nonstop'
authorJunio C Hamano <gitster@pobox.com>
Tue, 25 Sep 2012 17:40:21 +0000 (10:40 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 25 Sep 2012 17:40:21 +0000 (10:40 -0700)
Port to HP NonStop aka Tandem.

* js/hp-nonstop:
Port to HP NonStop

105 files changed:
.gitignore
Documentation/Makefile
Documentation/RelNotes/1.7.12.1.txt
Documentation/RelNotes/1.7.12.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.0.txt
Documentation/config.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-branch.txt
Documentation/git-clean.txt
Documentation/git-commit.txt
Documentation/git-fast-import.txt
Documentation/git-filter-branch.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-p4.txt
Documentation/git-shortlog.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcore-tutorial.txt
Documentation/gitignore.txt
Documentation/gittutorial.txt
Documentation/pretty-formats.txt
Documentation/pretty-options.txt
Documentation/rev-list-options.txt
Documentation/technical/api-sha1-array.txt
Documentation/technical/api-string-list.txt
Documentation/technical/pack-protocol.txt
Documentation/user-manual.txt
Makefile
attr.c
builtin.h
builtin/blame.c
builtin/check-attr.c
builtin/commit.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/grep.c
builtin/mailinfo.c
builtin/notes.c
cache.h
commit.c
commit.h
contrib/completion/git-completion.bash
contrib/completion/git-prompt.sh
contrib/git-jump/git-jump
diff.c
diff.h
fetch-pack.h
gettext.c
gettext.h
git-p4.py
graph.c
graph.h
grep.c
grep.h
http-walker.c
ident.c
notes.c
notes.h
read-cache.c
rerere.c
rerere.h
revision.c
sequencer.c
sequencer.h
sha1-array.c
sha1-array.h
strbuf.c
strbuf.h
string-list.c
string-list.h
symlinks.c
t/lib-git-p4.sh
t/perf/.gitignore
t/perf/perf-lib.sh
t/t0063-string-list.sh [new file with mode: 0755]
t/t3507-cherry-pick-conflict.sh
t/t3510-cherry-pick-sequence.sh
t/t4018-diff-funcname.sh
t/t4034-diff-words.sh
t/t4034/ada/expect [new file with mode: 0644]
t/t4034/ada/post [new file with mode: 0644]
t/t4034/ada/pre [new file with mode: 0644]
t/t5100-mailinfo.sh
t/t5100/info0017 [new file with mode: 0644]
t/t5100/msg0017 [new file with mode: 0644]
t/t5100/patch0017 [new file with mode: 0644]
t/t5100/sample.mbox
t/t5500-fetch-pack.sh
t/t7810-grep.sh
t/t8004-blame-with-conflicts.sh
t/t9805-git-p4-skip-submit-edit.sh
t/t9807-git-p4-submit.sh
t/t9810-git-p4-rcs.sh
t/t9815-git-p4-submit-fail.sh [new file with mode: 0755]
t/test-lib-functions.sh
t/test-lib.sh
test-string-list.c [new file with mode: 0644]
trace.c
transport.c
transport.h
userdiff.c
wt-status.c
wt-status.h
index 68fe464090606b95b40b8b99e2454edb52862abe..a188a82bb1d6ab080c434a7096bd263545169f3c 100644 (file)
 /test-run-command
 /test-sha1
 /test-sigchain
+/test-string-list
 /test-subprocess
 /test-svn-fe
 /common-cmds.h
index cf5916fe8b79e39eb57f97b36e82697694f41d7a..267dfe135ddef1d5e6cc452d6fdcd8b22b9e36a2 100644 (file)
@@ -44,9 +44,10 @@ man5dir=$(mandir)/man5
 man7dir=$(mandir)/man7
 # DESTDIR=
 
-ASCIIDOC=asciidoc
+ASCIIDOC = asciidoc
 ASCIIDOC_EXTRA =
 MANPAGE_XSL = manpage-normal.xsl
+XMLTO = xmlto
 XMLTO_EXTRA =
 INSTALL?=install
 RM ?= rm -f
@@ -245,7 +246,7 @@ manpage-base-url.xsl: manpage-base-url.xsl.in
 
 %.1 %.5 %.7 : %.xml manpage-base-url.xsl
        $(QUIET_XMLTO)$(RM) $@ && \
-       xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
+       $(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
 
 %.xml : %.txt
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
index 671e3d2eb52160bbac4be214577750f0bc90d105..b8f04af19f9691d046acc914acff78bbfd81f39f 100644 (file)
@@ -85,6 +85,12 @@ Fixes since v1.7.12
    rely on being able to parse "ls-files -s | while read a b c..."
    started to fail.  Protect them from such a misconfiguration.
 
+ * The attribute system may be asked for a path that itself or its
+   leading directories no longer exists in the working tree, and it is
+   fine if we cannot open .gitattribute file in such a case.  Failure
+   to open per-directory .gitattributes with error status other than
+   ENOENT and ENOTDIR should be diagnosed, but it wasn't.
+
  * After "gitk" showed the contents of a tag, neither "Reread
    references" nor "Reload" did not update what is shown as the
    contents of it, when the user overwrote the tag with "git tag -f".
@@ -92,6 +98,9 @@ Fixes since v1.7.12
  * "ciabot" script (in contrib/) has been updated with extensive
    documentation.
 
+ * "git-jump" script (in contrib/) did not work well when
+   diff.noprefix or diff.mnemonicprefix is in effect.
+
  * Older parts of the documentation described as if having a regular
    file in .git/refs/ hierarchy were the only way to have branches and
    tags, which is not true for quite some time.
@@ -121,3 +130,5 @@ Fixes since v1.7.12
    branch name is a parameter to the option, but the heading for the
    option description was "-B::", not "-B branch::", making the
    documentation misleading.
+
+Also contains numerous documentation updates.
diff --git a/Documentation/RelNotes/1.7.12.2.txt b/Documentation/RelNotes/1.7.12.2.txt
new file mode 100644 (file)
index 0000000..93c7b34
--- /dev/null
@@ -0,0 +1,32 @@
+Git 1.7.12.2 Release Notes
+==========================
+
+Fixes since v1.7.12.1
+---------------------
+
+ * Even during a conflicted merge, "git blame $path" always meant to
+   blame uncommitted changes to the "working tree" version; make it
+   more useful by showing cleanly merged parts as coming from the other
+   branch that is being merged.
+
+ * "git blame MAKEFILE" run in a history that has "Makefile" but not
+   "MAKEFILE" should say "No such file MAKEFILE in HEAD", but got
+   confused on a case insensitive filesystem and failed to do so.
+
+ * "git fetch --all", when passed "--no-tags", did not honor the
+   "--no-tags" option while fetching from individual remotes (the same
+   issue existed with "--tags", but combination "--all --tags" makes
+   much less sense than "--all --no-tags").
+
+ * "git log/diff/format-patch --stat" showed the "N line(s) added"
+   comment in user's locale and caused careless submitters to send
+   patches with such a line in them to projects whose project language
+   is not their language, mildly irritating others. Localization to
+   the line has been disabled for now.
+
+ * The subcommand to remove the definition of a remote in "git remote"
+   was named "rm" even though all other subcommands were spelled out.
+   Introduce "git remote remove" to remove confusion, and keep "rm" as
+   a backward compatible synonym.
+
+Also contains a handful of documentation updates.
index 90c4f4795fc5e95292d42dde52a7b41b0b76387d..87719bfe2b65f67f667e04720334679a1bba9ef6 100644 (file)
@@ -45,6 +45,11 @@ UI, Workflows & Features
  * "git cherry-pick" learned the "--allow-empty-message" option to
    allow it to replay a commit without any log message.
 
+ * After "git cherry-pick -s" gave control back to the user asking
+   help to resolve conflicts, concluding "git commit" used to need to
+   be run with "-s" if the user wants to sign it off; now the command
+   leaves the sign-off line in the log template.
+
  * "git daemon" learned the "--access-hook" option to allow an
    external command to decline service based on the client address,
    repository path, etc.
@@ -66,6 +71,9 @@ Foreign Interface
 
  * "git svn" has been updated to work with SVN 1.7.
 
+ * "git p4" learned "--conflicts" option to specify what to do when
+   encountering a conflict during "p4 submit".
+
 
 Performance, Internal Implementation, etc. (please report possible regressions)
 
@@ -116,6 +124,41 @@ Unless otherwise noted, all the fixes since v1.7.12 in the
 maintenance track are contained in this release (see release notes
 to them for details).
 
+ * "git blame MAKEFILE" run in a history that has "Makefile" but not
+   "MAKEFILE" should say "No such file MAKEFILE in HEAD", but got
+   confused on a case insensitive filesystem and failed to do so.
+   (merge 9aeaab6 jc/maint-blame-no-such-path later to maint).
+
+ * Even during a conflicted merge, "git blame $path" always meant to
+   blame uncommitted changes to the "working tree" version; make it
+   more useful by showing cleanly merged parts as coming from the other
+   branch that is being merged.
+   (merge 9aeaab6 jc/maint-blame-no-such-path later to maint).
+
+ * Documentation talked about "first line of commit log" when it meant
+   the title of the commit.  The description was clarified by defining
+   how the title is decided and rewording the casual mention of "first
+   line" to "title".
+   (merge 52ffe99 jw/doc-commit-title later to maint).
+
+ * The attribute system may be asked for a path that itself or its
+   leading directories no longer exists in the working tree, and it is
+   fine if we cannot open .gitattribute file in such a case.  Failure
+   to open per-directory .gitattributes with error status other than
+   ENOENT and ENOTDIR should be diagnosed, but it wasn't.
+
+ * "git log --all-match --grep=A --grep=B" ought to show commits that
+   mention both A and B, but when these three options are used with
+   --author or --committer, it showed commits that mention either A or
+   B (or both) instead.
+   (merge 39f2e01 jc/maint-log-grep-all-match later to maint).
+
+ * Earlier we made the diffstat summary line that shows the number of
+   lines added/deleted localizable, but it was found irritating having
+   to see them in various languages on a list whose discussion language
+   is English.
+   (merge 218adaa nd/maint-diffstat-summary later to maint).
+
  * "git fetch --all", when passed "--no-tags", did not honor the
    "--no-tags" option while fetching from individual remotes (the same
    issue existed with "--tags", but combination "--all --tags" makes
index 6416cae51158a1a960f91a0673c89f67e105f0cc..11f320b96267e7bd11f23f13842ca9f92cac6f7f 100644 (file)
@@ -559,8 +559,9 @@ core.whitespace::
 * `space-before-tab` treats a space character that appears immediately
   before a tab character in the initial indent part of the line as an
   error (enabled by default).
-* `indent-with-non-tab` treats a line that is indented with 8 or more
-  space characters as an error (not enabled by default).
+* `indent-with-non-tab` treats a line that is indented with space
+  characters instead of the equivalent tabs as an error (not enabled by
+  default).
 * `tab-in-indent` treats a tab character in the initial indent part of
   the line as an error (not enabled by default).
 * `blank-at-eof` treats blank lines added at the end of file as an error
index 39d326abc63af01d7cffe3d6f0c3df9279bc667e..b4d6476ac898db8ff99ab3639efce08050ef71c4 100644 (file)
@@ -10,7 +10,8 @@
 --depth=<depth>::
        Deepen the history of a 'shallow' repository created by
        `git clone` with `--depth=<depth>` option (see linkgit:git-clone[1])
-       by the specified number of commits.
+       to the specified number of commits from the tip of each remote
+       branch history. Tags for the deepened commits are not fetched.
 
 ifndef::git-pull[]
 --dry-run::
index 9c1d3957223355e00eea917d4a7dff6d50343f32..fd9e36b99f5506964ac8352b82bb7af05551a523 100644 (file)
@@ -155,7 +155,7 @@ Configuration
 The optional configuration variable `core.excludesfile` indicates a path to a
 file containing patterns of file names to exclude from git-add, similar to
 $GIT_DIR/info/exclude.  Patterns in the exclude file are used in addition to
-those in info/exclude.  See linkgit:gitrepository-layout[5].
+those in info/exclude.  See linkgit:gitignore[5].
 
 
 EXAMPLES
index 9c1d2f1781c15da786d0b1e9fb2d39562e474f29..45a225e0aa0ea662393eb65fcb32b702bdd608e6 100644 (file)
@@ -131,11 +131,13 @@ This option is only applicable in non-verbose mode.
        use `git branch --list <pattern>` to list matching branches.
 
 -v::
+-vv::
 --verbose::
        When in list mode,
        show sha1 and commit subject line for each head, along with
        relationship to upstream branch (if any). If given twice, print
-       the name of the upstream branch, as well.
+       the name of the upstream branch, as well (see also `git remote
+       show <remote>`).
 
 -q::
 --quiet::
index 79fb9841441d02cd635dcdd2aecef42d38c6b59d..9f42c0d0e63e8d23bfbf4b582a6a9a60280cca47 100644 (file)
@@ -63,6 +63,10 @@ OPTIONS
        Remove only files ignored by git.  This may be useful to rebuild
        everything from scratch, but keep manually created files.
 
+SEE ALSO
+--------
+linkgit:gitignore[5]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 4622297ec98cddc2e1f2e4417b9eeec11a3c91b1..9594ac8e9de9645be71b1528dcb94705f1695283 100644 (file)
@@ -389,8 +389,10 @@ DISCUSSION
 Though not required, it's a good idea to begin the commit message
 with a single short (less than 50 character) line summarizing the
 change, followed by a blank line and then a more thorough description.
-Tools that turn commits into email, for example, use the first line
-on the Subject: line and the rest of the commit in the body.
+The text up to the first blank line in a commit message is treated
+as the commit title, and that title is used throughout git.
+For example, linkgit:git-format-patch[1] turns a commit into email, and it uses
+the title on the Subject line and the rest of the commit in the body.
 
 include::i18n.txt[]
 
index 2620d28b4b38ed3f57ee426e27a2115db8160343..6603a7ab733f4c59dd8725ada536b44bf5e8d12a 100644 (file)
@@ -39,6 +39,10 @@ OPTIONS
        See ``Date Formats'' below for details about which formats
        are supported, and their syntax.
 
+-- done::
+       Terminate with error if there is no 'done' command at the
+       end of the stream.
+
 --force::
        Force updating modified existing branches, even if doing
        so would cause commits to be lost (as the new commit does
@@ -1047,7 +1051,9 @@ done::
        Error out if the stream ends without a 'done' command.
        Without this feature, errors causing the frontend to end
        abruptly at a convenient point in the stream can go
-       undetected.
+       undetected.  This may occur, for example, if an import
+       front end dies in mid-operation without emitting SIGTERM
+       or SIGKILL at its subordinate git fast-import instance.
 
 `option`
 ~~~~~~~~
index 15e7ac80c087eee7d0bf10af5653f421bcbee0dc..e2301f5c0106a35e5fa14d63944abeea2cb09e9b 100644 (file)
@@ -304,6 +304,11 @@ committed a merge between P1 and P2, it will be propagated properly
 and all children of the merge will become merge commits with P1,P2
 as their parents instead of the merge commit.
 
+*NOTE* the changes introduced by the commits, and which are not reverted
+by subsequent commits, will still be in the rewritten branch. If you want
+to throw out _changes_ together with the commits, you should use the
+interactive mode of 'git rebase'.
+
 You can rewrite the commit log messages using `--msg-filter`.  For
 example, 'git svn-id' strings in a repository created by 'git svn' can
 be removed this way:
@@ -314,11 +319,6 @@ git filter-branch --msg-filter '
 '
 -------------------------------------------------------
 
-To restrict rewriting to only part of the history, specify a revision
-range in addition to the new branch name.  The new branch name will
-point to the top-most revision that a 'git rev-list' of this range
-will print.
-
 If you need to add 'Acked-by' lines to, say, the last 10 commits (none
 of which is a merge), use this command:
 
@@ -329,11 +329,10 @@ git filter-branch --msg-filter '
 ' HEAD~10..HEAD
 --------------------------------------------------------
 
-*NOTE* the changes introduced by the commits, and which are not reverted
-by subsequent commits, will still be in the rewritten branch. If you want
-to throw out _changes_ together with the commits, you should use the
-interactive mode of 'git rebase'.
-
+To restrict rewriting to only part of the history, specify a revision
+range in addition to the new branch name.  The new branch name will
+point to the top-most revision that a 'git rev-list' of this range
+will print.
 
 Consider this history:
 
index c872b883ba25144457eccb9967bc68003a3332ea..db55a4e0bbc524c20f08e6862daaa97dd1785b71 100644 (file)
@@ -102,9 +102,10 @@ Fields that have name-email-date tuple as its value (`author`,
 and `date` to extract the named component.
 
 The complete message in a commit and tag object is `contents`.
-Its first line is `contents:subject`, the remaining lines
-are `contents:body` and the optional GPG signature
-is `contents:signature`.
+Its first line is `contents:subject`, where subject is the concatenation
+of all lines of the commit message up to the first blank line.  The next
+line is 'contents:body', where body is all of the lines after the first
+blank line.  Finally, the optional GPG signature is `contents:signature`.
 
 For sorting purposes, fields with numeric values sort in numeric
 order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
index 04c7346e3e8ba12465e71ad1197ed6a109a473b1..6d43f56279fe7a0d42510035e33424df0bc584f1 100644 (file)
@@ -58,10 +58,13 @@ output, unless the `--stdout` option is specified.
 If `-o` is specified, output files are created in <dir>.  Otherwise
 they are created in the current working directory.
 
-By default, the subject of a single patch is "[PATCH] First Line" and
-the subject when multiple patches are output is "[PATCH n/m] First
-Line". To force 1/1 to be added for a single patch, use `-n`.  To omit
-patch numbers from the subject, use `-N`.
+By default, the subject of a single patch is "[PATCH] " followed by
+the concatenation of lines from the commit message up to the first blank
+line (see the DISCUSSION section of linkgit:git-commit[1]).
+
+When multiple patches are output, the subject prefix will instead be
+"[PATCH n/m] ".  To force 1/1 to be added for a single patch, use `-n`.
+To omit patch numbers from the subject, use `-N`.
 
 If given `--thread`, `git-format-patch` will generate `In-Reply-To` and
 `References` headers to make the second and subsequent patch mails appear
index 8228f33e3fdf0e6ec9c66dd3f55ad8094dfeb6aa..beff6229c8cc4541ca3a8ddd0faf724fceb439e7 100644 (file)
@@ -163,7 +163,7 @@ All commands except clone accept these options.
 --git-dir <dir>::
        Set the 'GIT_DIR' environment variable.  See linkgit:git[1].
 
---verbose::
+--verbose, -v::
        Provide more progress information.
 
 Sync options
@@ -269,6 +269,24 @@ These options can be used to modify 'git p4 submit' behavior.
        Export tags from git as p4 labels. Tags found in git are applied
        to the perforce working directory.
 
+--dry-run, -n::
+       Show just what commits would be submitted to p4; do not change
+       state in git or p4.
+
+--prepare-p4-only::
+       Apply a commit to the p4 workspace, opening, adding and deleting
+       files in p4 as for a normal submit operation.  Do not issue the
+       final "p4 submit", but instead print a message about how to
+       submit manually or revert.  This option always stops after the
+       first (oldest) commit.  Git tags are not exported to p4.
+
+--conflict=(ask|skip|quit)::
+       Conflicts can occur when applying a commit to p4.  When this
+       happens, the default behavior ("ask") is to prompt whether to
+       skip this commit and continue, or quit.  This option can be used
+       to bypass the prompt, causing conflicting commits to be automatically
+       skipped, or to quit trying to apply commits, without prompting.
+
 Rebase options
 ~~~~~~~~~~~~~~
 These options can be used to modify 'git p4 rebase' behavior.
@@ -519,6 +537,10 @@ git-p4.labelExportRegexp::
        Only p4 labels matching this regular expression will be exported. The
        default value is '[a-zA-Z0-9_\-.]+$'.
 
+git-p4.conflict::
+       Specify submit behavior when a conflict with p4 is found, as per
+       --conflict.  The default behavior is 'ask'.
+
 IMPLEMENTATION DETAILS
 ----------------------
 * Changesets from p4 are imported using git fast-import.
index 01d8417316e23bc74f2c86df9004091710437ba7..afeb4cdf16df91f63197d390fdbf1540eff66837 100644 (file)
@@ -14,8 +14,7 @@ git log --pretty=short | 'git shortlog' [-h] [-n] [-s] [-e] [-w]
 DESCRIPTION
 -----------
 Summarizes 'git log' output in a format suitable for inclusion
-in release announcements. Each commit will be grouped by author and
-the first line of the commit message will be shown.
+in release announcements. Each commit will be grouped by author and title.
 
 Additionally, "[PATCH]" will be stripped from the commit description.
 
index 34d8a1bbdfe974f1ba83028f3875bca5b439f418..6710cb0a418fef503f0a8419e9b3cd617a15c485 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.12/git.html[documentation for release 1.7.12]
+* link:v1.7.12.1/git.html[documentation for release 1.7.12.1]
 
 * release notes for
+  link:RelNotes/1.7.12.1.txt[1.7.12.1],
   link:RelNotes/1.7.12.txt[1.7.12].
 
 * link:v1.7.11.7/git.html[documentation for release 1.7.11.7]
index 462b79c120ddb132bd125104240a488cc31f877b..99ed04d7abf7e0ee94feec6802e2dbf0da87f9d7 100644 (file)
@@ -511,6 +511,8 @@ configuration file (you still need to enable this with the
 attribute mechanism, via `.gitattributes`).  The following built in
 patterns are available:
 
+- `ada` suitable for source code in the Ada language.
+
 - `bibtex` suitable for files with BibTeX coded references.
 
 - `cpp` suitable for source code in the C and C++ languages.
index 9d893369a00b6a924876fb234c1082db2021abf3..5325c5a7d5cab3f74c3866319a60d35e4299da2e 100644 (file)
@@ -956,12 +956,11 @@ $ git show-branch --topo-order --more=1 master mybranch
 ------------------------------------------------
 
 The first two lines indicate that it is showing the two branches
-and the first line of the commit log message from their
-top-of-the-tree commits, you are currently on `master` branch
-(notice the asterisk `*` character), and the first column for
-the later output lines is used to show commits contained in the
+with the titles of their top-of-the-tree commits, you are currently on
+`master` branch (notice the asterisk `*` character), and the first
+column for the later output lines is used to show commits contained in the
 `master` branch, and the second column for the `mybranch`
-branch. Three commits are shown along with their log messages.
+branch. Three commits are shown along with their titles.
 All of them have non blank characters in the first column (`*`
 shows an ordinary commit on the current branch, `-` is a merge commit), which
 means they are now part of the `master` branch. Only the "Some
index c1f692a71e54e01f813d84f83eaff4a9514a27b3..96639e02bd143f73a9eab1dde7fbe175414ee785 100644 (file)
@@ -41,18 +41,24 @@ precedence, the last matching pattern decides the outcome):
    variable 'core.excludesfile'.
 
 Which file to place a pattern in depends on how the pattern is meant to
-be used. Patterns which should be version-controlled and distributed to
-other repositories via clone (i.e., files that all developers will want
-to ignore) should go into a `.gitignore` file. Patterns which are
-specific to a particular repository but which do not need to be shared
-with other related repositories (e.g., auxiliary files that live inside
-the repository but are specific to one user's workflow) should go into
-the `$GIT_DIR/info/exclude` file.  Patterns which a user wants git to
-ignore in all situations (e.g., backup or temporary files generated by
-the user's editor of choice) generally go into a file specified by
-`core.excludesfile` in the user's `~/.gitconfig`. Its default value is
-$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty,
-$HOME/.config/git/ignore is used instead.
+be used.
+
+ * Patterns which should be version-controlled and distributed to
+   other repositories via clone (i.e., files that all developers will want
+   to ignore) should go into a `.gitignore` file.
+
+ * Patterns which are
+   specific to a particular repository but which do not need to be shared
+   with other related repositories (e.g., auxiliary files that live inside
+   the repository but are specific to one user's workflow) should go into
+   the `$GIT_DIR/info/exclude` file.
+
+ * Patterns which a user wants git to
+   ignore in all situations (e.g., backup or temporary files generated by
+   the user's editor of choice) generally go into a file specified by
+   `core.excludesfile` in the user's `~/.gitconfig`. Its default value is
+   $XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or
+   empty, $HOME/.config/git/ignore is used instead.
 
 The underlying git plumbing tools, such as
 'git ls-files' and 'git read-tree', read
index dee050567e65301066629c566613b84c6c065169..f1cb6f3be67fbb03583a35601791f0db839fbe1b 100644 (file)
@@ -139,9 +139,11 @@ them to the index, and commit, all in one step.
 A note on commit messages: Though not required, it's a good idea to
 begin the commit message with a single short (less than 50 character)
 line summarizing the change, followed by a blank line and then a more
-thorough description.  Tools that turn commits into email, for
-example, use the first line on the Subject: line and the rest of the
-commit in the body.
+thorough description. The text up to the first blank line in a commit
+message is treated as the commit title, and that title is used
+throughout git.  For example, linkgit:git-format-patch[1] turns a
+commit into email, and it uses the title on the Subject line and the
+rest of the commit in the body.
 
 Git tracks content not files
 ----------------------------
index e3d8a83b23aff51c8dd4281f18de8661514b62b1..d9eddedc72a5e6362d68df1b126b76804b9676e1 100644 (file)
@@ -130,6 +130,9 @@ The placeholders are:
 - '%b': body
 - '%B': raw body (unwrapped subject and body)
 - '%N': commit notes
+- '%GG': raw verification message from GPG for a signed commit
+- '%G?': show either "G" for Good or "B" for Bad for a signed commit
+- '%GS': show the name of the signer for a signed commit
 - '%gD': reflog selector, e.g., `refs/stash@{1}`
 - '%gd': shortened reflog selector, e.g., `stash@{1}`
 - '%gn': reflog identity name
index 2a3dc8664f16957a05bc4d81824d7995517ac89c..5e499421a43526e133c7bd3be134da803b35c00d 100644 (file)
@@ -66,3 +66,7 @@ being displayed. Examples: "--notes=foo" will show only notes from
 --[no-]standard-notes::
        These options are deprecated. Use the above --notes/--no-notes
        options instead.
+
+--show-signature::
+       Check the validity of a signed commit object by passing the signature
+       to `gpg --verify` and show the output.
index 918c1109f263c667264c9961c118d549795eab9d..1fc2a18404bd34a2e1ac9f5fe21d64c1827f13a6 100644 (file)
@@ -3,8 +3,15 @@ Commit Limiting
 
 Besides specifying a range of commits that should be listed using the
 special notations explained in the description, additional commit
-limiting may be applied. Note that they are applied before commit
-ordering and formatting options, such as '--reverse'.
+limiting may be applied.
+
+Using more options generally further limits the output (e.g.
+`--since=<date1>` limits to commits newer than `<date1>`, and using it
+with `--grep=<pattern>` further limits to commits whose log message
+has a line that matches `<pattern>`), unless otherwise noted.
+
+Note that these are applied before commit
+ordering and formatting options, such as `--reverse`.
 
 --
 
@@ -39,16 +46,22 @@ endif::git-rev-list[]
 --committer=<pattern>::
 
        Limit the commits output to ones with author/committer
-       header lines that match the specified pattern (regular expression).
+       header lines that match the specified pattern (regular
+       expression).  With more than one `--author=<pattern>`,
+       commits whose author matches any of the given patterns are
+       chosen (similarly for multiple `--committer=<pattern>`).
 
 --grep=<pattern>::
 
        Limit the commits output to ones with log message that
-       matches the specified pattern (regular expression).
+       matches the specified pattern (regular expression).  With
+       more than one `--grep=<pattern>`, commits whose message
+       matches any of the given patterns are chosen (but see
+       `--all-match`).
 
 --all-match::
        Limit the commits output to ones that match all given --grep,
-       --author and --committer instead of ones that match at least one.
+       instead of ones that match at least one.
 
 -i::
 --regexp-ignore-case::
index 4a4bae81093d7a985625d42effd92db9a957ba78..45d1c517cd862878198db6023d5fdbd35f9fbb6c 100644 (file)
@@ -25,9 +25,6 @@ Functions
        the array (but note that some operations below may lose this
        ordering).
 
-`sha1_array_sort`::
-       Sort the elements in the array.
-
 `sha1_array_lookup`::
        Perform a binary search of the array for a specific sha1.
        If found, returns the offset (in number of elements) of the
index 5a0c14fcebfcf4d5cbad4900d062703412c501e1..94d7a2bd999ce5e8603f6d6639ba34d224c2bee5 100644 (file)
@@ -1,8 +1,9 @@
 string-list API
 ===============
 
-The string_list API offers a data structure and functions to handle sorted
-and unsorted string lists.
+The string_list API offers a data structure and functions to handle
+sorted and unsorted string lists.  A "sorted" list is one whose
+entries are sorted by string value in `strcmp()` order.
 
 The 'string_list' struct used to be called 'path_list', but was renamed
 because it is not specific to paths.
@@ -20,8 +21,9 @@ If you need something advanced, you can manually malloc() the `items`
 member (you need this if you add things later) and you should set the
 `nr` and `alloc` members in that case, too.
 
-. Adds new items to the list, using `string_list_append` or
-  `string_list_insert`.
+. Adds new items to the list, using `string_list_append`,
+  `string_list_append_nodup`, `string_list_insert`,
+  `string_list_split`, and/or `string_list_split_in_place`.
 
 . Can check if a string is in the list using `string_list_has_string` or
   `unsorted_string_list_has_string` and get it from the list using
@@ -29,18 +31,23 @@ member (you need this if you add things later) and you should set the
 
 . Can sort an unsorted list using `sort_string_list`.
 
+. Can remove duplicate items from a sorted list using
+  `string_list_remove_duplicates`.
+
 . Can remove individual items of an unsorted list using
   `unsorted_string_list_delete_item`.
 
+. Can remove items not matching a criterion from a sorted or unsorted
+  list using `filter_string_list`.
+
 . Finally it should free the list using `string_list_clear`.
 
 Example:
 
 ----
-struct string_list list;
+struct string_list list = STRING_LIST_INIT_NODUP;
 int i;
 
-memset(&list, 0, sizeof(struct string_list));
 string_list_append(&list, "foo");
 string_list_append(&list, "bar");
 for (i = 0; i < list.nr; i++)
@@ -60,6 +67,22 @@ Functions
 
 * General ones (works with sorted and unsorted lists as well)
 
+`filter_string_list`::
+
+       Apply a function to each item in a list, retaining only the
+       items for which the function returns true.  If free_util is
+       true, call free() on the util members of any items that have
+       to be deleted.  Preserve the order of the items that are
+       retained.
+
+`string_list_longest_prefix`::
+
+       Return the longest string within a string_list that is a
+       prefix (in the sense of prefixcmp()) of the specified string,
+       or NULL if no such prefix exists.  This function does not
+       require the string_list to be sorted (it does a linear
+       search).
+
 `print_string_list`::
 
        Dump a string_list to stdout, useful mainly for debugging purposes. It
@@ -96,15 +119,32 @@ write `string_list_insert(...)->util = ...;`.
        Look up a given string in the string_list, returning the containing
        string_list_item. If the string is not found, NULL is returned.
 
+`string_list_remove_duplicates`::
+
+       Remove all but the first of consecutive entries that have the
+       same string value.  If free_util is true, call free() on the
+       util members of any items that have to be deleted.
+
 * Functions for unsorted lists only
 
 `string_list_append`::
 
-       Append a new string to the end of the string_list.
+       Append a new string to the end of the string_list.  If
+       `strdup_string` is set, then the string argument is copied;
+       otherwise the new `string_list_entry` refers to the input
+       string.
+
+`string_list_append_nodup`::
+
+       Append a new string to the end of the string_list.  The new
+       `string_list_entry` always refers to the input string, even if
+       `strdup_string` is set.  This function can be used to hand
+       ownership of a malloc()ed string to a `string_list` that has
+       `strdup_string` set.
 
 `sort_string_list`::
 
-       Make an unsorted list sorted.
+       Sort the list's entries by string value in `strcmp()` order.
 
 `unsorted_string_list_has_string`::
 
@@ -124,6 +164,25 @@ counterpart for sorted lists, which performs a binary search.
        is set. The third parameter controls if the `util` pointer of the
        items should be freed or not.
 
+`string_list_split`::
+`string_list_split_in_place`::
+
+       Split a string into substrings on a delimiter character and
+       append the substrings to a `string_list`.  If `maxsplit` is
+       non-negative, then split at most `maxsplit` times.  Return the
+       number of substrings appended to the list.
++
+`string_list_split` requires a `string_list` that has `strdup_strings`
+set to true; it leaves the input string untouched and makes copies of
+the substrings in newly-allocated memory.
+`string_list_split_in_place` requires a `string_list` that has
+`strdup_strings` set to false; it splits the input string in place,
+overwriting the delimiter characters with NULs and creating new
+string_list_items that point into the original string (the original
+string must therefore not be modified or freed while the `string_list`
+is in use).
+
+
 Data structures
 ---------------
 
index 49cdc571cd7e276df2913b0ccf9d1e2320b31c9d..d51e20f3526e2681b8c05c296b91dd90a4d3e322 100644 (file)
@@ -259,8 +259,10 @@ a positive depth, this step is skipped.
 ----
 
 If the client has requested a positive depth, the server will compute
-the set of commits which are no deeper than the desired depth, starting
-at the client's wants. The server writes 'shallow' lines for each
+the set of commits which are no deeper than the desired depth. The set
+of commits start at the client's wants.
+
+The server writes 'shallow' lines for each
 commit whose parents will not be sent as a result. The server writes
 an 'unshallow' line for each commit which the client has indicated is
 shallow, but is no longer shallow at the currently requested depth
index 03d95dc290cecd2223116be90b591f75d40ced2b..85651b57ae466496e98e9c2507072f9fa8125c4d 100644 (file)
@@ -1136,9 +1136,12 @@ Creating good commit messages
 Though not required, it's a good idea to begin the commit message
 with a single short (less than 50 character) line summarizing the
 change, followed by a blank line and then a more thorough
-description.  Tools that turn commits into email, for example, use
-the first line on the Subject line and the rest of the commit in the
-body.
+description.  The text up to the first blank line in a commit
+message is treated as the commit title, and that title is used
+throughout git.  For example, linkgit:git-format-patch[1] turns a
+commit into email, and it uses the title on the Subject line and the
+rest of the commit in the body.
+
 
 [[ignoring-files]]
 Ignoring files
index 33a48ac87724a91944f887f7ed8c414c78687bfd..84136065b09a7f8f0fa710dcc928346f2f45d51f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -520,6 +520,7 @@ TEST_PROGRAMS_NEED_X += test-run-command
 TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
 TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
+TEST_PROGRAMS_NEED_X += test-string-list
 TEST_PROGRAMS_NEED_X += test-subprocess
 TEST_PROGRAMS_NEED_X += test-svn-fe
 
diff --git a/attr.c b/attr.c
index 3430faf2ccc5e11a9c04432b7a4f13160130e141..887a9ae46b7044489845abf2072339271caf2d4a 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -353,7 +353,7 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
        int lineno = 0;
 
        if (!fp) {
-               if (errno != ENOENT)
+               if (errno != ENOENT && errno != ENOTDIR)
                        warn_on_inaccessible(path);
                return NULL;
        }
index 8e377522fee4be94f9cdca40d44ecdcc18b42d8b..95116b85358637d3457288c7d28526cb1fa93630 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -21,7 +21,6 @@ struct fmt_merge_msg_opts {
 
 extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                         struct fmt_merge_msg_opts *);
-extern void commit_notes(struct notes_tree *t, const char *msg);
 
 struct notes_rewrite_cfg {
        struct notes_tree **trees;
@@ -33,7 +32,6 @@ struct notes_rewrite_cfg {
        int mode_from_env;
 };
 
-combine_notes_fn parse_combine_notes_fn(const char *v);
 struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
 int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
                          const unsigned char *from_obj, const unsigned char *to_obj);
index 0e102bf2c2aa26bd0dcbcb1788341eaa7ac2c037..c27ef21c2326afd29a68146d23c734c7ea708556 100644 (file)
@@ -2069,6 +2069,55 @@ static int git_blame_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
+static void verify_working_tree_path(struct commit *work_tree, const char *path)
+{
+       struct commit_list *parents;
+
+       for (parents = work_tree->parents; parents; parents = parents->next) {
+               const unsigned char *commit_sha1 = parents->item->object.sha1;
+               unsigned char blob_sha1[20];
+               unsigned mode;
+
+               if (!get_tree_entry(commit_sha1, path, blob_sha1, &mode) &&
+                   sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
+                       return;
+       }
+       die("no such path '%s' in HEAD", path);
+}
+
+static struct commit_list **append_parent(struct commit_list **tail, const unsigned char *sha1)
+{
+       struct commit *parent;
+
+       parent = lookup_commit_reference(sha1);
+       if (!parent)
+               die("no such commit %s", sha1_to_hex(sha1));
+       return &commit_list_insert(parent, tail)->next;
+}
+
+static void append_merge_parents(struct commit_list **tail)
+{
+       int merge_head;
+       const char *merge_head_file = git_path("MERGE_HEAD");
+       struct strbuf line = STRBUF_INIT;
+
+       merge_head = open(merge_head_file, O_RDONLY);
+       if (merge_head < 0) {
+               if (errno == ENOENT)
+                       return;
+               die("cannot open '%s' for reading", merge_head_file);
+       }
+
+       while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
+               unsigned char sha1[20];
+               if (line.len < 40 || get_sha1_hex(line.buf, sha1))
+                       die("unknown line in '%s': %s", merge_head_file, line.buf);
+               tail = append_parent(tail, sha1);
+       }
+       close(merge_head);
+       strbuf_release(&line);
+}
+
 /*
  * Prepare a dummy commit that represents the work tree (or staged) item.
  * Note that annotating work tree item never works in the reverse.
@@ -2079,6 +2128,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
 {
        struct commit *commit;
        struct origin *origin;
+       struct commit_list **parent_tail, *parent;
        unsigned char head_sha1[20];
        struct strbuf buf = STRBUF_INIT;
        const char *ident;
@@ -2086,20 +2136,38 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        int size, len;
        struct cache_entry *ce;
        unsigned mode;
-
-       if (get_sha1("HEAD", head_sha1))
-               die("No such ref: HEAD");
+       struct strbuf msg = STRBUF_INIT;
 
        time(&now);
        commit = xcalloc(1, sizeof(*commit));
-       commit->parents = xcalloc(1, sizeof(*commit->parents));
-       commit->parents->item = lookup_commit_reference(head_sha1);
        commit->object.parsed = 1;
        commit->date = now;
        commit->object.type = OBJ_COMMIT;
+       parent_tail = &commit->parents;
+
+       if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+               die("no such ref: HEAD");
+
+       parent_tail = append_parent(parent_tail, head_sha1);
+       append_merge_parents(parent_tail);
+       verify_working_tree_path(commit, path);
 
        origin = make_origin(commit, path);
 
+       ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
+       strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
+       for (parent = commit->parents; parent; parent = parent->next)
+               strbuf_addf(&msg, "parent %s\n",
+                           sha1_to_hex(parent->item->object.sha1));
+       strbuf_addf(&msg,
+                   "author %s\n"
+                   "committer %s\n\n"
+                   "Version of %s from %s\n",
+                   ident, ident, path,
+                   (!contents_from ? path :
+                    (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
+       commit->buffer = strbuf_detach(&msg, NULL);
+
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                const char *read_from;
@@ -2136,7 +2204,6 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        }
        else {
                /* Reading from stdin */
-               contents_from = "standard input";
                mode = 0;
                if (strbuf_read(&buf, 0, 0) < 0)
                        die_errno("failed to read from stdin");
@@ -2181,16 +2248,6 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
         */
        cache_tree_invalidate_path(active_cache_tree, path);
 
-       commit->buffer = xmalloc(400);
-       ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
-       snprintf(commit->buffer, 400,
-               "tree 0000000000000000000000000000000000000000\n"
-               "parent %s\n"
-               "author %s\n"
-               "committer %s\n\n"
-               "Version of %s from %s\n",
-               sha1_to_hex(head_sha1),
-               ident, ident, path, contents_from ? contents_from : path);
        return commit;
 }
 
index e1ff575daac8e1c12706c9555efcea0238cb7e64..075d01d30c58d40abb48ff820ba6f8d310f785d0 100644 (file)
@@ -9,7 +9,7 @@ static int cached_attrs;
 static int stdin_paths;
 static const char * const check_attr_usage[] = {
 N_("git check-attr [-a | --all | attr...] [--] pathname..."),
-N_("git check-attr --stdin [-a | --all | attr...] < <list-of-paths>"),
+N_("git check-attr --stdin [-z] [-a | --all | attr...] < <list-of-paths>"),
 NULL
 };
 
index 778cf16fde64ab81bbe75ec0949a77e6fd9ee702..4d50484837ece50276ca7a09239815dcca5b5f7d 100644 (file)
@@ -28,6 +28,7 @@
 #include "submodule.h"
 #include "gpg-interface.h"
 #include "column.h"
+#include "sequencer.h"
 
 static const char * const builtin_commit_usage[] = {
        N_("git commit [options] [--] <filepattern>..."),
@@ -466,8 +467,6 @@ static int is_a_merge(const struct commit *current_head)
        return !!(current_head->parents && current_head->parents->next);
 }
 
-static const char sign_off_header[] = "Signed-off-by: ";
-
 static void export_one(const char *var, const char *s, const char *e, int hack)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -552,47 +551,6 @@ static void determine_author_info(struct strbuf *author_ident)
        }
 }
 
-static int ends_rfc2822_footer(struct strbuf *sb)
-{
-       int ch;
-       int hit = 0;
-       int i, j, k;
-       int len = sb->len;
-       int first = 1;
-       const char *buf = sb->buf;
-
-       for (i = len - 1; i > 0; i--) {
-               if (hit && buf[i] == '\n')
-                       break;
-               hit = (buf[i] == '\n');
-       }
-
-       while (i < len - 1 && buf[i] == '\n')
-               i++;
-
-       for (; i < len; i = k) {
-               for (k = i; k < len && buf[k] != '\n'; k++)
-                       ; /* do nothing */
-               k++;
-
-               if ((buf[k] == ' ' || buf[k] == '\t') && !first)
-                       continue;
-
-               first = 0;
-
-               for (j = 0; i + j < len; j++) {
-                       ch = buf[i + j];
-                       if (ch == ':')
-                               break;
-                       if (isalnum(ch) ||
-                           (ch == '-'))
-                               continue;
-                       return 0;
-               }
-       }
-       return 1;
-}
-
 static char *cut_ident_timestamp_part(char *string)
 {
        char *ket = strrchr(string, '>');
@@ -717,21 +675,30 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                stripspace(&sb, 0);
 
        if (signoff) {
-               struct strbuf sob = STRBUF_INIT;
-               int i;
-
-               strbuf_addstr(&sob, sign_off_header);
-               strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
-                                            getenv("GIT_COMMITTER_EMAIL")));
-               strbuf_addch(&sob, '\n');
-               for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
-                       ; /* do nothing */
-               if (prefixcmp(sb.buf + i, sob.buf)) {
-                       if (!i || !ends_rfc2822_footer(&sb))
-                               strbuf_addch(&sb, '\n');
-                       strbuf_addbuf(&sb, &sob);
+               /*
+                * See if we have a Conflicts: block at the end. If yes, count
+                * its size, so we can ignore it.
+                */
+               int ignore_footer = 0;
+               int i, eol, previous = 0;
+               const char *nl;
+
+               for (i = 0; i < sb.len; i++) {
+                       nl = memchr(sb.buf + i, '\n', sb.len - i);
+                       if (nl)
+                               eol = nl - sb.buf;
+                       else
+                               eol = sb.len;
+                       if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) {
+                               ignore_footer = sb.len - previous;
+                               break;
+                       }
+                       while (i < eol)
+                               i++;
+                       previous = eol;
                }
-               strbuf_release(&sob);
+
+               append_signoff(&sb, ignore_footer);
        }
 
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
index fdda36f1497eaa971e81cc1df0fb21ada6d21120..e6443986b81050b01ebd7c57a7afd7abb5c65a45 100644 (file)
@@ -525,72 +525,59 @@ static void mark_recent_complete_commits(unsigned long cutoff)
        }
 }
 
-static void filter_refs(struct ref **refs, int nr_match, char **match)
+static int non_matching_ref(struct string_list_item *item, void *unused)
+{
+       if (item->util) {
+               item->util = NULL;
+               return 0;
+       }
+       else
+               return 1;
+}
+
+static void filter_refs(struct ref **refs, struct string_list *sought)
 {
-       struct ref **return_refs;
        struct ref *newlist = NULL;
        struct ref **newtail = &newlist;
        struct ref *ref, *next;
-       struct ref *fastarray[32];
-       int match_pos;
-
-       if (nr_match && !args.fetch_all) {
-               if (ARRAY_SIZE(fastarray) < nr_match)
-                       return_refs = xcalloc(nr_match, sizeof(struct ref *));
-               else {
-                       return_refs = fastarray;
-                       memset(return_refs, 0, sizeof(struct ref *) * nr_match);
-               }
-       }
-       else
-               return_refs = NULL;
+       int sought_pos;
 
-       match_pos = 0;
+       sought_pos = 0;
        for (ref = *refs; ref; ref = next) {
+               int keep = 0;
                next = ref->next;
                if (!memcmp(ref->name, "refs/", 5) &&
                    check_refname_format(ref->name + 5, 0))
                        ; /* trash */
-               else if (args.fetch_all &&
-                        (!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
-                       *newtail = ref;
-                       ref->next = NULL;
-                       newtail = &ref->next;
-                       continue;
-               }
                else {
-                       int cmp = -1;
-                       while (match_pos < nr_match) {
-                               cmp = strcmp(ref->name, match[match_pos]);
-                               if (cmp < 0) /* definitely do not have it */
-                                       break;
-                               else if (cmp == 0) { /* definitely have it */
-                                       match[match_pos][0] = '\0';
-                                       return_refs[match_pos] = ref;
+                       while (sought_pos < sought->nr) {
+                               int cmp = strcmp(ref->name, sought->items[sought_pos].string);
+                               if (cmp < 0)
+                                       break; /* definitely do not have it */
+                               else if (cmp == 0) {
+                                       keep = 1; /* definitely have it */
+                                       sought->items[sought_pos++].util = "matched";
                                        break;
                                }
-                               else /* might have it; keep looking */
-                                       match_pos++;
+                               else
+                                       sought_pos++; /* might have it; keep looking */
                        }
-                       if (!cmp)
-                               continue; /* we will link it later */
                }
-               free(ref);
-       }
 
-       if (!args.fetch_all) {
-               int i;
-               for (i = 0; i < nr_match; i++) {
-                       ref = return_refs[i];
-                       if (ref) {
-                               *newtail = ref;
-                               ref->next = NULL;
-                               newtail = &ref->next;
-                       }
+               if (! keep && args.fetch_all &&
+                   (!args.depth || prefixcmp(ref->name, "refs/tags/")))
+                       keep = 1;
+
+               if (keep) {
+                       *newtail = ref;
+                       ref->next = NULL;
+                       newtail = &ref->next;
+               } else {
+                       free(ref);
                }
-               if (return_refs != fastarray)
-                       free(return_refs);
        }
+
+       filter_string_list(sought, 0, non_matching_ref, NULL);
        *refs = newlist;
 }
 
@@ -599,7 +586,7 @@ static void mark_alternate_complete(const struct ref *ref, void *unused)
        mark_complete(NULL, ref->old_sha1, 0, NULL);
 }
 
-static int everything_local(struct ref **refs, int nr_match, char **match)
+static int everything_local(struct ref **refs, struct string_list *sought)
 {
        struct ref *ref;
        int retval;
@@ -650,7 +637,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                }
        }
 
-       filter_refs(refs, nr_match, match);
+       filter_refs(refs, sought);
 
        for (retval = 1, ref = *refs; ref ; ref = ref->next) {
                const unsigned char *remote = ref->old_sha1;
@@ -781,8 +768,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
 
 static struct ref *do_fetch_pack(int fd[2],
                const struct ref *orig_ref,
-               int nr_match,
-               char **match,
+               struct string_list *sought,
                char **pack_lockfile)
 {
        struct ref *ref = copy_ref_list(orig_ref);
@@ -839,7 +825,7 @@ static struct ref *do_fetch_pack(int fd[2],
                                agent_len, agent_feature);
        }
 
-       if (everything_local(&ref, nr_match, match)) {
+       if (everything_local(&ref, sought)) {
                packet_flush(fd[1]);
                goto all_done;
        }
@@ -859,19 +845,6 @@ static struct ref *do_fetch_pack(int fd[2],
        return ref;
 }
 
-static int remove_duplicates(int nr_heads, char **heads)
-{
-       int src, dst;
-
-       if (!nr_heads)
-               return 0;
-
-       for (src = dst = 1; src < nr_heads; src++)
-               if (strcmp(heads[src], heads[dst-1]))
-                       heads[dst++] = heads[src];
-       return dst;
-}
-
 static int fetch_pack_config(const char *var, const char *value, void *cb)
 {
        if (strcmp(var, "fetch.unpacklimit") == 0) {
@@ -922,8 +895,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        int i, ret;
        struct ref *ref = NULL;
        const char *dest = NULL;
-       int alloc_heads = 0, nr_heads = 0;
-       char **heads = NULL;
+       struct string_list sought = STRING_LIST_INIT_DUP;
        int fd[2];
        char *pack_lockfile = NULL;
        char **pack_lockfile_ptr = NULL;
@@ -1000,9 +972,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
         * Copy refs from cmdline to growable list, then append any
         * refs from the standard input:
         */
-       ALLOC_GROW(heads, argc - i, alloc_heads);
        for (; i < argc; i++)
-               heads[nr_heads++] = xstrdup(argv[i]);
+               string_list_append(&sought, xstrdup(argv[i]));
        if (args.stdin_refs) {
                if (args.stateless_rpc) {
                        /* in stateless RPC mode we use pkt-line to read
@@ -1015,17 +986,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                                        break;
                                if (line[n-1] == '\n')
                                        n--;
-                               ALLOC_GROW(heads, nr_heads + 1, alloc_heads);
-                               heads[nr_heads++] = xmemdupz(line, n);
+                               string_list_append(&sought, xmemdupz(line, n));
                        }
                }
                else {
                        /* read from stdin one ref per line, until EOF */
                        struct strbuf line = STRBUF_INIT;
-                       while (strbuf_getline(&line, stdin, '\n') != EOF) {
-                               ALLOC_GROW(heads, nr_heads + 1, alloc_heads);
-                               heads[nr_heads++] = strbuf_detach(&line, NULL);
-                       }
+                       while (strbuf_getline(&line, stdin, '\n') != EOF)
+                               string_list_append(&sought, strbuf_detach(&line, NULL));
                        strbuf_release(&line);
                }
        }
@@ -1042,7 +1010,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        get_remote_heads(fd[0], &ref, 0, NULL);
 
        ref = fetch_pack(&args, fd, conn, ref, dest,
-               nr_heads, heads, pack_lockfile_ptr);
+                        &sought, pack_lockfile_ptr);
        if (pack_lockfile) {
                printf("lock %s\n", pack_lockfile);
                fflush(stdout);
@@ -1050,21 +1018,18 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        close(fd[0]);
        close(fd[1]);
        if (finish_connect(conn))
-               ref = NULL;
-       ret = !ref;
-
-       if (!ret && nr_heads) {
-               /* If the heads to pull were given, we should have
-                * consumed all of them by matching the remote.
-                * Otherwise, 'git fetch remote no-such-ref' would
-                * silently succeed without issuing an error.
-                */
-               for (i = 0; i < nr_heads; i++)
-                       if (heads[i] && heads[i][0]) {
-                               error("no such remote ref %s", heads[i]);
-                               ret = 1;
-                       }
-       }
+               return 1;
+
+       ret = !ref || sought.nr;
+
+       /*
+        * If the heads to pull were given, we should have consumed
+        * all of them by matching the remote.  Otherwise, 'git fetch
+        * remote no-such-ref' would silently succeed without issuing
+        * an error.
+        */
+       for (i = 0; i < sought.nr; i++)
+               error("no such remote ref %s", sought.items[i].string);
        while (ref) {
                printf("%s %s\n",
                       sha1_to_hex(ref->old_sha1), ref->name);
@@ -1074,18 +1039,12 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        return ret;
 }
 
-static int compare_heads(const void *a, const void *b)
-{
-       return strcmp(*(const char **)a, *(const char **)b);
-}
-
 struct ref *fetch_pack(struct fetch_pack_args *my_args,
                       int fd[], struct child_process *conn,
                       const struct ref *ref,
-               const char *dest,
-               int nr_heads,
-               char **heads,
-               char **pack_lockfile)
+                      const char *dest,
+                      struct string_list *sought,
+                      char **pack_lockfile)
 {
        struct stat st;
        struct ref *ref_cpy;
@@ -1098,16 +1057,16 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                        st.st_mtime = 0;
        }
 
-       if (heads && nr_heads) {
-               qsort(heads, nr_heads, sizeof(*heads), compare_heads);
-               nr_heads = remove_duplicates(nr_heads, heads);
+       if (sought->nr) {
+               sort_string_list(sought);
+               string_list_remove_duplicates(sought, 0);
        }
 
        if (!ref) {
                packet_flush(fd[1]);
                die("no matching remote head");
        }
-       ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
+       ref_cpy = do_fetch_pack(fd, ref, sought, pack_lockfile);
 
        if (args.depth > 0) {
                struct cache_time mtime;
index d0dcc8888f14e8305250afcab7eb4ca7a3f6648c..4b5a89839b66f201bda42f06cabdf781d44f0192 100644 (file)
@@ -256,9 +256,8 @@ static int update_local_ref(struct ref *ref,
        if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
                if (verbosity > 0)
                        strbuf_addf(display, "= %-*s %-*s -> %s",
-                                   TRANSPORT_SUMMARY_WIDTH,
-                                   _("[up to date]"), REFCOL_WIDTH,
-                                   remote, pretty_ref);
+                                   TRANSPORT_SUMMARY(_("[up to date]")),
+                                   REFCOL_WIDTH, remote, pretty_ref);
                return 0;
        }
 
@@ -272,7 +271,7 @@ static int update_local_ref(struct ref *ref,
                 */
                strbuf_addf(display,
                            _("! %-*s %-*s -> %s  (can't fetch in current branch)"),
-                           TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+                           TRANSPORT_SUMMARY(_("[rejected]")),
                            REFCOL_WIDTH, remote, pretty_ref);
                return 1;
        }
@@ -283,7 +282,7 @@ static int update_local_ref(struct ref *ref,
                r = s_update_ref("updating tag", ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
                            r ? '!' : '-',
-                           TRANSPORT_SUMMARY_WIDTH, _("[tag update]"),
+                           TRANSPORT_SUMMARY(_("[tag update]")),
                            REFCOL_WIDTH, remote, pretty_ref,
                            r ? _("  (unable to update local ref)") : "");
                return r;
@@ -318,7 +317,7 @@ static int update_local_ref(struct ref *ref,
                r = s_update_ref(msg, ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
                            r ? '!' : '*',
-                           TRANSPORT_SUMMARY_WIDTH, what,
+                           TRANSPORT_SUMMARY(what),
                            REFCOL_WIDTH, remote, pretty_ref,
                            r ? _("  (unable to update local ref)") : "");
                return r;
@@ -358,7 +357,7 @@ static int update_local_ref(struct ref *ref,
                return r;
        } else {
                strbuf_addf(display, "! %-*s %-*s -> %s  %s",
-                           TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+                           TRANSPORT_SUMMARY(_("[rejected]")),
                            REFCOL_WIDTH, remote, pretty_ref,
                            _("(non-fast-forward)"));
                return 1;
@@ -555,7 +554,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
                        result |= delete_ref(ref->name, NULL, 0);
                if (verbosity >= 0) {
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
-                               TRANSPORT_SUMMARY_WIDTH, _("[deleted]"),
+                               TRANSPORT_SUMMARY(_("[deleted]")),
                                REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
index 09ca4c980e633895dd104307e76770da3f208a06..82530a61b483738382c810bcb141e33b06729d6f 100644 (file)
@@ -209,6 +209,7 @@ static void start_threads(struct grep_opt *opt)
                int err;
                struct grep_opt *o = grep_opt_dup(opt);
                o->output = strbuf_out;
+               o->debug = 0;
                compile_grep_patterns(o);
                err = pthread_create(&threads[i], NULL, run, o);
 
@@ -817,6 +818,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                           N_("indicate hit with exit status without output")),
                OPT_BOOLEAN(0, "all-match", &opt.all_match,
                        N_("show only matches from files that match all patterns")),
+               { OPTION_SET_INT, 0, "debug", &opt.debug, NULL,
+                 N_("show parse tree for grep expression"),
+                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },
                OPT_GROUP(""),
                { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
                        N_("pager"), N_("show matching files in the pager"),
index b691b771587ac676b23c40f8d9090a3750eaa43f..2b3f4d955eaad6f7a020291b628588188974e388 100644 (file)
@@ -160,10 +160,9 @@ static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
        const char *ends, *ap = strcasestr(line, name);
        size_t sz;
 
-       if (!ap) {
-               strbuf_setlen(attr, 0);
+       strbuf_setlen(attr, 0);
+       if (!ap)
                return 0;
-       }
        ap += strlen(name);
        if (*ap == '"') {
                ap++;
index 554c80167c31d4d42aefe2f02feacffef9424844..453457adb9dedac510c5a95e0e07cb0b4b46c80e 100644 (file)
@@ -19,6 +19,9 @@
 #include "string-list.h"
 #include "notes-merge.h"
 
+static void commit_notes(struct notes_tree *t, const char *msg);
+static combine_notes_fn parse_combine_notes_fn(const char *v);
+
 static const char * const git_notes_usage[] = {
        N_("git notes [--ref <notes_ref>] [list [<object>]]"),
        N_("git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
@@ -288,7 +291,7 @@ static int parse_reedit_arg(const struct option *opt, const char *arg, int unset
        return parse_reuse_arg(opt, arg, unset);
 }
 
-void commit_notes(struct notes_tree *t, const char *msg)
+static void commit_notes(struct notes_tree *t, const char *msg)
 {
        struct strbuf buf = STRBUF_INIT;
        unsigned char commit_sha1[20];
@@ -312,7 +315,7 @@ void commit_notes(struct notes_tree *t, const char *msg)
        strbuf_release(&buf);
 }
 
-combine_notes_fn parse_combine_notes_fn(const char *v)
+static combine_notes_fn parse_combine_notes_fn(const char *v)
 {
        if (!strcasecmp(v, "overwrite"))
                return combine_notes_overwrite;
diff --git a/cache.h b/cache.h
index eb4a0316d2d654160a51789dce5ad69fc9d1af45..a58df84bd374feda0e0c7fdfe65129db2cb3c252 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -442,7 +442,6 @@ extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
 extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
-extern int index_name_stage_pos(const struct index_state *, const char *name, int namelen, int stage);
 extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
@@ -902,9 +901,7 @@ extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
 extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 extern const char *fmt_name(const char *name, const char *email);
-extern const char *ident_default_name(void);
 extern const char *ident_default_email(void);
-extern const char *ident_default_date(void);
 extern const char *git_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 extern int git_ident_config(const char *, const char *, void *);
@@ -947,9 +944,7 @@ struct cache_def {
 extern int has_symlink_leading_path(const char *name, int len);
 extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 extern int check_leading_path(const char *name, int len);
-extern int threaded_check_leading_path(struct cache_def *cache, const char *name, int len);
 extern int has_dirs_only_path(const char *name, int len, int prefix_len);
-extern int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len);
 extern void schedule_dir_for_removal(const char *name, int len);
 extern void remove_scheduled_dirs(void);
 
@@ -1208,7 +1203,6 @@ extern void alloc_report(void);
 /* trace.c */
 __attribute__((format (printf, 1, 2)))
 extern void trace_printf(const char *format, ...);
-extern void trace_vprintf(const char *key, const char *format, va_list ap);
 __attribute__((format (printf, 2, 3)))
 extern void trace_argv_printf(const char **argv, const char *format, ...);
 extern void trace_repo_setup(const char *prefix);
index 0ea441d13b1430231f30d9524aed88b9719290b6..02467676a3e70684065ed668b53e64c1cc561e1e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -9,6 +9,8 @@
 #include "gpg-interface.h"
 #include "mergesort.h"
 
+static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
+
 int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
@@ -1073,8 +1075,9 @@ static int excluded_header_field(const char *field, size_t len, const char **exc
        return 0;
 }
 
-struct commit_extra_header *read_commit_extra_header_lines(const char *buffer, size_t size,
-                                                          const char **exclude)
+static struct commit_extra_header *read_commit_extra_header_lines(
+       const char *buffer, size_t size,
+       const char **exclude)
 {
        struct commit_extra_header *extra = NULL, **tail = &extra, *it = NULL;
        const char *line, *next, *eof, *eob;
index 6edce876739672b4fde2b6970e7ff55b8a921743..9f2131318d4c43fdaaca40e45e45e7d1af61cdfe 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -204,7 +204,6 @@ extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
                                struct commit_extra_header *);
 
 extern struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
-extern struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
 extern void free_commit_extra_headers(struct commit_extra_header *extra);
 
index 1b43329ed79bfb09eac7a017c5ab7466fedc497b..be800e09bdf0656951c415105e0d32ce0b0edf9e 100644 (file)
@@ -1018,7 +1018,8 @@ _git_commit ()
        --*)
                __gitcomp "
                        --all --author= --signoff --verify --no-verify
-                       --edit --amend --include --only --interactive
+                       --edit --no-edit
+                       --amend --include --only --interactive
                        --dry-run --reuse-message= --reedit-message=
                        --reset-author --file= --message= --template=
                        --cleanup= --untracked-files --untracked-files=
index 29b1ec9eb1797e0f2c3c9f7067222432150ba85f..bf20491ec33e467f6b9591d94a2e27bc9e3d93e9 100644 (file)
 #
 # If you would like to see the difference between HEAD and its upstream,
 # set GIT_PS1_SHOWUPSTREAM="auto".  A "<" indicates you are behind, ">"
-# indicates you are ahead, and "<>" indicates you have diverged.  You
-# can further control behaviour by setting GIT_PS1_SHOWUPSTREAM to a
-# space-separated list of values:
+# indicates you are ahead, "<>" indicates you have diverged and "="
+# indicates that there is no difference. You can further control
+# behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated list
+# of values:
 #
 #     verbose       show number of commits ahead/behind (+/-) upstream
 #     legacy        don't use the '--count' option available in recent
index a33674e47a669e45f257a92aaffad8002d190c5d..dc90cd6379de4e9bf1eadc69560867601227b55d 100755 (executable)
@@ -21,9 +21,9 @@ open_editor() {
 }
 
 mode_diff() {
-       git diff --relative "$@" |
+       git diff --no-prefix --relative "$@" |
        perl -ne '
-       if (m{^\+\+\+ b/(.*)}) { $file = $1; next }
+       if (m{^\+\+\+ (.*)}) { $file = $1; next }
        defined($file) or next;
        if (m/^@@ .*\+(\d+)/) { $line = $1; next }
        defined($line) or next;
diff --git a/diff.c b/diff.c
index e6846ca7507aa8a423c5e1f89390fa37723248ff..35d3f073859acf66dbd38c6096c0cc47bd26b6e2 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -25,7 +25,7 @@
 static int diff_detect_rename_default;
 static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
-int diff_use_color_default = -1;
+static int diff_use_color_default = -1;
 static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
@@ -1398,11 +1398,11 @@ int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
 
        if (!files) {
                assert(insertions == 0 && deletions == 0);
-               return fprintf(fp, "%s\n", _(" 0 files changed"));
+               return fprintf(fp, "%s\n", " 0 files changed");
        }
 
        strbuf_addf(&sb,
-                   Q_(" %d file changed", " %d files changed", files),
+                   (files == 1) ? " %d file changed" : " %d files changed",
                    files);
 
        /*
@@ -1419,8 +1419,7 @@ int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
                 * do not translate it.
                 */
                strbuf_addf(&sb,
-                           Q_(", %d insertion(+)", ", %d insertions(+)",
-                              insertions),
+                           (insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)",
                            insertions);
        }
 
@@ -1430,8 +1429,7 @@ int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
                 * do not translate it.
                 */
                strbuf_addf(&sb,
-                           Q_(", %d deletion(-)", ", %d deletions(-)",
-                              deletions),
+                           (deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)",
                            deletions);
        }
        strbuf_addch(&sb, '\n');
diff --git a/diff.h b/diff.h
index e25addb806a190f138a14ca64190fa5d2f311aaa..a658f85f2bc87864f673f737f70c8ad366ab7153 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -243,7 +243,6 @@ extern int parse_long_opt(const char *opt, const char **argv,
 
 extern int git_diff_basic_config(const char *var, const char *value, void *cb);
 extern int git_diff_ui_config(const char *var, const char *value, void *cb);
-extern int diff_use_color_default;
 extern void diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int);
 extern void diff_setup_done(struct diff_options *);
index 7c2069c97234453cb6a0a774c1859b7055b791d8..cb148719bfd3bace27a0ca9611f1c38066fb6ed4 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef FETCH_PACK_H
 #define FETCH_PACK_H
 
+#include "string-list.h"
+
 struct fetch_pack_args {
        const char *uploadpack;
        int unpacklimit;
@@ -17,12 +19,18 @@ struct fetch_pack_args {
                stateless_rpc:1;
 };
 
+/*
+ * sought contains the full names of remote references that should be
+ * updated from.  On return, the names that were found on the remote
+ * will have been removed from the list.  The util members of the
+ * string_list_items are used internally; they must be NULL on entry
+ * (and will be NULL on exit).
+ */
 struct ref *fetch_pack(struct fetch_pack_args *args,
-               int fd[], struct child_process *conn,
-               const struct ref *ref,
-               const char *dest,
-               int nr_heads,
-               char **heads,
-               char **pack_lockfile);
+                      int fd[], struct child_process *conn,
+                      const struct ref *ref,
+                      const char *dest,
+                      struct string_list *sought,
+                      char **pack_lockfile);
 
 #endif
index f75bca7f56b7b27c135d92acba816c40211fdece..71e954563d7068500bd41269dd57f01b097f7c3d 100644 (file)
--- a/gettext.c
+++ b/gettext.c
@@ -4,6 +4,8 @@
 
 #include "git-compat-util.h"
 #include "gettext.h"
+#include "strbuf.h"
+#include "utf8.h"
 
 #ifndef NO_GETTEXT
 #      include <locale.h>
@@ -27,10 +29,9 @@ int use_gettext_poison(void)
 #endif
 
 #ifndef NO_GETTEXT
+static const char *charset;
 static void init_gettext_charset(const char *domain)
 {
-       const char *charset;
-
        /*
           This trick arranges for messages to be emitted in the user's
           requested encoding, but avoids setting LC_CTYPE from the
@@ -128,4 +129,14 @@ void git_setup_gettext(void)
        init_gettext_charset("git");
        textdomain("git");
 }
+
+/* return the number of columns of string 's' in current locale */
+int gettext_width(const char *s)
+{
+       static int is_utf8 = -1;
+       if (is_utf8 == -1)
+               is_utf8 = !strcmp(charset, "UTF-8");
+
+       return is_utf8 ? utf8_strwidth(s) : strlen(s);
+}
 #endif
index 376297bf730d421fa13237350d2beeb525be5ec4..7671d09d04bb87541b11209b5cdbddbb9e4e59b8 100644 (file)
--- a/gettext.h
+++ b/gettext.h
 
 #ifndef NO_GETTEXT
 extern void git_setup_gettext(void);
+extern int gettext_width(const char *s);
 #else
 static inline void git_setup_gettext(void)
 {
 }
+static inline int gettext_width(const char *s)
+{
+       return strlen(s);
+}
 #endif
 
 #ifdef GETTEXT_POISON
index aed1a2de3295bcd57a8c8c602cbdd28a6a6f2f7f..882b1bbab53c6e184c7eadb5250c128b59c93482 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -844,6 +844,9 @@ def run(self, args):
         return True
 
 class P4Submit(Command, P4UserMap):
+
+    conflict_behavior_choices = ("ask", "skip", "quit")
+
     def __init__(self):
         Command.__init__(self)
         P4UserMap.__init__(self)
@@ -853,12 +856,19 @@ def __init__(self):
                 # preserve the user, requires relevant p4 permissions
                 optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"),
                 optparse.make_option("--export-labels", dest="exportLabels", action="store_true"),
+                optparse.make_option("--dry-run", "-n", dest="dry_run", action="store_true"),
+                optparse.make_option("--prepare-p4-only", dest="prepare_p4_only", action="store_true"),
+                optparse.make_option("--conflict", dest="conflict_behavior",
+                                     choices=self.conflict_behavior_choices)
         ]
         self.description = "Submit changes from git to the perforce depot."
         self.usage += " [name of git branch to submit into perforce depot]"
         self.origin = ""
         self.detectRenames = False
         self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
+        self.dry_run = False
+        self.prepare_p4_only = False
+        self.conflict_behavior = None
         self.isWindows = (platform.system() == "Windows")
         self.exportLabels = False
         self.p4HasMoveCommand = p4_has_command("move")
@@ -1088,7 +1098,10 @@ def edit_template(self, template_file):
                 return False
 
     def applyCommit(self, id):
-        print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
+        """Apply one commit, return True if it succeeded."""
+
+        print "Applying", read_pipe(["git", "show", "-s",
+                                     "--format=format:%h %s", id])
 
         (p4User, gitEmail) = self.p4UserForCommit(id)
 
@@ -1195,34 +1208,13 @@ def applyCommit(self, id):
                     patch_succeeded = True
 
         if not patch_succeeded:
-            print "What do you want to do?"
-            response = "x"
-            while response != "s" and response != "a" and response != "w":
-                response = raw_input("[s]kip this patch / [a]pply the patch forcibly "
-                                     "and with .rej files / [w]rite the patch to a file (patch.txt) ")
-            if response == "s":
-                print "Skipping! Good luck with the next patches..."
-                for f in editedFiles:
-                    p4_revert(f)
-                for f in filesToAdd:
-                    os.remove(f)
-                return
-            elif response == "a":
-                os.system(applyPatchCmd)
-                if len(filesToAdd) > 0:
-                    print "You may also want to call p4 add on the following files:"
-                    print " ".join(filesToAdd)
-                if len(filesToDelete):
-                    print "The following files should be scheduled for deletion with p4 delete:"
-                    print " ".join(filesToDelete)
-                die("Please resolve and submit the conflict manually and "
-                    + "continue afterwards with git p4 submit --continue")
-            elif response == "w":
-                system(diffcmd + " > patch.txt")
-                print "Patch saved to patch.txt in %s !" % self.clientPath
-                die("Please resolve and submit the conflict manually and "
-                    "continue afterwards with git p4 submit --continue")
+            for f in editedFiles:
+                p4_revert(f)
+            return False
 
+        #
+        # Apply the patch for real, and do add/delete/+x handling.
+        #
         system(applyPatchCmd)
 
         for f in filesToAdd:
@@ -1236,6 +1228,10 @@ def applyCommit(self, id):
             mode = filesToChangeExecBit[f]
             setP4ExecBit(f, mode)
 
+        #
+        # Build p4 change description, starting with the contents
+        # of the git commit message.
+        #
         logMessage = extractLogMessageFromGitCommit(id)
         logMessage = logMessage.strip()
         (logMessage, jobs) = self.separate_jobs_from_description(logMessage)
@@ -1244,8 +1240,16 @@ def applyCommit(self, id):
         submitTemplate = self.prepareLogMessage(template, logMessage, jobs)
 
         if self.preserveUser:
-           submitTemplate = submitTemplate + ("\n######## Actual user %s, modified after commit\n" % p4User)
+           submitTemplate += "\n######## Actual user %s, modified after commit\n" % p4User
+
+        if self.checkAuthorship and not self.p4UserIsMe(p4User):
+            submitTemplate += "######## git author %s does not match your p4 account.\n" % gitEmail
+            submitTemplate += "######## Use option --preserve-user to modify authorship.\n"
+            submitTemplate += "######## Variable git-p4.skipUserNameCheck hides this message.\n"
 
+        separatorLine = "######## everything below this line is just the diff #######\n"
+
+        # diff
         if os.environ.has_key("P4DIFF"):
             del(os.environ["P4DIFF"])
         diff = ""
@@ -1253,6 +1257,7 @@ def applyCommit(self, id):
             diff += p4_read_pipe(['diff', '-du',
                                   wildcard_encode(editedFile)])
 
+        # new file diff
         newdiff = ""
         for newFile in filesToAdd:
             newdiff += "==== new file ====\n"
@@ -1263,13 +1268,7 @@ def applyCommit(self, id):
                 newdiff += "+" + line
             f.close()
 
-        if self.checkAuthorship and not self.p4UserIsMe(p4User):
-            submitTemplate += "######## git author %s does not match your p4 account.\n" % gitEmail
-            submitTemplate += "######## Use option --preserve-user to modify authorship.\n"
-            submitTemplate += "######## Variable git-p4.skipUserNameCheck hides this message.\n"
-
-        separatorLine = "######## everything below this line is just the diff #######\n"
-
+        # change description file: submitTemplate, separatorLine, diff, newdiff
         (handle, fileName) = tempfile.mkstemp()
         tmpFile = os.fdopen(handle, "w+")
         if self.isWindows:
@@ -1279,8 +1278,47 @@ def applyCommit(self, id):
         tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
         tmpFile.close()
 
+        if self.prepare_p4_only:
+            #
+            # Leave the p4 tree prepared, and the submit template around
+            # and let the user decide what to do next
+            #
+            print
+            print "P4 workspace prepared for submission."
+            print "To submit or revert, go to client workspace"
+            print "  " + self.clientPath
+            print
+            print "To submit, use \"p4 submit\" to write a new description,"
+            print "or \"p4 submit -i %s\" to use the one prepared by" \
+                  " \"git p4\"." % fileName
+            print "You can delete the file \"%s\" when finished." % fileName
+
+            if self.preserveUser and p4User and not self.p4UserIsMe(p4User):
+                print "To preserve change ownership by user %s, you must\n" \
+                      "do \"p4 change -f <change>\" after submitting and\n" \
+                      "edit the User field."
+            if pureRenameCopy:
+                print "After submitting, renamed files must be re-synced."
+                print "Invoke \"p4 sync -f\" on each of these files:"
+                for f in pureRenameCopy:
+                    print "  " + f
+
+            print
+            print "To revert the changes, use \"p4 revert ...\", and delete"
+            print "the submit template file \"%s\"" % fileName
+            if filesToAdd:
+                print "Since the commit adds new files, they must be deleted:"
+                for f in filesToAdd:
+                    print "  " + f
+            print
+            return True
+
+        #
+        # Let the user edit the change description, then submit it.
+        #
         if self.edit_template(fileName):
             # read the edited message and submit
+            ret = True
             tmpFile = open(fileName, "rb")
             message = tmpFile.read()
             tmpFile.close()
@@ -1304,14 +1342,18 @@ def applyCommit(self, id):
 
         else:
             # skip this patch
+            ret = False
             print "Submission cancelled, undoing p4 changes."
             for f in editedFiles:
                 p4_revert(f)
             for f in filesToAdd:
                 p4_revert(f)
                 os.remove(f)
+            for f in filesToDelete:
+                p4_revert(f)
 
         os.remove(fileName)
+        return ret
 
     # Export git tags as p4 labels. Create a p4 label and then tag
     # with that.
@@ -1369,14 +1411,20 @@ def exportGitTags(self, gitTags):
             for mapping in clientSpec.mappings:
                 labelTemplate += "\t%s\n" % mapping.depot_side.path
 
-            p4_write_pipe(["label", "-i"], labelTemplate)
+            if self.dry_run:
+                print "Would create p4 label %s for tag" % name
+            elif self.prepare_p4_only:
+                print "Not creating p4 label %s for tag due to option" \
+                      " --prepare-p4-only" % name
+            else:
+                p4_write_pipe(["label", "-i"], labelTemplate)
 
-            # Use the label
-            p4_system(["tag", "-l", name] +
-                      ["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings])
+                # Use the label
+                p4_system(["tag", "-l", name] +
+                          ["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings])
 
-            if verbose:
-                print "created p4 label for tag %s" % name
+                if verbose:
+                    print "created p4 label for tag %s" % name
 
     def run(self, args):
         if len(args) == 0:
@@ -1403,6 +1451,16 @@ def run(self, args):
             if not self.canChangeChangelists():
                 die("Cannot preserve user names without p4 super-user or admin permissions")
 
+        # if not set from the command line, try the config file
+        if self.conflict_behavior is None:
+            val = gitConfig("git-p4.conflict")
+            if val:
+                if val not in self.conflict_behavior_choices:
+                    die("Invalid value '%s' for config git-p4.conflict" % val)
+            else:
+                val = "ask"
+            self.conflict_behavior = val
+
         if self.verbose:
             print "Origin branch is " + self.origin
 
@@ -1435,12 +1493,15 @@ def run(self, args):
             os.makedirs(self.clientPath)
 
         chdir(self.clientPath)
-        print "Synchronizing p4 checkout..."
-        if new_client_dir:
-            # old one was destroyed, and maybe nobody told p4
-            p4_sync("...", "-f")
+        if self.dry_run:
+            print "Would synchronize p4 checkout in %s" % self.clientPath
         else:
-            p4_sync("...")
+            print "Synchronizing p4 checkout..."
+            if new_client_dir:
+                # old one was destroyed, and maybe nobody told p4
+                p4_sync("...", "-f")
+            else:
+                p4_sync("...")
         self.check()
 
         commits = []
@@ -1487,14 +1548,64 @@ def run(self, args):
         if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true":
             self.diffOpts += " --find-copies-harder"
 
-        while len(commits) > 0:
-            commit = commits[0]
-            commits = commits[1:]
-            self.applyCommit(commit)
+        #
+        # Apply the commits, one at a time.  On failure, ask if should
+        # continue to try the rest of the patches, or quit.
+        #
+        if self.dry_run:
+            print "Would apply"
+        applied = []
+        last = len(commits) - 1
+        for i, commit in enumerate(commits):
+            if self.dry_run:
+                print " ", read_pipe(["git", "show", "-s",
+                                      "--format=format:%h %s", commit])
+                ok = True
+            else:
+                ok = self.applyCommit(commit)
+            if ok:
+                applied.append(commit)
+            else:
+                if self.prepare_p4_only and i < last:
+                    print "Processing only the first commit due to option" \
+                          " --prepare-p4-only"
+                    break
+                if i < last:
+                    quit = False
+                    while True:
+                        # prompt for what to do, or use the option/variable
+                        if self.conflict_behavior == "ask":
+                            print "What do you want to do?"
+                            response = raw_input("[s]kip this commit but apply"
+                                                 " the rest, or [q]uit? ")
+                            if not response:
+                                continue
+                        elif self.conflict_behavior == "skip":
+                            response = "s"
+                        elif self.conflict_behavior == "quit":
+                            response = "q"
+                        else:
+                            die("Unknown conflict_behavior '%s'" %
+                                self.conflict_behavior)
+
+                        if response[0] == "s":
+                            print "Skipping this commit, but applying the rest"
+                            break
+                        if response[0] == "q":
+                            print "Quitting"
+                            quit = True
+                            break
+                    if quit:
+                        break
+
+        chdir(self.oldWorkingDirectory)
 
-        if len(commits) == 0:
-            print "All changes applied!"
-            chdir(self.oldWorkingDirectory)
+        if self.dry_run:
+            pass
+        elif self.prepare_p4_only:
+            pass
+        elif len(commits) == len(applied):
+            print "All commits applied!"
 
             sync = P4Sync()
             sync.run([])
@@ -1502,6 +1613,20 @@ def run(self, args):
             rebase = P4Rebase()
             rebase.rebase()
 
+        else:
+            if len(applied) == 0:
+                print "No commits applied."
+            else:
+                print "Applied only the commits marked with '*':"
+                for c in commits:
+                    if c in applied:
+                        star = "*"
+                    else:
+                        star = " "
+                    print star, read_pipe(["git", "show", "-s",
+                                           "--format=format:%h %s",  c])
+                print "You will have to do 'git p4 sync' and rebase."
+
         if gitConfig("git-p4.exportLabels", "--bool") == "true":
             self.exportLabels = True
 
@@ -1512,6 +1637,10 @@ def run(self, args):
             missingGitTags = gitTags - p4Labels
             self.exportGitTags(missingGitTags)
 
+        # exit with error unless everything applied perfecly
+        if len(commits) != len(applied):
+                sys.exit(1)
+
         return True
 
 class View(object):
@@ -3015,7 +3144,7 @@ def main():
 
     args = sys.argv[2:]
 
-    options.append(optparse.make_option("--verbose", dest="verbose", action="store_true"))
+    options.append(optparse.make_option("--verbose", "-v", dest="verbose", action="store_true"))
     if cmd.needsGit:
         options.append(optparse.make_option("--git-dir", dest="gitdir"))
 
diff --git a/graph.c b/graph.c
index 7e0a099f8329757b24770ec6788bf83a8398d76d..e864fe2c6a21379398e454e60627e78a63a09462 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -7,6 +7,34 @@
 
 /* Internal API */
 
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf.  It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+static int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
+/*
+ * Set up a custom scheme for column colors.
+ *
+ * The default column color scheme inserts ANSI color escapes to colorize
+ * the graph. The various color escapes are stored in an array of strings
+ * where each entry corresponds to a color, except for the last entry,
+ * which denotes the escape for resetting the color back to the default.
+ * When generating the graph, strings from this array are inserted before
+ * and after the various column characters.
+ *
+ * This function allows you to enable a custom array of color escapes.
+ * The 'colors_max' argument is the index of the last "reset" entry.
+ *
+ * This functions must be called BEFORE graph_init() is called.
+ */
+static void graph_set_column_colors(const char **colors, unsigned short colors_max);
+
 /*
  * Output a padding line in the graph.
  * This is similar to graph_next_line().  However, it is guaranteed to
@@ -62,7 +90,7 @@ enum graph_state {
 static const char **column_colors;
 static unsigned short column_colors_max;
 
-void graph_set_column_colors(const char **colors, unsigned short colors_max)
+static void graph_set_column_colors(const char **colors, unsigned short colors_max)
 {
        column_colors = colors;
        column_colors_max = colors_max;
@@ -1116,7 +1144,7 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                graph_update_state(graph, GRAPH_PADDING);
 }
 
-int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
 {
        switch (graph->state) {
        case GRAPH_PADDING:
diff --git a/graph.h b/graph.h
index aff960c7e8f63f49e3ec16afb0b891cf257ce999..19b0f6631654f428cb76e9493325646163899a0b 100644 (file)
--- a/graph.h
+++ b/graph.h
@@ -4,22 +4,6 @@
 /* A graph is a pointer to this opaque structure */
 struct git_graph;
 
-/*
- * Set up a custom scheme for column colors.
- *
- * The default column color scheme inserts ANSI color escapes to colorize
- * the graph. The various color escapes are stored in an array of strings
- * where each entry corresponds to a color, except for the last entry,
- * which denotes the escape for resetting the color back to the default.
- * When generating the graph, strings from this array are inserted before
- * and after the various column characters.
- *
- * This function allows you to enable a custom array of color escapes.
- * The 'colors_max' argument is the index of the last "reset" entry.
- *
- * This functions must be called BEFORE graph_init() is called.
- */
-void graph_set_column_colors(const char **colors, unsigned short colors_max);
 
 /*
  * Create a new struct git_graph.
@@ -49,17 +33,6 @@ void graph_update(struct git_graph *graph, struct commit *commit);
  */
 int graph_is_commit_finished(struct git_graph const *graph);
 
-/*
- * Output the next line for a graph.
- * This formats the next graph line into the specified strbuf.  It is not
- * terminated with a newline.
- *
- * Returns 1 if the line includes the current commit, and 0 otherwise.
- * graph_next_line() will return 1 exactly once for each time
- * graph_update() is called.
- */
-int graph_next_line(struct git_graph *graph, struct strbuf *sb);
-
 
 /*
  * graph_show_*: helper functions for printing to stdout
diff --git a/grep.c b/grep.c
index 04e3ec6c6e90e8bd96495c7b40bcb014384b07d8..898be6ebfaf2a4247ee3a64993253237b4dcc706 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -3,6 +3,10 @@
 #include "userdiff.h"
 #include "xdiff-interface.h"
 
+static int grep_source_load(struct grep_source *gs);
+static int grep_source_is_binary(struct grep_source *gs);
+
+
 static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
                                        const char *origin, int no,
                                        enum grep_pat_token t,
@@ -332,6 +336,87 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
        return compile_pattern_or(list);
 }
 
+static void indent(int in)
+{
+       while (in-- > 0)
+               fputc(' ', stderr);
+}
+
+static void dump_grep_pat(struct grep_pat *p)
+{
+       switch (p->token) {
+       case GREP_AND: fprintf(stderr, "*and*"); break;
+       case GREP_OPEN_PAREN: fprintf(stderr, "*(*"); break;
+       case GREP_CLOSE_PAREN: fprintf(stderr, "*)*"); break;
+       case GREP_NOT: fprintf(stderr, "*not*"); break;
+       case GREP_OR: fprintf(stderr, "*or*"); break;
+
+       case GREP_PATTERN: fprintf(stderr, "pattern"); break;
+       case GREP_PATTERN_HEAD: fprintf(stderr, "pattern_head"); break;
+       case GREP_PATTERN_BODY: fprintf(stderr, "pattern_body"); break;
+       }
+
+       switch (p->token) {
+       default: break;
+       case GREP_PATTERN_HEAD:
+               fprintf(stderr, "<head %d>", p->field); break;
+       case GREP_PATTERN_BODY:
+               fprintf(stderr, "<body>"); break;
+       }
+       switch (p->token) {
+       default: break;
+       case GREP_PATTERN_HEAD:
+       case GREP_PATTERN_BODY:
+       case GREP_PATTERN:
+               fprintf(stderr, "%.*s", (int)p->patternlen, p->pattern);
+               break;
+       }
+       fputc('\n', stderr);
+}
+
+static void dump_grep_expression_1(struct grep_expr *x, int in)
+{
+       indent(in);
+       switch (x->node) {
+       case GREP_NODE_TRUE:
+               fprintf(stderr, "true\n");
+               break;
+       case GREP_NODE_ATOM:
+               dump_grep_pat(x->u.atom);
+               break;
+       case GREP_NODE_NOT:
+               fprintf(stderr, "(not\n");
+               dump_grep_expression_1(x->u.unary, in+1);
+               indent(in);
+               fprintf(stderr, ")\n");
+               break;
+       case GREP_NODE_AND:
+               fprintf(stderr, "(and\n");
+               dump_grep_expression_1(x->u.binary.left, in+1);
+               dump_grep_expression_1(x->u.binary.right, in+1);
+               indent(in);
+               fprintf(stderr, ")\n");
+               break;
+       case GREP_NODE_OR:
+               fprintf(stderr, "(or\n");
+               dump_grep_expression_1(x->u.binary.left, in+1);
+               dump_grep_expression_1(x->u.binary.right, in+1);
+               indent(in);
+               fprintf(stderr, ")\n");
+               break;
+       }
+}
+
+static void dump_grep_expression(struct grep_opt *opt)
+{
+       struct grep_expr *x = opt->pattern_expression;
+
+       if (opt->all_match)
+               fprintf(stderr, "[all-match]\n");
+       dump_grep_expression_1(x, 0);
+       fflush(NULL);
+}
+
 static struct grep_expr *grep_true_expr(void)
 {
        struct grep_expr *z = xcalloc(1, sizeof(*z));
@@ -395,7 +480,23 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
        return header_expr;
 }
 
-void compile_grep_patterns(struct grep_opt *opt)
+static struct grep_expr *grep_splice_or(struct grep_expr *x, struct grep_expr *y)
+{
+       struct grep_expr *z = x;
+
+       while (x) {
+               assert(x->node == GREP_NODE_OR);
+               if (x->u.binary.right &&
+                   x->u.binary.right->node == GREP_NODE_TRUE) {
+                       x->u.binary.right = y;
+                       break;
+               }
+               x = x->u.binary.right;
+       }
+       return z;
+}
+
+static void compile_grep_patterns_real(struct grep_opt *opt)
 {
        struct grep_pat *p;
        struct grep_expr *header_expr = prep_header_patterns(opt);
@@ -415,7 +516,7 @@ void compile_grep_patterns(struct grep_opt *opt)
 
        if (opt->all_match || header_expr)
                opt->extended = 1;
-       else if (!opt->extended)
+       else if (!opt->extended && !opt->debug)
                return;
 
        p = opt->pattern_list;
@@ -429,12 +530,22 @@ void compile_grep_patterns(struct grep_opt *opt)
 
        if (!opt->pattern_expression)
                opt->pattern_expression = header_expr;
+       else if (opt->all_match)
+               opt->pattern_expression = grep_splice_or(header_expr,
+                                                        opt->pattern_expression);
        else
                opt->pattern_expression = grep_or_expr(opt->pattern_expression,
                                                       header_expr);
        opt->all_match = 1;
 }
 
+void compile_grep_patterns(struct grep_opt *opt)
+{
+       compile_grep_patterns_real(opt);
+       if (opt->debug)
+               dump_grep_expression(opt);
+}
+
 static void free_pattern_expr(struct grep_expr *x)
 {
        switch (x->node) {
@@ -1358,7 +1469,7 @@ static int grep_source_load_file(struct grep_source *gs)
        return 0;
 }
 
-int grep_source_load(struct grep_source *gs)
+static int grep_source_load(struct grep_source *gs)
 {
        if (gs->buf)
                return 0;
@@ -1386,7 +1497,7 @@ void grep_source_load_driver(struct grep_source *gs)
        grep_attr_unlock();
 }
 
-int grep_source_is_binary(struct grep_source *gs)
+static int grep_source_is_binary(struct grep_source *gs)
 {
        grep_source_load_driver(gs);
        if (gs->driver->binary != -1)
diff --git a/grep.h b/grep.h
index 75afb7b10564a58f6427a03abccfb334facfea3b..8a28a676fc3b16a0857e3f4b80824d929b6b444d 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -98,6 +98,7 @@ struct grep_opt {
        int word_regexp;
        int fixed;
        int all_match;
+       int debug;
 #define GREP_BINARY_DEFAULT    0
 #define GREP_BINARY_NOMATCH    1
 #define GREP_BINARY_TEXT       2
@@ -158,11 +159,10 @@ struct grep_source {
 
 void grep_source_init(struct grep_source *gs, enum grep_source_type type,
                      const char *name, const void *identifier);
-int grep_source_load(struct grep_source *gs);
 void grep_source_clear_data(struct grep_source *gs);
 void grep_source_clear(struct grep_source *gs);
 void grep_source_load_driver(struct grep_source *gs);
-int grep_source_is_binary(struct grep_source *gs);
+
 
 int grep_source(struct grep_opt *opt, struct grep_source *gs);
 
index 51a906e9e3316582561304995e1a1bbbdcd6ca7c..1516c5eb290746bad4551515fda7930d2f55d32b 100644 (file)
@@ -396,7 +396,7 @@ static int fetch_indices(struct walker *walker, struct alt_base *repo)
        return ret;
 }
 
-static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
+static int http_fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
 {
        struct packed_git *target;
        int ret;
@@ -524,7 +524,7 @@ static int fetch(struct walker *walker, unsigned char *sha1)
        if (!fetch_object(walker, altbase, sha1))
                return 0;
        while (altbase) {
-               if (!fetch_pack(walker, altbase, sha1))
+               if (!http_fetch_pack(walker, altbase, sha1))
                        return 0;
                fetch_alternates(walker, data->alt->base);
                altbase = altbase->next;
diff --git a/ident.c b/ident.c
index 484e0a980308fd2201bf443815a58c41bea17073..a4bf206e2f88da3a7ee31a04c5c0dda2b1547baa 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -93,7 +93,7 @@ static void copy_email(const struct passwd *pw, struct strbuf *email)
        add_domainname(email);
 }
 
-const char *ident_default_name(void)
+static const char *ident_default_name(void)
 {
        if (!git_default_name.len) {
                copy_gecos(xgetpwuid_self(), &git_default_name);
@@ -117,7 +117,7 @@ const char *ident_default_email(void)
        return git_default_email.buf;
 }
 
-const char *ident_default_date(void)
+static const char *ident_default_date(void)
 {
        if (!git_default_date[0])
                datestamp(git_default_date, sizeof(git_default_date));
diff --git a/notes.c b/notes.c
index 93e9868d5d1aa536b70e981d3a4cd3c7969764d3..bc454e1eab2b0e1d264cee7bb1f84bdb3bb14237 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -1196,8 +1196,18 @@ void free_notes(struct notes_tree *t)
        memset(t, 0, sizeof(struct notes_tree));
 }
 
-void format_note(struct notes_tree *t, const unsigned char *object_sha1,
-               struct strbuf *sb, const char *output_encoding, int flags)
+/*
+ * Fill the given strbuf with the notes associated with the given object.
+ *
+ * If the given notes_tree structure is not initialized, it will be auto-
+ * initialized to the default value (see documentation for init_notes() above).
+ * If the given notes_tree is NULL, the internal/default notes_tree will be
+ * used instead.
+ *
+ * 'flags' is a bitwise combination of the flags for format_display_notes.
+ */
+static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
+                       struct strbuf *sb, const char *output_encoding, int flags)
 {
        static const char utf8[] = "utf-8";
        const unsigned char *sha1;
diff --git a/notes.h b/notes.h
index c716694b9e2ec89a59b98d1828fd78f59471e2c4..3592b19d2d9e5c67d9f55c8ad675ded086cbce17 100644 (file)
--- a/notes.h
+++ b/notes.h
@@ -241,20 +241,6 @@ void free_notes(struct notes_tree *t);
 #define NOTES_SHOW_HEADER 1
 #define NOTES_INDENT 2
 
-/*
- * Fill the given strbuf with the notes associated with the given object.
- *
- * If the given notes_tree structure is not initialized, it will be auto-
- * initialized to the default value (see documentation for init_notes() above).
- * If the given notes_tree is NULL, the internal/default notes_tree will be
- * used instead.
- *
- * 'flags' is a bitwise combination of the above formatting flags.
- */
-void format_note(struct notes_tree *t, const unsigned char *object_sha1,
-               struct strbuf *sb, const char *output_encoding, int flags);
-
-
 struct string_list;
 
 struct display_notes_opt {
index 79e3bbe0240e16629e8ba4b755d60f986132802a..fda78bc353afcfd4d01864f2a13158d9c9c55173 100644 (file)
@@ -424,7 +424,7 @@ int cache_name_compare(const char *name1, int len1, const char *name2, int len2)
        return cache_name_stage_compare(name1, len1, 0, name2, len2, 0);
 }
 
-int index_name_stage_pos(const struct index_state *istate, const char *name, int namelen, int stage)
+static int index_name_stage_pos(const struct index_state *istate, const char *name, int namelen, int stage)
 {
        int first, last;
 
index b57a34daa718a5ecf9750d3068a8ecee4f3e261f..a6a5cd57bef4aaf204f1bad293c18b5af6f57f53 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -25,7 +25,7 @@ const char *rerere_path(const char *hex, const char *file)
        return git_path("rr-cache/%s/%s", hex, file);
 }
 
-int has_rerere_resolution(const char *hex)
+static int has_rerere_resolution(const char *hex)
 {
        struct stat st;
        return !stat(rerere_path(hex, "postimage"), &st);
index fcd8bc10ba587d023d174d68020a185eab0f8d40..156d2aad804527d5ae766b5381f3b5e0d2176580 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -16,7 +16,6 @@ extern void *RERERE_RESOLVED;
 extern int setup_rerere(struct string_list *, int);
 extern int rerere(int);
 extern const char *rerere_path(const char *hex, const char *file);
-extern int has_rerere_resolution(const char *hex);
 extern int rerere_forget(const char **);
 extern int rerere_remaining(struct string_list *);
 extern void rerere_clear(struct string_list *);
index dc3fecf903aa0ce4927b9ebe5b95f99cc9c1018d..ae12e11fb74dcc51bfddd2acf94601b2bfba7811 100644 (file)
@@ -1598,6 +1598,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
                add_message_grep(revs, optarg);
                return argcount;
+       } else if (!strcmp(arg, "--grep-debug")) {
+               revs->grep_filter.debug = 1;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
index f86f116a15326f514fbe0405a9301f4ee2416344..e3723d20956719d2cba13ee6c6890ff1a8cfd21a 100644 (file)
@@ -17,7 +17,9 @@
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
-void remove_sequencer_state(void)
+const char sign_off_header[] = "Signed-off-by: ";
+
+static void remove_sequencer_state(void)
 {
        struct strbuf seq_dir = STRBUF_INIT;
 
@@ -233,6 +235,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
                die(_("%s: Unable to write new index file"), action_name(opts));
        rollback_lock_file(&index_lock);
 
+       if (opts->signoff)
+               append_signoff(msgbuf, 0);
+
        if (!clean) {
                int i;
                strbuf_addstr(msgbuf, "\nConflicts:\n");
@@ -1011,3 +1016,63 @@ int sequencer_pick_revisions(struct replay_opts *opts)
        save_opts(opts);
        return pick_commits(todo_list, opts);
 }
+
+static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer)
+{
+       int ch;
+       int hit = 0;
+       int i, j, k;
+       int len = sb->len - ignore_footer;
+       int first = 1;
+       const char *buf = sb->buf;
+
+       for (i = len - 1; i > 0; i--) {
+               if (hit && buf[i] == '\n')
+                       break;
+               hit = (buf[i] == '\n');
+       }
+
+       while (i < len - 1 && buf[i] == '\n')
+               i++;
+
+       for (; i < len; i = k) {
+               for (k = i; k < len && buf[k] != '\n'; k++)
+                       ; /* do nothing */
+               k++;
+
+               if ((buf[k] == ' ' || buf[k] == '\t') && !first)
+                       continue;
+
+               first = 0;
+
+               for (j = 0; i + j < len; j++) {
+                       ch = buf[i + j];
+                       if (ch == ':')
+                               break;
+                       if (isalnum(ch) ||
+                           (ch == '-'))
+                               continue;
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+void append_signoff(struct strbuf *msgbuf, int ignore_footer)
+{
+       struct strbuf sob = STRBUF_INIT;
+       int i;
+
+       strbuf_addstr(&sob, sign_off_header);
+       strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
+                               getenv("GIT_COMMITTER_EMAIL")));
+       strbuf_addch(&sob, '\n');
+       for (i = msgbuf->len - 1 - ignore_footer; i > 0 && msgbuf->buf[i - 1] != '\n'; i--)
+               ; /* do nothing */
+       if (prefixcmp(msgbuf->buf + i, sob.buf)) {
+               if (!i || !ends_rfc2822_footer(msgbuf, ignore_footer))
+                       strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, "\n", 1);
+               strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len);
+       }
+       strbuf_release(&sob);
+}
index d8494201e0a0ba052356de1094b25bac5b9024c0..9d57d57524d411c54d55346a5ecff67bc256595c 100644 (file)
@@ -44,9 +44,10 @@ struct replay_opts {
        struct rev_info *revs;
 };
 
-/* Removes SEQ_DIR. */
-extern void remove_sequencer_state(void);
-
 int sequencer_pick_revisions(struct replay_opts *opts);
 
+extern const char sign_off_header[];
+
+void append_signoff(struct strbuf *msgbuf, int ignore_footer);
+
 #endif
index b2f47f98fbba26ff7cab4c8651f4c4097e933f84..6f4a2246c9a912d06cf1a6995e07bf2008ec749d 100644 (file)
@@ -14,7 +14,7 @@ static int void_hashcmp(const void *a, const void *b)
        return hashcmp(a, b);
 }
 
-void sha1_array_sort(struct sha1_array *array)
+static void sha1_array_sort(struct sha1_array *array)
 {
        qsort(array->sha1, array->nr, sizeof(*array->sha1), void_hashcmp);
        array->sorted = 1;
index 4499b5dad40924e9be2b6aacd73153326e6d11ec..72bb33bec6c173633c2b5cd04db57b562fa23a9e 100644 (file)
@@ -11,7 +11,6 @@ struct sha1_array {
 #define SHA1_ARRAY_INIT { NULL, 0, 0, 0 }
 
 void sha1_array_append(struct sha1_array *array, const unsigned char *sha1);
-void sha1_array_sort(struct sha1_array *array);
 int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1);
 void sha1_array_clear(struct sha1_array *array);
 
index ec88266718879ebcf89ab95b4c89f83004467869..0510f76c24b3b3ce66baa0f05054d3f01b960b6e 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -445,8 +445,8 @@ static int is_rfc3986_unreserved(char ch)
                ch == '-' || ch == '_' || ch == '.' || ch == '~';
 }
 
-void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
-                         int reserved)
+static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
+                                int reserved)
 {
        strbuf_grow(sb, len);
        while (len--) {
@@ -465,15 +465,6 @@ void strbuf_addstr_urlencode(struct strbuf *sb, const char *s,
        strbuf_add_urlencode(sb, s, strlen(s), reserved);
 }
 
-void strbuf_addf_ln(struct strbuf *sb, const char *fmt, ...)
-{
-       va_list ap;
-       va_start(ap, fmt);
-       strbuf_vaddf(sb, fmt, ap);
-       va_end(ap);
-       strbuf_addch(sb, '\n');
-}
-
 int printf_ln(const char *fmt, ...)
 {
        int ret;
index b888d405db492c9851696cf6efe66e33d5e1dca8..be941ee481a735dcc73e3821c93e8ee682478d18 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -99,8 +99,6 @@ __attribute__((format (printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 __attribute__((format (printf,2,0)))
 extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
-__attribute__((format (printf,2,3)))
-extern void strbuf_addf_ln(struct strbuf *sb, const char *fmt, ...);
 
 extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
 
@@ -126,8 +124,6 @@ extern int launch_editor(const char *path, struct strbuf *buffer, const char *co
 extern int strbuf_branchname(struct strbuf *sb, const char *name);
 extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
-extern void strbuf_add_urlencode(struct strbuf *, const char *, size_t,
-                                int reserved);
 extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
                                    int reserved);
 
index d9810aba421cbbc844b45ea5ca713a480a699eb2..c54b816244f69614c67c532d20719081a4b34bd4 100644 (file)
@@ -92,6 +92,23 @@ struct string_list_item *string_list_lookup(struct string_list *list, const char
        return list->items + i;
 }
 
+void string_list_remove_duplicates(struct string_list *list, int free_util)
+{
+       if (list->nr > 1) {
+               int src, dst;
+               for (src = dst = 1; src < list->nr; src++) {
+                       if (!strcmp(list->items[dst - 1].string, list->items[src].string)) {
+                               if (list->strdup_strings)
+                                       free(list->items[src].string);
+                               if (free_util)
+                                       free(list->items[src].util);
+                       } else
+                               list->items[dst++] = list->items[src];
+               }
+               list->nr = dst;
+       }
+}
+
 int for_each_string_list(struct string_list *list,
                         string_list_each_func_t fn, void *cb_data)
 {
@@ -102,6 +119,43 @@ int for_each_string_list(struct string_list *list,
        return ret;
 }
 
+void filter_string_list(struct string_list *list, int free_util,
+                       string_list_each_func_t want, void *cb_data)
+{
+       int src, dst = 0;
+       for (src = 0; src < list->nr; src++) {
+               if (want(&list->items[src], cb_data)) {
+                       list->items[dst++] = list->items[src];
+               } else {
+                       if (list->strdup_strings)
+                               free(list->items[src].string);
+                       if (free_util)
+                               free(list->items[src].util);
+               }
+       }
+       list->nr = dst;
+}
+
+char *string_list_longest_prefix(const struct string_list *prefixes,
+                                const char *string)
+{
+       int i, max_len = -1;
+       char *retval = NULL;
+
+       for (i = 0; i < prefixes->nr; i++) {
+               char *prefix = prefixes->items[i].string;
+               if (!prefixcmp(string, prefix)) {
+                       int len = strlen(prefix);
+                       if (len > max_len) {
+                               retval = prefix;
+                               max_len = len;
+                       }
+               }
+       }
+
+       return retval;
+}
+
 void string_list_clear(struct string_list *list, int free_util)
 {
        if (list->items) {
@@ -148,13 +202,23 @@ void print_string_list(const struct string_list *p, const char *text)
                printf("%s:%p\n", p->items[i].string, p->items[i].util);
 }
 
-struct string_list_item *string_list_append(struct string_list *list, const char *string)
+struct string_list_item *string_list_append_nodup(struct string_list *list,
+                                                 char *string)
 {
+       struct string_list_item *retval;
        ALLOC_GROW(list->items, list->nr + 1, list->alloc);
-       list->items[list->nr].string =
-               list->strdup_strings ? xstrdup(string) : (char *)string;
-       list->items[list->nr].util = NULL;
-       return list->items + list->nr++;
+       retval = &list->items[list->nr++];
+       retval->string = string;
+       retval->util = NULL;
+       return retval;
+}
+
+struct string_list_item *string_list_append(struct string_list *list,
+                                           const char *string)
+{
+       return string_list_append_nodup(
+                       list,
+                       list->strdup_strings ? xstrdup(string) : (char *)string);
 }
 
 static int cmp_items(const void *a, const void *b)
@@ -194,3 +258,56 @@ void unsorted_string_list_delete_item(struct string_list *list, int i, int free_
        list->items[i] = list->items[list->nr-1];
        list->nr--;
 }
+
+int string_list_split(struct string_list *list, const char *string,
+                     int delim, int maxsplit)
+{
+       int count = 0;
+       const char *p = string, *end;
+
+       if (!list->strdup_strings)
+               die("internal error in string_list_split(): "
+                   "list->strdup_strings must be set");
+       for (;;) {
+               count++;
+               if (maxsplit >= 0 && count > maxsplit) {
+                       string_list_append(list, p);
+                       return count;
+               }
+               end = strchr(p, delim);
+               if (end) {
+                       string_list_append_nodup(list, xmemdupz(p, end - p));
+                       p = end + 1;
+               } else {
+                       string_list_append(list, p);
+                       return count;
+               }
+       }
+}
+
+int string_list_split_in_place(struct string_list *list, char *string,
+                              int delim, int maxsplit)
+{
+       int count = 0;
+       char *p = string, *end;
+
+       if (list->strdup_strings)
+               die("internal error in string_list_split_in_place(): "
+                   "list->strdup_strings must not be set");
+       for (;;) {
+               count++;
+               if (maxsplit >= 0 && count > maxsplit) {
+                       string_list_append(list, p);
+                       return count;
+               }
+               end = strchr(p, delim);
+               if (end) {
+                       *end = '\0';
+                       string_list_append(list, p);
+                       p = end + 1;
+               } else {
+                       string_list_append(list, p);
+                       return count;
+               }
+       }
+}
index 0684cb73bfd27846182479a4e192d682a44f201a..5efd07b44e020428326ff32ff258449ec347861f 100644 (file)
@@ -29,6 +29,24 @@ int for_each_string_list(struct string_list *list,
 #define for_each_string_list_item(item,list) \
        for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
 
+/*
+ * Apply want to each item in list, retaining only the ones for which
+ * the function returns true.  If free_util is true, call free() on
+ * the util members of any items that have to be deleted.  Preserve
+ * the order of the items that are retained.
+ */
+void filter_string_list(struct string_list *list, int free_util,
+                       string_list_each_func_t want, void *cb_data);
+
+/*
+ * Return the longest string in prefixes that is a prefix (in the
+ * sense of prefixcmp()) of string, or NULL if no such prefix exists.
+ * This function does not require the string_list to be sorted (it
+ * does a linear search).
+ */
+char *string_list_longest_prefix(const struct string_list *prefixes, const char *string);
+
+
 /* Use these functions only on sorted lists: */
 int string_list_has_string(const struct string_list *list, const char *string);
 int string_list_find_insert_index(const struct string_list *list, const char *string,
@@ -38,11 +56,64 @@ struct string_list_item *string_list_insert_at_index(struct string_list *list,
                                                     int insert_at, const char *string);
 struct string_list_item *string_list_lookup(struct string_list *list, const char *string);
 
+/*
+ * Remove all but the first of consecutive entries with the same
+ * string value.  If free_util is true, call free() on the util
+ * members of any items that have to be deleted.
+ */
+void string_list_remove_duplicates(struct string_list *sorted_list, int free_util);
+
+
 /* Use these functions only on unsorted lists: */
+
+/*
+ * Add string to the end of list.  If list->strdup_string is set, then
+ * string is copied; otherwise the new string_list_entry refers to the
+ * input string.
+ */
 struct string_list_item *string_list_append(struct string_list *list, const char *string);
+
+/*
+ * Like string_list_append(), except string is never copied.  When
+ * list->strdup_strings is set, this function can be used to hand
+ * ownership of a malloc()ed string to list without making an extra
+ * copy.
+ */
+struct string_list_item *string_list_append_nodup(struct string_list *list, char *string);
+
 void sort_string_list(struct string_list *list);
 int unsorted_string_list_has_string(struct string_list *list, const char *string);
 struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
                                                     const char *string);
+
 void unsorted_string_list_delete_item(struct string_list *list, int i, int free_util);
+
+/*
+ * Split string into substrings on character delim and append the
+ * substrings to list.  The input string is not modified.
+ * list->strdup_strings must be set, as new memory needs to be
+ * allocated to hold the substrings.  If maxsplit is non-negative,
+ * then split at most maxsplit times.  Return the number of substrings
+ * appended to list.
+ *
+ * Examples:
+ *   string_list_split(l, "foo:bar:baz", ':', -1) -> ["foo", "bar", "baz"]
+ *   string_list_split(l, "foo:bar:baz", ':', 0) -> ["foo:bar:baz"]
+ *   string_list_split(l, "foo:bar:baz", ':', 1) -> ["foo", "bar:baz"]
+ *   string_list_split(l, "foo:bar:", ':', -1) -> ["foo", "bar", ""]
+ *   string_list_split(l, "", ':', -1) -> [""]
+ *   string_list_split(l, ":", ':', -1) -> ["", ""]
+ */
+int string_list_split(struct string_list *list, const char *string,
+                     int delim, int maxsplit);
+
+/*
+ * Like string_list_split(), except that string is split in-place: the
+ * delimiter characters in string are overwritten with NULs, and the
+ * new string_list_items point into string (which therefore must not
+ * be modified or freed while the string_list is in use).
+ * list->strdup_strings must *not* be set.
+ */
+int string_list_split_in_place(struct string_list *list, char *string,
+                              int delim, int maxsplit);
 #endif /* STRING_LIST_H */
index 290036744bcfd390f91e75db49bf620bb12c12ea..c2b41a85013eec87a412badc4097f364d547726d 100644 (file)
@@ -1,5 +1,8 @@
 #include "cache.h"
 
+static int threaded_check_leading_path(struct cache_def *cache, const char *name, int len);
+static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len);
+
 /*
  * Returns the length (on a path component basis) of the longest
  * common prefix match of 'name_a' and 'name_b'.
@@ -231,7 +234,7 @@ int check_leading_path(const char *name, int len)
  * Return path length if leading path exists and is neither a
  * directory nor a symlink.
  */
-int threaded_check_leading_path(struct cache_def *cache, const char *name, int len)
+static int threaded_check_leading_path(struct cache_def *cache, const char *name, int len)
 {
        int flags;
        int match_len = lstat_cache_matchlen(cache, name, len, &flags,
@@ -263,7 +266,7 @@ int has_dirs_only_path(const char *name, int len, int prefix_len)
  * 'prefix_len', thus we then allow for symlinks in the prefix part as
  * long as those points to real existing directories.
  */
-int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len)
+static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len)
 {
        return lstat_cache(cache, name, len,
                           FL_DIR|FL_FULLPATH, prefix_len) &
index d748c36df596b640d403626fce3200d85d68253b..7061dce7e5de2f67e60fe6c42c40519f29cad4a1 100644 (file)
@@ -26,9 +26,10 @@ testid=${this_test#t}
 git_p4_test_start=9800
 P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
 
-export P4PORT=localhost:$P4DPORT
-export P4CLIENT=client
-export P4EDITOR=:
+P4PORT=localhost:$P4DPORT
+P4CLIENT=client
+P4EDITOR=:
+export P4PORT P4CLIENT P4EDITOR
 
 db="$TRASH_DIRECTORY/db"
 cli=$(test-path-utils real_path "$TRASH_DIRECTORY/cli")
index 50f5cc1ed901e5abd7839dcb58ff94aa96a9c641..982eb8e3a949a74e5c26a952a33364c25f6f5b74 100644 (file)
@@ -1,2 +1,3 @@
-build/
-test-results/
+/build/
+/test-results/
+/trash directory*/
index a1361e530c60609a52948dd0fd35642d57f84486..1d0bb9d0173af5908b46285dbd574642fb20a22e 100644 (file)
@@ -42,6 +42,7 @@ else
 fi
 
 TEST_NO_CREATE_REPO=t
+TEST_NO_MALLOC_=t
 
 . ../test-lib.sh
 
diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh
new file mode 100755 (executable)
index 0000000..41c8826
--- /dev/null
@@ -0,0 +1,121 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Michael Haggerty
+#
+
+test_description='Test string list functionality'
+
+. ./test-lib.sh
+
+test_split () {
+       cat >expected &&
+       test_expect_success "split $1 at $2, max $3" "
+               test-string-list split '$1' '$2' '$3' >actual &&
+               test_cmp expected actual &&
+               test-string-list split_in_place '$1' '$2' '$3' >actual &&
+               test_cmp expected actual
+       "
+}
+
+test_longest_prefix () {
+       test "$(test-string-list longest_prefix "$1" "$2")" = "$3"
+}
+
+test_no_longest_prefix () {
+       test_must_fail test-string-list longest_prefix "$1" "$2"
+}
+
+test_split "foo:bar:baz" ":" "-1" <<EOF
+3
+[0]: "foo"
+[1]: "bar"
+[2]: "baz"
+EOF
+
+test_split "foo:bar:baz" ":" "0" <<EOF
+1
+[0]: "foo:bar:baz"
+EOF
+
+test_split "foo:bar:baz" ":" "1" <<EOF
+2
+[0]: "foo"
+[1]: "bar:baz"
+EOF
+
+test_split "foo:bar:baz" ":" "2" <<EOF
+3
+[0]: "foo"
+[1]: "bar"
+[2]: "baz"
+EOF
+
+test_split "foo:bar:" ":" "-1" <<EOF
+3
+[0]: "foo"
+[1]: "bar"
+[2]: ""
+EOF
+
+test_split "" ":" "-1" <<EOF
+1
+[0]: ""
+EOF
+
+test_split ":" ":" "-1" <<EOF
+2
+[0]: ""
+[1]: ""
+EOF
+
+test_expect_success "test filter_string_list" '
+       test "x-" = "x$(test-string-list filter - y)" &&
+       test "x-" = "x$(test-string-list filter no y)" &&
+       test yes = "$(test-string-list filter yes y)" &&
+       test yes = "$(test-string-list filter no:yes y)" &&
+       test yes = "$(test-string-list filter yes:no y)" &&
+       test y1:y2 = "$(test-string-list filter y1:y2 y)" &&
+       test y2:y1 = "$(test-string-list filter y2:y1 y)" &&
+       test "x-" = "x$(test-string-list filter x1:x2 y)"
+'
+
+test_expect_success "test remove_duplicates" '
+       test "x-" = "x$(test-string-list remove_duplicates -)" &&
+       test "x" = "x$(test-string-list remove_duplicates "")" &&
+       test a = "$(test-string-list remove_duplicates a)" &&
+       test a = "$(test-string-list remove_duplicates a:a)" &&
+       test a = "$(test-string-list remove_duplicates a:a:a:a:a)" &&
+       test a:b = "$(test-string-list remove_duplicates a:b)" &&
+       test a:b = "$(test-string-list remove_duplicates a:a:b)" &&
+       test a:b = "$(test-string-list remove_duplicates a:b:b)" &&
+       test a:b:c = "$(test-string-list remove_duplicates a:b:c)" &&
+       test a:b:c = "$(test-string-list remove_duplicates a:a:b:c)" &&
+       test a:b:c = "$(test-string-list remove_duplicates a:b:b:c)" &&
+       test a:b:c = "$(test-string-list remove_duplicates a:b:c:c)" &&
+       test a:b:c = "$(test-string-list remove_duplicates a:a:b:b:c:c)" &&
+       test a:b:c = "$(test-string-list remove_duplicates a:a:a:b:b:b:c:c:c)"
+'
+
+test_expect_success "test longest_prefix" '
+       test_no_longest_prefix - '' &&
+       test_no_longest_prefix - x &&
+       test_longest_prefix "" x "" &&
+       test_longest_prefix x x x &&
+       test_longest_prefix "" foo "" &&
+       test_longest_prefix : foo "" &&
+       test_longest_prefix f foo f &&
+       test_longest_prefix foo foobar foo &&
+       test_longest_prefix foo foo foo &&
+       test_no_longest_prefix bar foo &&
+       test_no_longest_prefix bar:bar foo &&
+       test_no_longest_prefix foobar foo &&
+       test_longest_prefix foo:bar foo foo &&
+       test_longest_prefix foo:bar bar bar &&
+       test_longest_prefix foo::bar foo foo &&
+       test_longest_prefix foo:foobar foo foo &&
+       test_longest_prefix foobar:foo foo foo &&
+       test_longest_prefix foo: bar "" &&
+       test_longest_prefix :foo bar ""
+'
+
+test_done
index 0c81b3c427af47f0b0ef73170372ef9df70b4ade..c82f7210c4ca10e35d9d2000aa8da2d10194a6c6 100755 (executable)
@@ -30,6 +30,7 @@ test_expect_success setup '
        test_commit initial foo a &&
        test_commit base foo b &&
        test_commit picked foo c &&
+       test_commit --signoff picked-signed foo d &&
        git config advice.detachedhead false
 
 '
@@ -340,4 +341,35 @@ test_expect_success 'revert conflict, diff3 -m style' '
        test_cmp expected actual
 '
 
+test_expect_success 'failed cherry-pick does not forget -s' '
+       pristine_detach initial &&
+       test_must_fail git cherry-pick -s picked &&
+       test_i18ngrep -e "Signed-off-by" .git/MERGE_MSG
+'
+
+test_expect_success 'commit after failed cherry-pick does not add duplicated -s' '
+       pristine_detach initial &&
+       test_must_fail git cherry-pick -s picked-signed &&
+       git commit -a -s &&
+       test $(git show -s |grep -c "Signed-off-by") = 1
+'
+
+test_expect_success 'commit after failed cherry-pick adds -s at the right place' '
+       pristine_detach initial &&
+       test_must_fail git cherry-pick picked &&
+       git commit -a -s &&
+       pwd &&
+       cat <<EOF > expected &&
+picked
+
+Signed-off-by: C O Mitter <committer@example.com>
+
+Conflicts:
+       foo
+EOF
+
+       git show -s --pretty=format:%B > actual &&
+       test_cmp expected actual
+'
+
 test_done
index f4e6450d6a88ae0abd158e52634c47a23872ad2c..b5fb527b2e686d158a6e7d53d6246b9e66f80a7b 100755 (executable)
@@ -410,7 +410,7 @@ test_expect_success '--continue respects -x in first commit in multi-pick' '
        grep "cherry picked from.*$picked" msg
 '
 
-test_expect_success '--signoff is not automatically propagated to resolved conflict' '
+test_expect_failure '--signoff is automatically propagated to resolved conflict' '
        pristine_detach initial &&
        test_expect_code 1 git cherry-pick --signoff base..anotherpick &&
        echo "c" >foo &&
@@ -428,7 +428,7 @@ test_expect_success '--signoff is not automatically propagated to resolved confl
        grep "Signed-off-by:" anotherpick_msg
 '
 
-test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' '
+test_expect_failure '--signoff dropped for implicit commit of resolution, multi-pick case' '
        pristine_detach initial &&
        test_must_fail git cherry-pick -s picked anotherpick &&
        echo c >foo &&
@@ -441,7 +441,7 @@ test_expect_success '--signoff dropped for implicit commit of resolution, multi-
        ! grep Signed-off-by: msg
 '
 
-test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
+test_expect_failure 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
        pristine_detach initial &&
        test_must_fail git cherry-pick -s picked &&
        echo c >foo &&
index 4bd2a1c838e9a522c4376860e726455d3cdb92b0..082d3e83bddf242fb02f5124fd52d125520af6b8 100755 (executable)
@@ -105,7 +105,7 @@ test_expect_funcname () {
        grep "^@@.*@@ $1" diff
 }
 
-for p in bibtex cpp csharp fortran html java matlab objc pascal perl php python ruby tex
+for p in ada bibtex cpp csharp fortran html java matlab objc pascal perl php python ruby tex
 do
        test_expect_success "builtin $p pattern compiles" '
                echo "*.java diff=$p" >.gitattributes &&
index 30d42cb3bfd856a7d920119f1c4226c408a8f82f..40ab333a8a4849d620e1d41a75f611921f83055a 100755 (executable)
@@ -298,6 +298,7 @@ test_expect_success 'unset default driver' '
        test_unconfig diff.wordregex
 '
 
+test_language_driver ada
 test_language_driver bibtex
 test_language_driver cpp
 test_language_driver csharp
diff --git a/t/t4034/ada/expect b/t/t4034/ada/expect
new file mode 100644 (file)
index 0000000..be2376e
--- /dev/null
@@ -0,0 +1,27 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index d96fdd1..df21bb0 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,13 +1,13 @@<RESET>
+Ada.Text_IO.Put_Line("Hello World<RED>!<RESET><GREEN>?<RESET>");
+1 1e<RED>-<RESET>10 16#FE12#E2 3.141_592 '<RED>x<RESET><GREEN>y<RESET>'
+<RED>a<RESET><GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>**<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>(<RED>b<RESET><GREEN>y<RESET>)
+<RED>a<RESET><GREEN>x<RESET>:=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>/= <RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>=><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>..<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><><RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/ada/post b/t/t4034/ada/post
new file mode 100644 (file)
index 0000000..df21bb0
--- /dev/null
@@ -0,0 +1,13 @@
+Ada.Text_IO.Put_Line("Hello World?");
+1 1e10 16#FE12#E2 3.141_592 'y'
+x+y x-y
+x*y x/y
+x**y
+x(y)
+x:=y
+x=y x/= y
+x<y x<=y x>y x>=y
+x,y
+x=>y
+x..y
+x<>y
diff --git a/t/t4034/ada/pre b/t/t4034/ada/pre
new file mode 100644 (file)
index 0000000..d96fdd1
--- /dev/null
@@ -0,0 +1,13 @@
+Ada.Text_IO.Put_Line("Hello World!");
+1 1e-10 16#FE12#E2 3.141_592 'x'
+a+b a-b
+a*b a/b
+a**b
+a(b)
+a:=b
+a=b a/= b
+a<b a<=b a>b a>=b
+a,b
+a=>b
+a..b
+a<>b
index 81904d9ec8d341399b534c5d576262439d0e1ad6..3e64a7a65da86410dd0236975bef9bd0013a6405 100755 (executable)
@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \
        'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
        last=`cat last` &&
        echo total is $last &&
-       test `cat last` = 16'
+       test `cat last` = 17'
 
 check_mailinfo () {
        mail=$1 opt=$2
diff --git a/t/t5100/info0017 b/t/t5100/info0017
new file mode 100644 (file)
index 0000000..d2bc89f
--- /dev/null
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: A E I O U
+Date: Mon, 17 Sep 2012 14:23:44 -0700
+
diff --git a/t/t5100/msg0017 b/t/t5100/msg0017
new file mode 100644 (file)
index 0000000..2ee0900
--- /dev/null
@@ -0,0 +1,2 @@
+New content here
+
diff --git a/t/t5100/patch0017 b/t/t5100/patch0017
new file mode 100644 (file)
index 0000000..35cf84c
--- /dev/null
@@ -0,0 +1,6 @@
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++New content
index 34a09a0fc1c0ca9d1e68ed8bbf56e592c54e7227..8b2ae064c36b969ca20324154cea66bf26739de6 100644 (file)
@@ -683,3 +683,19 @@ index e69de29..d95f3ad 100644
 @@ -0,0 +1 @@
 +content
 
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: A E I O U
+Date: Mon, 17 Sep 2012 14:23:44 -0700
+MIME-Version: 1.0
+Content-Type: text/plain; charset="iso-2022-jp"
+Content-type: text/plain; charset="UTF-8"
+
+New content here
+
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++New content
index e80a2af348565a0a3d001ef28cb76427acfee495..6322e8ade8436dc4e66b9874638e7ca6b7d222b3 100755 (executable)
@@ -391,10 +391,55 @@ test_expect_success 'fetch mixed refs from cmdline and stdin' '
 test_expect_success 'test duplicate refs from stdin' '
        (
        cd client &&
-       test_must_fail git fetch-pack --stdin --no-progress .. <../input.dup
+       git fetch-pack --stdin --no-progress .. <../input.dup
        ) >output &&
        cut -d " " -f 2 <output | sort >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'set up tests of missing reference' '
+       cat >expect-error <<-\EOF
+       error: no such remote ref refs/heads/xyzzy
+       EOF
+'
+
+test_expect_success 'test lonely missing ref' '
+       (
+               cd client &&
+               test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy
+       ) >/dev/null 2>error-m &&
+       test_cmp expect-error error-m
+'
+
+test_expect_success 'test missing ref after existing' '
+       (
+               cd client &&
+               test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy
+       ) >/dev/null 2>error-em &&
+       test_cmp expect-error error-em
+'
+
+test_expect_success 'test missing ref before existing' '
+       (
+               cd client &&
+               test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A
+       ) >/dev/null 2>error-me &&
+       test_cmp expect-error error-me
+'
+
+test_expect_success 'test --all, --depth, and explicit head' '
+       (
+               cd client &&
+               git fetch-pack --no-progress --all --depth=1 .. refs/heads/A
+       ) >out-adh 2>error-adh
+'
+
+test_expect_success 'test --all, --depth, and explicit tag' '
+       git tag OLDTAG refs/heads/B~5 &&
+       (
+               cd client &&
+               git fetch-pack --no-progress --all --depth=1 .. refs/tags/OLDTAG
+       ) >out-adt 2>error-adt
+'
+
 test_done
index 35d357d4c8bdc0ef313a2fc269a9ca81b22489f1..91db352cc7ed5faa24eb27542d5ce8c4fe622303 100755 (executable)
@@ -502,31 +502,41 @@ test_expect_success 'log grep setup' '
 
 test_expect_success 'log grep (1)' '
        git log --author=author --pretty=tformat:%s >actual &&
-       ( echo third ; echo initial ) >expect &&
+       {
+               echo third && echo initial
+       } >expect &&
        test_cmp expect actual
 '
 
 test_expect_success 'log grep (2)' '
        git log --author=" * " -F --pretty=tformat:%s >actual &&
-       ( echo second ) >expect &&
+       {
+               echo second
+       } >expect &&
        test_cmp expect actual
 '
 
 test_expect_success 'log grep (3)' '
        git log --author="^A U" --pretty=tformat:%s >actual &&
-       ( echo third ; echo initial ) >expect &&
+       {
+               echo third && echo initial
+       } >expect &&
        test_cmp expect actual
 '
 
 test_expect_success 'log grep (4)' '
        git log --author="frotz\.com>$" --pretty=tformat:%s >actual &&
-       ( echo second ) >expect &&
+       {
+               echo second
+       } >expect &&
        test_cmp expect actual
 '
 
 test_expect_success 'log grep (5)' '
        git log --author=Thor -F --pretty=tformat:%s >actual &&
-       ( echo third ; echo initial ) >expect &&
+       {
+               echo third && echo initial
+       } >expect &&
        test_cmp expect actual
 '
 
@@ -536,11 +546,19 @@ test_expect_success 'log grep (6)' '
        test_cmp expect actual
 '
 
-test_expect_success 'log --grep --author implicitly uses all-match' '
-       # grep matches initial and second but not third
-       # author matches only initial and third
-       git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
-       echo initial >expect &&
+test_expect_success 'log with multiple --grep uses union' '
+       git log --grep=i --grep=r --format=%s >actual &&
+       {
+               echo fourth && echo third && echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log --all-match with multiple --grep uses intersection' '
+       git log --all-match --grep=i --grep=r --format=%s >actual &&
+       {
+               echo third
+       } >expect &&
        test_cmp expect actual
 '
 
@@ -552,7 +570,47 @@ test_expect_success 'log with multiple --author uses union' '
        test_cmp expect actual
 '
 
-test_expect_success 'log with --grep and multiple --author uses all-match' '
+test_expect_success 'log --all-match with multiple --author still uses union' '
+       git log --all-match --author="Thor" --author="Aster" --format=%s >actual &&
+       {
+           echo third && echo second && echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log --grep --author uses intersection' '
+       # grep matches only third and fourth
+       # author matches only initial and third
+       git log --author="A U Thor" --grep=r --format=%s >actual &&
+       {
+               echo third
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' '
+       # grep matches initial and second but not third
+       # author matches only initial and third
+       git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
+       {
+               echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log ---all-match -grep --author --author still takes union of authors and intersects with grep' '
+       # grep matches only initial and third
+       # author matches all but second
+       git log --all-match --author="Thor" --author="Night" --grep=i --format=%s >actual &&
+       {
+           echo third && echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log --grep --author --author takes union of authors and intersects with grep' '
+       # grep matches only initial and third
+       # author matches all but second
        git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
        {
            echo third && echo initial
@@ -560,9 +618,13 @@ test_expect_success 'log with --grep and multiple --author uses all-match' '
        test_cmp expect actual
 '
 
-test_expect_success 'log with --grep and multiple --author uses all-match' '
-       git log --author="Thor" --author="Night" --grep=q --format=%s >actual &&
-       >expect &&
+test_expect_success 'log --all-match --grep --grep --author takes intersection' '
+       # grep matches only third
+       # author matches only initial and third
+       git log --all-match --author="A U Thor" --grep=i --grep=r --format=%s >actual &&
+       {
+               echo third
+       } >expect &&
        test_cmp expect actual
 '
 
index ba19ac127e630c01e009fa6eda1fac0086d7184d..9c353ab22276c052de054281dd57808d1826a355 100755 (executable)
@@ -66,7 +66,7 @@ test_expect_success \
        git blame file2
 '
 
-test_expect_success 'blame runs on conflicted file in stages 1,3' '
+test_expect_success 'blame does not crash with conflicted file in stages 1,3' '
        git blame file1
 '
 
index fb3c8ec12c7a3724c3b4f719c91357cc4afed078..ff2cc79701cce5ff570ff3c11d1ad0b60459d9ef 100755 (executable)
@@ -38,7 +38,7 @@ test_expect_success 'no config, unedited, say no' '
                cd "$git" &&
                echo line >>file1 &&
                git commit -a -m "change 3 (not really)" &&
-               printf "bad response\nn\n" | git p4 submit &&
+               printf "bad response\nn\n" | test_expect_code 1 git p4 submit &&
                p4 changes //depot/... >wc &&
                test_line_count = 2 wc
        )
index 9394fd4e9b5bd7eedc1f3d0552aa71d429b64308..0ae048f29f548eac6cfa37fbabcd9b7f80440515 100755 (executable)
@@ -54,6 +54,47 @@ test_expect_success 'submit --origin' '
        )
 '
 
+test_expect_success 'submit --dry-run' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               test_commit "dry-run1" &&
+               test_commit "dry-run2" &&
+               git p4 submit --dry-run >out &&
+               test_i18ngrep "Would apply" out
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing "dry-run1.t" &&
+               test_path_is_missing "dry-run2.t"
+       )
+'
+
+test_expect_success 'submit --dry-run --export-labels' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo dry-run1 >dry-run1 &&
+               git add dry-run1 &&
+               git commit -m "dry-run1" dry-run1 &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit &&
+               echo dry-run2 >dry-run2 &&
+               git add dry-run2 &&
+               git commit -m "dry-run2" dry-run2 &&
+               git tag -m "dry-run-tag1" dry-run-tag1 HEAD^ &&
+               git p4 submit --dry-run --export-labels >out &&
+               test_i18ngrep "Would create p4 label" out
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file "dry-run1" &&
+               test_path_is_missing "dry-run2"
+       )
+'
+
 test_expect_success 'submit with allowSubmit' '
        test_when_finished cleanup_git &&
        git p4 clone --dest="$git" //depot &&
@@ -334,6 +375,30 @@ test_expect_success 'description with Jobs section and bogus following text' '
                make_job $(cat jobname) &&
                test_must_fail git p4 submit 2>err &&
                test_i18ngrep "Unknown field name" err
+       ) &&
+       (
+               cd "$cli" &&
+               p4 revert desc6 &&
+               rm desc6
+       )
+'
+
+test_expect_success 'submit --prepare-p4-only' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo prep-only-add >prep-only-add &&
+               git add prep-only-add &&
+               git commit -m "prep only add" &&
+               git p4 submit --prepare-p4-only >out &&
+               test_i18ngrep "prepared for submission" out &&
+               test_i18ngrep "must be deleted" out
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file prep-only-add &&
+               p4 fstat -T action prep-only-add | grep -w add
        )
 '
 
index e9daa9c4f6bab493ad5351d4ec9c7c00c6152e68..fe30ad881f67c9f1fdcb2ee40922880126ec13cc 100755 (executable)
@@ -160,9 +160,6 @@ test_expect_success 'cleanup after failure' '
 # the cli file so that submit will get a conflict.  Make sure that
 # scrubbing doesn't make a mess of things.
 #
-# Assumes that git-p4 exits leaving the p4 file open, with the
-# conflict-generating patch unapplied.
-#
 # This might happen only if the git repo is behind the p4 repo at
 # submit time, and there is a conflict.
 #
@@ -181,14 +178,11 @@ test_expect_success 'do not scrub plain text' '
                        sed -i "s/^line5/line5 p4 edit/" file_text &&
                        p4 submit -d "file5 p4 edit"
                ) &&
-               ! git p4 submit &&
+               echo s | test_expect_code 1 git p4 submit &&
                (
-                       # exepct something like:
-                       #    file_text - file(s) not opened on this client
-                       # but not copious diff output
+                       # make sure the file is not left open
                        cd "$cli" &&
-                       p4 diff file_text >wc &&
-                       test_line_count = 1 wc
+                       ! p4 fstat -T action file_text
                )
        )
 '
@@ -343,44 +337,6 @@ test_expect_failure 'Add keywords in git which do not match the default p4 value
        )
 '
 
-# Check that the existing merge conflict handling still works.
-# Modify kwfile1.c in git, and delete in p4. We should be able
-# to skip the git commit.
-#
-test_expect_success 'merge conflict handling still works' '
-       test_when_finished cleanup_git &&
-       (
-               cd "$cli" &&
-               echo "Hello:\$Id\$" >merge2.c &&
-               echo "World" >>merge2.c &&
-               p4 add -t ktext merge2.c &&
-               p4 submit -d "add merge test file"
-       ) &&
-       git p4 clone --dest="$git" //depot &&
-       (
-               cd "$git" &&
-               sed -e "/Hello/d" merge2.c >merge2.c.tmp &&
-               mv merge2.c.tmp merge2.c &&
-               git add merge2.c &&
-               git commit -m "Modifying merge2.c"
-       ) &&
-       (
-               cd "$cli" &&
-               p4 delete merge2.c &&
-               p4 submit -d "remove merge test file"
-       ) &&
-       (
-               cd "$git" &&
-               test -f merge2.c &&
-               git config git-p4.skipSubmitEdit true &&
-               git config git-p4.attemptRCSCleanup true &&
-               !(echo "s" | git p4 submit) &&
-               git rebase --skip &&
-               ! test -f merge2.c
-       )
-'
-
-
 test_expect_success 'kill p4d' '
        kill_p4d
 '
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
new file mode 100755 (executable)
index 0000000..d2b7b3d
--- /dev/null
@@ -0,0 +1,429 @@
+#!/bin/sh
+
+test_description='git p4 submit failure handling'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+       start_p4d
+'
+
+test_expect_success 'init depot' '
+       (
+               cd "$cli" &&
+               p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i &&
+               echo line1 >file1 &&
+               p4 add file1 &&
+               p4 submit -d "line1 in file1"
+       )
+'
+
+test_expect_success 'conflict on one commit' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$cli" &&
+               p4 open file1 &&
+               echo line2 >>file1 &&
+               p4 submit -d "line2 in file1"
+       ) &&
+       (
+               # now this commit should cause a conflict
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               echo line3 >>file1 &&
+               git add file1 &&
+               git commit -m "line3 in file1 will conflict" &&
+               test_expect_code 1 git p4 submit >out &&
+               test_i18ngrep "No commits applied" out
+       )
+'
+
+test_expect_success 'conflict on second of two commits' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$cli" &&
+               p4 open file1 &&
+               echo line3 >>file1 &&
+               p4 submit -d "line3 in file1"
+       ) &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               # this commit is okay
+               test_commit "first_commit_okay" &&
+               # now this submit should cause a conflict
+               echo line4 >>file1 &&
+               git add file1 &&
+               git commit -m "line4 in file1 will conflict" &&
+               test_expect_code 1 git p4 submit >out &&
+               test_i18ngrep "Applied only the commits" out
+       )
+'
+
+test_expect_success 'conflict on first of two commits, skip' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$cli" &&
+               p4 open file1 &&
+               echo line4 >>file1 &&
+               p4 submit -d "line4 in file1"
+       ) &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               # this submit should cause a conflict
+               echo line5 >>file1 &&
+               git add file1 &&
+               git commit -m "line5 in file1 will conflict" &&
+               # but this commit is okay
+               test_commit "okay_commit_after_skip" &&
+               echo s | test_expect_code 1 git p4 submit >out &&
+               test_i18ngrep "Applied only the commits" out
+       )
+'
+
+test_expect_success 'conflict on first of two commits, quit' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$cli" &&
+               p4 open file1 &&
+               echo line7 >>file1 &&
+               p4 submit -d "line7 in file1"
+       ) &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               # this submit should cause a conflict
+               echo line8 >>file1 &&
+               git add file1 &&
+               git commit -m "line8 in file1 will conflict" &&
+               # but this commit is okay
+               test_commit "okay_commit_after_quit" &&
+               echo q | test_expect_code 1 git p4 submit >out &&
+               test_i18ngrep "No commits applied" out
+       )
+'
+
+test_expect_success 'conflict cli and config options' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git p4 submit --conflict=ask &&
+               git p4 submit --conflict=skip &&
+               git p4 submit --conflict=quit &&
+               test_expect_code 2 git p4 submit --conflict=foo &&
+               test_expect_code 2 git p4 submit --conflict &&
+               git config git-p4.conflict foo &&
+               test_expect_code 1 git p4 submit &&
+               git config --unset git-p4.conflict &&
+               git p4 submit
+       )
+'
+
+test_expect_success 'conflict on first of two commits, --conflict=skip' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$cli" &&
+               p4 open file1 &&
+               echo line9 >>file1 &&
+               p4 submit -d "line9 in file1"
+       ) &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               # this submit should cause a conflict
+               echo line10 >>file1 &&
+               git add file1 &&
+               git commit -m "line10 in file1 will conflict" &&
+               # but this commit is okay
+               test_commit "okay_commit_after_auto_skip" &&
+               test_expect_code 1 git p4 submit --conflict=skip >out &&
+               test_i18ngrep "Applied only the commits" out
+       )
+'
+
+test_expect_success 'conflict on first of two commits, --conflict=quit' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$cli" &&
+               p4 open file1 &&
+               echo line11 >>file1 &&
+               p4 submit -d "line11 in file1"
+       ) &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               # this submit should cause a conflict
+               echo line12 >>file1 &&
+               git add file1 &&
+               git commit -m "line12 in file1 will conflict" &&
+               # but this commit is okay
+               test_commit "okay_commit_after_auto_quit" &&
+               test_expect_code 1 git p4 submit --conflict=quit >out &&
+               test_i18ngrep "No commits applied" out
+       )
+'
+
+#
+# Cleanup after submit fail, all cases.  Some modifications happen
+# before trying to apply the patch.  Make sure these are unwound
+# properly.  Put each one in a diff along with something that will
+# obviously conflict.  Make sure it is back to normal after.
+#
+
+test_expect_success 'cleanup edit p4 populate' '
+       (
+               cd "$cli" &&
+               echo text file >text &&
+               p4 add text &&
+               echo text+x file >text+x &&
+               chmod 755 text+x &&
+               p4 add text+x &&
+               p4 submit -d "populate p4"
+       )
+'
+
+setup_conflict() {
+       # clone before modifying file1 to force it to conflict
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       # ticks outside subshells
+       test_tick &&
+       (
+               cd "$cli" &&
+               p4 open file1 &&
+               echo $test_tick >>file1 &&
+               p4 submit -d "$test_tick in file1"
+       ) &&
+       test_tick &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               # easy conflict
+               echo $test_tick >>file1 &&
+               git add file1
+               # caller will add more and submit
+       )
+}
+
+test_expect_success 'cleanup edit after submit fail' '
+       setup_conflict &&
+       (
+               cd "$git" &&
+               echo another line >>text &&
+               git add text &&
+               git commit -m "conflict" &&
+               test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               # make sure it is not open
+               ! p4 fstat -T action text
+       )
+'
+
+test_expect_success 'cleanup add after submit fail' '
+       setup_conflict &&
+       (
+               cd "$git" &&
+               echo new file >textnew &&
+               git add textnew &&
+               git commit -m "conflict" &&
+               test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               # make sure it is not there
+               # and that p4 thinks it is not added
+               #   P4 returns 0 both for "not there but added" and
+               #   "not there", so grep.
+               test_path_is_missing textnew &&
+               p4 fstat -T action textnew 2>&1 | grep "no such file"
+       )
+'
+
+test_expect_success 'cleanup delete after submit fail' '
+       setup_conflict &&
+       (
+               cd "$git" &&
+               git rm text+x &&
+               git commit -m "conflict" &&
+               test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               # make sure it is there
+               test_path_is_file text+x &&
+               ! p4 fstat -T action text+x
+       )
+'
+
+test_expect_success 'cleanup copy after submit fail' '
+       setup_conflict &&
+       (
+               cd "$git" &&
+               cp text text2 &&
+               git add text2 &&
+               git commit -m "conflict" &&
+               git config git-p4.detectCopies true &&
+               git config git-p4.detectCopiesHarder true &&
+               # make sure setup is okay
+               git diff-tree -r -C --find-copies-harder HEAD | grep text2 | grep C100 &&
+               test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing text2 &&
+               p4 fstat -T action text2 2>&1 | grep "no such file"
+       )
+'
+
+test_expect_success 'cleanup rename after submit fail' '
+       setup_conflict &&
+       (
+               cd "$git" &&
+               git mv text text2 &&
+               git commit -m "conflict" &&
+               git config git-p4.detectRenames true &&
+               # make sure setup is okay
+               git diff-tree -r -M HEAD | grep text2 | grep R100 &&
+               test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing text2 &&
+               p4 fstat -T action text2 2>&1 | grep "no such file"
+       )
+'
+
+#
+# Cleanup after deciding not to submit during editTemplate.  This
+# involves unwinding more work, because files have been added, deleted
+# and chmod-ed now.  Same approach as above.
+#
+
+test_expect_success 'cleanup edit after submit cancel' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo line >>text &&
+               git add text &&
+               git commit -m text &&
+               echo n | test_expect_code 1 git p4 submit &&
+               git reset --hard HEAD^
+       ) &&
+       (
+               cd "$cli" &&
+               ! p4 fstat -T action text &&
+               test_cmp "$git"/text text
+       )
+'
+
+test_expect_success 'cleanup add after submit cancel' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo line >textnew &&
+               git add textnew &&
+               git commit -m textnew &&
+               echo n | test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing textnew &&
+               p4 fstat -T action textnew 2>&1 | grep "no such file"
+       )
+'
+
+test_expect_success 'cleanup delete after submit cancel' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git rm text &&
+               git commit -m "rm text" &&
+               echo n | test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file text &&
+               ! p4 fstat -T action text
+       )
+'
+
+test_expect_success 'cleanup copy after submit cancel' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               cp text text2 &&
+               git add text2 &&
+               git commit -m text2 &&
+               git config git-p4.detectCopies true &&
+               git config git-p4.detectCopiesHarder true &&
+               git diff-tree -r -C --find-copies-harder HEAD | grep text2 | grep C100 &&
+               echo n | test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing text2 &&
+               p4 fstat -T action text2 2>&1 | grep "no such file"
+       )
+'
+
+test_expect_success 'cleanup rename after submit cancel' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git mv text text2 &&
+               git commit -m text2 &&
+               git config git-p4.detectRenames true &&
+               git diff-tree -r -M HEAD | grep text2 | grep R100 &&
+               echo n | test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing text2 &&
+               p4 fstat -T action text2 2>&1 | grep "no such file"
+               test_path_is_file text &&
+               ! p4 fstat -T action text
+       )
+'
+
+test_expect_success 'cleanup chmod after submit cancel' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               chmod u+x text &&
+               chmod u-x text+x &&
+               git add text text+x &&
+               git commit -m "chmod texts" &&
+               echo n | test_expect_code 1 git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file text &&
+               ! p4 fstat -T action text &&
+               stat --format=%A text | egrep ^-r-- &&
+               test_path_is_file text+x &&
+               ! p4 fstat -T action text+x &&
+               stat --format=%A text+x | egrep ^-r-x
+       )
+'
+
+test_expect_success 'kill p4d' '
+       kill_p4d
+'
+
+test_done
index 9bc57d27e961720c98f75f95ab3186723e77f88b..8889ba5104cd3f7c783a179d45816d3a82685ef0 100644 (file)
@@ -144,11 +144,22 @@ test_pause () {
 
 test_commit () {
        notick= &&
-       if test "z$1" = "z--notick"
-       then
-               notick=yes
+       signoff= &&
+       while test $# != 0
+       do
+               case "$1" in
+               --notick)
+                       notick=yes
+                       ;;
+               --signoff)
+                       signoff="$1"
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
                shift
-       fi &&
+       done &&
        file=${2:-"$1.t"} &&
        echo "${3-$1}" > "$file" &&
        git add "$file" &&
@@ -156,7 +167,7 @@ test_commit () {
        then
                test_tick
        fi &&
-       git commit -m "$1" &&
+       git commit $signoff -m "$1" &&
        git tag "$1"
 }
 
index f8e3733ea06478d4eccb483ef6c2e6b196bde02f..aad46066adf0021b721bb3a2e235e2b012f13422 100644 (file)
@@ -93,6 +93,27 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR
 
+# Add libc MALLOC and MALLOC_PERTURB test
+# only if we are not executing the test with valgrind
+if expr " $GIT_TEST_OPTS " : ".* --valgrind " >/dev/null ||
+   test -n "TEST_NO_MALLOC_"
+then
+       setup_malloc_check () {
+               : nothing
+       }
+       teardown_malloc_check () {
+               : nothing
+       }
+else
+       setup_malloc_check () {
+               MALLOC_CHECK_=3 MALLOC_PERTURB_=165
+               export MALLOC_CHECK_ MALLOC_PERTURB_
+       }
+       teardown_malloc_check () {
+               unset MALLOC_CHECK_ MALLOC_PERTURB_
+       }
+fi
+
 # Protect ourselves from common misconfiguration to export
 # CDPATH into the environment
 unset CDPATH
@@ -302,7 +323,9 @@ test_run_ () {
 
        if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
        then
+               setup_malloc_check
                test_eval_ "$test_cleanup"
+               teardown_malloc_check
        fi
        if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"
        then
diff --git a/test-string-list.c b/test-string-list.c
new file mode 100644 (file)
index 0000000..4693295
--- /dev/null
@@ -0,0 +1,123 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * Parse an argument into a string list.  arg should either be a
+ * ':'-separated list of strings, or "-" to indicate an empty string
+ * list (as opposed to "", which indicates a string list containing a
+ * single empty string).  list->strdup_strings must be set.
+ */
+static void parse_string_list(struct string_list *list, const char *arg)
+{
+       if (!strcmp(arg, "-"))
+               return;
+
+       (void)string_list_split(list, arg, ':', -1);
+}
+
+static void write_list(const struct string_list *list)
+{
+       int i;
+       for (i = 0; i < list->nr; i++)
+               printf("[%d]: \"%s\"\n", i, list->items[i].string);
+}
+
+static void write_list_compact(const struct string_list *list)
+{
+       int i;
+       if (!list->nr)
+               printf("-\n");
+       else {
+               printf("%s", list->items[0].string);
+               for (i = 1; i < list->nr; i++)
+                       printf(":%s", list->items[i].string);
+               printf("\n");
+       }
+}
+
+static int prefix_cb(struct string_list_item *item, void *cb_data)
+{
+       const char *prefix = (const char *)cb_data;
+       return !prefixcmp(item->string, prefix);
+}
+
+int main(int argc, char **argv)
+{
+       if (argc == 5 && !strcmp(argv[1], "split")) {
+               struct string_list list = STRING_LIST_INIT_DUP;
+               int i;
+               const char *s = argv[2];
+               int delim = *argv[3];
+               int maxsplit = atoi(argv[4]);
+
+               i = string_list_split(&list, s, delim, maxsplit);
+               printf("%d\n", i);
+               write_list(&list);
+               string_list_clear(&list, 0);
+               return 0;
+       }
+
+       if (argc == 5 && !strcmp(argv[1], "split_in_place")) {
+               struct string_list list = STRING_LIST_INIT_NODUP;
+               int i;
+               char *s = xstrdup(argv[2]);
+               int delim = *argv[3];
+               int maxsplit = atoi(argv[4]);
+
+               i = string_list_split_in_place(&list, s, delim, maxsplit);
+               printf("%d\n", i);
+               write_list(&list);
+               string_list_clear(&list, 0);
+               free(s);
+               return 0;
+       }
+
+       if (argc == 4 && !strcmp(argv[1], "filter")) {
+               /*
+                * Retain only the items that have the specified prefix.
+                * Arguments: list|- prefix
+                */
+               struct string_list list = STRING_LIST_INIT_DUP;
+               const char *prefix = argv[3];
+
+               parse_string_list(&list, argv[2]);
+               filter_string_list(&list, 0, prefix_cb, (void *)prefix);
+               write_list_compact(&list);
+               string_list_clear(&list, 0);
+               return 0;
+       }
+
+       if (argc == 3 && !strcmp(argv[1], "remove_duplicates")) {
+               struct string_list list = STRING_LIST_INIT_DUP;
+
+               parse_string_list(&list, argv[2]);
+               string_list_remove_duplicates(&list, 0);
+               write_list_compact(&list);
+               string_list_clear(&list, 0);
+               return 0;
+       }
+
+       if (argc == 4 && !strcmp(argv[1], "longest_prefix")) {
+               /* arguments: <colon-separated-prefixes>|- <string> */
+               struct string_list prefixes = STRING_LIST_INIT_DUP;
+               int retval;
+               const char *prefix_string = argv[2];
+               const char *string = argv[3];
+               const char *match;
+
+               parse_string_list(&prefixes, prefix_string);
+               match = string_list_longest_prefix(&prefixes, string);
+               if (match) {
+                       printf("%s\n", match);
+                       retval = 0;
+               }
+               else
+                       retval = 1;
+               string_list_clear(&prefixes, 0);
+               return retval;
+       }
+
+       fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+               argv[1] ? argv[1] : "(there was none)");
+       return 1;
+}
diff --git a/trace.c b/trace.c
index d95341693fa9b27caed38ffe1308838bedb49f83..5ec0e3bd16b5cfc59254c6e10d4555ef3029aa36 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -62,7 +62,7 @@ static int get_trace_fd(const char *key, int *need_close)
 static const char err_msg[] = "Could not trace into fd given by "
        "GIT_TRACE environment variable";
 
-void trace_vprintf(const char *key, const char *fmt, va_list ap)
+static void trace_vprintf(const char *key, const char *fmt, va_list ap)
 {
        struct strbuf buf = STRBUF_INIT;
 
index 1811b500d92b1120a01d0ac09f86c0218f3d163b..9932f402dfee2605dbb498b120813aebaa3961f8 100644 (file)
@@ -518,8 +518,7 @@ static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
        struct git_transport_data *data = transport->data;
-       char **heads = xmalloc(nr_heads * sizeof(*heads));
-       char **origh = xmalloc(nr_heads * sizeof(*origh));
+       struct string_list sought = STRING_LIST_INIT_DUP;
        const struct ref *refs;
        char *dest = xstrdup(transport->url);
        struct fetch_pack_args args;
@@ -538,7 +537,7 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.depth = data->options.depth;
 
        for (i = 0; i < nr_heads; i++)
-               origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
+               string_list_append(&sought, to_fetch[i]->name);
 
        if (!data->got_remote_heads) {
                connect_setup(transport, 0, 0);
@@ -548,7 +547,7 @@ static int fetch_refs_via_pack(struct transport *transport,
 
        refs = fetch_pack(&args, data->fd, data->conn,
                          refs_tmp ? refs_tmp : transport->remote_refs,
-                         dest, nr_heads, heads, &transport->pack_lockfile);
+                         dest, &sought, &transport->pack_lockfile);
        close(data->fd[0]);
        close(data->fd[1]);
        if (finish_connect(data->conn))
@@ -558,10 +557,7 @@ static int fetch_refs_via_pack(struct transport *transport,
 
        free_refs(refs_tmp);
 
-       for (i = 0; i < nr_heads; i++)
-               free(origh[i]);
-       free(origh);
-       free(heads);
+       string_list_clear(&sought, 0);
        free(dest);
        return (refs ? 0 : -1);
 }
index b866c126e695810131cdab537b8b994c0c32e14e..3b21c4abe6d8890f99fe0169792605c98644fafd 100644 (file)
@@ -106,6 +106,7 @@ struct transport {
 #define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
 
 #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
+#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
index 1e7184f7f00bc75872369761116141c087fa1c1c..ed958ef6b8b912436ad0145fe650adea083412e3 100644 (file)
@@ -14,6 +14,15 @@ static int drivers_alloc;
        { name, NULL, -1, { pattern, REG_EXTENDED | REG_ICASE }, \
          word_regex "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" }
 static struct userdiff_driver builtin_drivers[] = {
+IPATTERN("ada",
+        "!^(.*[ \t])?(is new|renames|is separate)([ \t].*)?$\n"
+        "!^[ \t]*with[ \t].*$\n"
+        "^[ \t]*((procedure|function)[ \t]+.*)$\n"
+        "^[ \t]*((package|protected|task)[ \t]+.*)$",
+        /* -- */
+        "[a-zA-Z][a-zA-Z0-9_]*"
+        "|[0-9][-+0-9#_.eE]"
+        "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"),
 IPATTERN("fortran",
         "!^([C*]|[ \t]*!)\n"
         "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
index 4eeaf2396a0b6eb19519bbeb389c8b5d63955f18..2a9658bad4c5035893e9363b51985f285dd3b34c 100644 (file)
@@ -100,8 +100,8 @@ void status_printf(struct wt_status *s, const char *color,
        va_end(ap);
 }
 
-void status_printf_more(struct wt_status *s, const char *color,
-                       const char *fmt, ...)
+static void status_printf_more(struct wt_status *s, const char *color,
+                              const char *fmt, ...)
 {
        va_list ap;
 
index f8fc58cc0ac8ec73d81fba441b1899b934eaadb4..236b41fd345ca3dd95ce450ac3fb3fca2b725e47 100644 (file)
@@ -92,7 +92,5 @@ void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, .
        ;
 void status_printf(struct wt_status *s, const char *color, const char *fmt, ...)
        ;
-void status_printf_more(struct wt_status *s, const char *color, const char *fmt, ...)
-       __attribute__((format(printf, 3, 4)));
 
 #endif /* STATUS_H */