]> asedeno.scripts.mit.edu Git - git.git/commitdiff
Merge branch 'mv/unknown'
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Oct 2007 11:28:24 +0000 (04:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Oct 2007 11:28:24 +0000 (04:28 -0700)
* mv/unknown:
  Don't use "<unknown>" for placeholders and suppress printing of empty user formats.

113 files changed:
Documentation/RelNotes-1.5.3.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.3.4.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/core-tutorial.txt
Documentation/diff-options.txt
Documentation/git-branch.txt
Documentation/git-bundle.txt
Documentation/git-checkout.txt
Documentation/git-for-each-ref.txt
Documentation/git-gc.txt
Documentation/git-merge.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-send-email.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitignore.txt
Documentation/hooks.txt
Documentation/merge-options.txt
Documentation/user-manual.txt
INSTALL
archive-tar.c
attr.c
builtin-add.c
builtin-apply.c
builtin-archive.c
builtin-blame.c
builtin-branch.c
builtin-check-attr.c
builtin-checkout-index.c
builtin-commit-tree.c
builtin-fetch--tool.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-gc.c
builtin-log.c
builtin-ls-files.c
builtin-ls-tree.c
builtin-mv.c
builtin-pack-objects.c
builtin-rerere.c
builtin-rev-list.c
builtin-revert.c
builtin-shortlog.c
builtin-show-branch.c
builtin-stripspace.c
builtin-tag.c
builtin-update-index.c
builtin.h
cache-tree.c
cache-tree.h
cache.h
combine-diff.c
commit.c
commit.h
configure.ac
connect.c
contrib/emacs/git.el
contrib/hooks/post-receive-email
convert.c
date.c
diff.c
diffcore-order.c
diffcore-rename.c
diffcore.h
entry.c
fast-import.c
fetch.c
git-add--interactive.perl
git-am.sh
git-checkout.sh
git-commit.sh
git-compat-util.h
git-merge.sh
git-mergetool.sh
git-pull.sh
git-quiltimport.sh
git-rebase--interactive.sh
git-remote.perl
git-repack.sh
git-send-email.perl
git-submodule.sh
git-svnimport.perl
git.c
http-push.c
imap-send.c
interpolate.c
log-tree.c
merge-recursive.c
mktag.c
mktree.c
quote.c
quote.h
read-cache.c
refs.c
revision.c
rsh.c
sha1_file.c
strbuf.c
strbuf.h
t/t3060-ls-files-with-tree.sh [new file with mode: 0755]
t/t3404-rebase-interactive.sh
t/t5000-tar-tree.sh
t/t5403-post-checkout-hook.sh [new file with mode: 0755]
t/t5505-remote.sh [new file with mode: 0755]
t/t6300-for-each-ref.sh [new file with mode: 0644]
t/t7600-merge.sh [new file with mode: 0755]
t/t9101-git-svn-props.sh
t/t9104-git-svn-follow-parent.sh
tag.c
trace.c

diff --git a/Documentation/RelNotes-1.5.3.3.txt b/Documentation/RelNotes-1.5.3.3.txt
new file mode 100644 (file)
index 0000000..2a7bfdd
--- /dev/null
@@ -0,0 +1,31 @@
+GIT v1.5.3.3 Release Notes
+==========================
+
+Fixes since v1.5.3.2
+--------------------
+
+ * git-quiltimport did not like it when a patch described in the
+   series file does not exist.
+
+ * p4 importer missed executable bit in some cases.
+
+ * The default shell on some FreeBSD did not execute the
+   argument parsing code correctly and made git unusable.
+
+ * git-svn incorrectly spawned pager even when the user user
+   explicitly asked not to.
+
+ * sample post-receive hook overquoted the envelope sender
+   value.
+
+ * git-am got confused when the patch contained a change that is
+   only about type and not contents.
+
+ * git-mergetool did not show our and their version of the
+   conflicted file when started from a subdirectory of the
+   project.
+
+ * git-mergetool did not pass correct options when invoking diff3.
+
+ * git-log sometimes invoked underlying "diff" machinery
+   unnecessarily.
diff --git a/Documentation/RelNotes-1.5.3.4.txt b/Documentation/RelNotes-1.5.3.4.txt
new file mode 100644 (file)
index 0000000..b04b3a4
--- /dev/null
@@ -0,0 +1,35 @@
+GIT v1.5.3.4 Release Notes
+==========================
+
+Fixes since v1.5.3.3
+--------------------
+
+ * Change to "git-ls-files" in v1.5.3.3 that was introduced to support
+   partial commit of removal better had a segfaulting bug, which was
+   diagnosed and fixed by Keith and Carl.
+
+ * Performance improvements for rename detection has been backported
+   from the 'master' branch.
+
+ * "git-for-each-ref --format='%(numparent)'" was not working
+   correctly at all, and --format='%(parent)' was not working for
+   merge commits.
+
+ * Sample "post-receive-hook" incorrectly sent out push
+   notification e-mails marked as "From: " the committer of the
+   commit that happened to be at the tip of the branch that was
+   pushed, not from the person who pushed.
+
+ * "git-remote" did not exit non-zero status upon error.
+
+ * "git-add -i" did not respond very well to EOF from tty nor
+   bogus input.
+
+ * "git-rebase -i" squash subcommand incorrectly made the
+   author of later commit the author of resulting commit,
+   instead of taking from the first one in the squashed series.
+
+ * "git-stash apply --index" was not documented.
+
+ * autoconfiguration learned that "ar" command is found as "gas" on
+   some systems.
index 015910f27a450cdaec80f3bfc2679243126736c0..971fd9f16fba0bf07983a5aa9d016a20e059d7b6 100644 (file)
@@ -337,6 +337,12 @@ branch.<name>.merge::
        branch.<name>.merge to the desired branch, and use the special setting
        `.` (a period) for branch.<name>.remote.
 
+branch.<name>.mergeoptions::
+       Sets default options for merging into branch <name>. The syntax and
+       supported options are equal to that of gitlink:git-merge[1], but
+       option values containing whitespace characters are currently not
+       supported.
+
 clean.requireForce::
        A boolean to make git-clean do nothing unless given -f or -n.  Defaults
        to false.
@@ -439,6 +445,19 @@ gc.aggressiveWindow::
        algorithm used by 'git gc --aggressive'.  This defaults
        to 10.
 
+gc.auto::
+       When there are approximately more than this many loose
+       objects in the repository, `git gc --auto` will pack them.
+       Some Porcelain commands use this command to perform a
+       light-weight garbage collection from time to time.  Setting
+       this to 0 disables it.
+
+gc.autopacklimit::
+       When there are more than this many packs that are not
+       marked with `*.keep` file in the repository, `git gc
+       --auto` consolidates them into one larger pack.  Setting
+       this to 0 disables this.
+
 gc.packrefs::
        `git gc` does not run `git pack-refs` in a bare repository by
        default so that older dumb-transport clients can still fetch
@@ -579,7 +598,7 @@ merge.summary::
 
 merge.tool::
        Controls which merge resolution program is used by
-       gitlink:git-mergetool[l].  Valid values are: "kdiff3", "tkdiff",
+       gitlink:git-mergetool[1].  Valid values are: "kdiff3", "tkdiff",
        "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
 
 merge.verbosity::
index 4b4fd9a50639ad6e1517e426c027c5855c1b79f7..6b2590d0723ad94a45c9cae174935839df3331d8 100644 (file)
@@ -1459,7 +1459,8 @@ Although git is a truly distributed system, it is often
 convenient to organize your project with an informal hierarchy
 of developers. Linux kernel development is run this way. There
 is a nice illustration (page 17, "Merges to Mainline") in
-link:http://tinyurl.com/a2jdg[Randy Dunlap's presentation].
+link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf
+[Randy Dunlap's presentation].
 
 It should be stressed that this hierarchy is purely *informal*.
 There is nothing fundamental in git that enforces the "chain of
index 228ccaf10ab6bf7e4a3909d31ac31afbd2361d8c..b1f528ae8864e429a509365a79d16bb8341620e1 100644 (file)
 
 --ext-diff::
        Allow an external diff helper to be executed. If you set an
-       external diff driver with gitlink:gitattributes(5), you need
-       to use this option with gitlink:git-log(1) and friends.
+       external diff driver with gitlink:gitattributes[5], you need
+       to use this option with gitlink:git-log[1] and friends.
 
 --no-ext-diff::
        Disallow external diff drivers.
index 33bc31b0d4491169cdbfb06f3dc6c42ec84b1365..b7285bcdbc80a2aa574fa13050c00f1ce9975419 100644 (file)
@@ -26,6 +26,10 @@ It will start out with a head equal to the one given as <start-point>.
 If no <start-point> is given, the branch will be created with a head
 equal to that of the currently checked out branch.
 
+Note that this will create the new branch, but it will not switch the
+working tree to it; use "git checkout <newbranch>" to switch to the
+new branch.
+
 When a local branch is started off a remote branch, git can setup the
 branch so that gitlink:git-pull[1] will appropriately merge from that
 remote branch.  If this behavior is desired, it is possible to make it
@@ -91,6 +95,21 @@ OPTIONS
 --no-abbrev::
        Display the full sha1s in output listing rather than abbreviating them.
 
+--track::
+       Set up configuration so that git-pull will automatically
+       retrieve data from the remote branch.  Use this if you always
+       pull from the same remote branch into the new branch, or if you
+       don't want to use "git pull <repository> <refspec>" explicitly.  Set the
+       branch.autosetupmerge configuration variable to true if you
+       want git-checkout and git-branch to always behave as if
+       '--track' were given.
+
+--no-track::
+       When -b is given and a branch is created off a remote branch,
+       set up configuration so that git-pull will not retrieve data
+       from the remote branch, ignoring the branch.autosetupmerge
+       configuration variable.
+
 <branchname>::
        The name of the branch to create or delete.
        The new branch name must pass all checks defined by
index 5051e2bada6d2409f40a04bbd25a5201df7d836e..0cc6511bdf35f29ce946726485f71eeddddce56b 100644 (file)
@@ -103,14 +103,20 @@ We set a tag in R1 (lastR2bundle) after the previous such transport,
 and move it afterwards to help build the bundle.
 
 in R1 on A:
+
+------------
 $ git-bundle create mybundle master ^lastR2bundle
 $ git tag -f lastR2bundle master
+------------
 
 (move mybundle from A to B by some mechanism)
 
 in R2 on B:
+
+------------
 $ git-bundle verify mybundle
 $ git-fetch mybundle  refspec
+------------
 
 where refspec is refInBundle:localRef
 
@@ -124,9 +130,11 @@ Also, with something like this in your config:
 You can first sneakernet the bundle file to ~/tmp/file.bdl and
 then these commands:
 
+------------
 $ git ls-remote bundle
 $ git fetch bundle
 $ git pull bundle
+------------
 
 would treat it as if it is talking with a remote side over the
 network.
index 734928bf96c35fbe3f66c6693415e0a0d05f9412..2e58481ed68b0cc8540b7126a9c5c389e3f67197 100644 (file)
@@ -50,7 +50,9 @@ OPTIONS
 --track::
        When -b is given and a branch is created off a remote branch,
        set up configuration so that git-pull will automatically
-       retrieve data from the remote branch.  Set the
+       retrieve data from the remote branch.  Use this if you always
+       pull from the same remote branch into the new branch, or if you
+       don't want to use "git pull <repository> <refspec>" explicitly.  Set the
        branch.autosetupmerge configuration variable to true if you
        want git-checkout and git-branch to always behave as if
        '--track' were given.
index 6df8e8500450ad65a2de86e3daa0ab2f7692a2d2..f1f90cca62f61327a13b5ab41862596ca24a9b8f 100644 (file)
@@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to
 the object referred by the ref does not cause an error.  It
 returns an empty string instead.
 
+As a special case for the date-type fields, you may specify a format for
+the date by adding one of `:default`, `:relative`, `:short`, `:local`,
+`:iso8601` or `:rfc2822` to the end of the fieldname; e.g.
+`%(taggerdate:relative)`.
+
 
 EXAMPLES
 --------
index c7742ca9630b13d1eeef16d175f8ca840ddff4b0..b9d5660eacee03bde2360b97b80f3378972fe678 100644 (file)
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
 
 SYNOPSIS
 --------
-'git-gc' [--prune] [--aggressive]
+'git-gc' [--prune] [--aggressive] [--auto]
 
 DESCRIPTION
 -----------
@@ -43,6 +43,20 @@ OPTIONS
        persistent, so this option only needs to be used occasionally; every
        few hundred changesets or so.
 
+--auto::
+       With this option, `git gc` checks if there are too many
+       loose objects in the repository and runs
+       gitlink:git-repack[1] with `-d -l` option to pack them.
+       The threshold for loose objects is set with `gc.auto` configuration
+       variable, and can be disabled by setting it to 0.  Some
+       Porcelain commands use this after they perform operation
+       that could create many loose objects automatically.
+       Additionally, when there are too many packs are present,
+       they are consolidated into one larger pack by running
+       the `git-repack` command with `-A` option.  The
+       threshold for number of packs is set with
+       `gc.autopacklimit` configuration variable.
+
 Configuration
 -------------
 
index 144bc16ff26bbf1d311482e6be15fc034994ce49..bca4212e565c95f79a76a14cc4444e72e472a22c 100644 (file)
@@ -56,8 +56,12 @@ merge.verbosity::
        message if conflicts were detected. Level 1 outputs only
        conflicts, 2 outputs conflicts and file changes.  Level 5 and
        above outputs debugging information.  The default is level 2.
-       Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable.
+       Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
 
+branch.<name>.mergeoptions::
+       Sets default options for merging into branch <name>. The syntax and
+       supported options are equal to that of git-merge, but option values
+       containing whitespace characters are currently not supported.
 
 HOW MERGE WORKS
 ---------------
index 0858fa8a6326a0c457bc9b4bb63ae35d3001ef65..e8e75790fc21b403f428b8cca72ab42a072e1b38 100644 (file)
@@ -298,7 +298,7 @@ rebasing.
 If you want to fold two or more commits into one, replace the command
 "pick" with "squash" for the second and subsequent commit.  If the
 commits had different authors, it will attribute the squashed commit to
-the author of the last commit.
+the author of the first commit.
 
 In both cases, or when a "pick" does not succeed (because of merge
 errors), the loop will stop to let you fix things, and you can continue
index 94b9f177724993b800e5d0bd4e67f986bfffc188..027ba11bdb67ba540b63ec12e5a6b920cd2e5f7e 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git-remote'
 'git-remote' add [-t <branch>] [-m <branch>] [-f] [--mirror] <name> <url>
+'git-remote' rm <name>
 'git-remote' show <name>
 'git-remote' prune <name>
 'git-remote' update [group]
@@ -50,6 +51,11 @@ In mirror mode, enabled with `--mirror`, the refs will not be stored
 in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option
 only makes sense in bare repositories.
 
+'rm'::
+
+Remove the remote named <name>. All remote tracking branches and
+configuration settings for the remote are removed.
+
 'show'::
 
 Gives some information about the remote <name>.
index 1ec61affab506705499a80ec944b290896b993a0..3727776a0bf3196cd1d99ba4c2756767dca00567 100644 (file)
@@ -91,6 +91,11 @@ The --cc option must be repeated for each user you want on the cc list.
        `/usr/lib/sendmail` if such program is available, or
        `localhost` otherwise.
 
+--smtp-server-port::
+       Specifies a port different from the default port (SMTP
+       servers typically listen to smtp port 25 and ssmtp port
+       465).
+
 --smtp-user, --smtp-pass::
        Username and password for SMTP-AUTH. Defaults are the values of
        the configuration values 'sendemail.smtpuser' and
index 05f40cff6cbaa9d8a08176a5a601b7e43d17e5bf..5723bb06f087f62e463b110686b850987103140d 100644 (file)
@@ -63,7 +63,7 @@ show [<stash>]::
        it will accept any format known to `git-diff` (e.g., `git-stash show
        -p stash@\{1}` to view the second most recent stash in patch form).
 
-apply [<stash>]::
+apply [--index] [<stash>]::
 
        Restore the changes recorded in the stash on top of the current
        working tree state.  When no `<stash>` is given, applies the latest
@@ -71,6 +71,11 @@ apply [<stash>]::
 +
 This operation can fail with conflicts; you need to resolve them
 by hand in the working tree.
++
+If the `--index` option is used, then tries to reinstate not only the working
+tree's changes, but also the index's ones. However, this can fail, when you
+have conflicts (which are stored in the index, where you therefore can no
+longer apply the changes as they were originally).
 
 clear::
        Remove all the stashed states. Note that those states will then
index 2c48936fcd72c5276ae2fed217bd9b9564342f03..335e973a6a1d5350c1558eb68985ffe812c09212 100644 (file)
@@ -21,6 +21,9 @@ add::
        repository is cloned at the specified path, added to the
        changeset and registered in .gitmodules.   If no path is
        specified, the path is deduced from the repository specification.
+       If the repository url begins with ./ or ../, it is stored as
+       given but resolved as a relative path from the main project's
+       url when cloning.
 
 status::
        Show the status of the submodules. This will print the SHA-1 of the
index cb59639f777889cb909933886d31ebcce1bbd199..abce801e48386e07fb4014ad596d1b0ad1d382a1 100644 (file)
@@ -46,6 +46,7 @@ Documentation for older releases are available here:
 * link:v1.5.3/git.html[documentation for release 1.5.3]
 
 * release notes for
+  link:RelNotes-1.5.3.3.txt[1.5.3.3],
   link:RelNotes-1.5.3.2.txt[1.5.3.2],
   link:RelNotes-1.5.3.1.txt[1.5.3.1].
 
index d0e951ee6f1c391459f822b8ecbb4c1237b209db..20cf8ff81673265629028b49c34e0393063fd6b1 100644 (file)
@@ -145,17 +145,6 @@ sign `$` upon checkout.  Any byte sequence that begins with
 with `$Id$` upon check-in.
 
 
-Interaction between checkin/checkout attributes
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In the check-in codepath, the worktree file is first converted
-with `ident` (if specified), and then with `crlf` (again, if
-specified and applicable).
-
-In the check-out codepath, the blob content is first converted
-with `crlf`, and then `ident`.
-
-
 `filter`
 ^^^^^^^^
 
@@ -175,11 +164,10 @@ but makes the filter a no-op passthru.
 The content filtering is done to massage the content into a
 shape that is more convenient for the platform, filesystem, and
 the user to use.  The keyword here is "more convenient" and not
-"turning something unusable into usable".  In other words, it is
-"hanging yourself because we gave you a long rope" if your
-project uses filtering mechanism in such a way that it makes
-your project unusable unless the checkout is done with a
-specific filter in effect.
+"turning something unusable into usable".  In other words, the
+intent is that if someone unsets the filter driver definition,
+or does not have the appropriate filter program, the project
+should still be usable.
 
 
 Interaction between checkin/checkout attributes
index 9c83095693447a46f36299f417d71262890fb098..e8b8581f5280eefc470b216a80210f5f552a780a 100644 (file)
@@ -26,7 +26,7 @@ precedence, the last matching pattern decides the outcome):
 
  * Patterns read from a `.gitignore` file in the same directory
    as the path, or in any parent directory, with patterns in the
-   higher level files (up to the root) being overriden by those in
+   higher level files (up to the root) being overridden by those in
    lower level files down to the directory containing the file.
    These patterns match relative to the location of the
    `.gitignore` file.  A project normally includes such
index 58b954759610ca272b72a05db0cd14e2868ff301..f110162b0155b3b17bc3133c5f42504290c1de4d 100644 (file)
@@ -87,6 +87,20 @@ parameter, and is invoked after a commit is made.
 This hook is meant primarily for notification, and cannot affect
 the outcome of `git-commit`.
 
+post-checkout
+-----------
+
+This hook is invoked when a `git-checkout` is run after having updated the
+worktree.  The hook is given three parameters: the ref of the previous HEAD,
+the ref of the new HEAD (which may or may not have changed), and a flag
+indicating whether the checkout was a branch checkout (changing branches,
+flag=1) or a file checkout (retrieving a file from the index, flag=0).
+This hook cannot affect the outcome of `git-checkout`.
+
+This hook can be used to perform repository validity checks, auto-display
+differences from the previous HEAD if different, or set working dir metadata
+properties.
+
 post-merge
 -----------
 
index d64c259bb35d3140b371e8717a2553146d3f92f5..9f1fc825503a7c972fe162f4e2a87781e0f783f3 100644 (file)
        not autocommit, to give the user a chance to inspect and
        further tweak the merge result before committing.
 
+--commit::
+       Perform the merge and commit the result. This option can
+       be used to override --no-commit.
+
 --squash::
        Produce the working tree and index state as if a real
        merge happened, but do not actually make a commit or
        top of the current branch whose effect is the same as
        merging another branch (or more in case of an octopus).
 
+--no-squash::
+       Perform the merge and commit the result. This option can
+       be used to override --squash.
+
+--no-ff::
+       Generate a merge commit even if the merge resolved as a
+       fast-forward.
+
+--ff::
+       Do not generate a merge commit if the merge resolved as
+       a fast-forward, only update the branch pointer. This is
+       the default behavior of git-merge.
+
 -s <strategy>, \--strategy=<strategy>::
        Use the given merge strategy; can be supplied more than
        once to specify them in the order they should be tried.
index a085ca1d3919205d20923d73301f3d79ea1605af..c7fdf25e27c94e9b152592a28bf7acc64dfa0e07 100644 (file)
@@ -2856,8 +2856,7 @@ between two related tree objects, since it can ignore any entries with
 identical object names.
 
 (Note: in the presence of submodules, trees may also have commits as
-entries.   See gitlink:git-submodule[1] and gitlink:gitmodules.txt[1]
-for partial documentation.)
+entries.  See <<submodules>> for documentation.)
 
 Note that the files all have mode 644 or 755: git actually only pays
 attention to the executable bit.
@@ -3163,12 +3162,45 @@ information as long as you have the name of the tree that it described.
 Submodules
 ==========
 
-This tutorial explains how to create and publish a repository with submodules
-using the gitlink:git-submodule[1] command.
-
-Submodules maintain their own identity; the submodule support just stores the
-submodule repository location and commit ID, so other developers who clone the
-superproject can easily clone all the submodules at the same revision.
+Large projects are often composed of smaller, self-contained modules.  For
+example, an embedded Linux distribution's source tree would include every
+piece of software in the distribution with some local modifications; a movie
+player might need to build against a specific, known-working version of a
+decompression library; several independent programs might all share the same
+build scripts.
+
+With centralized revision control systems this is often accomplished by
+including every module in one single repository.  Developers can check out
+all modules or only the modules they need to work with.  They can even modify
+files across several modules in a single commit while moving things around
+or updating APIs and translations.
+
+Git does not allow partial checkouts, so duplicating this approach in Git
+would force developers to keep a local copy of modules they are not
+interested in touching.  Commits in an enormous checkout would be slower
+than you'd expect as Git would have to scan every directory for changes.
+If modules have a lot of local history, clones would take forever.
+
+On the plus side, distributed revision control systems can much better
+integrate with external sources.  In a centralized model, a single arbitrary
+snapshot of the external project is exported from its own revision control
+and then imported into the local revision control on a vendor branch.  All
+the history is hidden.  With distributed revision control you can clone the
+entire external history and much more easily follow development and re-merge
+local changes.
+
+Git's submodule support allows a repository to contain, as a subdirectory, a
+checkout of an external project.  Submodules maintain their own identity;
+the submodule support just stores the submodule repository location and
+commit ID, so other developers who clone the containing project
+("superproject") can easily clone all the submodules at the same revision.
+Partial checkouts of the superproject are possible: you can tell Git to
+clone none, some or all of the submodules.
+
+The gitlink:git-submodule[1] command is available since Git 1.5.3.  Users
+with Git 1.5.2 can look up the submodule commits in the repository and
+manually check them out; earlier versions won't recognize the submodules at
+all.
 
 To see how submodule support works, create (for example) four example
 repositories that can be used later as a submodule:
@@ -3213,8 +3245,8 @@ The `git submodule add` command does a couple of things:
 
 - It clones the submodule under the current directory and by default checks out
   the master branch.
-- It adds the submodule's clone path to the `.gitmodules` file and adds this
-  file to the index, ready to be committed.
+- It adds the submodule's clone path to the gitlink:gitmodules[5] file and
+  adds this file to the index, ready to be committed.
 - It adds the submodule's current commit ID to the index, ready to be
   committed.
 
@@ -4277,5 +4309,3 @@ Write a chapter on using plumbing and writing scripts.
 Alternates, clone -reference, etc.
 
 git unpack-objects -r for recovery
-
-submodules
diff --git a/INSTALL b/INSTALL
index 289b046a443c0647624607d471289b2c7dcd470b..f1eb4049b9f839c1b3d1aa5a4d7387d0e93f0f5c 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -79,6 +79,9 @@ Issues of note:
        - "perl" and POSIX-compliant shells are needed to use most of
          the barebone Porcelainish scripts.
 
+       - "cpio" is used by git-merge for saving and restoring the index,
+         and by git-clone when doing a local (possibly hardlinked) clone.
+
  - Some platform specific issues are dealt with Makefile rules,
    but depending on your specific installation, you may not
    have all the libraries/tools needed, or you may have
index c0d95dab0d965ff0ae961d72d7eae4c021c5c05a..e1bced56093dc08bbc260736637af3356b8598bb 100644 (file)
@@ -3,7 +3,6 @@
  */
 #include "cache.h"
 #include "commit.h"
-#include "strbuf.h"
 #include "tar.h"
 #include "builtin.h"
 #include "archive.h"
@@ -79,19 +78,6 @@ static void write_trailer(void)
        }
 }
 
-static void strbuf_append_string(struct strbuf *sb, const char *s)
-{
-       int slen = strlen(s);
-       int total = sb->len + slen;
-       if (total + 1 > sb->alloc) {
-               sb->buf = xrealloc(sb->buf, total + 1);
-               sb->alloc = total + 1;
-       }
-       memcpy(sb->buf + sb->len, s, slen);
-       sb->len = total;
-       sb->buf[total] = '\0';
-}
-
 /*
  * pax extended header records have the format "%u %s=%s\n".  %u contains
  * the size of the whole string (including the %u), the first %s is the
@@ -101,26 +87,17 @@ static void strbuf_append_string(struct strbuf *sb, const char *s)
 static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
                                      const char *value, unsigned int valuelen)
 {
-       char *p;
-       int len, total, tmp;
+       int len, tmp;
 
        /* "%u %s=%s\n" */
        len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
        for (tmp = len; tmp > 9; tmp /= 10)
                len++;
 
-       total = sb->len + len;
-       if (total > sb->alloc) {
-               sb->buf = xrealloc(sb->buf, total);
-               sb->alloc = total;
-       }
-
-       p = sb->buf;
-       p += sprintf(p, "%u %s=", len, keyword);
-       memcpy(p, value, valuelen);
-       p += valuelen;
-       *p = '\n';
-       sb->len = total;
+       strbuf_grow(sb, len);
+       strbuf_addf(sb, "%u %s=", len, keyword);
+       strbuf_add(sb, value, valuelen);
+       strbuf_addch(sb, '\n');
 }
 
 static unsigned int ustar_header_chksum(const struct ustar_header *header)
@@ -154,8 +131,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
        struct strbuf ext_header;
 
        memset(&header, 0, sizeof(header));
-       ext_header.buf = NULL;
-       ext_header.len = ext_header.alloc = 0;
+       strbuf_init(&ext_header, 0);
 
        if (!sha1) {
                *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
@@ -167,7 +143,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
                sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
        } else {
                if (verbose)
-                       fprintf(stderr, "%.*s\n", path->len, path->buf);
+                       fprintf(stderr, "%.*s\n", (int)path->len, path->buf);
                if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
                        *header.typeflag = TYPEFLAG_DIR;
                        mode = (mode | 0777) & ~tar_umask;
@@ -226,8 +202,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
 
        if (ext_header.len > 0) {
                write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
-               free(ext_header.buf);
        }
+       strbuf_release(&ext_header);
        write_blocked(&header, sizeof(header));
        if (S_ISREG(mode) && buffer && size > 0)
                write_blocked(buffer, size);
@@ -236,11 +212,11 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
 static void write_global_extended_header(const unsigned char *sha1)
 {
        struct strbuf ext_header;
-       ext_header.buf = NULL;
-       ext_header.len = ext_header.alloc = 0;
+
+       strbuf_init(&ext_header, 0);
        strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
        write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
-       free(ext_header.buf);
+       strbuf_release(&ext_header);
 }
 
 static int git_tar_config(const char *var, const char *value)
@@ -261,28 +237,17 @@ static int write_tar_entry(const unsigned char *sha1,
                            const char *base, int baselen,
                            const char *filename, unsigned mode, int stage)
 {
-       static struct strbuf path;
-       int filenamelen = strlen(filename);
+       static struct strbuf path = STRBUF_INIT;
        void *buffer;
        enum object_type type;
        unsigned long size;
 
-       if (!path.alloc) {
-               path.buf = xmalloc(PATH_MAX);
-               path.alloc = PATH_MAX;
-               path.len = path.eof = 0;
-       }
-       if (path.alloc < baselen + filenamelen + 1) {
-               free(path.buf);
-               path.buf = xmalloc(baselen + filenamelen + 1);
-               path.alloc = baselen + filenamelen + 1;
-       }
-       memcpy(path.buf, base, baselen);
-       memcpy(path.buf + baselen, filename, filenamelen);
-       path.len = baselen + filenamelen;
-       path.buf[path.len] = '\0';
+       strbuf_reset(&path);
+       strbuf_grow(&path, PATH_MAX);
+       strbuf_add(&path, base, baselen);
+       strbuf_addstr(&path, filename);
        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
-               strbuf_append_string(&path, "/");
+               strbuf_addch(&path, '/');
                buffer = NULL;
                size = 0;
        } else {
diff --git a/attr.c b/attr.c
index 129399310ae061a66527ce0b723cc0aeb30ef34c..92704a3f61c6d43d0377d86feb6598e68797022b 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -160,12 +160,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
                else if (!equals)
                        e->setto = ATTR__TRUE;
                else {
-                       char *value;
-                       int vallen = ep - equals;
-                       value = xmalloc(vallen);
-                       memcpy(value, equals+1, vallen-1);
-                       value[vallen-1] = 0;
-                       e->setto = value;
+                       e->setto = xmemdupz(equals + 1, ep - equals - 1);
                }
                e->attr = git_attr(cp, len);
        }
index 0d7d0ce4209114245ca07842d7d5a4546e5b6cfd..f9a65803d8dcbb9ae7eb3a3c61d8ac345b84d1cd 100644 (file)
@@ -71,12 +71,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
        baselen = common_prefix(pathspec);
        path = ".";
        base = "";
-       if (baselen) {
-               char *common = xmalloc(baselen + 1);
-               memcpy(common, *pathspec, baselen);
-               common[baselen] = 0;
-               path = base = common;
-       }
+       if (baselen)
+               path = base = xmemdupz(*pathspec, baselen);
 
        /* Read the directory and prune it */
        read_directory(dir, path, base, baselen, pathspec);
index 6777231c665044a486c16fd7fb11b53ed83a0474..047a60d1a43a24c3e7d086cf9f87cb36835e8495 100644 (file)
@@ -163,15 +163,14 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c
        fputs(pre, output);
        if (patch->old_name && patch->new_name &&
            strcmp(patch->old_name, patch->new_name)) {
-               write_name_quoted(NULL, 0, patch->old_name, 1, output);
+               quote_c_style(patch->old_name, NULL, output, 0);
                fputs(" => ", output);
-               write_name_quoted(NULL, 0, patch->new_name, 1, output);
-       }
-       else {
+               quote_c_style(patch->new_name, NULL, output, 0);
+       } else {
                const char *n = patch->new_name;
                if (!n)
                        n = patch->old_name;
-               write_name_quoted(NULL, 0, n, 1, output);
+               quote_c_style(n, NULL, output, 0);
        }
        fputs(post, output);
 }
@@ -179,36 +178,18 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c
 #define CHUNKSIZE (8192)
 #define SLOP (16)
 
-static void *read_patch_file(int fd, unsigned long *sizep)
+static void read_patch_file(struct strbuf *sb, int fd)
 {
-       unsigned long size = 0, alloc = CHUNKSIZE;
-       void *buffer = xmalloc(alloc);
-
-       for (;;) {
-               ssize_t nr = alloc - size;
-               if (nr < 1024) {
-                       alloc += CHUNKSIZE;
-                       buffer = xrealloc(buffer, alloc);
-                       nr = alloc - size;
-               }
-               nr = xread(fd, (char *) buffer + size, nr);
-               if (!nr)
-                       break;
-               if (nr < 0)
-                       die("git-apply: read returned %s", strerror(errno));
-               size += nr;
-       }
-       *sizep = size;
+       if (strbuf_read(sb, fd, 0) < 0)
+               die("git-apply: read returned %s", strerror(errno));
 
        /*
         * Make sure that we have some slop in the buffer
         * so that we can do speculative "memcmp" etc, and
         * see to it that it is NUL-filled.
         */
-       if (alloc < size + SLOP)
-               buffer = xrealloc(buffer, size + SLOP);
-       memset((char *) buffer + size, 0, SLOP);
-       return buffer;
+       strbuf_grow(sb, SLOP);
+       memset(sb->buf + sb->len, 0, SLOP);
 }
 
 static unsigned long linelen(const char *buffer, unsigned long size)
@@ -244,35 +225,33 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
 {
        int len;
        const char *start = line;
-       char *name;
 
        if (*line == '"') {
+               struct strbuf name;
+
                /* Proposed "new-style" GNU patch/diff format; see
                 * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
                 */
-               name = unquote_c_style(line, NULL);
-               if (name) {
-                       char *cp = name;
-                       while (p_value) {
+               strbuf_init(&name, 0);
+               if (!unquote_c_style(&name, line, NULL)) {
+                       char *cp;
+
+                       for (cp = name.buf; p_value; p_value--) {
                                cp = strchr(cp, '/');
                                if (!cp)
                                        break;
                                cp++;
-                               p_value--;
                        }
                        if (cp) {
                                /* name can later be freed, so we need
                                 * to memmove, not just return cp
                                 */
-                               memmove(name, cp, strlen(cp) + 1);
+                               strbuf_remove(&name, 0, cp - name.buf);
                                free(def);
-                               return name;
-                       }
-                       else {
-                               free(name);
-                               name = NULL;
+                               return strbuf_detach(&name, NULL);
                        }
                }
+               strbuf_release(&name);
        }
 
        for (;;) {
@@ -304,13 +283,10 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
                int deflen = strlen(def);
                if (deflen < len && !strncmp(start, def, deflen))
                        return def;
+               free(def);
        }
 
-       name = xmalloc(len + 1);
-       memcpy(name, start, len);
-       name[len] = 0;
-       free(def);
-       return name;
+       return xmemdupz(start, len);
 }
 
 static int count_slashes(const char *cp)
@@ -583,29 +559,30 @@ static const char *stop_at_slash(const char *line, int llen)
  */
 static char *git_header_name(char *line, int llen)
 {
-       int len;
        const char *name;
        const char *second = NULL;
+       size_t len;
 
        line += strlen("diff --git ");
        llen -= strlen("diff --git ");
 
        if (*line == '"') {
                const char *cp;
-               char *first = unquote_c_style(line, &second);
-               if (!first)
-                       return NULL;
+               struct strbuf first;
+               struct strbuf sp;
+
+               strbuf_init(&first, 0);
+               strbuf_init(&sp, 0);
+
+               if (unquote_c_style(&first, line, &second))
+                       goto free_and_fail1;
 
                /* advance to the first slash */
-               cp = stop_at_slash(first, strlen(first));
-               if (!cp || cp == first) {
-                       /* we do not accept absolute paths */
-               free_first_and_fail:
-                       free(first);
-                       return NULL;
-               }
-               len = strlen(cp+1);
-               memmove(first, cp+1, len+1); /* including NUL */
+               cp = stop_at_slash(first.buf, first.len);
+               /* we do not accept absolute paths */
+               if (!cp || cp == first.buf)
+                       goto free_and_fail1;
+               strbuf_remove(&first, 0, cp + 1 - first.buf);
 
                /* second points at one past closing dq of name.
                 * find the second name.
@@ -614,40 +591,40 @@ static char *git_header_name(char *line, int llen)
                        second++;
 
                if (line + llen <= second)
-                       goto free_first_and_fail;
+                       goto free_and_fail1;
                if (*second == '"') {
-                       char *sp = unquote_c_style(second, NULL);
-                       if (!sp)
-                               goto free_first_and_fail;
-                       cp = stop_at_slash(sp, strlen(sp));
-                       if (!cp || cp == sp) {
-                       free_both_and_fail:
-                               free(sp);
-                               goto free_first_and_fail;
-                       }
+                       if (unquote_c_style(&sp, second, NULL))
+                               goto free_and_fail1;
+                       cp = stop_at_slash(sp.buf, sp.len);
+                       if (!cp || cp == sp.buf)
+                               goto free_and_fail1;
                        /* They must match, otherwise ignore */
-                       if (strcmp(cp+1, first))
-                               goto free_both_and_fail;
-                       free(sp);
-                       return first;
+                       if (strcmp(cp + 1, first.buf))
+                               goto free_and_fail1;
+                       strbuf_release(&sp);
+                       return strbuf_detach(&first, NULL);
                }
 
                /* unquoted second */
                cp = stop_at_slash(second, line + llen - second);
                if (!cp || cp == second)
-                       goto free_first_and_fail;
+                       goto free_and_fail1;
                cp++;
-               if (line + llen - cp != len + 1 ||
-                   memcmp(first, cp, len))
-                       goto free_first_and_fail;
-               return first;
+               if (line + llen - cp != first.len + 1 ||
+                   memcmp(first.buf, cp, first.len))
+                       goto free_and_fail1;
+               return strbuf_detach(&first, NULL);
+
+       free_and_fail1:
+               strbuf_release(&first);
+               strbuf_release(&sp);
+               return NULL;
        }
 
        /* unquoted first name */
        name = stop_at_slash(line, llen);
        if (!name || name == line)
                return NULL;
-
        name++;
 
        /* since the first name is unquoted, a dq if exists must be
@@ -655,28 +632,30 @@ static char *git_header_name(char *line, int llen)
         */
        for (second = name; second < line + llen; second++) {
                if (*second == '"') {
-                       const char *cp = second;
+                       struct strbuf sp;
                        const char *np;
-                       char *sp = unquote_c_style(second, NULL);
-
-                       if (!sp)
-                               return NULL;
-                       np = stop_at_slash(sp, strlen(sp));
-                       if (!np || np == sp) {
-                       free_second_and_fail:
-                               free(sp);
-                               return NULL;
-                       }
+
+                       strbuf_init(&sp, 0);
+                       if (unquote_c_style(&sp, second, NULL))
+                               goto free_and_fail2;
+
+                       np = stop_at_slash(sp.buf, sp.len);
+                       if (!np || np == sp.buf)
+                               goto free_and_fail2;
                        np++;
-                       len = strlen(np);
-                       if (len < cp - name &&
+
+                       len = sp.buf + sp.len - np;
+                       if (len < second - name &&
                            !strncmp(np, name, len) &&
                            isspace(name[len])) {
                                /* Good */
-                               memmove(sp, np, len + 1);
-                               return sp;
+                               strbuf_remove(&sp, 0, np - sp.buf);
+                               return strbuf_detach(&sp, NULL);
                        }
-                       goto free_second_and_fail;
+
+               free_and_fail2:
+                       strbuf_release(&sp);
+                       return NULL;
                }
        }
 
@@ -700,10 +679,7 @@ static char *git_header_name(char *line, int llen)
                                        break;
                        }
                        if (second[len] == '\n' && !memcmp(name, second, len)) {
-                               char *ret = xmalloc(len + 1);
-                               memcpy(ret, name, len);
-                               ret[len] = 0;
-                               return ret;
+                               return xmemdupz(name, len);
                        }
                }
        }
@@ -1397,96 +1373,66 @@ static const char minuses[]= "--------------------------------------------------
 
 static void show_stats(struct patch *patch)
 {
-       const char *prefix = "";
-       char *name = patch->new_name;
-       char *qname = NULL;
-       int len, max, add, del, total;
-
-       if (!name)
-               name = patch->old_name;
+       struct strbuf qname;
+       char *cp = patch->new_name ? patch->new_name : patch->old_name;
+       int max, add, del;
 
-       if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
-               qname = xmalloc(len + 1);
-               quote_c_style(name, qname, NULL, 0);
-               name = qname;
-       }
+       strbuf_init(&qname, 0);
+       quote_c_style(cp, &qname, NULL, 0);
 
        /*
         * "scale" the filename
         */
-       len = strlen(name);
        max = max_len;
        if (max > 50)
                max = 50;
-       if (len > max) {
-               char *slash;
-               prefix = "...";
-               max -= 3;
-               name += len - max;
-               slash = strchr(name, '/');
-               if (slash)
-                       name = slash;
+
+       if (qname.len > max) {
+               cp = strchr(qname.buf + qname.len + 3 - max, '/');
+               if (!cp)
+                       cp = qname.buf + qname.len + 3 - max;
+               strbuf_splice(&qname, 0, cp - qname.buf, "...", 3);
+       }
+
+       if (patch->is_binary) {
+               printf(" %-*s |  Bin\n", max, qname.buf);
+               strbuf_release(&qname);
+               return;
        }
-       len = max;
+
+       printf(" %-*s |", max, qname.buf);
+       strbuf_release(&qname);
 
        /*
         * scale the add/delete
         */
-       max = max_change;
-       if (max + len > 70)
-               max = 70 - len;
-
+       max = max + max_change > 70 ? 70 - max : max_change;
        add = patch->lines_added;
        del = patch->lines_deleted;
-       total = add + del;
 
        if (max_change > 0) {
-               total = (total * max + max_change / 2) / max_change;
+               int total = ((add + del) * max + max_change / 2) / max_change;
                add = (add * max + max_change / 2) / max_change;
                del = total - add;
        }
-       if (patch->is_binary)
-               printf(" %s%-*s |  Bin\n", prefix, len, name);
-       else
-               printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
-                      len, name, patch->lines_added + patch->lines_deleted,
-                      add, pluses, del, minuses);
-       free(qname);
+       printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
+               add, pluses, del, minuses);
 }
 
-static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p)
+static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
 {
-       int fd;
-       unsigned long got;
-       unsigned long nsize;
-       char *nbuf;
-       unsigned long size = *size_p;
-       char *buf = *buf_p;
-
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
-               return readlink(path, buf, size) != size;
+               strbuf_grow(buf, st->st_size);
+               if (readlink(path, buf->buf, st->st_size) != st->st_size)
+                       return -1;
+               strbuf_setlen(buf, st->st_size);
+               return 0;
        case S_IFREG:
-               fd = open(path, O_RDONLY);
-               if (fd < 0)
-                       return error("unable to open %s", path);
-               got = 0;
-               for (;;) {
-                       ssize_t ret = xread(fd, buf + got, size - got);
-                       if (ret <= 0)
-                               break;
-                       got += ret;
-               }
-               close(fd);
-               nsize = got;
-               nbuf = convert_to_git(path, buf, &nsize);
-               if (nbuf) {
-                       free(buf);
-                       *buf_p = nbuf;
-                       *alloc_p = nsize;
-                       *size_p = nsize;
-               }
-               return got != size;
+               if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
+                       return error("unable to open or read %s", path);
+               convert_to_git(path, buf->buf, buf->len, buf);
+               return 0;
        default:
                return -1;
        }
@@ -1591,12 +1537,6 @@ static void remove_last_line(const char **rbuf, int *rsize)
        *rsize = offset + 1;
 }
 
-struct buffer_desc {
-       char *buffer;
-       unsigned long size;
-       unsigned long alloc;
-};
-
 static int apply_line(char *output, const char *patch, int plen)
 {
        /* plen is number of bytes to be copied from patch,
@@ -1673,10 +1613,9 @@ static int apply_line(char *output, const char *patch, int plen)
        return output + plen - buf;
 }
 
-static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)
+static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof)
 {
        int match_beginning, match_end;
-       char *buf = desc->buffer;
        const char *patch = frag->patch;
        int offset, size = frag->size;
        char *old = xmalloc(size);
@@ -1787,24 +1726,17 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
        lines = 0;
        pos = frag->newpos;
        for (;;) {
-               offset = find_offset(buf, desc->size,
+               offset = find_offset(buf->buf, buf->len,
                                     oldlines, oldsize, pos, &lines);
-               if (match_end && offset + oldsize != desc->size)
+               if (match_end && offset + oldsize != buf->len)
                        offset = -1;
                if (match_beginning && offset)
                        offset = -1;
                if (offset >= 0) {
-                       int diff;
-                       unsigned long size, alloc;
-
                        if (new_whitespace == strip_whitespace &&
-                           (desc->size - oldsize - offset == 0)) /* end of file? */
+                           (buf->len - oldsize - offset == 0)) /* end of file? */
                                newsize -= new_blank_lines_at_end;
 
-                       diff = newsize - oldsize;
-                       size = desc->size + diff;
-                       alloc = desc->alloc;
-
                        /* Warn if it was necessary to reduce the number
                         * of context lines.
                         */
@@ -1814,19 +1746,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                                        " to apply fragment at %d\n",
                                        leading, trailing, pos + lines);
 
-                       if (size > alloc) {
-                               alloc = size + 8192;
-                               desc->alloc = alloc;
-                               buf = xrealloc(buf, alloc);
-                               desc->buffer = buf;
-                       }
-                       desc->size = size;
-                       memmove(buf + offset + newsize,
-                               buf + offset + oldsize,
-                               size - offset - newsize);
-                       memcpy(buf + offset, newlines, newsize);
+                       strbuf_splice(buf, offset, oldsize, newlines, newsize);
                        offset = 0;
-
                        break;
                }
 
@@ -1862,12 +1783,11 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
        return offset;
 }
 
-static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
+static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
 {
-       unsigned long dst_size;
        struct fragment *fragment = patch->fragments;
-       void *data;
-       void *result;
+       unsigned long len;
+       void *dst;
 
        /* Binary patch is irreversible without the optional second hunk */
        if (apply_in_reverse) {
@@ -1878,29 +1798,24 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
                                     ? patch->new_name : patch->old_name);
                fragment = fragment->next;
        }
-       data = (void*) fragment->patch;
        switch (fragment->binary_patch_method) {
        case BINARY_DELTA_DEFLATED:
-               result = patch_delta(desc->buffer, desc->size,
-                                    data,
-                                    fragment->size,
-                                    &dst_size);
-               free(desc->buffer);
-               desc->buffer = result;
-               break;
+               dst = patch_delta(buf->buf, buf->len, fragment->patch,
+                                 fragment->size, &len);
+               if (!dst)
+                       return -1;
+               /* XXX patch_delta NUL-terminates */
+               strbuf_attach(buf, dst, len, len + 1);
+               return 0;
        case BINARY_LITERAL_DEFLATED:
-               free(desc->buffer);
-               desc->buffer = data;
-               dst_size = fragment->size;
-               break;
+               strbuf_reset(buf);
+               strbuf_add(buf, fragment->patch, fragment->size);
+               return 0;
        }
-       if (!desc->buffer)
-               return -1;
-       desc->size = desc->alloc = dst_size;
-       return 0;
+       return -1;
 }
 
-static int apply_binary(struct buffer_desc *desc, struct patch *patch)
+static int apply_binary(struct strbuf *buf, struct patch *patch)
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
@@ -1919,7 +1834,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
                /* See if the old one matches what the patch
                 * applies to.
                 */
-               hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
+               hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
                        return error("the patch applies to '%s' (%s), "
                                     "which does not match the "
@@ -1928,16 +1843,14 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
        }
        else {
                /* Otherwise, the old one must be empty. */
-               if (desc->size)
+               if (buf->len)
                        return error("the patch applies to an empty "
                                     "'%s' but it is not empty", name);
        }
 
        get_sha1_hex(patch->new_sha1_prefix, sha1);
        if (is_null_sha1(sha1)) {
-               free(desc->buffer);
-               desc->alloc = desc->size = 0;
-               desc->buffer = NULL;
+               strbuf_release(buf);
                return 0; /* deletion patch */
        }
 
@@ -1945,43 +1858,44 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
                /* We already have the postimage */
                enum object_type type;
                unsigned long size;
+               char *result;
 
-               free(desc->buffer);
-               desc->buffer = read_sha1_file(sha1, &type, &size);
-               if (!desc->buffer)
+               result = read_sha1_file(sha1, &type, &size);
+               if (!result)
                        return error("the necessary postimage %s for "
                                     "'%s' cannot be read",
                                     patch->new_sha1_prefix, name);
-               desc->alloc = desc->size = size;
-       }
-       else {
-               /* We have verified desc matches the preimage;
+               /* XXX read_sha1_file NUL-terminates */
+               strbuf_attach(buf, result, size, size + 1);
+       else {
+               /* We have verified buf matches the preimage;
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
-               if (apply_binary_fragment(desc, patch))
+               if (apply_binary_fragment(buf, patch))
                        return error("binary patch does not apply to '%s'",
                                     name);
 
                /* verify that the result matches */
-               hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
+               hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
-                       return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
+                       return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
+                               name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
 
        return 0;
 }
 
-static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
+static int apply_fragments(struct strbuf *buf, struct patch *patch)
 {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
 
        if (patch->is_binary)
-               return apply_binary(desc, patch);
+               return apply_binary(buf, patch);
 
        while (frag) {
-               if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
+               if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {
                        error("patch failed: %s:%ld", name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
@@ -1992,76 +1906,56 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
        return 0;
 }
 
-static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
-                               unsigned long *size_p)
+static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
 {
        if (!ce)
                return 0;
 
        if (S_ISGITLINK(ntohl(ce->ce_mode))) {
-               *buf_p = xmalloc(100);
-               *size_p = snprintf(*buf_p, 100,
-                       "Subproject commit %s\n", sha1_to_hex(ce->sha1));
+               strbuf_grow(buf, 100);
+               strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
        } else {
                enum object_type type;
-               *buf_p = read_sha1_file(ce->sha1, &type, size_p);
-               if (!*buf_p)
+               unsigned long sz;
+               char *result;
+
+               result = read_sha1_file(ce->sha1, &type, &sz);
+               if (!result)
                        return -1;
+               /* XXX read_sha1_file NUL-terminates */
+               strbuf_attach(buf, result, sz, sz + 1);
        }
        return 0;
 }
 
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
-       char *buf;
-       unsigned long size, alloc;
-       struct buffer_desc desc;
+       struct strbuf buf;
 
-       size = 0;
-       alloc = 0;
-       buf = NULL;
+       strbuf_init(&buf, 0);
        if (cached) {
-               if (read_file_or_gitlink(ce, &buf, &size))
+               if (read_file_or_gitlink(ce, &buf))
                        return error("read of %s failed", patch->old_name);
-               alloc = size;
        } else if (patch->old_name) {
                if (S_ISGITLINK(patch->old_mode)) {
-                       if (ce)
-                               read_file_or_gitlink(ce, &buf, &size);
-                       else {
+                       if (ce) {
+                               read_file_or_gitlink(ce, &buf);
+                       else {
                                /*
                                 * There is no way to apply subproject
                                 * patch without looking at the index.
                                 */
                                patch->fragments = NULL;
-                               size = 0;
                        }
-               }
-               else {
-                       size = xsize_t(st->st_size);
-                       alloc = size + 8192;
-                       buf = xmalloc(alloc);
-                       if (read_old_data(st, patch->old_name,
-                                         &buf, &alloc, &size))
-                               return error("read of %s failed",
-                                            patch->old_name);
+               } else {
+                       if (read_old_data(st, patch->old_name, &buf))
+                               return error("read of %s failed", patch->old_name);
                }
        }
 
-       desc.size = size;
-       desc.alloc = alloc;
-       desc.buffer = buf;
-
-       if (apply_fragments(&desc, patch) < 0)
+       if (apply_fragments(&buf, patch) < 0)
                return -1; /* note with --reject this succeeds. */
-
-       /* NUL terminate the result */
-       if (desc.alloc <= desc.size)
-               desc.buffer = xrealloc(desc.buffer, desc.size + 1);
-       desc.buffer[desc.size] = 0;
-
-       patch->result = desc.buffer;
-       patch->resultsize = desc.size;
+       patch->result = strbuf_detach(&buf, &patch->resultsize);
 
        if (0 < patch->is_delete && patch->resultsize)
                return error("removal patch leaves file contents");
@@ -2315,13 +2209,8 @@ static void numstat_patch_list(struct patch *patch)
                if (patch->is_binary)
                        printf("-\t-\t");
                else
-                       printf("%d\t%d\t",
-                              patch->lines_added, patch->lines_deleted);
-               if (line_termination && quote_c_style(name, NULL, NULL, 0))
-                       quote_c_style(name, NULL, stdout, 0);
-               else
-                       fputs(name, stdout);
-               putchar(line_termination);
+                       printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
+               write_name_quoted(name, stdout, line_termination);
        }
 }
 
@@ -2486,7 +2375,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
 {
        int fd;
-       char *nbuf;
+       struct strbuf nbuf;
 
        if (S_ISGITLINK(mode)) {
                struct stat st;
@@ -2505,23 +2394,16 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        if (fd < 0)
                return -1;
 
-       nbuf = convert_to_working_tree(path, buf, &size);
-       if (nbuf)
-               buf = nbuf;
-
-       while (size) {
-               int written = xwrite(fd, buf, size);
-               if (written < 0)
-                       die("writing file %s: %s", path, strerror(errno));
-               if (!written)
-                       die("out of space writing file %s", path);
-               buf += written;
-               size -= written;
+       strbuf_init(&nbuf, 0);
+       if (convert_to_working_tree(path, buf, size, &nbuf)) {
+               size = nbuf.len;
+               buf  = nbuf.buf;
        }
+       write_or_die(fd, buf, size);
+       strbuf_release(&nbuf);
+
        if (close(fd) < 0)
                die("closing file %s: %s", path, strerror(errno));
-       if (nbuf)
-               free(nbuf);
        return 0;
 }
 
@@ -2754,22 +2636,22 @@ static void prefix_patches(struct patch *p)
 
 static int apply_patch(int fd, const char *filename, int inaccurate_eof)
 {
-       unsigned long offset, size;
-       char *buffer = read_patch_file(fd, &size);
+       size_t offset;
+       struct strbuf buf;
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
 
+       strbuf_init(&buf, 0);
        patch_input_file = filename;
-       if (!buffer)
-               return -1;
+       read_patch_file(&buf, fd);
        offset = 0;
-       while (size > 0) {
+       while (offset < buf.len) {
                struct patch *patch;
                int nr;
 
                patch = xcalloc(1, sizeof(*patch));
                patch->inaccurate_eof = inaccurate_eof;
-               nr = parse_chunk(buffer + offset, size, patch);
+               nr = parse_chunk(buf.buf + offset, buf.len, patch);
                if (nr < 0)
                        break;
                if (apply_in_reverse)
@@ -2787,7 +2669,6 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
                        skipped_patch++;
                }
                offset += nr;
-               size -= nr;
        }
 
        if (whitespace_error && (new_whitespace == error_on_whitespace))
@@ -2822,7 +2703,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
        if (summary)
                summary_patch_list(list);
 
-       free(buffer);
+       strbuf_release(&buf);
        return 0;
 }
 
index a90c65ce54a1c3e415b46465a2b4c7bfdffca9ff..04385dea05110053db72e30a77e6d4a10bc7875b 100644 (file)
@@ -81,95 +81,79 @@ static int run_remote_archiver(const char *remote, int argc,
        return !!rv;
 }
 
-static void *format_subst(const struct commit *commit, const char *format,
-                          unsigned long *sizep)
+static void format_subst(const struct commit *commit,
+                         const char *src, size_t len,
+                         struct strbuf *buf)
 {
-       unsigned long len = *sizep, result_len = 0;
-       const char *a = format;
-       char *result = NULL;
+       char *to_free = NULL;
+       struct strbuf fmt;
 
+       if (src == buf->buf)
+               to_free = strbuf_detach(buf, NULL);
+       strbuf_init(&fmt, 0);
        for (;;) {
                const char *b, *c;
-               char *fmt, *formatted = NULL;
-               unsigned long a_len, fmt_len, formatted_len, allocated = 0;
 
-               b = memmem(a, len, "$Format:", 8);
-               if (!b || a + len < b + 9)
+               b = memmem(src, len, "$Format:", 8);
+               if (!b || src + len < b + 9)
                        break;
                c = memchr(b + 8, '$', len - 8);
                if (!c)
                        break;
 
-               a_len = b - a;
-               fmt_len = c - b - 8;
-               fmt = xmalloc(fmt_len + 1);
-               memcpy(fmt, b + 8, fmt_len);
-               fmt[fmt_len] = '\0';
-
-               formatted_len = format_commit_message(commit, fmt, &formatted,
-                                                     &allocated);
-               free(fmt);
-               result = xrealloc(result, result_len + a_len + formatted_len);
-               memcpy(result + result_len, a, a_len);
-               memcpy(result + result_len + a_len, formatted, formatted_len);
-               result_len += a_len + formatted_len;
-               len -= c + 1 - a;
-               a = c + 1;
-       }
+               strbuf_reset(&fmt);
+               strbuf_add(&fmt, b + 8, c - b - 8);
 
-       if (result && len) {
-               result = xrealloc(result, result_len + len);
-               memcpy(result + result_len, a, len);
-               result_len += len;
+               strbuf_add(buf, src, b - src);
+               format_commit_message(commit, fmt.buf, buf);
+               len -= c + 1 - src;
+               src  = c + 1;
        }
-
-       *sizep = result_len;
-
-       return result;
+       strbuf_add(buf, src, len);
+       strbuf_release(&fmt);
+       free(to_free);
 }
 
-static void *convert_to_archive(const char *path,
-                                const void *src, unsigned long *sizep,
-                                const struct commit *commit)
+static int convert_to_archive(const char *path,
+                              const void *src, size_t len,
+                              struct strbuf *buf,
+                              const struct commit *commit)
 {
        static struct git_attr *attr_export_subst;
        struct git_attr_check check[1];
 
        if (!commit)
-               return NULL;
+               return 0;
 
-        if (!attr_export_subst)
-                attr_export_subst = git_attr("export-subst", 12);
+       if (!attr_export_subst)
+               attr_export_subst = git_attr("export-subst", 12);
 
        check[0].attr = attr_export_subst;
        if (git_checkattr(path, ARRAY_SIZE(check), check))
-               return NULL;
+               return 0;
        if (!ATTR_TRUE(check[0].value))
-               return NULL;
+               return 0;
 
-       return format_subst(commit, src, sizep);
+       format_subst(commit, src, len, buf);
+       return 1;
 }
 
 void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
                            unsigned int mode, enum object_type *type,
-                           unsigned long *size,
+                           unsigned long *sizep,
                            const struct commit *commit)
 {
-       void *buffer, *converted;
+       void *buffer;
 
-       buffer = read_sha1_file(sha1, type, size);
+       buffer = read_sha1_file(sha1, type, sizep);
        if (buffer && S_ISREG(mode)) {
-               converted = convert_to_working_tree(path, buffer, size);
-               if (converted) {
-                       free(buffer);
-                       buffer = converted;
-               }
+               struct strbuf buf;
 
-               converted = convert_to_archive(path, buffer, size, commit);
-               if (converted) {
-                       free(buffer);
-                       buffer = converted;
-               }
+               strbuf_init(&buf, 0);
+               strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
+               convert_to_working_tree(path, buf.buf, buf.len, &buf);
+               convert_to_archive(path, buf.buf, buf.len, &buf, commit);
+               buffer = strbuf_detach(&buf, sizep);
        }
 
        return buffer;
index dc88a953a519e975f426f6158b73542a2ea120dd..e3112a2d5bb2158bcc189942090a936c3f2b7cf6 100644 (file)
@@ -1430,8 +1430,7 @@ static void get_commit_info(struct commit *commit,
 static void write_filename_info(const char *path)
 {
        printf("filename ");
-       write_name_quoted(NULL, 0, path, 1, stdout);
-       putchar('\n');
+       write_name_quoted(path, stdout, '\n');
 }
 
 /*
@@ -2001,11 +2000,9 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
        struct commit *commit;
        struct origin *origin;
        unsigned char head_sha1[20];
-       char *buf;
+       struct strbuf buf;
        const char *ident;
-       int fd;
        time_t now;
-       unsigned long fin_size;
        int size, len;
        struct cache_entry *ce;
        unsigned mode;
@@ -2023,9 +2020,11 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
 
        origin = make_origin(commit, path);
 
+       strbuf_init(&buf, 0);
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                const char *read_from;
+               unsigned long fin_size;
 
                if (contents_from) {
                        if (stat(contents_from, &st) < 0)
@@ -2038,19 +2037,16 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
                        read_from = path;
                }
                fin_size = xsize_t(st.st_size);
-               buf = xmalloc(fin_size+1);
                mode = canon_mode(st.st_mode);
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
-                       fd = open(read_from, O_RDONLY);
-                       if (fd < 0)
-                               die("cannot open %s", read_from);
-                       if (read_in_full(fd, buf, fin_size) != fin_size)
-                               die("cannot read %s", read_from);
+                       if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
+                               die("cannot open or read %s", read_from);
                        break;
                case S_IFLNK:
-                       if (readlink(read_from, buf, fin_size+1) != fin_size)
+                       if (readlink(read_from, buf.buf, buf.alloc) != fin_size)
                                die("cannot readlink %s", read_from);
+                       buf.len = fin_size;
                        break;
                default:
                        die("unsupported file type %s", read_from);
@@ -2059,26 +2055,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
        else {
                /* Reading from stdin */
                contents_from = "standard input";
-               buf = NULL;
-               fin_size = 0;
                mode = 0;
-               while (1) {
-                       ssize_t cnt = 8192;
-                       buf = xrealloc(buf, fin_size + cnt);
-                       cnt = xread(0, buf + fin_size, cnt);
-                       if (cnt < 0)
-                               die("read error %s from stdin",
-                                   strerror(errno));
-                       if (!cnt)
-                               break;
-                       fin_size += cnt;
-               }
-               buf = xrealloc(buf, fin_size + 1);
+               if (strbuf_read(&buf, 0, 0) < 0)
+                       die("read error %s from stdin", strerror(errno));
        }
-       buf[fin_size] = 0;
-       origin->file.ptr = buf;
-       origin->file.size = fin_size;
-       pretend_sha1_file(buf, fin_size, OBJ_BLOB, origin->blob_sha1);
+       origin->file.ptr = buf.buf;
+       origin->file.size = buf.len;
+       pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
        commit->util = origin;
 
        /*
index 5f5c1823cb27cf1173c87f43bd0d2de95a06bf46..3da8b55b8f49c52794d337a026a4f5a5ae727b24 100644 (file)
@@ -268,23 +268,22 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
        }
 
        if (verbose) {
-               char *subject = NULL;
-               unsigned long subject_len = 0;
+               struct strbuf subject;
                const char *sub = " **** invalid ref ****";
 
+               strbuf_init(&subject, 0);
+
                commit = lookup_commit(item->sha1);
                if (commit && !parse_commit(commit)) {
-                       pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
-                                           &subject, &subject_len, 0,
-                                           NULL, NULL, 0);
-                       sub = subject;
+                       pretty_print_commit(CMIT_FMT_ONELINE, commit,
+                                           &subject, 0, NULL, NULL, 0);
+                       sub = subject.buf;
                }
                printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
                       maxwidth, item->name,
                       branch_get_color(COLOR_BRANCH_RESET),
                       find_unique_abbrev(item->sha1, abbrev), sub);
-               if (subject)
-                       free(subject);
+               strbuf_release(&subject);
        } else {
                printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
                       branch_get_color(COLOR_BRANCH_RESET));
index d94973379cee27c47426b61a13ae0f90508fed9b..6afdfa10a166a97c1115b1430221262228622c5c 100644 (file)
@@ -56,7 +56,7 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
                        else if (ATTR_UNSET(value))
                                value = "unspecified";
 
-                       write_name_quoted("", 0, argv[i], 1, stdout);
+                       quote_c_style(argv[i], NULL, stdout, 0);
                        printf(": %s: %s\n", argv[j+1], value);
                }
        }
index 75377b9cab75ac75c6d5d7f79fdcf64bdb27cff2..70d619da8d051f8d739911cfbbf8a5255787ec07 100644 (file)
@@ -38,7 +38,6 @@
  */
 #include "builtin.h"
 #include "cache.h"
-#include "strbuf.h"
 #include "quote.h"
 #include "cache-tree.h"
 
@@ -67,9 +66,7 @@ static void write_tempfile_record(const char *name, int prefix_length)
                fputs(topath[checkout_stage], stdout);
 
        putchar('\t');
-       write_name_quoted("", 0, name + prefix_length,
-               line_termination, stdout);
-       putchar(line_termination);
+       write_name_quoted(name + prefix_length, stdout, line_termination);
 
        for (i = 0; i < 4; i++) {
                topath[i][0] = 0;
@@ -271,28 +268,28 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        }
 
        if (read_from_stdin) {
-               struct strbuf buf;
+               struct strbuf buf, nbuf;
+
                if (all)
                        die("git-checkout-index: don't mix '--all' and '--stdin'");
-               strbuf_init(&buf);
-               while (1) {
-                       char *path_name;
-                       const char *p;
 
-                       read_line(&buf, stdin, line_termination);
-                       if (buf.eof)
-                               break;
-                       if (line_termination && buf.buf[0] == '"')
-                               path_name = unquote_c_style(buf.buf, NULL);
-                       else
-                               path_name = buf.buf;
-                       p = prefix_path(prefix, prefix_length, path_name);
+               strbuf_init(&buf, 0);
+               strbuf_init(&nbuf, 0);
+               while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
+                       const char *p;
+                       if (line_termination && buf.buf[0] == '"') {
+                               strbuf_reset(&nbuf);
+                               if (unquote_c_style(&nbuf, buf.buf, NULL))
+                                       die("line is badly quoted");
+                               strbuf_swap(&buf, &nbuf);
+                       }
+                       p = prefix_path(prefix, prefix_length, buf.buf);
                        checkout_file(p, prefix_length);
-                       if (p < path_name || p > path_name + strlen(path_name))
+                       if (p < buf.buf || p > buf.buf + buf.len)
                                free((char *)p);
-                       if (path_name != buf.buf)
-                               free(path_name);
                }
+               strbuf_release(&nbuf);
+               strbuf_release(&buf);
        }
 
        if (all)
index ccbcbe30dab634d9ff393f1e849c18388b9d53d4..88b0ab36eba6ded8f1a39e0d4c83122b7e026874 100644 (file)
 /*
  * FIXME! Share the code with "write-tree.c"
  */
-static void init_buffer(char **bufp, unsigned int *sizep)
-{
-       *bufp = xmalloc(BLOCKING);
-       *sizep = 0;
-}
-
-static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
-{
-       char one_line[2048];
-       va_list args;
-       int len;
-       unsigned long alloc, size, newsize;
-       char *buf;
-
-       va_start(args, fmt);
-       len = vsnprintf(one_line, sizeof(one_line), fmt, args);
-       va_end(args);
-       size = *sizep;
-       newsize = size + len + 1;
-       alloc = (size + 32767) & ~32767;
-       buf = *bufp;
-       if (newsize > alloc) {
-               alloc = (newsize + 32767) & ~32767;
-               buf = xrealloc(buf, alloc);
-               *bufp = buf;
-       }
-       *sizep = newsize - 1;
-       memcpy(buf + size, one_line, len);
-}
-
 static void check_valid(unsigned char *sha1, enum object_type expect)
 {
        enum object_type type = sha1_object_info(sha1, NULL);
@@ -87,9 +57,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        int parents = 0;
        unsigned char tree_sha1[20];
        unsigned char commit_sha1[20];
-       char comment[1000];
-       char *buffer;
-       unsigned int size;
+       struct strbuf buffer;
        int encoding_is_utf8;
 
        git_config(git_default_config);
@@ -118,8 +86,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        /* Not having i18n.commitencoding is the same as having utf-8 */
        encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
 
-       init_buffer(&buffer, &size);
-       add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1));
+       strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
+       strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1));
 
        /*
         * NOTE! This ordering means that the same exact tree merged with a
@@ -127,26 +95,24 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
         * if everything else stays the same.
         */
        for (i = 0; i < parents; i++)
-               add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i]));
+               strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i]));
 
        /* Person/date information */
-       add_buffer(&buffer, &size, "author %s\n", git_author_info(1));
-       add_buffer(&buffer, &size, "committer %s\n", git_committer_info(1));
+       strbuf_addf(&buffer, "author %s\n", git_author_info(1));
+       strbuf_addf(&buffer, "committer %s\n", git_committer_info(1));
        if (!encoding_is_utf8)
-               add_buffer(&buffer, &size,
-                               "encoding %s\n", git_commit_encoding);
-       add_buffer(&buffer, &size, "\n");
+               strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+       strbuf_addch(&buffer, '\n');
 
        /* And add the comment */
-       while (fgets(comment, sizeof(comment), stdin) != NULL)
-               add_buffer(&buffer, &size, "%s", comment);
+       if (strbuf_read(&buffer, 0, 0) < 0)
+               die("git-commit-tree: read returned %s", strerror(errno));
 
        /* And check the encoding */
-       buffer[size] = '\0';
-       if (encoding_is_utf8 && !is_utf8(buffer))
+       if (encoding_is_utf8 && !is_utf8(buffer.buf))
                fprintf(stderr, commit_utf8_warn);
 
-       if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) {
+       if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) {
                printf("%s\n", sha1_to_hex(commit_sha1));
                return 0;
        }
index 24c7e6f7dbcee4d02daad7182bb7587c4c9c3418..1e43d792216248c1abe3504c239ccd325f8d5ef1 100644 (file)
@@ -3,26 +3,14 @@
 #include "refs.h"
 #include "commit.h"
 
-#define CHUNK_SIZE 1024
-
 static char *get_stdin(void)
 {
-       size_t offset = 0;
-       char *data = xmalloc(CHUNK_SIZE);
-
-       while (1) {
-               ssize_t cnt = xread(0, data + offset, CHUNK_SIZE);
-               if (cnt < 0)
-                       die("error reading standard input: %s",
-                           strerror(errno));
-               if (cnt == 0) {
-                       data[offset] = 0;
-                       break;
-               }
-               offset += cnt;
-               data = xrealloc(data, offset + CHUNK_SIZE);
+       struct strbuf buf;
+       strbuf_init(&buf, 0);
+       if (strbuf_read(&buf, 0, 1024) < 0) {
+               die("error reading standard input: %s", strerror(errno));
        }
-       return data;
+       return strbuf_detach(&buf, NULL);
 }
 
 static void show_new(enum object_type type, unsigned char *sha1_new)
@@ -234,19 +222,15 @@ static char *find_local_name(const char *remote_name, const char *refs,
                }
                if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
                        const char *local_part = ref + len + 1;
-                       char *ret;
                        int retlen;
 
                        if (!next)
                                retlen = strlen(local_part);
                        else
                                retlen = next - local_part;
-                       ret = xmalloc(retlen + 1);
-                       memcpy(ret, local_part, retlen);
-                       ret[retlen] = 0;
                        *force_p = single_force;
                        *not_for_merge_p = not_for_merge;
-                       return ret;
+                       return xmemdupz(local_part, retlen);
                }
                ref = next;
        }
index ae60fccea74077b4d2456919d2f911f8a257c5b4..8a3c962f8920bb883287053c850adf78312ff19b 100644 (file)
@@ -140,12 +140,10 @@ static int handle_line(char *line)
        if (!strcmp(".", src) || !strcmp(src, origin)) {
                int len = strlen(origin);
                if (origin[0] == '\'' && origin[len - 1] == '\'') {
-                       char *new_origin = xmalloc(len - 1);
-                       memcpy(new_origin, origin + 1, len - 2);
-                       new_origin[len - 2] = 0;
-                       origin = new_origin;
-               } else
+                       origin = xmemdupz(origin + 1, len - 2);
+               } else {
                        origin = xstrdup(origin);
+               }
        } else {
                char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
                sprintf(new_origin, "%s of %s", origin, src);
@@ -211,14 +209,11 @@ static void shortlog(const char *name, unsigned char *sha1,
 
                bol += 2;
                eol = strchr(bol, '\n');
-
                if (eol) {
-                       int len = eol - bol;
-                       oneline = xmalloc(len + 1);
-                       memcpy(oneline, bol, len);
-                       oneline[len] = 0;
-               } else
+                       oneline = xmemdupz(bol, eol - bol);
+               } else {
                        oneline = xstrdup(bol);
+               }
                append_to_list(&subjects, oneline, NULL);
        }
 
index 0afa1c5c41e79a05ddebb7d874956163510daf0b..c74ef2800c839a5537707c5c64aa55acb2a9efad 100644 (file)
@@ -43,7 +43,7 @@ static struct {
        { "objectsize", FIELD_ULONG },
        { "objectname" },
        { "tree" },
-       { "parent" }, /* NEEDSWORK: how to address 2nd and later parents? */
+       { "parent" },
        { "numparent", FIELD_ULONG },
        { "object" },
        { "type" },
@@ -87,7 +87,6 @@ static int used_atom_cnt, sort_atom_limit, need_tagged;
 static int parse_atom(const char *atom, const char *ep)
 {
        const char *sp;
-       char *n;
        int i, at;
 
        sp = atom;
@@ -106,7 +105,16 @@ static int parse_atom(const char *atom, const char *ep)
        /* Is the atom a valid one? */
        for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
                int len = strlen(valid_atom[i].name);
-               if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))
+               /*
+                * If the atom name has a colon, strip it and everything after
+                * it off - it specifies the format for this entry, and
+                * shouldn't be used for checking against the valid_atom
+                * table.
+                */
+               const char *formatp = strchr(sp, ':');
+               if (!formatp || ep < formatp)
+                       formatp = ep;
+               if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
                        break;
        }
 
@@ -120,10 +128,7 @@ static int parse_atom(const char *atom, const char *ep)
                             (sizeof *used_atom) * used_atom_cnt);
        used_atom_type = xrealloc(used_atom_type,
                                  (sizeof(*used_atom_type) * used_atom_cnt));
-       n = xmalloc(ep - atom + 1);
-       memcpy(n, atom, ep - atom);
-       n[ep-atom] = 0;
-       used_atom[at] = n;
+       used_atom[at] = xmemdupz(atom, ep - atom);
        used_atom_type[at] = valid_atom[i].cmp_type;
        return at;
 }
@@ -262,24 +267,26 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
                }
                if (!strcmp(name, "numparent")) {
                        char *s = xmalloc(40);
+                       v->ul = num_parents(commit);
                        sprintf(s, "%lu", v->ul);
                        v->s = s;
-                       v->ul = num_parents(commit);
                }
                else if (!strcmp(name, "parent")) {
                        int num = num_parents(commit);
                        int i;
                        struct commit_list *parents;
-                       char *s = xmalloc(42 * num);
+                       char *s = xmalloc(41 * num + 1);
                        v->s = s;
                        for (i = 0, parents = commit->parents;
                             parents;
-                            parents = parents->next, i = i + 42) {
+                            parents = parents->next, i = i + 41) {
                                struct commit *parent = parents->item;
                                strcpy(s+i, sha1_to_hex(parent->object.sha1));
                                if (parents->next)
                                        s[i+40] = ' ';
                        }
+                       if (!i)
+                               *s = '\0';
                }
        }
 }
@@ -305,54 +312,50 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un
 static const char *copy_line(const char *buf)
 {
        const char *eol = strchr(buf, '\n');
-       char *line;
-       int len;
        if (!eol)
                return "";
-       len = eol - buf;
-       line = xmalloc(len + 1);
-       memcpy(line, buf, len);
-       line[len] = 0;
-       return line;
+       return xmemdupz(buf, eol - buf);
 }
 
 static const char *copy_name(const char *buf)
 {
-       const char *eol = strchr(buf, '\n');
-       const char *eoname = strstr(buf, " <");
-       char *line;
-       int len;
-       if (!(eoname && eol && eoname < eol))
-               return "";
-       len = eoname - buf;
-       line = xmalloc(len + 1);
-       memcpy(line, buf, len);
-       line[len] = 0;
-       return line;
+       const char *cp;
+       for (cp = buf; *cp && *cp != '\n'; cp++) {
+               if (!strncmp(cp, " <", 2))
+                       return xmemdupz(buf, cp - buf);
+       }
+       return "";
 }
 
 static const char *copy_email(const char *buf)
 {
        const char *email = strchr(buf, '<');
        const char *eoemail = strchr(email, '>');
-       char *line;
-       int len;
        if (!email || !eoemail)
                return "";
-       eoemail++;
-       len = eoemail - email;
-       line = xmalloc(len + 1);
-       memcpy(line, email, len);
-       line[len] = 0;
-       return line;
+       return xmemdupz(email, eoemail + 1 - email);
 }
 
-static void grab_date(const char *buf, struct atom_value *v)
+static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
 {
        const char *eoemail = strstr(buf, "> ");
        char *zone;
        unsigned long timestamp;
        long tz;
+       enum date_mode date_mode = DATE_NORMAL;
+       const char *formatp;
+
+       /*
+        * We got here because atomname ends in "date" or "date<something>";
+        * it's not possible that <something> is not ":<format>" because
+        * parse_atom() wouldn't have allowed it, so we can assume that no
+        * ":" means no format is specified, and use the default.
+        */
+       formatp = strchr(atomname, ':');
+       if (formatp != NULL) {
+               formatp++;
+               date_mode = parse_date_format(formatp);
+       }
 
        if (!eoemail)
                goto bad;
@@ -362,7 +365,7 @@ static void grab_date(const char *buf, struct atom_value *v)
        tz = strtol(zone, NULL, 10);
        if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
                goto bad;
-       v->s = xstrdup(show_date(timestamp, tz, 0));
+       v->s = xstrdup(show_date(timestamp, tz, date_mode));
        v->ul = timestamp;
        return;
  bad:
@@ -389,7 +392,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                if (name[wholen] != 0 &&
                    strcmp(name + wholen, "name") &&
                    strcmp(name + wholen, "email") &&
-                   strcmp(name + wholen, "date"))
+                   prefixcmp(name + wholen, "date"))
                        continue;
                if (!wholine)
                        wholine = find_wholine(who, wholen, buf, sz);
@@ -401,8 +404,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                        v->s = copy_name(wholine);
                else if (!strcmp(name + wholen, "email"))
                        v->s = copy_email(wholine);
-               else if (!strcmp(name + wholen, "date"))
-                       grab_date(wholine, v);
+               else if (!prefixcmp(name + wholen, "date"))
+                       grab_date(wholine, v, name);
        }
 
        /* For a tag or a commit object, if "creator" or "creatordate" is
@@ -422,8 +425,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                if (deref)
                        name++;
 
-               if (!strcmp(name, "creatordate"))
-                       grab_date(wholine, v);
+               if (!prefixcmp(name, "creatordate"))
+                       grab_date(wholine, v, name);
                else if (!strcmp(name, "creator"))
                        v->s = copy_line(wholine);
        }
index 939748261041049f31d62935ec08f062bdfa6e79..23ad2b6a21a81f469e813173ba04d626949e972f 100644 (file)
@@ -20,6 +20,8 @@ static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]";
 
 static int pack_refs = 1;
 static int aggressive_window = -1;
+static int gc_auto_threshold = 6700;
+static int gc_auto_pack_limit = 20;
 
 #define MAX_ADD 10
 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
@@ -41,6 +43,14 @@ static int gc_config(const char *var, const char *value)
                aggressive_window = git_config_int(var, value);
                return 0;
        }
+       if (!strcmp(var, "gc.auto")) {
+               gc_auto_threshold = git_config_int(var, value);
+               return 0;
+       }
+       if (!strcmp(var, "gc.autopacklimit")) {
+               gc_auto_pack_limit = git_config_int(var, value);
+               return 0;
+       }
        return git_default_config(var, value);
 }
 
@@ -57,10 +67,113 @@ static void append_option(const char **cmd, const char *opt, int max_length)
        cmd[i] = NULL;
 }
 
+static int too_many_loose_objects(void)
+{
+       /*
+        * Quickly check if a "gc" is needed, by estimating how
+        * many loose objects there are.  Because SHA-1 is evenly
+        * distributed, we can check only one and get a reasonable
+        * estimate.
+        */
+       char path[PATH_MAX];
+       const char *objdir = get_object_directory();
+       DIR *dir;
+       struct dirent *ent;
+       int auto_threshold;
+       int num_loose = 0;
+       int needed = 0;
+
+       if (gc_auto_threshold <= 0)
+               return 0;
+
+       if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
+               warning("insanely long object directory %.*s", 50, objdir);
+               return 0;
+       }
+       dir = opendir(path);
+       if (!dir)
+               return 0;
+
+       auto_threshold = (gc_auto_threshold + 255) / 256;
+       while ((ent = readdir(dir)) != NULL) {
+               if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
+                   ent->d_name[38] != '\0')
+                       continue;
+               if (++num_loose > auto_threshold) {
+                       needed = 1;
+                       break;
+               }
+       }
+       closedir(dir);
+       return needed;
+}
+
+static int too_many_packs(void)
+{
+       struct packed_git *p;
+       int cnt;
+
+       if (gc_auto_pack_limit <= 0)
+               return 0;
+
+       prepare_packed_git();
+       for (cnt = 0, p = packed_git; p; p = p->next) {
+               char path[PATH_MAX];
+               size_t len;
+               int keep;
+
+               if (!p->pack_local)
+                       continue;
+               len = strlen(p->pack_name);
+               if (PATH_MAX <= len + 1)
+                       continue; /* oops, give up */
+               memcpy(path, p->pack_name, len-5);
+               memcpy(path + len - 5, ".keep", 6);
+               keep = access(p->pack_name, F_OK) && (errno == ENOENT);
+               if (keep)
+                       continue;
+               /*
+                * Perhaps check the size of the pack and count only
+                * very small ones here?
+                */
+               cnt++;
+       }
+       return gc_auto_pack_limit <= cnt;
+}
+
+static int need_to_gc(void)
+{
+       int ac = 0;
+
+       /*
+        * Setting gc.auto and gc.autopacklimit to 0 or negative can
+        * disable the automatic gc.
+        */
+       if (gc_auto_threshold <= 0 && gc_auto_pack_limit <= 0)
+               return 0;
+
+       /*
+        * If there are too many loose objects, but not too many
+        * packs, we run "repack -d -l".  If there are too many packs,
+        * we run "repack -A -d -l".  Otherwise we tell the caller
+        * there is no need.
+        */
+       argv_repack[ac++] = "repack";
+       if (too_many_packs())
+               argv_repack[ac++] = "-A";
+       else if (!too_many_loose_objects())
+               return 0;
+       argv_repack[ac++] = "-d";
+       argv_repack[ac++] = "-l";
+       argv_repack[ac++] = NULL;
+       return 1;
+}
+
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
        int i;
        int prune = 0;
+       int auto_gc = 0;
        char buf[80];
 
        git_config(gc_config);
@@ -82,12 +195,24 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        }
                        continue;
                }
-               /* perhaps other parameters later... */
+               if (!strcmp(arg, "--auto")) {
+                       auto_gc = 1;
+                       continue;
+               }
                break;
        }
        if (i != argc)
                usage(builtin_gc_usage);
 
+       if (auto_gc) {
+               /*
+                * Auto-gc should be least intrusive as possible.
+                */
+               prune = 0;
+               if (!need_to_gc())
+                       return 0;
+       }
+
        if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_pack_refs[0]);
 
@@ -103,5 +228,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_rerere[0]);
 
+       if (auto_gc && too_many_loose_objects())
+               warning("There are too many unreachable loose objects; "
+                       "run 'git prune' to remove them.");
+
        return 0;
 }
index c6cc3aef5270fe64821146376354239d54de5a26..e8b982db7cf7c98bff9d64affcb573b3d9676cb1 100644 (file)
@@ -441,8 +441,6 @@ static const char *clean_message_id(const char *msg_id)
 {
        char ch;
        const char *a, *z, *m;
-       char *n;
-       size_t len;
 
        m = msg_id;
        while ((ch = *m) && (isspace(ch) || (ch == '<')))
@@ -458,11 +456,7 @@ static const char *clean_message_id(const char *msg_id)
                die("insane in-reply-to: %s", msg_id);
        if (++z == m)
                return a;
-       len = z - a;
-       n = xmalloc(len + 1);
-       memcpy(n, a, len);
-       n[len] = 0;
-       return n;
+       return xmemdupz(a, z - a);
 }
 
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
@@ -541,9 +535,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        endpos = strchr(committer, '>');
                        if (!endpos)
                                die("bogos committer info %s\n", committer);
-                       add_signoff = xmalloc(endpos - committer + 2);
-                       memcpy(add_signoff, committer, endpos - committer + 1);
-                       add_signoff[endpos - committer + 1] = 0;
+                       add_signoff = xmemdupz(committer, endpos - committer + 1);
                }
                else if (!strcmp(argv[i], "--attach")) {
                        rev.mime_boundary = git_version_string;
@@ -792,13 +784,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                        sign = '-';
 
                if (verbose) {
-                       char *buf = NULL;
-                       unsigned long buflen = 0;
-                       pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
-                                           &buf, &buflen, 0, NULL, NULL, 0);
+                       struct strbuf buf;
+                       strbuf_init(&buf, 0);
+                       pretty_print_commit(CMIT_FMT_ONELINE, commit,
+                                           &buf, 0, NULL, NULL, 0);
                        printf("%c %s %s\n", sign,
-                              sha1_to_hex(commit->object.sha1), buf);
-                       free(buf);
+                              sha1_to_hex(commit->object.sha1), buf.buf);
+                       strbuf_release(&buf);
                }
                else {
                        printf("%c %s\n", sign,
index 6c1db86e8056285cb25359d55f5ebf9dd0e6f489..b70da1863b221386a073ec8b7138cf0d91f52159 100644 (file)
@@ -84,8 +84,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
                return;
 
        fputs(tag, stdout);
-       write_name_quoted("", 0, ent->name + offset, line_terminator, stdout);
-       putchar(line_terminator);
+       write_name_quoted(ent->name + offset, stdout, line_terminator);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -208,21 +207,15 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
 
        if (!show_stage) {
                fputs(tag, stdout);
-               write_name_quoted("", 0, ce->name + offset,
-                                 line_terminator, stdout);
-               putchar(line_terminator);
-       }
-       else {
+       } else {
                printf("%s%06o %s %d\t",
                       tag,
                       ntohl(ce->ce_mode),
                       abbrev ? find_unique_abbrev(ce->sha1,abbrev)
                                : sha1_to_hex(ce->sha1),
                       ce_stage(ce));
-               write_name_quoted("", 0, ce->name + offset,
-                                 line_terminator, stdout);
-               putchar(line_terminator);
        }
+       write_name_quoted(ce->name + offset, stdout, line_terminator);
 }
 
 static void show_files(struct dir_struct *dir, const char *prefix)
@@ -280,7 +273,8 @@ static void prune_cache(const char *prefix)
 
        if (pos < 0)
                pos = -pos-1;
-       active_cache += pos;
+       memmove(active_cache, active_cache + pos,
+               (active_nr - pos) * sizeof(struct cache_entry *));
        active_nr -= pos;
        first = 0;
        last = active_nr;
@@ -299,7 +293,6 @@ static void prune_cache(const char *prefix)
 static const char *verify_pathspec(const char *prefix)
 {
        const char **p, *n, *prev;
-       char *real_prefix;
        unsigned long max;
 
        prev = NULL;
@@ -326,14 +319,8 @@ static const char *verify_pathspec(const char *prefix)
        if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
                die("git-ls-files: cannot generate relative filenames containing '..'");
 
-       real_prefix = NULL;
        prefix_len = max;
-       if (max) {
-               real_prefix = xmalloc(max + 1);
-               memcpy(real_prefix, prev, max);
-               real_prefix[max] = 0;
-       }
-       return real_prefix;
+       return max ? xmemdupz(prev, max) : NULL;
 }
 
 /*
index cb4be4fabb84bafd5518e81d2fd0ed6ee191641c..7abe333ce99a3448373119c1a811341cb15f1d10 100644 (file)
@@ -112,10 +112,8 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
                               abbrev ? find_unique_abbrev(sha1, abbrev)
                                      : sha1_to_hex(sha1));
        }
-       write_name_quoted(base + chomp_prefix, baselen - chomp_prefix,
-                         pathname,
-                         line_termination, stdout);
-       putchar(line_termination);
+       write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,
+                         pathname, stdout, line_termination);
        return retval;
 }
 
index b95b7d286ab5358a89a0309927b9f5e5e23fc4d0..b9446516915c998e41e645a7cc3e28c25ee21dc1 100644 (file)
@@ -22,10 +22,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
        for (i = 0; i < count; i++) {
                int length = strlen(result[i]);
                if (length > 0 && result[i][length - 1] == '/') {
-                       char *without_slash = xmalloc(length);
-                       memcpy(without_slash, result[i], length - 1);
-                       without_slash[length - 1] = '\0';
-                       result[i] = without_slash;
+                       result[i] = xmemdupz(result[i], length - 1);
                }
                if (base_name) {
                        const char *last_slash = strrchr(result[i], '/');
index a15906bdb2021e68a014344cad4e73e9de3367ca..0be539ed7fd9bf95bb40515b560c7615ed318f37 100644 (file)
@@ -25,7 +25,7 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
        [--window=N] [--window-memory=N] [--depth=N] \n\
        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
        [--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
-       [--stdout | base-name] [<ref-list | <object-list]";
+       [--stdout | base-name] [--keep-unreachable] [<ref-list | <object-list]";
 
 struct object_entry {
        struct pack_idx_entry idx;
@@ -61,7 +61,7 @@ static struct object_entry **written_list;
 static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
 
 static int non_empty;
-static int no_reuse_delta, no_reuse_object;
+static int no_reuse_delta, no_reuse_object, keep_unreachable;
 static int local;
 static int incremental;
 static int allow_ofs_delta;
@@ -1807,15 +1807,19 @@ static void read_object_list_from_stdin(void)
        }
 }
 
+#define OBJECT_ADDED (1u<<20)
+
 static void show_commit(struct commit *commit)
 {
        add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
+       commit->object.flags |= OBJECT_ADDED;
 }
 
 static void show_object(struct object_array_entry *p)
 {
        add_preferred_base_object(p->name);
        add_object_entry(p->item->sha1, p->item->type, p->name, 0);
+       p->item->flags |= OBJECT_ADDED;
 }
 
 static void show_edge(struct commit *commit)
@@ -1823,6 +1827,86 @@ static void show_edge(struct commit *commit)
        add_preferred_base(commit->object.sha1);
 }
 
+struct in_pack_object {
+       off_t offset;
+       struct object *object;
+};
+
+struct in_pack {
+       int alloc;
+       int nr;
+       struct in_pack_object *array;
+};
+
+static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack)
+{
+       in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->sha1, p);
+       in_pack->array[in_pack->nr].object = object;
+       in_pack->nr++;
+}
+
+/*
+ * Compare the objects in the offset order, in order to emulate the
+ * "git-rev-list --objects" output that produced the pack originally.
+ */
+static int ofscmp(const void *a_, const void *b_)
+{
+       struct in_pack_object *a = (struct in_pack_object *)a_;
+       struct in_pack_object *b = (struct in_pack_object *)b_;
+
+       if (a->offset < b->offset)
+               return -1;
+       else if (a->offset > b->offset)
+               return 1;
+       else
+               return hashcmp(a->object->sha1, b->object->sha1);
+}
+
+static void add_objects_in_unpacked_packs(struct rev_info *revs)
+{
+       struct packed_git *p;
+       struct in_pack in_pack;
+       uint32_t i;
+
+       memset(&in_pack, 0, sizeof(in_pack));
+
+       for (p = packed_git; p; p = p->next) {
+               const unsigned char *sha1;
+               struct object *o;
+
+               for (i = 0; i < revs->num_ignore_packed; i++) {
+                       if (matches_pack_name(p, revs->ignore_packed[i]))
+                               break;
+               }
+               if (revs->num_ignore_packed <= i)
+                       continue;
+               if (open_pack_index(p))
+                       die("cannot open pack index");
+
+               ALLOC_GROW(in_pack.array,
+                          in_pack.nr + p->num_objects,
+                          in_pack.alloc);
+
+               for (i = 0; i < p->num_objects; i++) {
+                       sha1 = nth_packed_object_sha1(p, i);
+                       o = lookup_unknown_object(sha1);
+                       if (!(o->flags & OBJECT_ADDED))
+                               mark_in_pack_object(o, p, &in_pack);
+                       o->flags |= OBJECT_ADDED;
+               }
+       }
+
+       if (in_pack.nr) {
+               qsort(in_pack.array, in_pack.nr, sizeof(in_pack.array[0]),
+                     ofscmp);
+               for (i = 0; i < in_pack.nr; i++) {
+                       struct object *o = in_pack.array[i].object;
+                       add_object_entry(o->sha1, o->type, "", 0);
+               }
+       }
+       free(in_pack.array);
+}
+
 static void get_object_list(int ac, const char **av)
 {
        struct rev_info revs;
@@ -1854,6 +1938,9 @@ static void get_object_list(int ac, const char **av)
        prepare_revision_walk(&revs);
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object);
+
+       if (keep_unreachable)
+               add_objects_in_unpacked_packs(&revs);
 }
 
 static int adjust_perm(const char *path, mode_t mode)
@@ -1983,6 +2070,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        use_internal_rev_list = 1;
                        continue;
                }
+               if (!strcmp("--keep-unreachable", arg)) {
+                       keep_unreachable = 1;
+                       continue;
+               }
                if (!strcmp("--unpacked", arg) ||
                    !prefixcmp(arg, "--unpacked=") ||
                    !strcmp("--reflog", arg) ||
index 29d057c98cc255e718df540a62177101d9789abe..b8206744c12307b3efbdebc99e5bcdd4a8632212 100644 (file)
@@ -66,40 +66,15 @@ static int write_rr(struct path_list *rr, int out_fd)
        return commit_lock_file(&write_lock);
 }
 
-struct buffer {
-       char *ptr;
-       int nr, alloc;
-};
-
-static void append_line(struct buffer *buffer, const char *line)
-{
-       int len = strlen(line);
-
-       if (buffer->nr + len > buffer->alloc) {
-               buffer->alloc = alloc_nr(buffer->nr + len);
-               buffer->ptr = xrealloc(buffer->ptr, buffer->alloc);
-       }
-       memcpy(buffer->ptr + buffer->nr, line, len);
-       buffer->nr += len;
-}
-
-static void clear_buffer(struct buffer *buffer)
-{
-       free(buffer->ptr);
-       buffer->ptr = NULL;
-       buffer->nr = buffer->alloc = 0;
-}
-
 static int handle_file(const char *path,
         unsigned char *sha1, const char *output)
 {
        SHA_CTX ctx;
        char buf[1024];
        int hunk = 0, hunk_no = 0;
-       struct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 };
-       struct buffer *one = &minus, *two = &plus;
+       struct strbuf one, two;
        FILE *f = fopen(path, "r");
-       FILE *out;
+       FILE *out = NULL;
 
        if (!f)
                return error("Could not open %s", path);
@@ -110,51 +85,50 @@ static int handle_file(const char *path,
                        fclose(f);
                        return error("Could not write %s", output);
                }
-       } else
-               out = NULL;
+       }
 
        if (sha1)
                SHA1_Init(&ctx);
 
+       strbuf_init(&one, 0);
+       strbuf_init(&two,  0);
        while (fgets(buf, sizeof(buf), f)) {
                if (!prefixcmp(buf, "<<<<<<< "))
                        hunk = 1;
                else if (!prefixcmp(buf, "======="))
                        hunk = 2;
                else if (!prefixcmp(buf, ">>>>>>> ")) {
-                       int one_is_longer = (one->nr > two->nr);
-                       int common_len = one_is_longer ? two->nr : one->nr;
-                       int cmp = memcmp(one->ptr, two->ptr, common_len);
+                       int cmp = strbuf_cmp(&one, &two);
 
                        hunk_no++;
                        hunk = 0;
-                       if ((cmp > 0) || ((cmp == 0) && one_is_longer)) {
-                               struct buffer *swap = one;
-                               one = two;
-                               two = swap;
+                       if (cmp > 0) {
+                               strbuf_swap(&one, &two);
                        }
                        if (out) {
                                fputs("<<<<<<<\n", out);
-                               fwrite(one->ptr, one->nr, 1, out);
+                               fwrite(one.buf, one.len, 1, out);
                                fputs("=======\n", out);
-                               fwrite(two->ptr, two->nr, 1, out);
+                               fwrite(two.buf, two.len, 1, out);
                                fputs(">>>>>>>\n", out);
                        }
                        if (sha1) {
-                               SHA1_Update(&ctx, one->ptr, one->nr);
-                               SHA1_Update(&ctx, "\0", 1);
-                               SHA1_Update(&ctx, two->ptr, two->nr);
-                               SHA1_Update(&ctx, "\0", 1);
+                               SHA1_Update(&ctx, one.buf ? one.buf : "",
+                                           one.len + 1);
+                               SHA1_Update(&ctx, two.buf ? two.buf : "",
+                                           two.len + 1);
                        }
-                       clear_buffer(one);
-                       clear_buffer(two);
+                       strbuf_reset(&one);
+                       strbuf_reset(&two);
                } else if (hunk == 1)
-                       append_line(one, buf);
+                       strbuf_addstr(&one, buf);
                else if (hunk == 2)
-                       append_line(two, buf);
+                       strbuf_addstr(&two, buf);
                else if (out)
                        fputs(buf, out);
        }
+       strbuf_release(&one);
+       strbuf_release(&two);
 
        fclose(f);
        if (out)
index 0b74eb35bcfae2dfe2145b33b1d56c0b90d7385f..33726b8d8426b4878ed63184dc280957ba4434e0 100644 (file)
@@ -80,14 +80,13 @@ static void show_commit(struct commit *commit)
                putchar('\n');
 
        if (revs.verbose_header) {
-               char *buf = NULL;
-               unsigned long buflen = 0;
-               pretty_print_commit(revs.commit_format, commit, ~0,
-                                   &buf, &buflen,
-                                   revs.abbrev, NULL, NULL, revs.date_mode);
-               if (*buf)
-                       printf("%s%c", buf, hdr_termination);
-               free(buf);
+               struct strbuf buf;
+               strbuf_init(&buf, 0);
+               pretty_print_commit(revs.commit_format, commit,
+                                       &buf, revs.abbrev, NULL, NULL, revs.date_mode);
+               if (buf.len)
+                       printf("%s%c", buf.buf, hdr_termination);
+               strbuf_release(&buf);
        }
        maybe_flush_or_die(stdout, "stdout");
        if (commit->parents) {
index 499bbe7343a635f1c7fc024ed6a1436042dcb8ac..a655c8ee2ab25ef778b182fbd5ff298a732c1cfd 100644 (file)
@@ -168,9 +168,7 @@ static void set_author_ident_env(const char *message)
                        char *line, *pend, *email, *timestamp;
 
                        p += 7;
-                       line = xmalloc(eol + 1 - p);
-                       memcpy(line, p, eol - p);
-                       line[eol - p] = '\0';
+                       line = xmemdupz(p, eol - p);
                        email = strchr(line, '<');
                        if (!email)
                                die ("Could not extract author email from %s",
index 16af6199ab2bc8a663d16f78a5d461a5bee05bc7..3fe754677d3f7ab11419a04dd828c70b5958ed87 100644 (file)
@@ -39,10 +39,7 @@ static void insert_author_oneline(struct path_list *list,
        while (authorlen > 0 && isspace(author[authorlen - 1]))
                authorlen--;
 
-       buffer = xmalloc(authorlen + 1);
-       memcpy(buffer, author, authorlen);
-       buffer[authorlen] = '\0';
-
+       buffer = xmemdupz(author, authorlen);
        item = path_list_insert(buffer, list);
        if (item->util == NULL)
                item->util = xcalloc(1, sizeof(struct path_list));
@@ -66,13 +63,9 @@ static void insert_author_oneline(struct path_list *list,
                oneline++;
                onelinelen--;
        }
-
        while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
                onelinelen--;
-
-       buffer = xmalloc(onelinelen + 1);
-       memcpy(buffer, oneline, onelinelen);
-       buffer[onelinelen] = '\0';
+       buffer = xmemdupz(oneline, onelinelen);
 
        if (dot3) {
                int dot3len = strlen(dot3);
index 4fa87f6081f74fa667a415038ca64c5f4a7cf775..07a0c2316bec4dd8341ea494b02874a1b12483cd 100644 (file)
@@ -259,16 +259,15 @@ static void join_revs(struct commit_list **list_p,
 
 static void show_one_commit(struct commit *commit, int no_name)
 {
-       char *pretty = NULL;
+       struct strbuf pretty;
        const char *pretty_str = "(unavailable)";
-       unsigned long pretty_len = 0;
        struct commit_name *name = commit->util;
 
+       strbuf_init(&pretty, 0);
        if (commit->object.parsed) {
-               pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
-                                   &pretty, &pretty_len,
-                                   0, NULL, NULL, 0);
-               pretty_str = pretty;
+               pretty_print_commit(CMIT_FMT_ONELINE, commit,
+                                       &pretty, 0, NULL, NULL, 0);
+               pretty_str = pretty.buf;
        }
        if (!prefixcmp(pretty_str, "[PATCH] "))
                pretty_str += 8;
@@ -289,7 +288,7 @@ static void show_one_commit(struct commit *commit, int no_name)
                               find_unique_abbrev(commit->object.sha1, 7));
        }
        puts(pretty_str);
-       free(pretty);
+       strbuf_release(&pretty);
 }
 
 static char *ref_name[MAX_REVS + 1];
index 916355ca5d04ec571d4100c98b969b693c830a18..c0b21301ba4c126a49ed38b6983756b99a25aae0 100644 (file)
@@ -8,17 +8,13 @@
  */
 static size_t cleanup(char *line, size_t len)
 {
-       if (len) {
-               if (line[len - 1] == '\n')
-                       len--;
-
-               while (len) {
-                       unsigned char c = line[len - 1];
-                       if (!isspace(c))
-                               break;
-                       len--;
-               }
+       while (len) {
+               unsigned char c = line[len - 1];
+               if (!isspace(c))
+                       break;
+               len--;
        }
+
        return len;
 }
 
@@ -34,66 +30,60 @@ static size_t cleanup(char *line, size_t len)
  * If the input has only empty lines and spaces,
  * no output will be produced.
  *
- * If last line has a newline at the end, it will be removed.
+ * If last line does not have a newline at the end, one is added.
  *
  * Enable skip_comments to skip every line starting with "#".
  */
-size_t stripspace(char *buffer, size_t length, int skip_comments)
+void stripspace(struct strbuf *sb, int skip_comments)
 {
-       int empties = -1;
+       int empties = 0;
        size_t i, j, len, newlen;
        char *eol;
 
-       for (i = j = 0; i < length; i += len, j += newlen) {
-               eol = memchr(buffer + i, '\n', length - i);
-               len = eol ? eol - (buffer + i) + 1 : length - i;
+       /* We may have to add a newline. */
+       strbuf_grow(sb, 1);
+
+       for (i = j = 0; i < sb->len; i += len, j += newlen) {
+               eol = memchr(sb->buf + i, '\n', sb->len - i);
+               len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
 
-               if (skip_comments && len && buffer[i] == '#') {
+               if (skip_comments && len && sb->buf[i] == '#') {
                        newlen = 0;
                        continue;
                }
-               newlen = cleanup(buffer + i, len);
+               newlen = cleanup(sb->buf + i, len);
 
                /* Not just an empty line? */
                if (newlen) {
-                       if (empties != -1)
-                               buffer[j++] = '\n';
-                       if (empties > 0)
-                               buffer[j++] = '\n';
+                       if (empties > 0 && j > 0)
+                               sb->buf[j++] = '\n';
                        empties = 0;
-                       memmove(buffer + j, buffer + i, newlen);
-                       continue;
+                       memmove(sb->buf + j, sb->buf + i, newlen);
+                       sb->buf[newlen + j++] = '\n';
+               } else {
+                       empties++;
                }
-               if (empties < 0)
-                       continue;
-               empties++;
        }
 
-       return j;
+       strbuf_setlen(sb, j);
 }
 
 int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
-       char *buffer;
-       unsigned long size;
+       struct strbuf buf;
        int strip_comments = 0;
 
        if (argc > 1 && (!strcmp(argv[1], "-s") ||
                                !strcmp(argv[1], "--strip-comments")))
                strip_comments = 1;
 
-       size = 1024;
-       buffer = xmalloc(size);
-       if (read_fd(0, &buffer, &size)) {
-               free(buffer);
+       strbuf_init(&buf, 0);
+       if (strbuf_read(&buf, 0, 1024) < 0)
                die("could not read the input");
-       }
 
-       size = stripspace(buffer, size, strip_comments);
-       write_or_die(1, buffer, size);
-       if (size)
-               putc('\n', stdout);
+       stripspace(&buf, strip_comments);
 
-       free(buffer);
+       write_or_die(1, buf.buf, buf.len);
+       strbuf_release(&buf);
        return 0;
 }
index 3a9d2eea71434c532bd0fe572bc799c0b91f4f44..66e5a5830792471a44c9211d4eafcf2b1ff6f0dd 100644 (file)
@@ -17,12 +17,11 @@ static const char builtin_tag_usage[] =
 
 static char signingkey[1000];
 
-static void launch_editor(const char *path, char **buffer, unsigned long *len)
+static void launch_editor(const char *path, struct strbuf *buffer)
 {
        const char *editor, *terminal;
        struct child_process child;
        const char *args[3];
-       int fd;
 
        editor = getenv("GIT_EDITOR");
        if (!editor && editor_program)
@@ -52,15 +51,9 @@ static void launch_editor(const char *path, char **buffer, unsigned long *len)
        if (run_command(&child))
                die("There was a problem with the editor %s.", editor);
 
-       fd = open(path, O_RDONLY);
-       if (fd < 0)
-               die("could not open '%s': %s", path, strerror(errno));
-       if (read_fd(fd, buffer, len)) {
-               free(*buffer);
+       if (strbuf_read_file(buffer, path, 0) < 0)
                die("could not read message file '%s': %s",
-                                               path, strerror(errno));
-       }
-       close(fd);
+                   path, strerror(errno));
 }
 
 struct tag_filter {
@@ -184,7 +177,7 @@ static int verify_tag(const char *name, const char *ref,
        return 0;
 }
 
-static ssize_t do_sign(char *buffer, size_t size, size_t max)
+static int do_sign(struct strbuf *buffer)
 {
        struct child_process gpg;
        const char *args[4];
@@ -216,22 +209,22 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max)
        if (start_command(&gpg))
                return error("could not run gpg.");
 
-       if (write_in_full(gpg.in, buffer, size) != size) {
+       if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
                close(gpg.in);
                finish_command(&gpg);
                return error("gpg did not accept the tag data");
        }
        close(gpg.in);
        gpg.close_in = 0;
-       len = read_in_full(gpg.out, buffer + size, max - size);
+       len = strbuf_read(buffer, gpg.out, 1024);
 
        if (finish_command(&gpg) || !len || len < 0)
                return error("gpg failed to sign the tag");
 
-       if (len == max - size)
+       if (len < 0)
                return error("could not read the entire signature from gpg.");
 
-       return size + len;
+       return 0;
 }
 
 static const char tag_template[] =
@@ -254,15 +247,13 @@ static int git_tag_config(const char *var, const char *value)
        return git_default_config(var, value);
 }
 
-#define MAX_SIGNATURE_LENGTH 1024
-/* message must be NULL or allocated, it will be reallocated and freed */
 static void create_tag(const unsigned char *object, const char *tag,
-                      char *message, int sign, unsigned char *result)
+                      struct strbuf *buf, int message, int sign,
+                          unsigned char *result)
 {
        enum object_type type;
-       char header_buf[1024], *buffer = NULL;
-       int header_len, max_size;
-       unsigned long size = 0;
+       char header_buf[1024];
+       int header_len;
 
        type = sha1_object_info(object, NULL);
        if (type <= OBJ_NONE)
@@ -294,53 +285,37 @@ static void create_tag(const unsigned char *object, const char *tag,
                write_or_die(fd, tag_template, strlen(tag_template));
                close(fd);
 
-               launch_editor(path, &buffer, &size);
+               launch_editor(path, buf);
 
                unlink(path);
                free(path);
        }
-       else {
-               buffer = message;
-               size = strlen(message);
-       }
 
-       size = stripspace(buffer, size, 1);
+       stripspace(buf, 1);
 
-       if (!message && !size)
+       if (!message && !buf->len)
                die("no tag message?");
 
-       /* insert the header and add the '\n' if needed: */
-       max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1;
-       buffer = xrealloc(buffer, max_size);
-       if (size)
-               buffer[size++] = '\n';
-       memmove(buffer + header_len, buffer, size);
-       memcpy(buffer, header_buf, header_len);
-       size += header_len;
-
-       if (sign) {
-               ssize_t r = do_sign(buffer, size, max_size);
-               if (r < 0)
-                       die("unable to sign the tag");
-               size = r;
-       }
+       strbuf_insert(buf, 0, header_buf, header_len);
 
-       if (write_sha1_file(buffer, size, tag_type, result) < 0)
+       if (sign && do_sign(buf) < 0)
+               die("unable to sign the tag");
+       if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
                die("unable to write tag file");
-       free(buffer);
 }
 
 int cmd_tag(int argc, const char **argv, const char *prefix)
 {
+       struct strbuf buf;
        unsigned char object[20], prev[20];
-       int annotate = 0, sign = 0, force = 0, lines = 0;
-       char *message = NULL;
+       int annotate = 0, sign = 0, force = 0, lines = 0, message = 0;
        char ref[PATH_MAX];
        const char *object_ref, *tag;
        int i;
        struct ref_lock *lock;
 
        git_config(git_tag_config);
+       strbuf_init(&buf, 0);
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -376,13 +351,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                                die("option -m needs an argument.");
                        if (message)
                                die("only one -F or -m option is allowed.");
-                       message = xstrdup(argv[i]);
+                       strbuf_addstr(&buf, argv[i]);
+                       message = 1;
                        continue;
                }
                if (!strcmp(arg, "-F")) {
-                       unsigned long len;
-                       int fd;
-
                        annotate = 1;
                        i++;
                        if (i == argc)
@@ -390,20 +363,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        if (message)
                                die("only one -F or -m option is allowed.");
 
-                       if (!strcmp(argv[i], "-"))
-                               fd = 0;
-                       else {
-                               fd = open(argv[i], O_RDONLY);
-                               if (fd < 0)
-                                       die("could not open '%s': %s",
+                       if (!strcmp(argv[i], "-")) {
+                               if (strbuf_read(&buf, 0, 1024) < 0)
+                                       die("cannot read %s", argv[i]);
+                       } else {
+                               if (strbuf_read_file(&buf, argv[i], 1024) < 0)
+                                       die("could not open or read '%s': %s",
                                                argv[i], strerror(errno));
                        }
-                       len = 1024;
-                       message = xmalloc(len);
-                       if (read_fd(fd, &message, &len)) {
-                               free(message);
-                               die("cannot read %s", argv[i]);
-                       }
+                       message = 1;
                        continue;
                }
                if (!strcmp(arg, "-u")) {
@@ -451,7 +419,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                die("tag '%s' already exists", tag);
 
        if (annotate)
-               create_tag(object, tag, message, sign, object);
+               create_tag(object, tag, &buf, message, sign, object);
 
        lock = lock_any_ref_for_update(ref, prev, 0);
        if (!lock)
@@ -459,5 +427,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (write_ref_sha1(lock, object, NULL) < 0)
                die("%s: cannot update the ref", ref);
 
+       strbuf_release(&buf);
        return 0;
 }
index 55fb679d68f141253f5d0ba5c73033d267e2a271..e1a938d8971f11e1a1e963913ab23ff6acc0cea9 100644 (file)
@@ -4,7 +4,6 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "cache.h"
-#include "strbuf.h"
 #include "quote.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
@@ -296,8 +295,11 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
 static void read_index_info(int line_termination)
 {
        struct strbuf buf;
-       strbuf_init(&buf);
-       while (1) {
+       struct strbuf uq;
+
+       strbuf_init(&buf, 0);
+       strbuf_init(&uq, 0);
+       while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
                char *ptr, *tab;
                char *path_name;
                unsigned char sha1[20];
@@ -321,10 +323,6 @@ static void read_index_info(int line_termination)
                 * This format is to put higher order stages into the
                 * index file and matches git-ls-files --stage output.
                 */
-               read_line(&buf, stdin, line_termination);
-               if (buf.eof)
-                       break;
-
                errno = 0;
                ul = strtoul(buf.buf, &ptr, 8);
                if (ptr == buf.buf || *ptr != ' '
@@ -349,15 +347,17 @@ static void read_index_info(int line_termination)
                if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
                        goto bad_line;
 
-               if (line_termination && ptr[0] == '"')
-                       path_name = unquote_c_style(ptr, NULL);
-               else
-                       path_name = ptr;
+               path_name = ptr;
+               if (line_termination && path_name[0] == '"') {
+                       strbuf_reset(&uq);
+                       if (unquote_c_style(&uq, path_name, NULL)) {
+                               die("git-update-index: bad quoting of path name");
+                       }
+                       path_name = uq.buf;
+               }
 
                if (!verify_path(path_name)) {
                        fprintf(stderr, "Ignoring path %s\n", path_name);
-                       if (path_name != ptr)
-                               free(path_name);
                        continue;
                }
 
@@ -377,13 +377,13 @@ static void read_index_info(int line_termination)
                                die("git-update-index: unable to update %s",
                                    path_name);
                }
-               if (path_name != ptr)
-                       free(path_name);
                continue;
 
        bad_line:
                die("malformed index info %s", buf.buf);
        }
+       strbuf_release(&buf);
+       strbuf_release(&uq);
 }
 
 static const char update_index_usage[] =
@@ -706,27 +706,27 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                        free((char*)p);
        }
        if (read_from_stdin) {
-               struct strbuf buf;
-               strbuf_init(&buf);
-               while (1) {
-                       char *path_name;
+               struct strbuf buf, nbuf;
+
+               strbuf_init(&buf, 0);
+               strbuf_init(&nbuf, 0);
+               while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
                        const char *p;
-                       read_line(&buf, stdin, line_termination);
-                       if (buf.eof)
-                               break;
-                       if (line_termination && buf.buf[0] == '"')
-                               path_name = unquote_c_style(buf.buf, NULL);
-                       else
-                               path_name = buf.buf;
-                       p = prefix_path(prefix, prefix_length, path_name);
+                       if (line_termination && buf.buf[0] == '"') {
+                               strbuf_reset(&nbuf);
+                               if (unquote_c_style(&nbuf, buf.buf, NULL))
+                                       die("line is badly quoted");
+                               strbuf_swap(&buf, &nbuf);
+                       }
+                       p = prefix_path(prefix, prefix_length, buf.buf);
                        update_one(p, NULL, 0);
                        if (set_executable_bit)
                                chmod_path(set_executable_bit, p);
-                       if (p < path_name || p > path_name + strlen(path_name))
-                               free((char*) p);
-                       if (path_name != buf.buf)
-                               free(path_name);
+                       if (p < buf.buf || p > buf.buf + buf.len)
+                               free((char *)p);
                }
+               strbuf_release(&nbuf);
+               strbuf_release(&buf);
        }
 
  finish:
index 03ee7bf780be93601f9190b733984ad82951952b..d6f2c76b86174e6353c3d6146368e3ff71406a22 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -7,7 +7,6 @@ extern const char git_version_string[];
 extern const char git_usage_string[];
 
 extern void help_unknown_cmd(const char *cmd);
-extern size_t stripspace(char *buffer, size_t length, int skip_comments);
 extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 extern void prune_packed_objects(int);
 
index 077f03436941e9c0bf31d3bb2002c1e36b8817b9..50b35264fd0405a299700ef8bf4a61f416f30e46 100644 (file)
@@ -235,8 +235,7 @@ static int update_one(struct cache_tree *it,
                      int missing_ok,
                      int dryrun)
 {
-       unsigned long size, offset;
-       char *buffer;
+       struct strbuf buffer;
        int i;
 
        if (0 <= it->entry_count && has_sha1_file(it->sha1))
@@ -293,9 +292,7 @@ static int update_one(struct cache_tree *it,
        /*
         * Then write out the tree object for this level.
         */
-       size = 8192;
-       buffer = xmalloc(size);
-       offset = 0;
+       strbuf_init(&buffer, 8192);
 
        for (i = 0; i < entries; i++) {
                struct cache_entry *ce = cache[i];
@@ -332,15 +329,9 @@ static int update_one(struct cache_tree *it,
                if (!ce->ce_mode)
                        continue; /* entry being removed */
 
-               if (size < offset + entlen + 100) {
-                       size = alloc_nr(offset + entlen + 100);
-                       buffer = xrealloc(buffer, size);
-               }
-               offset += sprintf(buffer + offset,
-                                 "%o %.*s", mode, entlen, path + baselen);
-               buffer[offset++] = 0;
-               hashcpy((unsigned char*)buffer + offset, sha1);
-               offset += 20;
+               strbuf_grow(&buffer, entlen + 100);
+               strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
+               strbuf_add(&buffer, sha1, 20);
 
 #if DEBUG
                fprintf(stderr, "cache-tree update-one %o %.*s\n",
@@ -349,10 +340,10 @@ static int update_one(struct cache_tree *it,
        }
 
        if (dryrun)
-               hash_sha1_file(buffer, offset, tree_type, it->sha1);
+               hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
        else
-               write_sha1_file(buffer, offset, tree_type, it->sha1);
-       free(buffer);
+               write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
+       strbuf_release(&buffer);
        it->entry_count = i;
 #if DEBUG
        fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
@@ -378,12 +369,8 @@ int cache_tree_update(struct cache_tree *it,
        return 0;
 }
 
-static void *write_one(struct cache_tree *it,
-                      char *path,
-                      int pathlen,
-                      char *buffer,
-                      unsigned long *size,
-                      unsigned long *offset)
+static void write_one(struct strbuf *buffer, struct cache_tree *it,
+                      const char *path, int pathlen)
 {
        int i;
 
@@ -393,13 +380,9 @@ static void *write_one(struct cache_tree *it,
         * tree-sha1 (missing if invalid)
         * subtree_nr "cache-tree" entries for subtrees.
         */
-       if (*size < *offset + pathlen + 100) {
-               *size = alloc_nr(*offset + pathlen + 100);
-               buffer = xrealloc(buffer, *size);
-       }
-       *offset += sprintf(buffer + *offset, "%.*s%c%d %d\n",
-                          pathlen, path, 0,
-                          it->entry_count, it->subtree_nr);
+       strbuf_grow(buffer, pathlen + 100);
+       strbuf_add(buffer, path, pathlen);
+       strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr);
 
 #if DEBUG
        if (0 <= it->entry_count)
@@ -412,8 +395,7 @@ static void *write_one(struct cache_tree *it,
 #endif
 
        if (0 <= it->entry_count) {
-               hashcpy((unsigned char*)buffer + *offset, it->sha1);
-               *offset += 20;
+               strbuf_add(buffer, it->sha1, 20);
        }
        for (i = 0; i < it->subtree_nr; i++) {
                struct cache_tree_sub *down = it->down[i];
@@ -423,21 +405,13 @@ static void *write_one(struct cache_tree *it,
                                             prev->name, prev->namelen) <= 0)
                                die("fatal - unsorted cache subtree");
                }
-               buffer = write_one(down->cache_tree, down->name, down->namelen,
-                                  buffer, size, offset);
+               write_one(buffer, down->cache_tree, down->name, down->namelen);
        }
-       return buffer;
 }
 
-void *cache_tree_write(struct cache_tree *root, unsigned long *size_p)
+void cache_tree_write(struct strbuf *sb, struct cache_tree *root)
 {
-       char path[PATH_MAX];
-       unsigned long size = 8192;
-       char *buffer = xmalloc(size);
-
-       *size_p = 0;
-       path[0] = 0;
-       return write_one(root, path, 0, buffer, &size, size_p);
+       write_one(sb, root, "", 0);
 }
 
 static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
index 119407e3a10562166fc61e009613842b213dfcfc..8243228e49ffd7078a783582be6ce79c97541a9c 100644 (file)
@@ -22,7 +22,7 @@ void cache_tree_free(struct cache_tree **);
 void cache_tree_invalidate_path(struct cache_tree *, const char *);
 struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
 
-void *cache_tree_write(struct cache_tree *root, unsigned long *size_p);
+void cache_tree_write(struct strbuf *, struct cache_tree *root);
 struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
 
 int cache_tree_fully_valid(struct cache_tree *);
diff --git a/cache.h b/cache.h
index 824650016677353cfa8c8a140eb3d904f56d60ee..e0abcd697ce54853bbf4545d20a4ae88a95b533a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -2,6 +2,7 @@
 #define CACHE_H
 
 #include "git-compat-util.h"
+#include "strbuf.h"
 
 #include SHA1_HEADER
 #include <zlib.h>
@@ -270,7 +271,6 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat
 extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
-extern int read_fd(int fd, char **return_buf, unsigned long *return_size);
 extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
@@ -432,6 +432,7 @@ const char *show_date(unsigned long time, int timezone, enum date_mode mode);
 int parse_date(const char *date, char *buf, int bufsize);
 void datestamp(char *buf, int bufsize);
 unsigned long approxidate(const char *);
+enum date_mode parse_date_format(const char *format);
 
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
@@ -530,6 +531,7 @@ extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsign
 extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
+extern int matches_pack_name(struct packed_git *p, const char *name);
 
 /* Dumb servers support */
 extern int update_server_info(int);
@@ -585,14 +587,13 @@ extern void *alloc_object_node(void);
 extern void alloc_report(void);
 
 /* trace.c */
-extern int nfasprintf(char **str, const char *fmt, ...);
-extern int nfvasprintf(char **str, const char *fmt, va_list va);
 extern void trace_printf(const char *format, ...);
 extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
 
 /* convert.c */
-extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep);
-extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep);
+/* returns 1 if *dst was used */
+extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
+extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 
 /* diff.c */
 extern int diff_auto_refresh_index;
index ef622340a52afb3b31b1cdf678ae0a83fb85c923..fe5a2a1953a06204ad6f1c045bd06dd984d3acc6 100644 (file)
@@ -650,10 +650,7 @@ static void dump_quoted_path(const char *prefix, const char *path,
                             const char *c_meta, const char *c_reset)
 {
        printf("%s%s", c_meta, prefix);
-       if (quote_c_style(path, NULL, NULL, 0))
-               quote_c_style(path, NULL, stdout, 0);
-       else
-               printf("%s", path);
+       quote_c_style(path, NULL, stdout, 0);
        printf("%s\n", c_reset);
 }
 
@@ -900,16 +897,7 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
                putchar(inter_name_termination);
        }
 
-       if (line_termination) {
-               if (quote_c_style(p->path, NULL, NULL, 0))
-                       quote_c_style(p->path, NULL, stdout, 0);
-               else
-                       printf("%s", p->path);
-               putchar(line_termination);
-       }
-       else {
-               printf("%s%c", p->path, line_termination);
-       }
+       write_name_quoted(p->path, stdout, line_termination);
 }
 
 void show_combined_diff(struct combine_diff_path *p,
index c9a18180be5872acb9fc4e021b61492b71a0d9c9..20fb2209cbcbc19343e7e52378e74afef52e3a6e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -458,11 +458,11 @@ void clear_commit_marks(struct commit *commit, unsigned int mark)
 /*
  * Generic support for pretty-printing the header
  */
-static int get_one_line(const char *msg, unsigned long len)
+static int get_one_line(const char *msg)
 {
        int ret = 0;
 
-       while (len--) {
+       for (;;) {
                char c = *msg++;
                if (!c)
                        break;
@@ -485,31 +485,25 @@ static int is_rfc2047_special(char ch)
        return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
 }
 
-static int add_rfc2047(char *buf, const char *line, int len,
+static void add_rfc2047(struct strbuf *sb, const char *line, int len,
                       const char *encoding)
 {
-       char *bp = buf;
-       int i, needquote;
-       char q_encoding[128];
-       const char *q_encoding_fmt = "=?%s?q?";
+       int i, last;
 
-       for (i = needquote = 0; !needquote && i < len; i++) {
+       for (i = 0; i < len; i++) {
                int ch = line[i];
                if (non_ascii(ch))
-                       needquote++;
-               if ((i + 1 < len) &&
-                   (ch == '=' && line[i+1] == '?'))
-                       needquote++;
+                       goto needquote;
+               if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
+                       goto needquote;
        }
-       if (!needquote)
-               return sprintf(buf, "%.*s", len, line);
-
-       i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding);
-       if (sizeof(q_encoding) < i)
-               die("Insanely long encoding name %s", encoding);
-       memcpy(bp, q_encoding, i);
-       bp += i;
-       for (i = 0; i < len; i++) {
+       strbuf_add(sb, line, len);
+       return;
+
+needquote:
+       strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
+       strbuf_addf(sb, "=?%s?q?", encoding);
+       for (i = last = 0; i < len; i++) {
                unsigned ch = line[i] & 0xFF;
                /*
                 * We encode ' ' using '=20' even though rfc2047
@@ -518,40 +512,30 @@ static int add_rfc2047(char *buf, const char *line, int len,
                 * leave the underscore in place.
                 */
                if (is_rfc2047_special(ch) || ch == ' ') {
-                       sprintf(bp, "=%02X", ch);
-                       bp += 3;
+                       strbuf_add(sb, line + last, i - last);
+                       strbuf_addf(sb, "=%02X", ch);
+                       last = i + 1;
                }
-               else
-                       *bp++ = ch;
        }
-       memcpy(bp, "?=", 2);
-       bp += 2;
-       return bp - buf;
-}
-
-static unsigned long bound_rfc2047(unsigned long len, const char *encoding)
-{
-       /* upper bound of q encoded string of length 'len' */
-       unsigned long elen = strlen(encoding);
-
-       return len * 3 + elen + 100;
+       strbuf_add(sb, line + last, len - last);
+       strbuf_addstr(sb, "?=");
 }
 
-static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
+static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
                         const char *line, enum date_mode dmode,
                         const char *encoding)
 {
        char *date;
        int namelen;
        unsigned long time;
-       int tz, ret;
+       int tz;
        const char *filler = "    ";
 
        if (fmt == CMIT_FMT_ONELINE)
-               return 0;
+               return;
        date = strchr(line, '>');
        if (!date)
-               return 0;
+               return;
        namelen = ++date - line;
        time = strtoul(date, &date, 10);
        tz = strtol(date, NULL, 10);
@@ -560,42 +544,34 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
                char *name_tail = strchr(line, '<');
                int display_name_length;
                if (!name_tail)
-                       return 0;
+                       return;
                while (line < name_tail && isspace(name_tail[-1]))
                        name_tail--;
                display_name_length = name_tail - line;
                filler = "";
-               strcpy(buf, "From: ");
-               ret = strlen(buf);
-               ret += add_rfc2047(buf + ret, line, display_name_length,
-                                  encoding);
-               memcpy(buf + ret, name_tail, namelen - display_name_length);
-               ret += namelen - display_name_length;
-               buf[ret++] = '\n';
-       }
-       else {
-               ret = sprintf(buf, "%s: %.*s%.*s\n", what,
+               strbuf_addstr(sb, "From: ");
+               add_rfc2047(sb, line, display_name_length, encoding);
+               strbuf_add(sb, name_tail, namelen - display_name_length);
+               strbuf_addch(sb, '\n');
+       } else {
+               strbuf_addf(sb, "%s: %.*s%.*s\n", what,
                              (fmt == CMIT_FMT_FULLER) ? 4 : 0,
                              filler, namelen, line);
        }
        switch (fmt) {
        case CMIT_FMT_MEDIUM:
-               ret += sprintf(buf + ret, "Date:   %s\n",
-                              show_date(time, tz, dmode));
+               strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, dmode));
                break;
        case CMIT_FMT_EMAIL:
-               ret += sprintf(buf + ret, "Date: %s\n",
-                              show_date(time, tz, DATE_RFC2822));
+               strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
                break;
        case CMIT_FMT_FULLER:
-               ret += sprintf(buf + ret, "%sDate: %s\n", what,
-                              show_date(time, tz, dmode));
+               strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
                break;
        default:
                /* notin' */
                break;
        }
-       return ret;
 }
 
 static int is_empty_line(const char *line, int *len_p)
@@ -607,16 +583,16 @@ static int is_empty_line(const char *line, int *len_p)
        return !len;
 }
 
-static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *commit, int abbrev)
+static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
+                       const struct commit *commit, int abbrev)
 {
        struct commit_list *parent = commit->parents;
-       int offset;
 
        if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
            !parent || !parent->next)
-               return 0;
+               return;
 
-       offset = sprintf(buf, "Merge:");
+       strbuf_addstr(sb, "Merge:");
 
        while (parent) {
                struct commit *p = parent->item;
@@ -629,10 +605,9 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
                dots = (abbrev && strlen(hex) != 40) ?  "..." : "";
                parent = parent->next;
 
-               offset += sprintf(buf + offset, " %s%s", hex, dots);
+               strbuf_addf(sb, " %s%s", hex, dots);
        }
-       buf[offset++] = '\n';
-       return offset;
+       strbuf_addch(sb, '\n');
 }
 
 static char *get_header(const struct commit *commit, const char *key)
@@ -653,11 +628,7 @@ static char *get_header(const struct commit *commit, const char *key)
                if (eol - line > key_len &&
                    !strncmp(line, key, key_len) &&
                    line[key_len] == ' ') {
-                       int len = eol - line - key_len;
-                       char *ret = xmalloc(len);
-                       memcpy(ret, line + key_len + 1, len - 1);
-                       ret[len - 1] = '\0';
-                       return ret;
+                       return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
                }
                line = next;
        }
@@ -665,47 +636,34 @@ static char *get_header(const struct commit *commit, const char *key)
 
 static char *replace_encoding_header(char *buf, const char *encoding)
 {
-       char *encoding_header = strstr(buf, "\nencoding ");
-       char *header_end = strstr(buf, "\n\n");
-       char *end_of_encoding_header;
-       int encoding_header_pos;
-       int encoding_header_len;
-       int new_len;
-       int need_len;
-       int buflen = strlen(buf) + 1;
-
-       if (!header_end)
-               header_end = buf + buflen;
-       if (!encoding_header || encoding_header >= header_end)
-               return buf;
-       encoding_header++;
-       end_of_encoding_header = strchr(encoding_header, '\n');
-       if (!end_of_encoding_header)
+       struct strbuf tmp;
+       size_t start, len;
+       char *cp = buf;
+
+       /* guess if there is an encoding header before a \n\n */
+       while (strncmp(cp, "encoding ", strlen("encoding "))) {
+               cp = strchr(cp, '\n');
+               if (!cp || *++cp == '\n')
+                       return buf;
+       }
+       start = cp - buf;
+       cp = strchr(cp, '\n');
+       if (!cp)
                return buf; /* should not happen but be defensive */
-       end_of_encoding_header++;
-
-       encoding_header_len = end_of_encoding_header - encoding_header;
-       encoding_header_pos = encoding_header - buf;
+       len = cp + 1 - (buf + start);
 
+       strbuf_init(&tmp, 0);
+       strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1);
        if (is_encoding_utf8(encoding)) {
                /* we have re-coded to UTF-8; drop the header */
-               memmove(encoding_header, end_of_encoding_header,
-                       buflen - (encoding_header_pos + encoding_header_len));
-               return buf;
-       }
-       new_len = strlen(encoding);
-       need_len = new_len + strlen("encoding \n");
-       if (encoding_header_len < need_len) {
-               buf = xrealloc(buf, buflen + (need_len - encoding_header_len));
-               encoding_header = buf + encoding_header_pos;
-               end_of_encoding_header = encoding_header + encoding_header_len;
+               strbuf_remove(&tmp, start, len);
+       } else {
+               /* just replaces XXXX in 'encoding XXXX\n' */
+               strbuf_splice(&tmp, start + strlen("encoding "),
+                                         len - strlen("encoding \n"),
+                                         encoding, strlen(encoding));
        }
-       memmove(end_of_encoding_header + (need_len - encoding_header_len),
-               end_of_encoding_header,
-               buflen - (encoding_header_pos + encoding_header_len));
-       memcpy(encoding_header + 9, encoding, strlen(encoding));
-       encoding_header[9 + new_len] = '\n';
-       return buf;
+       return strbuf_detach(&tmp, NULL);
 }
 
 static char *logmsg_reencode(const struct commit *commit,
@@ -747,7 +705,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
        start = end + 1;
        while (end > 0 && isspace(msg[end - 1]))
                end--;
-       table[0].value = xstrndup(msg, end);
+       table[0].value = xmemdupz(msg, end);
 
        if (start >= len)
                return;
@@ -759,7 +717,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
        if (end >= len)
                return;
 
-       table[1].value = xstrndup(msg + start, end - start);
+       table[1].value = xmemdupz(msg + start, end - start);
 
        /* parse date */
        for (start = end + 1; start < len && isspace(msg[start]); start++)
@@ -770,7 +728,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
        if (msg + start == ep)
                return;
 
-       table[5].value = xstrndup(msg + start, ep - (msg + start));
+       table[5].value = xmemdupz(msg + start, ep - (msg + start));
 
        /* parse tz */
        for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
@@ -787,8 +745,8 @@ static void fill_person(struct interp *table, const char *msg, int len)
        interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
 }
 
-long format_commit_message(const struct commit *commit, const void *format,
-                           char **buf_p, unsigned long *space_p)
+void format_commit_message(const struct commit *commit,
+                           const void *format, struct strbuf *sb)
 {
        struct interp table[] = {
                { "%H" },       /* commit hash */
@@ -841,6 +799,7 @@ long format_commit_message(const struct commit *commit, const void *format,
        };
        struct commit_list *p;
        char parents[1024];
+       unsigned long len;
        int i;
        enum { HEADER, SUBJECT, BODY } state;
        const char *msg = commit->buffer;
@@ -896,7 +855,7 @@ long format_commit_message(const struct commit *commit, const void *format,
                        ; /* do nothing */
 
                if (state == SUBJECT) {
-                       table[ISUBJECT].value = xstrndup(msg + i, eol - i);
+                       table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
                        i = eol;
                }
                if (i == eol) {
@@ -912,27 +871,21 @@ long format_commit_message(const struct commit *commit, const void *format,
                                        msg + i + 10, eol - i - 10);
                else if (!prefixcmp(msg + i, "encoding "))
                        table[IENCODING].value =
-                               xstrndup(msg + i + 9, eol - i - 9);
+                               xmemdupz(msg + i + 9, eol - i - 9);
                i = eol;
        }
        if (msg[i])
                table[IBODY].value = xstrdup(msg + i);
 
-       do {
-               char *buf = *buf_p;
-               unsigned long space = *space_p;
-
-               space = interpolate(buf, space, format,
-                                   table, ARRAY_SIZE(table));
-               if (!space)
-                       break;
-               buf = xrealloc(buf, space);
-               *buf_p = buf;
-               *space_p = space;
-       } while (1);
+       len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
+                               format, table, ARRAY_SIZE(table));
+       if (len > strbuf_avail(sb)) {
+               strbuf_grow(sb, len);
+               interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
+                                       format, table, ARRAY_SIZE(table));
+       }
+       strbuf_setlen(sb, sb->len + len);
        interp_clear_table(table, ARRAY_SIZE(table));
-
-       return strlen(*buf_p);
 }
 
 static void pp_header(enum cmit_fmt fmt,
@@ -941,34 +894,24 @@ static void pp_header(enum cmit_fmt fmt,
                      const char *encoding,
                      const struct commit *commit,
                      const char **msg_p,
-                     unsigned long *len_p,
-                     unsigned long *ofs_p,
-                     char **buf_p,
-                     unsigned long *space_p)
+                     struct strbuf *sb)
 {
        int parents_shown = 0;
 
        for (;;) {
                const char *line = *msg_p;
-               char *dst;
-               int linelen = get_one_line(*msg_p, *len_p);
-               unsigned long len;
+               int linelen = get_one_line(*msg_p);
 
                if (!linelen)
                        return;
                *msg_p += linelen;
-               *len_p -= linelen;
 
                if (linelen == 1)
                        /* End of header */
                        return;
 
-               ALLOC_GROW(*buf_p, linelen + *ofs_p + 20, *space_p);
-               dst = *buf_p + *ofs_p;
-
                if (fmt == CMIT_FMT_RAW) {
-                       memcpy(dst, line, linelen);
-                       *ofs_p += linelen;
+                       strbuf_add(sb, line, linelen);
                        continue;
                }
 
@@ -986,10 +929,8 @@ static void pp_header(enum cmit_fmt fmt,
                             parent = parent->next, num++)
                                ;
                        /* with enough slop */
-                       num = *ofs_p + num * 50 + 20;
-                       ALLOC_GROW(*buf_p, num, *space_p);
-                       dst = *buf_p + *ofs_p;
-                       *ofs_p += add_merge_info(fmt, dst, commit, abbrev);
+                       strbuf_grow(sb, num * 50 + 20);
+                       add_merge_info(fmt, sb, commit, abbrev);
                        parents_shown = 1;
                }
 
@@ -999,129 +940,82 @@ static void pp_header(enum cmit_fmt fmt,
                 * FULLER shows both authors and dates.
                 */
                if (!memcmp(line, "author ", 7)) {
-                       len = linelen;
-                       if (fmt == CMIT_FMT_EMAIL)
-                               len = bound_rfc2047(linelen, encoding);
-                       ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p);
-                       dst = *buf_p + *ofs_p;
-                       *ofs_p += add_user_info("Author", fmt, dst,
-                                               line + 7, dmode, encoding);
+                       strbuf_grow(sb, linelen + 80);
+                       add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
                }
-
                if (!memcmp(line, "committer ", 10) &&
                    (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
-                       len = linelen;
-                       if (fmt == CMIT_FMT_EMAIL)
-                               len = bound_rfc2047(linelen, encoding);
-                       ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p);
-                       dst = *buf_p + *ofs_p;
-                       *ofs_p += add_user_info("Commit", fmt, dst,
-                                               line + 10, dmode, encoding);
+                       strbuf_grow(sb, linelen + 80);
+                       add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
                }
        }
 }
 
 static void pp_title_line(enum cmit_fmt fmt,
                          const char **msg_p,
-                         unsigned long *len_p,
-                         unsigned long *ofs_p,
-                         char **buf_p,
-                         unsigned long *space_p,
-                         int indent,
+                         struct strbuf *sb,
                          const char *subject,
                          const char *after_subject,
                          const char *encoding,
                          int plain_non_ascii)
 {
-       char *title;
-       unsigned long title_alloc, title_len;
-       unsigned long len;
+       struct strbuf title;
+
+       strbuf_init(&title, 80);
 
-       title_len = 0;
-       title_alloc = 80;
-       title = xmalloc(title_alloc);
        for (;;) {
                const char *line = *msg_p;
-               int linelen = get_one_line(line, *len_p);
-               *msg_p += linelen;
-               *len_p -= linelen;
+               int linelen = get_one_line(line);
 
+               *msg_p += linelen;
                if (!linelen || is_empty_line(line, &linelen))
                        break;
 
-               if (title_alloc <= title_len + linelen + 2) {
-                       title_alloc = title_len + linelen + 80;
-                       title = xrealloc(title, title_alloc);
-               }
-               len = 0;
-               if (title_len) {
+               strbuf_grow(&title, linelen + 2);
+               if (title.len) {
                        if (fmt == CMIT_FMT_EMAIL) {
-                               len++;
-                               title[title_len++] = '\n';
+                               strbuf_addch(&title, '\n');
                        }
-                       len++;
-                       title[title_len++] = ' ';
+                       strbuf_addch(&title, ' ');
                }
-               memcpy(title + title_len, line, linelen);
-               title_len += linelen;
+               strbuf_add(&title, line, linelen);
        }
 
-       /* Enough slop for the MIME header and rfc2047 */
-       len = bound_rfc2047(title_len, encoding)+ 1000;
-       if (subject)
-               len += strlen(subject);
-       if (after_subject)
-               len += strlen(after_subject);
-       if (encoding)
-               len += strlen(encoding);
-       ALLOC_GROW(*buf_p, title_len + *ofs_p + len, *space_p);
-
+       strbuf_grow(sb, title.len + 1024);
        if (subject) {
-               len = strlen(subject);
-               memcpy(*buf_p + *ofs_p, subject, len);
-               *ofs_p += len;
-               *ofs_p += add_rfc2047(*buf_p + *ofs_p,
-                                     title, title_len, encoding);
+               strbuf_addstr(sb, subject);
+               add_rfc2047(sb, title.buf, title.len, encoding);
        } else {
-               memcpy(*buf_p + *ofs_p, title, title_len);
-               *ofs_p += title_len;
+               strbuf_addbuf(sb, &title);
        }
-       (*buf_p)[(*ofs_p)++] = '\n';
+       strbuf_addch(sb, '\n');
+
        if (plain_non_ascii) {
                const char *header_fmt =
                        "MIME-Version: 1.0\n"
                        "Content-Type: text/plain; charset=%s\n"
                        "Content-Transfer-Encoding: 8bit\n";
-               *ofs_p += snprintf(*buf_p + *ofs_p,
-                                  *space_p - *ofs_p,
-                                  header_fmt, encoding);
+               strbuf_addf(sb, header_fmt, encoding);
        }
        if (after_subject) {
-               len = strlen(after_subject);
-               memcpy(*buf_p + *ofs_p, after_subject, len);
-               *ofs_p += len;
+               strbuf_addstr(sb, after_subject);
        }
-       free(title);
        if (fmt == CMIT_FMT_EMAIL) {
-               ALLOC_GROW(*buf_p, *ofs_p + 20, *space_p);
-               (*buf_p)[(*ofs_p)++] = '\n';
+               strbuf_addch(sb, '\n');
        }
+       strbuf_release(&title);
 }
 
 static void pp_remainder(enum cmit_fmt fmt,
                         const char **msg_p,
-                        unsigned long *len_p,
-                        unsigned long *ofs_p,
-                        char **buf_p,
-                        unsigned long *space_p,
+                        struct strbuf *sb,
                         int indent)
 {
        int first = 1;
        for (;;) {
                const char *line = *msg_p;
-               int linelen = get_one_line(line, *len_p);
+               int linelen = get_one_line(line);
                *msg_p += linelen;
-               *len_p -= linelen;
 
                if (!linelen)
                        break;
@@ -1134,36 +1028,32 @@ static void pp_remainder(enum cmit_fmt fmt,
                }
                first = 0;
 
-               ALLOC_GROW(*buf_p, *ofs_p + linelen + indent + 20, *space_p);
+               strbuf_grow(sb, linelen + indent + 20);
                if (indent) {
-                       memset(*buf_p + *ofs_p, ' ', indent);
-                       *ofs_p += indent;
+                       memset(sb->buf + sb->len, ' ', indent);
+                       strbuf_setlen(sb, sb->len + indent);
                }
-               memcpy(*buf_p + *ofs_p, line, linelen);
-               *ofs_p += linelen;
-               (*buf_p)[(*ofs_p)++] = '\n';
+               strbuf_add(sb, line, linelen);
+               strbuf_addch(sb, '\n');
        }
 }
 
-unsigned long pretty_print_commit(enum cmit_fmt fmt,
-                                 const struct commit *commit,
-                                 unsigned long len,
-                                 char **buf_p, unsigned long *space_p,
-                                 int abbrev, const char *subject,
-                                 const char *after_subject,
+void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
+                                 struct strbuf *sb, int abbrev,
+                                 const char *subject, const char *after_subject,
                                  enum date_mode dmode)
 {
-       unsigned long offset = 0;
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg = commit->buffer;
        int plain_non_ascii = 0;
        char *reencoded;
        const char *encoding;
-       char *buf;
 
-       if (fmt == CMIT_FMT_USERFORMAT)
-               return format_commit_message(commit, user_format, buf_p, space_p);
+       if (fmt == CMIT_FMT_USERFORMAT) {
+               format_commit_message(commit, user_format, sb);
+               return;
+       }
 
        encoding = (git_log_output_encoding
                    ? git_log_output_encoding
@@ -1173,7 +1063,6 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
        reencoded = logmsg_reencode(commit, encoding);
        if (reencoded) {
                msg = reencoded;
-               len = strlen(reencoded);
        }
 
        if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
@@ -1188,14 +1077,13 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
        if (fmt == CMIT_FMT_EMAIL && !after_subject) {
                int i, ch, in_body;
 
-               for (in_body = i = 0; (ch = msg[i]) && i < len; i++) {
+               for (in_body = i = 0; (ch = msg[i]); i++) {
                        if (!in_body) {
                                /* author could be non 7-bit ASCII but
                                 * the log may be so; skip over the
                                 * header part first.
                                 */
-                               if (ch == '\n' &&
-                                   i + 1 < len && msg[i+1] == '\n')
+                               if (ch == '\n' && msg[i+1] == '\n')
                                        in_body = 1;
                        }
                        else if (non_ascii(ch)) {
@@ -1205,59 +1093,44 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
                }
        }
 
-       pp_header(fmt, abbrev, dmode, encoding,
-                 commit, &msg, &len,
-                 &offset, buf_p, space_p);
+       pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb);
        if (fmt != CMIT_FMT_ONELINE && !subject) {
-               ALLOC_GROW(*buf_p, offset + 20, *space_p);
-               (*buf_p)[offset++] = '\n';
+               strbuf_addch(sb, '\n');
        }
 
        /* Skip excess blank lines at the beginning of body, if any... */
        for (;;) {
-               int linelen = get_one_line(msg, len);
+               int linelen = get_one_line(msg);
                int ll = linelen;
                if (!linelen)
                        break;
                if (!is_empty_line(msg, &ll))
                        break;
                msg += linelen;
-               len -= linelen;
        }
 
        /* These formats treat the title line specially. */
-       if (fmt == CMIT_FMT_ONELINE
-           || fmt == CMIT_FMT_EMAIL)
-               pp_title_line(fmt, &msg, &len, &offset,
-                             buf_p, space_p, indent,
-                             subject, after_subject, encoding,
-                             plain_non_ascii);
-
-       beginning_of_body = offset;
-       if (fmt != CMIT_FMT_ONELINE)
-               pp_remainder(fmt, &msg, &len, &offset,
-                            buf_p, space_p, indent);
-
-       while (offset && isspace((*buf_p)[offset-1]))
-               offset--;
+       if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+               pp_title_line(fmt, &msg, sb, subject,
+                             after_subject, encoding, plain_non_ascii);
 
-       ALLOC_GROW(*buf_p, offset + 20, *space_p);
-       buf = *buf_p;
+       beginning_of_body = sb->len;
+       if (fmt != CMIT_FMT_ONELINE)
+               pp_remainder(fmt, &msg, sb, indent);
+       strbuf_rtrim(sb);
 
        /* Make sure there is an EOLN for the non-oneline case */
        if (fmt != CMIT_FMT_ONELINE)
-               buf[offset++] = '\n';
+               strbuf_addch(sb, '\n');
 
        /*
         * The caller may append additional body text in e-mail
         * format.  Make sure we did not strip the blank line
         * between the header and the body.
         */
-       if (fmt == CMIT_FMT_EMAIL && offset <= beginning_of_body)
-               buf[offset++] = '\n';
-       buf[offset] = '\0';
+       if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
+               strbuf_addch(sb, '\n');
        free(reencoded);
-       return offset;
 }
 
 struct commit *pop_commit(struct commit_list **stack)
@@ -1336,12 +1209,12 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
                next=next->next;
        }
        /*
-         * find the tips
-         *
-         * tips are nodes not reachable from any other node in the list
-         *
-         * the tips serve as a starting set for the work queue.
-         */
+        * find the tips
+        *
+        * tips are nodes not reachable from any other node in the list
+        *
+        * the tips serve as a starting set for the work queue.
+        */
        next=*list;
        insert = &work;
        while (next) {
@@ -1368,9 +1241,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
                        if (pn) {
                                /*
                                 * parents are only enqueued for emission
-                                 * when all their children have been emitted thereby
-                                 * guaranteeing topological order.
-                                 */
+                                * when all their children have been emitted thereby
+                                * guaranteeing topological order.
+                                */
                                pn->indegree--;
                                if (!pn->indegree) {
                                        if (!lifo)
@@ -1382,9 +1255,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
                        parents=parents->next;
                }
                /*
-                 * work_item is a commit all of whose children
-                 * have already been emitted. we can emit it now.
-                 */
+                * work_item is a commit all of whose children
+                * have already been emitted. we can emit it now.
+                */
                *pptr = work_node->list_item;
                pptr = &(*pptr)->next;
                *pptr = NULL;
@@ -1480,8 +1353,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
 }
 
 struct commit_list *get_merge_bases(struct commit *one,
-                                   struct commit *two,
-                                    int cleanup)
+                                       struct commit *two, int cleanup)
 {
        struct commit_list *list;
        struct commit **rslt;
index a8d76616d2ae6965ebca3c197232e1b7c9294585..b779de8cbca74220aeebbc1df194b5f7301f51d0 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -3,6 +3,7 @@
 
 #include "object.h"
 #include "tree.h"
+#include "strbuf.h"
 #include "decorate.h"
 
 struct commit_list {
@@ -61,8 +62,12 @@ enum cmit_fmt {
 };
 
 extern enum cmit_fmt get_commit_format(const char *arg);
-extern long format_commit_message(const struct commit *commit, const void *template, char **buf_p, unsigned long *space_p);
-extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode);
+extern void format_commit_message(const struct commit *commit,
+                                  const void *format, struct strbuf *sb);
+extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
+                                struct strbuf *,
+                                int abbrev, const char *subject,
+                                const char *after_subject, enum date_mode);
 
 /** Removes the first commit from a list sorted by date, and adds all
  * of its parents.
index 84fd7f1e1f2fd670de38a43bfe65d3988fc4767e..ed7cc895d276ac4d7548211a364a4e6952f9b7bd 100644 (file)
@@ -104,7 +104,7 @@ AC_MSG_NOTICE([CHECKS for programs])
 #
 AC_PROG_CC([cc gcc])
 #AC_PROG_INSTALL               # needs install-sh or install.sh in sources
-AC_CHECK_TOOL(AR, ar, :)
+AC_CHECK_TOOLS(AR, [gar ar], :)
 AC_CHECK_PROGS(TAR, [gtar tar])
 # TCLTK_PATH will be set to some value if we want Tcl/Tk
 # or will be empty otherwise.
index 8b1e9935a85b639f6c48298d07299f72bceaad29..06d279e37ca73b6128479a990b9cd5dbd7cb1317 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -393,9 +393,7 @@ static int git_proxy_command_options(const char *var, const char *value)
                        if (matchlen == 4 &&
                            !memcmp(value, "none", 4))
                                matchlen = 0;
-                       git_proxy_command = xmalloc(matchlen + 1);
-                       memcpy(git_proxy_command, value, matchlen);
-                       git_proxy_command[matchlen] = 0;
+                       git_proxy_command = xmemdupz(value, matchlen);
                }
                return 0;
        }
@@ -579,16 +577,13 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
        if (pid < 0)
                die("unable to fork");
        if (!pid) {
-               char command[MAX_CMD_LEN];
-               char *posn = command;
-               int size = MAX_CMD_LEN;
-               int of = 0;
+               struct strbuf cmd;
 
-               of |= add_to_string(&posn, &size, prog, 0);
-               of |= add_to_string(&posn, &size, " ", 0);
-               of |= add_to_string(&posn, &size, path, 1);
-
-               if (of)
+               strbuf_init(&cmd, MAX_CMD_LEN);
+               strbuf_addstr(&cmd, prog);
+               strbuf_addch(&cmd, ' ');
+               sq_quote_buf(&cmd, path);
+               if (cmd.len >= MAX_CMD_LEN)
                        die("command line too long");
 
                dup2(pipefd[1][0], 0);
@@ -608,10 +603,10 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
                                ssh_basename++;
 
                        if (!port)
-                               execlp(ssh, ssh_basename, host, command, NULL);
+                               execlp(ssh, ssh_basename, host, cmd.buf, NULL);
                        else
                                execlp(ssh, ssh_basename, "-p", port, host,
-                                      command, NULL);
+                                      cmd.buf, NULL);
                }
                else {
                        unsetenv(ALTERNATE_DB_ENVIRONMENT);
@@ -620,7 +615,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
                        unsetenv(GIT_WORK_TREE_ENVIRONMENT);
                        unsetenv(GRAFT_ENVIRONMENT);
                        unsetenv(INDEX_ENVIRONMENT);
-                       execlp("sh", "sh", "-c", command, NULL);
+                       execlp("sh", "sh", "-c", cmd.buf, NULL);
                }
                die("exec failed");
        }
index 2d77fd47ece21af7b79b3e5806521e208d5d92b0..4286d160a02bdbeff272cc306ab01b85b87b8cbe 100644 (file)
@@ -36,7 +36,6 @@
 ;; TODO
 ;;  - portability to XEmacs
 ;;  - better handling of subprocess errors
-;;  - hook into file save (after-save-hook)
 ;;  - diff against other branch
 ;;  - renaming files from the status buffer
 ;;  - creating tags
@@ -220,22 +219,15 @@ and returns the process output as a string."
     (message "Running git %s...done" (car args))
     buffer))
 
-(defun git-run-command (buffer env &rest args)
-  (message "Running git %s..." (car args))
-  (apply #'git-call-process-env buffer env args)
-  (message "Running git %s...done" (car args)))
-
 (defun git-run-command-region (buffer start end env &rest args)
   "Run a git command with specified buffer region as input."
-  (message "Running git %s..." (car args))
   (unless (eq 0 (if env
                     (git-run-process-region
                      buffer start end "env"
                      (append (git-get-env-strings env) (list "git") args))
                   (git-run-process-region
                    buffer start end "git" args)))
-    (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))
-  (message "Running git %s...done" (car args)))
+    (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
 
 (defun git-run-hook (hook env &rest args)
   "Run a git hook and display its output if any."
@@ -312,6 +304,13 @@ and returns the process output as a string."
               "\"")
     name))
 
+(defun git-success-message (text files)
+  "Print a success message after having handled FILES."
+  (let ((n (length files)))
+    (if (equal n 1)
+        (message "%s %s" text (car files))
+      (message "%s %d files" text n))))
+
 (defun git-get-top-dir (dir)
   "Retrieve the top-level directory of a git tree."
   (let ((cdup (with-output-to-string
@@ -338,7 +337,7 @@ and returns the process output as a string."
     (sort-lines nil (point-min) (point-max))
     (save-buffer))
   (when created
-    (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
+    (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
   (git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
 
 ; propertize definition for XEmacs, stolen from erc-compat
@@ -485,33 +484,34 @@ and returns the process output as a string."
   "Remove everything from the status list."
   (ewoc-filter status (lambda (info) nil)))
 
-(defun git-set-files-state (files state)
-  "Set the state of a list of files."
-  (dolist (info files)
-    (unless (eq (git-fileinfo->state info) state)
-      (setf (git-fileinfo->state info) state)
-      (setf (git-fileinfo->rename-state info) nil)
-      (setf (git-fileinfo->orig-name info) nil)
-      (setf (git-fileinfo->needs-refresh info) t))))
-
-(defun git-set-filenames-state (status files state)
-  "Set the state of a list of named files."
+(defun git-set-fileinfo-state (info state)
+  "Set the state of a file info."
+  (unless (eq (git-fileinfo->state info) state)
+    (setf (git-fileinfo->state info) state
+          (git-fileinfo->old-perm info) 0
+          (git-fileinfo->new-perm info) 0
+          (git-fileinfo->rename-state info) nil
+          (git-fileinfo->orig-name info) nil
+          (git-fileinfo->needs-refresh info) t)))
+
+(defun git-status-filenames-map (status func files &rest args)
+  "Apply FUNC to the status files names in the FILES list."
   (when files
     (setq files (sort files #'string-lessp))
     (let ((file (pop files))
           (node (ewoc-nth status 0)))
       (while (and file node)
         (let ((info (ewoc-data node)))
-          (cond ((string-lessp (git-fileinfo->name info) file)
-                 (setq node (ewoc-next status node)))
-                ((string-equal (git-fileinfo->name info) file)
-                 (unless (eq (git-fileinfo->state info) state)
-                   (setf (git-fileinfo->state info) state)
-                   (setf (git-fileinfo->rename-state info) nil)
-                   (setf (git-fileinfo->orig-name info) nil)
-                   (setf (git-fileinfo->needs-refresh info) t))
-                 (setq file (pop files)))
-                (t (setq file (pop files)))))))
+          (if (string-lessp (git-fileinfo->name info) file)
+              (setq node (ewoc-next status node))
+            (if (string-equal (git-fileinfo->name info) file)
+                (apply func info args))
+            (setq file (pop files))))))))
+
+(defun git-set-filenames-state (status files state)
+  "Set the state of a list of named files."
+  (when files
+    (git-status-filenames-map status #'git-set-fileinfo-state files state)
     (unless state  ;; delete files whose state has been set to nil
       (ewoc-filter status (lambda (info) (git-fileinfo->state info))))))
 
@@ -599,7 +599,7 @@ and returns the process output as a string."
 Return the list of files that haven't been handled."
   (let (infolist)
     (with-temp-buffer
-      (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
+      (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files)
       (goto-char (point-min))
       (while (re-search-forward
               ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
@@ -632,7 +632,7 @@ Return the list of files that haven't been handled."
 Return the list of files that haven't been handled."
   (let (infolist)
     (with-temp-buffer
-      (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files))
+      (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
       (goto-char (point-min))
       (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
         (let ((name (match-string 1)))
@@ -644,7 +644,7 @@ Return the list of files that haven't been handled."
 (defun git-run-ls-unmerged (status files)
   "Run git-ls-files -u on FILES and parse the results into STATUS."
   (with-temp-buffer
-    (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files)
+    (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files)
     (goto-char (point-min))
     (let (unmerged-files)
       (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
@@ -747,11 +747,11 @@ Return the list of files that haven't been handled."
         ('deleted (push info deleted))
         ('modified (push info modified))))
     (when added
-      (apply #'git-run-command nil env "update-index" "--add" "--" (git-get-filenames added)))
+      (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added)))
     (when deleted
-      (apply #'git-run-command nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
+      (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
     (when modified
-      (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified)))))
+      (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified)))))
 
 (defun git-run-pre-commit-hook ()
   "Run the pre-commit hook if any."
@@ -783,6 +783,7 @@ Return the list of files that haven't been handled."
                       head-tree (git-rev-parse "HEAD^{tree}")))
               (if files
                   (progn
+                    (message "Running git commit...")
                     (git-read-tree head-tree index-file)
                     (git-update-index nil files)         ;update both the default index
                     (git-update-index index-file files)  ;and the temporary one
@@ -793,8 +794,8 @@ Return the list of files that haven't been handled."
                             (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
                             (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
                             (with-current-buffer buffer (erase-buffer))
-                            (git-set-files-state files 'uptodate)
-                            (git-run-command nil nil "rerere")
+                            (dolist (info files) (git-set-fileinfo-state info 'uptodate))
+                            (git-call-process-env nil nil "rerere")
                             (git-refresh-files)
                             (git-refresh-ewoc-hf git-status)
                             (message "Committed %s." commit)
@@ -905,8 +906,9 @@ Return the list of files that haven't been handled."
   (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
-    (apply #'git-run-command nil nil "update-index" "--add" "--" files)
-    (git-update-status-files files 'uptodate)))
+    (apply #'git-call-process-env nil nil "update-index" "--add" "--" files)
+    (git-update-status-files files 'uptodate)
+    (git-success-message "Added" files)))
 
 (defun git-ignore-file ()
   "Add marked file(s) to the ignore list."
@@ -915,7 +917,8 @@ Return the list of files that haven't been handled."
     (unless files
       (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
     (dolist (f files) (git-append-to-ignore f))
-    (git-update-status-files files 'ignored)))
+    (git-update-status-files files 'ignored)
+    (git-success-message "Ignored" files)))
 
 (defun git-remove-file ()
   "Remove the marked file(s)."
@@ -928,8 +931,9 @@ Return the list of files that haven't been handled."
         (progn
           (dolist (name files)
             (when (file-exists-p name) (delete-file name)))
-          (apply #'git-run-command nil nil "update-index" "--remove" "--" files)
-          (git-update-status-files files nil))
+          (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files)
+          (git-update-status-files files nil)
+          (git-success-message "Removed" files))
       (message "Aborting"))))
 
 (defun git-revert-file ()
@@ -947,18 +951,20 @@ Return the list of files that haven't been handled."
           ('unmerged (push (git-fileinfo->name info) modified))
           ('modified (push (git-fileinfo->name info) modified))))
       (when added
-        (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added))
+        (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added))
       (when modified
-        (apply #'git-run-command nil nil "checkout" "HEAD" modified))
-      (git-update-status-files (append added modified) 'uptodate))))
+        (apply #'git-call-process-env nil nil "checkout" "HEAD" modified))
+      (git-update-status-files (append added modified) 'uptodate)
+      (git-success-message "Reverted" files))))
 
 (defun git-resolve-file ()
   "Resolve conflicts in marked file(s)."
   (interactive)
   (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
     (when files
-      (apply #'git-run-command nil nil "update-index" "--" files)
-      (git-update-status-files files 'uptodate))))
+      (apply #'git-call-process-env nil nil "update-index" "--" files)
+      (git-update-status-files files 'uptodate)
+      (git-success-message "Resolved" files))))
 
 (defun git-remove-handled ()
   "Remove handled files from the status list."
@@ -985,9 +991,11 @@ Return the list of files that haven't been handled."
   (interactive)
   (if (setq git-show-ignored (not git-show-ignored))
       (progn
+        (message "Inserting ignored files...")
         (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i")
         (git-refresh-files)
-        (git-refresh-ewoc-hf git-status))
+        (git-refresh-ewoc-hf git-status)
+        (message "Inserting ignored files...done"))
     (git-remove-handled)))
 
 (defun git-toggle-show-unknown ()
@@ -995,9 +1003,11 @@ Return the list of files that haven't been handled."
   (interactive)
   (if (setq git-show-unknown (not git-show-unknown))
       (progn
+        (message "Inserting unknown files...")
         (git-run-ls-files-with-excludes git-status nil 'unknown "-o")
         (git-refresh-files)
-        (git-refresh-ewoc-hf git-status))
+        (git-refresh-ewoc-hf git-status)
+        (message "Inserting unknown files...done"))
     (git-remove-handled)))
 
 (defun git-setup-diff-buffer (buffer)
@@ -1197,12 +1207,23 @@ Return the list of files that haven't been handled."
   (interactive)
   (let* ((status git-status)
          (pos (ewoc-locate status))
+         (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info)))))
          (cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
     (unless status (error "Not in git-status buffer."))
-    (git-run-command nil nil "update-index" "--refresh")
+    (message "Refreshing git status...")
+    (git-call-process-env nil nil "update-index" "--refresh")
     (git-clear-status status)
     (git-update-status-files nil)
+    ; restore file marks
+    (when marked-files
+      (git-status-filenames-map status
+                                (lambda (info)
+                                        (setf (git-fileinfo->marked info) t)
+                                        (setf (git-fileinfo->needs-refresh info) t))
+                                marked-files)
+      (git-refresh-files))
     ; move point to the current file name if any
+    (message "Refreshing git status...done")
     (let ((node (and cur-name (git-find-status-file status cur-name))))
       (when node (ewoc-goto-node status node)))))
 
@@ -1324,9 +1345,24 @@ Commands:
         (cd dir)
         (git-status-mode)
         (git-refresh-status)
-        (goto-char (point-min)))
+        (goto-char (point-min))
+        (add-hook 'after-save-hook 'git-update-saved-file))
     (message "%s is not a git working tree." dir)))
 
+(defun git-update-saved-file ()
+  "Update the corresponding git-status buffer when a file is saved.
+Meant to be used in `after-save-hook'."
+  (let* ((file (expand-file-name buffer-file-name))
+         (dir (condition-case nil (git-get-top-dir (file-name-directory file))))
+         (buffer (and dir (git-find-status-buffer dir))))
+    (when buffer
+      (with-current-buffer buffer
+        (let ((filename (file-relative-name file dir)))
+          ; skip files located inside the .git directory
+          (unless (string-match "^\\.git/" filename)
+            (git-call-process-env nil nil "add" "--refresh" "--" filename)
+            (git-update-status-files (list filename) 'uptodate)))))))
+
 (defun git-help ()
   "Display help for Git mode."
   (interactive)
index 1f88099df4b2213c0dca58b22567bc1d91e4adc2..cbbd02fadd753215b3477c178b74d79a59424ee7 100644 (file)
@@ -177,7 +177,6 @@ generate_email_header()
        # --- Email (all stdout will be the email)
        # Generate header
        cat <<-EOF
-       From: $committer
        To: $recipients
        Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
        X-Git-Refname: $refname
index d77c8eb8b2802d675b320ddeb063c1cf70cc57d8..0d5e909c696dac8b900713a992c72cd9cacf94e3 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -80,24 +80,19 @@ static int is_binary(unsigned long size, struct text_stat *stats)
        return 0;
 }
 
-static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action)
+static int crlf_to_git(const char *path, const char *src, size_t len,
+                       struct strbuf *buf, int action)
 {
-       char *buffer, *dst;
-       unsigned long size, nsize;
        struct text_stat stats;
+       char *dst;
 
-       if ((action == CRLF_BINARY) || !auto_crlf)
-               return NULL;
-
-       size = *sizep;
-       if (!size)
-               return NULL;
-
-       gather_stats(src, size, &stats);
+       if ((action == CRLF_BINARY) || !auto_crlf || !len)
+               return 0;
 
+       gather_stats(src, len, &stats);
        /* No CR? Nothing to convert, regardless. */
        if (!stats.cr)
-               return NULL;
+               return 0;
 
        if (action == CRLF_GUESS) {
                /*
@@ -106,24 +101,17 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep
                 * stuff?
                 */
                if (stats.cr != stats.crlf)
-                       return NULL;
+                       return 0;
 
                /*
                 * And add some heuristics for binary vs text, of course...
                 */
-               if (is_binary(size, &stats))
-                       return NULL;
+               if (is_binary(len, &stats))
+                       return 0;
        }
 
-       /*
-        * Ok, allocate a new buffer, fill it in, and return it
-        * to let the caller know that we switched buffers.
-        */
-       nsize = size - stats.crlf;
-       buffer = xmalloc(nsize);
-       *sizep = nsize;
-
-       dst = buffer;
+       strbuf_grow(buf, len);
+       dst = buf->buf;
        if (action == CRLF_GUESS) {
                /*
                 * If we guessed, we already know we rejected a file with
@@ -134,71 +122,72 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep
                        unsigned char c = *src++;
                        if (c != '\r')
                                *dst++ = c;
-               } while (--size);
+               } while (--len);
        } else {
                do {
                        unsigned char c = *src++;
-                       if (! (c == '\r' && (1 < size && *src == '\n')))
+                       if (! (c == '\r' && (1 < len && *src == '\n')))
                                *dst++ = c;
-               } while (--size);
+               } while (--len);
        }
-
-       return buffer;
+       strbuf_setlen(buf, dst - buf->buf);
+       return 1;
 }
 
-static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action)
+static int crlf_to_worktree(const char *path, const char *src, size_t len,
+                            struct strbuf *buf, int action)
 {
-       char *buffer, *dst;
-       unsigned long size, nsize;
+       char *to_free = NULL;
        struct text_stat stats;
-       unsigned char last;
 
        if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
            auto_crlf <= 0)
-               return NULL;
+               return 0;
 
-       size = *sizep;
-       if (!size)
-               return NULL;
+       if (!len)
+               return 0;
 
-       gather_stats(src, size, &stats);
+       gather_stats(src, len, &stats);
 
        /* No LF? Nothing to convert, regardless. */
        if (!stats.lf)
-               return NULL;
+               return 0;
 
        /* Was it already in CRLF format? */
        if (stats.lf == stats.crlf)
-               return NULL;
+               return 0;
 
        if (action == CRLF_GUESS) {
                /* If we have any bare CR characters, we're not going to touch it */
                if (stats.cr != stats.crlf)
-                       return NULL;
+                       return 0;
 
-               if (is_binary(size, &stats))
-                       return NULL;
+               if (is_binary(len, &stats))
+                       return 0;
        }
 
-       /*
-        * Ok, allocate a new buffer, fill it in, and return it
-        * to let the caller know that we switched buffers.
-        */
-       nsize = size + stats.lf - stats.crlf;
-       buffer = xmalloc(nsize);
-       *sizep = nsize;
-       last = 0;
-
-       dst = buffer;
-       do {
-               unsigned char c = *src++;
-               if (c == '\n' && last != '\r')
-                       *dst++ = '\r';
-               *dst++ = c;
-               last = c;
-       } while (--size);
-
-       return buffer;
+       /* are we "faking" in place editing ? */
+       if (src == buf->buf)
+               to_free = strbuf_detach(buf, NULL);
+
+       strbuf_grow(buf, len + stats.lf - stats.crlf);
+       for (;;) {
+               const char *nl = memchr(src, '\n', len);
+               if (!nl)
+                       break;
+               if (nl > src && nl[-1] == '\r') {
+                       strbuf_add(buf, src, nl + 1 - src);
+               } else {
+                       strbuf_add(buf, src, nl - src);
+                       strbuf_addstr(buf, "\r\n");
+               }
+               len -= nl + 1 - src;
+               src  = nl + 1;
+       }
+       strbuf_add(buf, src, len);
+
+       free(to_free);
+       return 1;
 }
 
 static int filter_buffer(const char *path, const char *src,
@@ -246,8 +235,8 @@ static int filter_buffer(const char *path, const char *src,
        return (write_err || status);
 }
 
-static char *apply_filter(const char *path, const char *src,
-                         unsigned long *sizep, const char *cmd)
+static int apply_filter(const char *path, const char *src, size_t len,
+                        struct strbuf *dst, const char *cmd)
 {
        /*
         * Create a pipeline to have the command filter the buffer's
@@ -255,21 +244,19 @@ static char *apply_filter(const char *path, const char *src,
         *
         * (child --> cmd) --> us
         */
-       const int SLOP = 4096;
        int pipe_feed[2];
-       int status;
-       char *dst;
-       unsigned long dstsize, dstalloc;
+       int status, ret = 1;
        struct child_process child_process;
+       struct strbuf nbuf;
 
        if (!cmd)
-               return NULL;
+               return 0;
 
        memset(&child_process, 0, sizeof(child_process));
 
        if (pipe(pipe_feed) < 0) {
                error("cannot create pipe to run external filter %s", cmd);
-               return NULL;
+               return 0;
        }
 
        fflush(NULL);
@@ -278,54 +265,37 @@ static char *apply_filter(const char *path, const char *src,
                error("cannot fork to run external filter %s", cmd);
                close(pipe_feed[0]);
                close(pipe_feed[1]);
-               return NULL;
+               return 0;
        }
        if (!child_process.pid) {
                dup2(pipe_feed[1], 1);
                close(pipe_feed[0]);
                close(pipe_feed[1]);
-               exit(filter_buffer(path, src, *sizep, cmd));
+               exit(filter_buffer(path, src, len, cmd));
        }
        close(pipe_feed[1]);
 
-       dstalloc = *sizep;
-       dst = xmalloc(dstalloc);
-       dstsize = 0;
-
-       while (1) {
-               ssize_t numread = xread(pipe_feed[0], dst + dstsize,
-                                       dstalloc - dstsize);
-
-               if (numread <= 0) {
-                       if (!numread)
-                               break;
-                       error("read from external filter %s failed", cmd);
-                       free(dst);
-                       dst = NULL;
-                       break;
-               }
-               dstsize += numread;
-               if (dstalloc <= dstsize + SLOP) {
-                       dstalloc = dstsize + SLOP;
-                       dst = xrealloc(dst, dstalloc);
-               }
+       strbuf_init(&nbuf, 0);
+       if (strbuf_read(&nbuf, pipe_feed[0], len) < 0) {
+               error("read from external filter %s failed", cmd);
+               ret = 0;
        }
        if (close(pipe_feed[0])) {
-               error("read from external filter %s failed", cmd);
-               free(dst);
-               dst = NULL;
+               ret = error("read from external filter %s failed", cmd);
+               ret = 0;
        }
-
        status = finish_command(&child_process);
        if (status) {
-               error("external filter %s failed %d", cmd, -status);
-               free(dst);
-               dst = NULL;
+               ret = error("external filter %s failed %d", cmd, -status);
+               ret = 0;
        }
 
-       if (dst)
-               *sizep = dstsize;
-       return dst;
+       if (ret) {
+               *dst = nbuf;
+       } else {
+               strbuf_release(&nbuf);
+       }
+       return ret;
 }
 
 static struct convert_driver {
@@ -353,13 +323,8 @@ static int read_convert_config(const char *var, const char *value)
                if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
                        break;
        if (!drv) {
-               char *namebuf;
                drv = xcalloc(1, sizeof(struct convert_driver));
-               namebuf = xmalloc(namelen + 1);
-               memcpy(namebuf, name, namelen);
-               namebuf[namelen] = 0;
-               drv->name = namebuf;
-               drv->next = NULL;
+               drv->name = xmemdupz(name, namelen);
                *user_convert_tail = drv;
                user_convert_tail = &(drv->next);
        }
@@ -449,137 +414,104 @@ static int count_ident(const char *cp, unsigned long size)
        return cnt;
 }
 
-static char *ident_to_git(const char *path, const char *src, unsigned long *sizep, int ident)
+static int ident_to_git(const char *path, const char *src, size_t len,
+                        struct strbuf *buf, int ident)
 {
-       int cnt;
-       unsigned long size;
-       char *dst, *buf;
+       char *dst, *dollar;
 
-       if (!ident)
-               return NULL;
-       size = *sizep;
-       cnt = count_ident(src, size);
-       if (!cnt)
-               return NULL;
-       buf = xmalloc(size);
-
-       for (dst = buf; size; size--) {
-               char ch = *src++;
-               *dst++ = ch;
-               if ((ch == '$') && (3 <= size) &&
-                   !memcmp("Id:", src, 3)) {
-                       unsigned long rem = size - 3;
-                       const char *cp = src + 3;
-                       do {
-                               ch = *cp++;
-                               if (ch == '$')
-                                       break;
-                               rem--;
-                       } while (rem);
-                       if (!rem)
-                               continue;
+       if (!ident || !count_ident(src, len))
+               return 0;
+
+       strbuf_grow(buf, len);
+       dst = buf->buf;
+       for (;;) {
+               dollar = memchr(src, '$', len);
+               if (!dollar)
+                       break;
+               memcpy(dst, src, dollar + 1 - src);
+               dst += dollar + 1 - src;
+               len -= dollar + 1 - src;
+               src  = dollar + 1;
+
+               if (len > 3 && !memcmp(src, "Id:", 3)) {
+                       dollar = memchr(src + 3, '$', len - 3);
+                       if (!dollar)
+                               break;
                        memcpy(dst, "Id$", 3);
                        dst += 3;
-                       size -= (cp - src);
-                       src = cp;
+                       len -= dollar + 1 - src;
+                       src  = dollar + 1;
                }
        }
-
-       *sizep = dst - buf;
-       return buf;
+       memcpy(dst, src, len);
+       strbuf_setlen(buf, dst + len - buf->buf);
+       return 1;
 }
 
-static char *ident_to_worktree(const char *path, const char *src, unsigned long *sizep, int ident)
+static int ident_to_worktree(const char *path, const char *src, size_t len,
+                             struct strbuf *buf, int ident)
 {
-       int cnt;
-       unsigned long size;
-       char *dst, *buf;
        unsigned char sha1[20];
+       char *to_free = NULL, *dollar;
+       int cnt;
 
        if (!ident)
-               return NULL;
+               return 0;
 
-       size = *sizep;
-       cnt = count_ident(src, size);
+       cnt = count_ident(src, len);
        if (!cnt)
-               return NULL;
+               return 0;
 
-       hash_sha1_file(src, size, "blob", sha1);
-       buf = xmalloc(size + cnt * 43);
-
-       for (dst = buf; size; size--) {
-               const char *cp;
-               /* Fetch next source character, move the pointer on */
-               char ch = *src++;
-               /* Copy the current character to the destination */
-               *dst++ = ch;
-               /* If the current character is "$" or there are less than three
-                * remaining bytes or the two bytes following this one are not
-                * "Id", then simply read the next character */
-               if ((ch != '$') || (size < 3) || memcmp("Id", src, 2))
-                       continue;
-               /*
-                * Here when
-                *  - There are more than 2 bytes remaining
-                *  - The current three bytes are "$Id"
-                * with
-                *  - ch == "$"
-                *  - src[0] == "I"
-                */
+       /* are we "faking" in place editing ? */
+       if (src == buf->buf)
+               to_free = strbuf_detach(buf, NULL);
+       hash_sha1_file(src, len, "blob", sha1);
 
-               /*
-                * It's possible that an expanded Id has crept its way into the
-                * repository, we cope with that by stripping the expansion out
-                */
-               if (src[2] == ':') {
-                       /* Expanded keywords have "$Id:" at the front */
+       strbuf_grow(buf, len + cnt * 43);
+       for (;;) {
+               /* step 1: run to the next '$' */
+               dollar = memchr(src, '$', len);
+               if (!dollar)
+                       break;
+               strbuf_add(buf, src, dollar + 1 - src);
+               len -= dollar + 1 - src;
+               src  = dollar + 1;
 
-                       /* discard up to but not including the closing $ */
-                       unsigned long rem = size - 3;
-                       /* Point at first byte after the ":" */
-                       cp = src + 3;
-                       /*
-                        * Throw away characters until either
-                        *  - we reach a "$"
-                        *  - we run out of bytes (rem == 0)
-                        */
-                       do {
-                               ch = *cp;
-                               if (ch == '$')
-                                       break;
-                               cp++;
-                               rem--;
-                       } while (rem);
-                       /* If the above finished because it ran out of characters, then
-                        * this is an incomplete keyword, so don't run the expansion */
-                       if (!rem)
-                               continue;
-               } else if (src[2] == '$')
-                       cp = src + 2;
-               else
-                       /* Anything other than "$Id:XXX$" or $Id$ and we skip the
-                        * expansion */
+               /* step 2: does it looks like a bit like Id:xxx$ or Id$ ? */
+               if (len < 3 || memcmp("Id", src, 2))
                        continue;
 
-               /* cp is now pointing at the last $ of the keyword */
-
-               memcpy(dst, "Id: ", 4);
-               dst += 4;
-               memcpy(dst, sha1_to_hex(sha1), 40);
-               dst += 40;
-               *dst++ = ' ';
+               /* step 3: skip over Id$ or Id:xxxxx$ */
+               if (src[2] == '$') {
+                       src += 3;
+                       len -= 3;
+               } else if (src[2] == ':') {
+                       /*
+                        * It's possible that an expanded Id has crept its way into the
+                        * repository, we cope with that by stripping the expansion out
+                        */
+                       dollar = memchr(src + 3, '$', len - 3);
+                       if (!dollar) {
+                               /* incomplete keyword, no more '$', so just quit the loop */
+                               break;
+                       }
 
-               /* Adjust for the characters we've discarded */
-               size -= (cp - src);
-               src = cp;
+                       len -= dollar + 1 - src;
+                       src  = dollar + 1;
+               } else {
+                       /* it wasn't a "Id$" or "Id:xxxx$" */
+                       continue;
+               }
 
-               /* Copy the final "$" */
-               *dst++ = *src++;
-               size--;
+               /* step 4: substitute */
+               strbuf_addstr(buf, "Id: ");
+               strbuf_add(buf, sha1_to_hex(sha1), 40);
+               strbuf_addstr(buf, " $");
        }
+       strbuf_add(buf, src, len);
 
-       *sizep = dst - buf;
-       return buf;
+       free(to_free);
+       return 1;
 }
 
 static int git_path_check_crlf(const char *path, struct git_attr_check *check)
@@ -618,13 +550,12 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)
        return !!ATTR_TRUE(value);
 }
 
-char *convert_to_git(const char *path, const char *src, unsigned long *sizep)
+int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst)
 {
        struct git_attr_check check[3];
        int crlf = CRLF_GUESS;
-       int ident = 0;
+       int ident = 0, ret = 0;
        char *filter = NULL;
-       char *buf, *buf2;
 
        setup_convert_check(check);
        if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
@@ -636,30 +567,25 @@ char *convert_to_git(const char *path, const char *src, unsigned long *sizep)
                        filter = drv->clean;
        }
 
-       buf = apply_filter(path, src, sizep, filter);
-
-       buf2 = crlf_to_git(path, buf ? buf : src, sizep, crlf);
-       if (buf2) {
-               free(buf);
-               buf = buf2;
+       ret |= apply_filter(path, src, len, dst, filter);
+       if (ret) {
+               src = dst->buf;
+               len = dst->len;
        }
-
-       buf2 = ident_to_git(path, buf ? buf : src, sizep, ident);
-       if (buf2) {
-               free(buf);
-               buf = buf2;
+       ret |= crlf_to_git(path, src, len, dst, crlf);
+       if (ret) {
+               src = dst->buf;
+               len = dst->len;
        }
-
-       return buf;
+       return ret | ident_to_git(path, src, len, dst, ident);
 }
 
-char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep)
+int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
 {
        struct git_attr_check check[3];
        int crlf = CRLF_GUESS;
-       int ident = 0;
+       int ident = 0, ret = 0;
        char *filter = NULL;
-       char *buf, *buf2;
 
        setup_convert_check(check);
        if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
@@ -671,19 +597,15 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long *
                        filter = drv->smudge;
        }
 
-       buf = ident_to_worktree(path, src, sizep, ident);
-
-       buf2 = crlf_to_worktree(path, buf ? buf : src, sizep, crlf);
-       if (buf2) {
-               free(buf);
-               buf = buf2;
+       ret |= ident_to_worktree(path, src, len, dst, ident);
+       if (ret) {
+               src = dst->buf;
+               len = dst->len;
        }
-
-       buf2 = apply_filter(path, buf ? buf : src, sizep, filter);
-       if (buf2) {
-               free(buf);
-               buf = buf2;
+       ret |= crlf_to_worktree(path, src, len, dst, crlf);
+       if (ret) {
+               src = dst->buf;
+               len = dst->len;
        }
-
-       return buf;
+       return ret | apply_filter(path, src, len, dst, filter);
 }
diff --git a/date.c b/date.c
index 93bef6efbe38cb8983fdda14b75ce772f90e1b6a..8f7050027053a4e2390097e341327b117404c26a 100644 (file)
--- a/date.c
+++ b/date.c
@@ -584,6 +584,26 @@ int parse_date(const char *date, char *result, int maxlen)
        return date_string(then, offset, result, maxlen);
 }
 
+enum date_mode parse_date_format(const char *format)
+{
+       if (!strcmp(format, "relative"))
+               return DATE_RELATIVE;
+       else if (!strcmp(format, "iso8601") ||
+                !strcmp(format, "iso"))
+               return DATE_ISO8601;
+       else if (!strcmp(format, "rfc2822") ||
+                !strcmp(format, "rfc"))
+               return DATE_RFC2822;
+       else if (!strcmp(format, "short"))
+               return DATE_SHORT;
+       else if (!strcmp(format, "local"))
+               return DATE_LOCAL;
+       else if (!strcmp(format, "default"))
+               return DATE_NORMAL;
+       else
+               die("unknown date format %s", format);
+}
+
 void datestamp(char *buf, int bufsize)
 {
        time_t now;
diff --git a/diff.c b/diff.c
index 0ee9ea1c1b47b82710a3850e7e9f679e4486f7bd..6648e015213913e693b4450230a5a0896a21f7a0 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -83,13 +83,8 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val
                if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
                        break;
        if (!drv) {
-               char *namebuf;
                drv = xcalloc(1, sizeof(struct ll_diff_driver));
-               namebuf = xmalloc(namelen + 1);
-               memcpy(namebuf, name, namelen);
-               namebuf[namelen] = 0;
-               drv->name = namebuf;
-               drv->next = NULL;
+               drv->name = xmemdupz(name, namelen);
                if (!user_diff_tail)
                        user_diff_tail = &user_diff;
                *user_diff_tail = drv;
@@ -126,12 +121,8 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v
                if (!strncmp(pp->name, name, namelen) && !pp->name[namelen])
                        break;
        if (!pp) {
-               char *namebuf;
                pp = xcalloc(1, sizeof(*pp));
-               namebuf = xmalloc(namelen + 1);
-               memcpy(namebuf, name, namelen);
-               namebuf[namelen] = 0;
-               pp->name = namebuf;
+               pp->name = xmemdupz(name, namelen);
                pp->next = funcname_pattern_list;
                funcname_pattern_list = pp;
        }
@@ -190,44 +181,23 @@ int git_diff_ui_config(const char *var, const char *value)
        return git_default_config(var, value);
 }
 
-static char *quote_one(const char *str)
-{
-       int needlen;
-       char *xp;
-
-       if (!str)
-               return NULL;
-       needlen = quote_c_style(str, NULL, NULL, 0);
-       if (!needlen)
-               return xstrdup(str);
-       xp = xmalloc(needlen + 1);
-       quote_c_style(str, xp, NULL, 0);
-       return xp;
-}
-
 static char *quote_two(const char *one, const char *two)
 {
        int need_one = quote_c_style(one, NULL, NULL, 1);
        int need_two = quote_c_style(two, NULL, NULL, 1);
-       char *xp;
+       struct strbuf res;
 
+       strbuf_init(&res, 0);
        if (need_one + need_two) {
-               if (!need_one) need_one = strlen(one);
-               if (!need_two) need_one = strlen(two);
-
-               xp = xmalloc(need_one + need_two + 3);
-               xp[0] = '"';
-               quote_c_style(one, xp + 1, NULL, 1);
-               quote_c_style(two, xp + need_one + 1, NULL, 1);
-               strcpy(xp + need_one + need_two + 1, "\"");
-               return xp;
+               strbuf_addch(&res, '"');
+               quote_c_style(one, &res, NULL, 1);
+               quote_c_style(two, &res, NULL, 1);
+               strbuf_addch(&res, '"');
+       } else {
+               strbuf_addstr(&res, one);
+               strbuf_addstr(&res, two);
        }
-       need_one = strlen(one);
-       need_two = strlen(two);
-       xp = xmalloc(need_one + need_two + 1);
-       strcpy(xp, one);
-       strcpy(xp + need_one, two);
-       return xp;
+       return strbuf_detach(&res, NULL);
 }
 
 static const char *external_diff(void)
@@ -679,27 +649,20 @@ static char *pprint_rename(const char *a, const char *b)
 {
        const char *old = a;
        const char *new = b;
-       char *name = NULL;
+       struct strbuf name;
        int pfx_length, sfx_length;
        int len_a = strlen(a);
        int len_b = strlen(b);
+       int a_midlen, b_midlen;
        int qlen_a = quote_c_style(a, NULL, NULL, 0);
        int qlen_b = quote_c_style(b, NULL, NULL, 0);
 
+       strbuf_init(&name, 0);
        if (qlen_a || qlen_b) {
-               if (qlen_a) len_a = qlen_a;
-               if (qlen_b) len_b = qlen_b;
-               name = xmalloc( len_a + len_b + 5 );
-               if (qlen_a)
-                       quote_c_style(a, name, NULL, 0);
-               else
-                       memcpy(name, a, len_a);
-               memcpy(name + len_a, " => ", 4);
-               if (qlen_b)
-                       quote_c_style(b, name + len_a + 4, NULL, 0);
-               else
-                       memcpy(name + len_a + 4, b, len_b + 1);
-               return name;
+               quote_c_style(a, &name, NULL, 0);
+               strbuf_addstr(&name, " => ");
+               quote_c_style(b, &name, NULL, 0);
+               return strbuf_detach(&name, NULL);
        }
 
        /* Find common prefix */
@@ -728,24 +691,26 @@ static char *pprint_rename(const char *a, const char *b)
         * pfx{sfx-a => sfx-b}
         * name-a => name-b
         */
+       a_midlen = len_a - pfx_length - sfx_length;
+       b_midlen = len_b - pfx_length - sfx_length;
+       if (a_midlen < 0)
+               a_midlen = 0;
+       if (b_midlen < 0)
+               b_midlen = 0;
+
+       strbuf_grow(&name, pfx_length + a_midlen + b_midlen + sfx_length + 7);
        if (pfx_length + sfx_length) {
-               int a_midlen = len_a - pfx_length - sfx_length;
-               int b_midlen = len_b - pfx_length - sfx_length;
-               if (a_midlen < 0) a_midlen = 0;
-               if (b_midlen < 0) b_midlen = 0;
-
-               name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7);
-               sprintf(name, "%.*s{%.*s => %.*s}%s",
-                       pfx_length, a,
-                       a_midlen, a + pfx_length,
-                       b_midlen, b + pfx_length,
-                       a + len_a - sfx_length);
+               strbuf_add(&name, a, pfx_length);
+               strbuf_addch(&name, '{');
        }
-       else {
-               name = xmalloc(len_a + len_b + 5);
-               sprintf(name, "%s => %s", a, b);
+       strbuf_add(&name, a + pfx_length, a_midlen);
+       strbuf_addstr(&name, " => ");
+       strbuf_add(&name, b + pfx_length, b_midlen);
+       if (pfx_length + sfx_length) {
+               strbuf_addch(&name, '}');
+               strbuf_add(&name, a + len_a - sfx_length, sfx_length);
        }
-       return name;
+       return strbuf_detach(&name, NULL);
 }
 
 struct diffstat_t {
@@ -858,12 +823,13 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
                int change = file->added + file->deleted;
 
                if (!file->is_renamed) {  /* renames are already quoted by pprint_rename */
-                       len = quote_c_style(file->name, NULL, NULL, 0);
-                       if (len) {
-                               char *qname = xmalloc(len + 1);
-                               quote_c_style(file->name, qname, NULL, 0);
+                       struct strbuf buf;
+                       strbuf_init(&buf, 0);
+                       if (quote_c_style(file->name, &buf, NULL, 0)) {
                                free(file->name);
-                               file->name = qname;
+                               file->name = strbuf_detach(&buf, NULL);
+                       } else {
+                               strbuf_release(&buf);
                        }
                }
 
@@ -1001,12 +967,12 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
                        printf("-\t-\t");
                else
                        printf("%d\t%d\t", file->added, file->deleted);
-               if (options->line_termination && !file->is_renamed &&
-                   quote_c_style(file->name, NULL, NULL, 0))
-                       quote_c_style(file->name, NULL, stdout, 0);
-               else
+               if (!file->is_renamed) {
+                       write_name_quoted(file->name, stdout, options->line_termination);
+               } else {
                        fputs(file->name, stdout);
-               putchar(options->line_termination);
+                       putchar(options->line_termination);
+               }
        }
 }
 
@@ -1545,26 +1511,15 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
 
 static int populate_from_stdin(struct diff_filespec *s)
 {
-#define INCREMENT 1024
-       char *buf;
-       unsigned long size;
-       ssize_t got;
-
-       size = 0;
-       buf = NULL;
-       while (1) {
-               buf = xrealloc(buf, size + INCREMENT);
-               got = xread(0, buf + size, INCREMENT);
-               if (!got)
-                       break; /* EOF */
-               if (got < 0)
-                       return error("error while reading from stdin %s",
+       struct strbuf buf;
+
+       strbuf_init(&buf, 0);
+       if (strbuf_read(&buf, 0, 0) < 0)
+               return error("error while reading from stdin %s",
                                     strerror(errno));
-               size += got;
-       }
+
        s->should_munmap = 0;
-       s->data = buf;
-       s->size = size;
+       s->data = strbuf_detach(&buf, &s->size);
        s->should_free = 1;
        return 0;
 }
@@ -1609,10 +1564,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
 
        if (!s->sha1_valid ||
            reuse_worktree_file(s->path, s->sha1, 0)) {
+               struct strbuf buf;
                struct stat st;
                int fd;
-               char *buf;
-               unsigned long size;
 
                if (!strcmp(s->path, "-"))
                        return populate_from_stdin(s);
@@ -1653,13 +1607,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                /*
                 * Convert from working tree format to canonical git format
                 */
-               size = s->size;
-               buf = convert_to_git(s->path, s->data, &size);
-               if (buf) {
+               strbuf_init(&buf, 0);
+               if (convert_to_git(s->path, s->data, s->size, &buf)) {
                        munmap(s->data, s->size);
                        s->should_munmap = 0;
-                       s->data = buf;
-                       s->size = size;
+                       s->data = strbuf_detach(&buf, &s->size);
                        s->should_free = 1;
                }
        }
@@ -1675,7 +1627,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
        return 0;
 }
 
-void diff_free_filespec_data(struct diff_filespec *s)
+void diff_free_filespec_blob(struct diff_filespec *s)
 {
        if (s->should_free)
                free(s->data);
@@ -1686,6 +1638,11 @@ void diff_free_filespec_data(struct diff_filespec *s)
                s->should_free = s->should_munmap = 0;
                s->data = NULL;
        }
+}
+
+void diff_free_filespec_data(struct diff_filespec *s)
+{
+       diff_free_filespec_blob(s);
        free(s->cnt_data);
        s->cnt_data = NULL;
 }
@@ -1962,50 +1919,46 @@ static int similarity_index(struct diff_filepair *p)
 static void run_diff(struct diff_filepair *p, struct diff_options *o)
 {
        const char *pgm = external_diff();
-       char msg[PATH_MAX*2+300], *xfrm_msg;
-       struct diff_filespec *one;
-       struct diff_filespec *two;
+       struct strbuf msg;
+       char *xfrm_msg;
+       struct diff_filespec *one = p->one;
+       struct diff_filespec *two = p->two;
        const char *name;
        const char *other;
-       char *name_munged, *other_munged;
        int complete_rewrite = 0;
-       int len;
+
 
        if (DIFF_PAIR_UNMERGED(p)) {
-               /* unmerged */
                run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
                return;
        }
 
-       name = p->one->path;
+       name  = p->one->path;
        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
-       name_munged = quote_one(name);
-       other_munged = quote_one(other);
-       one = p->one; two = p->two;
-
        diff_fill_sha1_info(one);
        diff_fill_sha1_info(two);
 
-       len = 0;
+       strbuf_init(&msg, PATH_MAX * 2 + 300);
        switch (p->status) {
        case DIFF_STATUS_COPIED:
-               len += snprintf(msg + len, sizeof(msg) - len,
-                               "similarity index %d%%\n"
-                               "copy from %s\n"
-                               "copy to %s\n",
-                               similarity_index(p), name_munged, other_munged);
+               strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
+               strbuf_addstr(&msg, "\ncopy from ");
+               quote_c_style(name, &msg, NULL, 0);
+               strbuf_addstr(&msg, "\ncopy to ");
+               quote_c_style(other, &msg, NULL, 0);
+               strbuf_addch(&msg, '\n');
                break;
        case DIFF_STATUS_RENAMED:
-               len += snprintf(msg + len, sizeof(msg) - len,
-                               "similarity index %d%%\n"
-                               "rename from %s\n"
-                               "rename to %s\n",
-                               similarity_index(p), name_munged, other_munged);
+               strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
+               strbuf_addstr(&msg, "\nrename from ");
+               quote_c_style(name, &msg, NULL, 0);
+               strbuf_addstr(&msg, "\nrename to ");
+               quote_c_style(other, &msg, NULL, 0);
+               strbuf_addch(&msg, '\n');
                break;
        case DIFF_STATUS_MODIFIED:
                if (p->score) {
-                       len += snprintf(msg + len, sizeof(msg) - len,
-                                       "dissimilarity index %d%%\n",
+                       strbuf_addf(&msg, "dissimilarity index %d%%\n",
                                        similarity_index(p));
                        complete_rewrite = 1;
                        break;
@@ -2025,19 +1978,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
                            (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
                                abbrev = 40;
                }
-               len += snprintf(msg + len, sizeof(msg) - len,
-                               "index %.*s..%.*s",
+               strbuf_addf(&msg, "index %.*s..%.*s",
                                abbrev, sha1_to_hex(one->sha1),
                                abbrev, sha1_to_hex(two->sha1));
                if (one->mode == two->mode)
-                       len += snprintf(msg + len, sizeof(msg) - len,
-                                       " %06o", one->mode);
-               len += snprintf(msg + len, sizeof(msg) - len, "\n");
+                       strbuf_addf(&msg, " %06o", one->mode);
+               strbuf_addch(&msg, '\n');
        }
 
-       if (len)
-               msg[--len] = 0;
-       xfrm_msg = len ? msg : NULL;
+       if (msg.len)
+               strbuf_setlen(&msg, msg.len - 1);
+       xfrm_msg = msg.len ? msg.buf : NULL;
 
        if (!pgm &&
            DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
@@ -2056,8 +2007,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
                run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
                             complete_rewrite);
 
-       free(name_munged);
-       free(other_munged);
+       strbuf_release(&msg);
 }
 
 static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
@@ -2513,72 +2463,30 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)
        return sha1_to_hex(sha1);
 }
 
-static void diff_flush_raw(struct diff_filepair *p,
-                          struct diff_options *options)
+static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
 {
-       int two_paths;
-       char status[10];
-       int abbrev = options->abbrev;
-       const char *path_one, *path_two;
-       int inter_name_termination = '\t';
-       int line_termination = options->line_termination;
-
-       if (!line_termination)
-               inter_name_termination = 0;
+       int line_termination = opt->line_termination;
+       int inter_name_termination = line_termination ? '\t' : '\0';
 
-       path_one = p->one->path;
-       path_two = p->two->path;
-       if (line_termination) {
-               path_one = quote_one(path_one);
-               path_two = quote_one(path_two);
+       if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
+               printf(":%06o %06o %s ", p->one->mode, p->two->mode,
+                      diff_unique_abbrev(p->one->sha1, opt->abbrev));
+               printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
        }
-
-       if (p->score)
-               sprintf(status, "%c%03d", p->status, similarity_index(p));
-       else {
-               status[0] = p->status;
-               status[1] = 0;
-       }
-       switch (p->status) {
-       case DIFF_STATUS_COPIED:
-       case DIFF_STATUS_RENAMED:
-               two_paths = 1;
-               break;
-       case DIFF_STATUS_ADDED:
-       case DIFF_STATUS_DELETED:
-               two_paths = 0;
-               break;
-       default:
-               two_paths = 0;
-               break;
-       }
-       if (!(options->output_format & DIFF_FORMAT_NAME_STATUS)) {
-               printf(":%06o %06o %s ",
-                      p->one->mode, p->two->mode,
-                      diff_unique_abbrev(p->one->sha1, abbrev));
-               printf("%s ",
-                      diff_unique_abbrev(p->two->sha1, abbrev));
+       if (p->score) {
+               printf("%c%03d%c", p->status, similarity_index(p),
+                          inter_name_termination);
+       } else {
+               printf("%c%c", p->status, inter_name_termination);
        }
-       printf("%s%c%s", status, inter_name_termination,
-                       two_paths || p->one->mode ?  path_one : path_two);
-       if (two_paths)
-               printf("%c%s", inter_name_termination, path_two);
-       putchar(line_termination);
-       if (path_one != p->one->path)
-               free((void*)path_one);
-       if (path_two != p->two->path)
-               free((void*)path_two);
-}
-
-static void diff_flush_name(struct diff_filepair *p, struct diff_options *opt)
-{
-       char *path = p->two->path;
 
-       if (opt->line_termination)
-               path = quote_one(p->two->path);
-       printf("%s%c", path, opt->line_termination);
-       if (p->two->path != path)
-               free(path);
+       if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {
+               write_name_quoted(p->one->path, stdout, inter_name_termination);
+               write_name_quoted(p->two->path, stdout, line_termination);
+       } else {
+               const char *path = p->one->mode ? p->one->path : p->two->path;
+               write_name_quoted(path, stdout, line_termination);
+       }
 }
 
 int diff_unmodified_pair(struct diff_filepair *p)
@@ -2588,14 +2496,11 @@ int diff_unmodified_pair(struct diff_filepair *p)
         * let transformers to produce diff_filepairs any way they want,
         * and filter and clean them up here before producing the output.
         */
-       struct diff_filespec *one, *two;
+       struct diff_filespec *one = p->one, *two = p->two;
 
        if (DIFF_PAIR_UNMERGED(p))
                return 0; /* unmerged is interesting */
 
-       one = p->one;
-       two = p->two;
-
        /* deletion, addition, mode or type change
         * and rename are all interesting.
         */
@@ -2784,32 +2689,27 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
        else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
                diff_flush_raw(p, opt);
        else if (fmt & DIFF_FORMAT_NAME)
-               diff_flush_name(p, opt);
+               write_name_quoted(p->two->path, stdout, opt->line_termination);
 }
 
 static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
 {
-       char *name = quote_one(fs->path);
        if (fs->mode)
-               printf(" %s mode %06o %s\n", newdelete, fs->mode, name);
+               printf(" %s mode %06o ", newdelete, fs->mode);
        else
-               printf(" %s %s\n", newdelete, name);
-       free(name);
+               printf(" %s ", newdelete);
+       write_name_quoted(fs->path, stdout, '\n');
 }
 
 
 static void show_mode_change(struct diff_filepair *p, int show_name)
 {
        if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
+               printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode,
+                       show_name ? ' ' : '\n');
                if (show_name) {
-                       char *name = quote_one(p->two->path);
-                       printf(" mode change %06o => %06o %s\n",
-                              p->one->mode, p->two->mode, name);
-                       free(name);
+                       write_name_quoted(p->two->path, stdout, '\n');
                }
-               else
-                       printf(" mode change %06o => %06o\n",
-                              p->one->mode, p->two->mode);
        }
 }
 
@@ -2839,12 +2739,11 @@ static void diff_summary(struct diff_filepair *p)
                break;
        default:
                if (p->score) {
-                       char *name = quote_one(p->two->path);
-                       printf(" rewrite %s (%d%%)\n", name,
-                              similarity_index(p));
-                       free(name);
-                       show_mode_change(p, 0);
-               } else  show_mode_change(p, 1);
+                       puts(" rewrite ");
+                       write_name_quoted(p->two->path, stdout, ' ');
+                       printf("(%d%%)\n", similarity_index(p));
+               }
+               show_mode_change(p, !p->score);
                break;
        }
 }
@@ -2858,14 +2757,14 @@ struct patch_id_t {
 static int remove_space(char *line, int len)
 {
        int i;
-        char *dst = line;
-        unsigned char c;
+       char *dst = line;
+       unsigned char c;
 
-        for (i = 0; i < len; i++)
-                if (!isspace((c = line[i])))
-                        *dst++ = c;
+       for (i = 0; i < len; i++)
+               if (!isspace((c = line[i])))
+                       *dst++ = c;
 
-        return dst - line;
+       return dst - line;
 }
 
 static void patch_id_consume(void *priv, char *line, unsigned long len)
index 2a4bd8232eb185f195c513c3509a72a92d172818..23e93852d8c701760d56e7e728dba7c08367fbe8 100644 (file)
@@ -48,11 +48,8 @@ static void prepare_order(const char *orderfile)
                                if (*ep == '\n') {
                                        *ep = 0;
                                        order[cnt] = cp;
-                               }
-                               else {
-                                       order[cnt] = xmalloc(ep-cp+1);
-                                       memcpy(order[cnt], cp, ep-cp);
-                                       order[cnt][ep-cp] = 0;
+                               } else {
+                                       order[cnt] = xmemdupz(cp, ep - cp);
                                }
                                cnt++;
                        }
index 41b35c3a9e6935f6cd8563de732321e74e115765..142e5376dd741377c311075816f139a0949ee82f 100644 (file)
@@ -184,7 +184,8 @@ static int estimate_similarity(struct diff_filespec *src,
        if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
                return 0;
 
-       if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
+       if ((!src->cnt_data && diff_populate_filespec(src, 0))
+               || (!dst->cnt_data && diff_populate_filespec(dst, 0)))
                return 0; /* error but caught downstream */
 
 
@@ -377,10 +378,10 @@ void diffcore_rename(struct diff_options *options)
                        m->score = estimate_similarity(one, two,
                                                       minimum_score);
                        m->name_score = basename_same(one, two);
-                       diff_free_filespec_data(one);
+                       diff_free_filespec_blob(one);
                }
                /* We do not need the text anymore */
-               diff_free_filespec_data(two);
+               diff_free_filespec_blob(two);
                dst_cnt++;
        }
        /* cost matrix sorted by most to least similar pair */
index eef17c4ca2e81c572fb110e9eb11e8ed9d51f9a0..eb618b1ec00113dabcd5231f141f82e1cdfdca46 100644 (file)
@@ -48,6 +48,7 @@ extern void fill_filespec(struct diff_filespec *, const unsigned char *,
 
 extern int diff_populate_filespec(struct diff_filespec *, int);
 extern void diff_free_filespec_data(struct diff_filespec *);
+extern void diff_free_filespec_blob(struct diff_filespec *);
 extern int diff_filespec_is_binary(struct diff_filespec *);
 
 struct diff_filepair {
diff --git a/entry.c b/entry.c
index fc3a506ecef4ed654f26c11996df109dd135a4ed..98f5f6d4ecfc0dabae9a920bdf3beadbf20abcaf 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -104,7 +104,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
        long wrote;
 
        switch (ntohl(ce->ce_mode) & S_IFMT) {
-               char *buf, *new;
+               char *new;
+               struct strbuf buf;
                unsigned long size;
 
        case S_IFREG:
@@ -116,10 +117,10 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                /*
                 * Convert from git internal format to working tree format
                 */
-               buf = convert_to_working_tree(ce->name, new, &size);
-               if (buf) {
+               strbuf_init(&buf, 0);
+               if (convert_to_working_tree(ce->name, new, size, &buf)) {
                        free(new);
-                       new = buf;
+                       new = strbuf_detach(&buf, &size);
                }
 
                if (to_tempfile) {
index 078079d404d8245d73ea8ef36d764f2bb311d0a9..e9c80be4cd3f2833dd227e3d2f3c69e2145935e2 100644 (file)
@@ -149,7 +149,6 @@ Format of STDIN stream:
 #include "pack.h"
 #include "refs.h"
 #include "csum-file.h"
-#include "strbuf.h"
 #include "quote.h"
 
 #define PACK_ID_BITS 16
@@ -183,11 +182,10 @@ struct mark_set
 
 struct last_object
 {
-       void *data;
-       unsigned long len;
+       struct strbuf data;
        uint32_t offset;
        unsigned int depth;
-       unsigned no_free:1;
+       unsigned no_swap : 1;
 };
 
 struct mem_pool
@@ -251,12 +249,6 @@ struct tag
        unsigned char sha1[20];
 };
 
-struct dbuf
-{
-       void *buffer;
-       size_t capacity;
-};
-
 struct hash_list
 {
        struct hash_list *next;
@@ -317,15 +309,15 @@ static struct mark_set *marks;
 static const char* mark_file;
 
 /* Our last blob */
-static struct last_object last_blob;
+static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 };
 
 /* Tree management */
 static unsigned int tree_entry_alloc = 1000;
 static void *avail_tree_entry;
 static unsigned int avail_tree_table_sz = 100;
 static struct avail_tree_content **avail_tree_table;
-static struct dbuf old_tree;
-static struct dbuf new_tree;
+static struct strbuf old_tree = STRBUF_INIT;
+static struct strbuf new_tree = STRBUF_INIT;
 
 /* Branch data */
 static unsigned long max_active_branches = 5;
@@ -340,14 +332,14 @@ static struct tag *last_tag;
 
 /* Input stream parsing */
 static whenspec_type whenspec = WHENSPEC_RAW;
-static struct strbuf command_buf;
+static struct strbuf command_buf = STRBUF_INIT;
 static int unread_command_buf;
 static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL};
 static struct recent_command *cmd_tail = &cmd_hist;
 static struct recent_command *rc_free;
 static unsigned int cmd_save = 100;
 static uintmax_t next_mark;
-static struct dbuf new_data;
+static struct strbuf new_data = STRBUF_INIT;
 
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
@@ -567,17 +559,6 @@ static char *pool_strdup(const char *s)
        return r;
 }
 
-static void size_dbuf(struct dbuf *b, size_t maxlen)
-{
-       if (b->buffer) {
-               if (b->capacity >= maxlen)
-                       return;
-               free(b->buffer);
-       }
-       b->capacity = ((maxlen / 1024) + 1) * 1024;
-       b->buffer = xmalloc(b->capacity);
-}
-
 static void insert_mark(uintmax_t idnum, struct object_entry *oe)
 {
        struct mark_set *s = marks;
@@ -968,9 +949,7 @@ static void end_packfile(void)
        free(old_p);
 
        /* We can't carry a delta across packfiles. */
-       free(last_blob.data);
-       last_blob.data = NULL;
-       last_blob.len = 0;
+       strbuf_release(&last_blob.data);
        last_blob.offset = 0;
        last_blob.depth = 0;
 }
@@ -1006,8 +985,7 @@ static size_t encode_header(
 
 static int store_object(
        enum object_type type,
-       void *dat,
-       size_t datlen,
+       struct strbuf *dat,
        struct last_object *last,
        unsigned char *sha1out,
        uintmax_t mark)
@@ -1021,10 +999,10 @@ static int store_object(
        z_stream s;
 
        hdrlen = sprintf((char*)hdr,"%s %lu", typename(type),
-               (unsigned long)datlen) + 1;
+               (unsigned long)dat->len) + 1;
        SHA1_Init(&c);
        SHA1_Update(&c, hdr, hdrlen);
-       SHA1_Update(&c, dat, datlen);
+       SHA1_Update(&c, dat->buf, dat->len);
        SHA1_Final(sha1, &c);
        if (sha1out)
                hashcpy(sha1out, sha1);
@@ -1043,11 +1021,11 @@ static int store_object(
                return 1;
        }
 
-       if (last && last->data && last->depth < max_depth) {
-               delta = diff_delta(last->data, last->len,
-                       dat, datlen,
+       if (last && last->data.buf && last->depth < max_depth) {
+               delta = diff_delta(last->data.buf, last->data.len,
+                       dat->buf, dat->len,
                        &deltalen, 0);
-               if (delta && deltalen >= datlen) {
+               if (delta && deltalen >= dat->len) {
                        free(delta);
                        delta = NULL;
                }
@@ -1060,8 +1038,8 @@ static int store_object(
                s.next_in = delta;
                s.avail_in = deltalen;
        } else {
-               s.next_in = dat;
-               s.avail_in = datlen;
+               s.next_in = (void *)dat->buf;
+               s.avail_in = dat->len;
        }
        s.avail_out = deflateBound(&s, s.avail_in);
        s.next_out = out = xmalloc(s.avail_out);
@@ -1084,8 +1062,8 @@ static int store_object(
 
                        memset(&s, 0, sizeof(s));
                        deflateInit(&s, zlib_compression_level);
-                       s.next_in = dat;
-                       s.avail_in = datlen;
+                       s.next_in = (void *)dat->buf;
+                       s.avail_in = dat->len;
                        s.avail_out = deflateBound(&s, s.avail_in);
                        s.next_out = out = xrealloc(out, s.avail_out);
                        while (deflate(&s, Z_FINISH) == Z_OK)
@@ -1119,7 +1097,7 @@ static int store_object(
        } else {
                if (last)
                        last->depth = 0;
-               hdrlen = encode_header(type, datlen, hdr);
+               hdrlen = encode_header(type, dat->len, hdr);
                write_or_die(pack_data->pack_fd, hdr, hdrlen);
                pack_size += hdrlen;
        }
@@ -1130,11 +1108,12 @@ static int store_object(
        free(out);
        free(delta);
        if (last) {
-               if (!last->no_free)
-                       free(last->data);
-               last->data = dat;
+               if (last->no_swap) {
+                       last->data = *dat;
+               } else {
+                       strbuf_swap(&last->data, dat);
+               }
                last->offset = e->offset;
-               last->len = datlen;
        }
        return 0;
 }
@@ -1230,14 +1209,10 @@ static int tecmp1 (const void *_a, const void *_b)
                b->name->str_dat, b->name->str_len, b->versions[1].mode);
 }
 
-static void mktree(struct tree_content *t,
-       int v,
-       unsigned long *szp,
-       struct dbuf *b)
+static void mktree(struct tree_content *t, int v, struct strbuf *b)
 {
        size_t maxlen = 0;
        unsigned int i;
-       char *c;
 
        if (!v)
                qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0);
@@ -1249,28 +1224,23 @@ static void mktree(struct tree_content *t,
                        maxlen += t->entries[i]->name->str_len + 34;
        }
 
-       size_dbuf(b, maxlen);
-       c = b->buffer;
+       strbuf_reset(b);
+       strbuf_grow(b, maxlen);
        for (i = 0; i < t->entry_count; i++) {
                struct tree_entry *e = t->entries[i];
                if (!e->versions[v].mode)
                        continue;
-               c += sprintf(c, "%o", (unsigned int)e->versions[v].mode);
-               *c++ = ' ';
-               strcpy(c, e->name->str_dat);
-               c += e->name->str_len + 1;
-               hashcpy((unsigned char*)c, e->versions[v].sha1);
-               c += 20;
+               strbuf_addf(b, "%o %s%c", (unsigned int)e->versions[v].mode,
+                                       e->name->str_dat, '\0');
+               strbuf_add(b, e->versions[v].sha1, 20);
        }
-       *szp = c - (char*)b->buffer;
 }
 
 static void store_tree(struct tree_entry *root)
 {
        struct tree_content *t = root->tree;
        unsigned int i, j, del;
-       unsigned long new_len;
-       struct last_object lo;
+       struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
        struct object_entry *le;
 
        if (!is_null_sha1(root->versions[1].sha1))
@@ -1282,23 +1252,15 @@ static void store_tree(struct tree_entry *root)
        }
 
        le = find_object(root->versions[0].sha1);
-       if (!S_ISDIR(root->versions[0].mode)
-               || !le
-               || le->pack_id != pack_id) {
-               lo.data = NULL;
-               lo.depth = 0;
-               lo.no_free = 0;
-       } else {
-               mktree(t, 0, &lo.len, &old_tree);
-               lo.data = old_tree.buffer;
+       if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
+               mktree(t, 0, &old_tree);
+               lo.data = old_tree;
                lo.offset = le->offset;
                lo.depth = t->delta_depth;
-               lo.no_free = 1;
        }
 
-       mktree(t, 1, &new_len, &new_tree);
-       store_object(OBJ_TREE, new_tree.buffer, new_len,
-               &lo, root->versions[1].sha1, 0);
+       mktree(t, 1, &new_tree);
+       store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0);
 
        t->delta_depth = lo.depth;
        for (i = 0, j = 0, del = 0; i < t->entry_count; i++) {
@@ -1585,20 +1547,25 @@ static void dump_marks(void)
                        mark_file, strerror(errno));
 }
 
-static void read_next_command(void)
+static int read_next_command(void)
 {
+       static int stdin_eof = 0;
+
+       if (stdin_eof) {
+               unread_command_buf = 0;
+               return EOF;
+       }
+
        do {
                if (unread_command_buf) {
                        unread_command_buf = 0;
-                       if (command_buf.eof)
-                               return;
                } else {
                        struct recent_command *rc;
 
-                       command_buf.buf = NULL;
-                       read_line(&command_buf, stdin, '\n');
-                       if (command_buf.eof)
-                               return;
+                       strbuf_detach(&command_buf, NULL);
+                       stdin_eof = strbuf_getline(&command_buf, stdin, '\n');
+                       if (stdin_eof)
+                               return EOF;
 
                        rc = rc_free;
                        if (rc)
@@ -1617,6 +1584,8 @@ static void read_next_command(void)
                        cmd_tail = rc;
                }
        } while (command_buf.buf[0] == '#');
+
+       return 0;
 }
 
 static void skip_optional_lf(void)
@@ -1636,42 +1605,35 @@ static void cmd_mark(void)
                next_mark = 0;
 }
 
-static void *cmd_data (size_t *size)
+static void cmd_data(struct strbuf *sb)
 {
-       size_t length;
-       char *buffer;
+       strbuf_reset(sb);
 
        if (prefixcmp(command_buf.buf, "data "))
                die("Expected 'data n' command, found: %s", command_buf.buf);
 
        if (!prefixcmp(command_buf.buf + 5, "<<")) {
                char *term = xstrdup(command_buf.buf + 5 + 2);
-               size_t sz = 8192, term_len = command_buf.len - 5 - 2;
-               length = 0;
-               buffer = xmalloc(sz);
-               command_buf.buf = NULL;
+               size_t term_len = command_buf.len - 5 - 2;
+
                for (;;) {
-                       read_line(&command_buf, stdin, '\n');
-                       if (command_buf.eof)
+                       if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
                                die("EOF in data (terminator '%s' not found)", term);
                        if (term_len == command_buf.len
                                && !strcmp(term, command_buf.buf))
                                break;
-                       ALLOC_GROW(buffer, length + command_buf.len, sz);
-                       memcpy(buffer + length,
-                               command_buf.buf,
-                               command_buf.len - 1);
-                       length += command_buf.len - 1;
-                       buffer[length++] = '\n';
+                       strbuf_addbuf(sb, &command_buf);
+                       strbuf_addch(sb, '\n');
                }
                free(term);
        }
        else {
-               size_t n = 0;
+               size_t n = 0, length;
+
                length = strtoul(command_buf.buf + 5, NULL, 10);
-               buffer = xmalloc(length);
+
                while (n < length) {
-                       size_t s = fread(buffer + n, 1, length - n, stdin);
+                       size_t s = strbuf_fread(sb, length - n, stdin);
                        if (!s && feof(stdin))
                                die("EOF in data (%lu bytes remaining)",
                                        (unsigned long)(length - n));
@@ -1680,8 +1642,6 @@ static void *cmd_data (size_t *size)
        }
 
        skip_optional_lf();
-       *size = length;
-       return buffer;
 }
 
 static int validate_raw_date(const char *src, char *result, int maxlen)
@@ -1744,15 +1704,12 @@ static char *parse_ident(const char *buf)
 
 static void cmd_new_blob(void)
 {
-       size_t l;
-       void *d;
+       static struct strbuf buf = STRBUF_INIT;
 
        read_next_command();
        cmd_mark();
-       d = cmd_data(&l);
-
-       if (store_object(OBJ_BLOB, d, l, &last_blob, NULL, next_mark))
-               free(d);
+       cmd_data(&buf);
+       store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark);
 }
 
 static void unload_one_branch(void)
@@ -1802,7 +1759,7 @@ static void load_branch(struct branch *b)
 static void file_change_m(struct branch *b)
 {
        const char *p = command_buf.buf + 2;
-       char *p_uq;
+       static struct strbuf uq = STRBUF_INIT;
        const char *endp;
        struct object_entry *oe = oe;
        unsigned char sha1[20];
@@ -1840,22 +1797,23 @@ static void file_change_m(struct branch *b)
        if (*p++ != ' ')
                die("Missing space after SHA1: %s", command_buf.buf);
 
-       p_uq = unquote_c_style(p, &endp);
-       if (p_uq) {
+       strbuf_reset(&uq);
+       if (!unquote_c_style(&uq, p, &endp)) {
                if (*endp)
                        die("Garbage after path in: %s", command_buf.buf);
-               p = p_uq;
+               p = uq.buf;
        }
 
        if (inline_data) {
-               size_t l;
-               void *d;
-               if (!p_uq)
-                       p = p_uq = xstrdup(p);
+               static struct strbuf buf = STRBUF_INIT;
+
+               if (p != uq.buf) {
+                       strbuf_addstr(&uq, p);
+                       p = uq.buf;
+               }
                read_next_command();
-               d = cmd_data(&l);
-               if (store_object(OBJ_BLOB, d, l, &last_blob, sha1, 0))
-                       free(d);
+               cmd_data(&buf);
+               store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
        } else if (oe) {
                if (oe->type != OBJ_BLOB)
                        die("Not a blob (actually a %s): %s",
@@ -1870,58 +1828,54 @@ static void file_change_m(struct branch *b)
        }
 
        tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode, NULL);
-       free(p_uq);
 }
 
 static void file_change_d(struct branch *b)
 {
        const char *p = command_buf.buf + 2;
-       char *p_uq;
+       static struct strbuf uq = STRBUF_INIT;
        const char *endp;
 
-       p_uq = unquote_c_style(p, &endp);
-       if (p_uq) {
+       strbuf_reset(&uq);
+       if (!unquote_c_style(&uq, p, &endp)) {
                if (*endp)
                        die("Garbage after path in: %s", command_buf.buf);
-               p = p_uq;
+               p = uq.buf;
        }
        tree_content_remove(&b->branch_tree, p, NULL);
-       free(p_uq);
 }
 
 static void file_change_cr(struct branch *b, int rename)
 {
        const char *s, *d;
-       char *s_uq, *d_uq;
+       static struct strbuf s_uq = STRBUF_INIT;
+       static struct strbuf d_uq = STRBUF_INIT;
        const char *endp;
        struct tree_entry leaf;
 
        s = command_buf.buf + 2;
-       s_uq = unquote_c_style(s, &endp);
-       if (s_uq) {
+       strbuf_reset(&s_uq);
+       if (!unquote_c_style(&s_uq, s, &endp)) {
                if (*endp != ' ')
                        die("Missing space after source: %s", command_buf.buf);
-       }
-       else {
+       } else {
                endp = strchr(s, ' ');
                if (!endp)
                        die("Missing space after source: %s", command_buf.buf);
-               s_uq = xmalloc(endp - s + 1);
-               memcpy(s_uq, s, endp - s);
-               s_uq[endp - s] = 0;
+               strbuf_add(&s_uq, s, endp - s);
        }
-       s = s_uq;
+       s = s_uq.buf;
 
        endp++;
        if (!*endp)
                die("Missing dest: %s", command_buf.buf);
 
        d = endp;
-       d_uq = unquote_c_style(d, &endp);
-       if (d_uq) {
+       strbuf_reset(&d_uq);
+       if (!unquote_c_style(&d_uq, d, &endp)) {
                if (*endp)
                        die("Garbage after dest in: %s", command_buf.buf);
-               d = d_uq;
+               d = d_uq.buf;
        }
 
        memset(&leaf, 0, sizeof(leaf));
@@ -1935,9 +1889,6 @@ static void file_change_cr(struct branch *b, int rename)
                leaf.versions[1].sha1,
                leaf.versions[1].mode,
                leaf.tree);
-
-       free(s_uq);
-       free(d_uq);
 }
 
 static void file_change_deleteall(struct branch *b)
@@ -2062,9 +2013,8 @@ static struct hash_list *cmd_merge(unsigned int *count)
 
 static void cmd_new_commit(void)
 {
+       static struct strbuf msg = STRBUF_INIT;
        struct branch *b;
-       void *msg;
-       size_t msglen;
        char *sp;
        char *author = NULL;
        char *committer = NULL;
@@ -2089,7 +2039,7 @@ static void cmd_new_commit(void)
        }
        if (!committer)
                die("Expected committer but didn't get one");
-       msg = cmd_data(&msglen);
+       cmd_data(&msg);
        read_next_command();
        cmd_from(b);
        merge_list = cmd_merge(&merge_count);
@@ -2101,7 +2051,7 @@ static void cmd_new_commit(void)
        }
 
        /* file_change* */
-       while (!command_buf.eof && command_buf.len > 1) {
+       while (command_buf.len > 0) {
                if (!prefixcmp(command_buf.buf, "M "))
                        file_change_m(b);
                else if (!prefixcmp(command_buf.buf, "D "))
@@ -2116,53 +2066,47 @@ static void cmd_new_commit(void)
                        unread_command_buf = 1;
                        break;
                }
-               read_next_command();
+               if (read_next_command() == EOF)
+                       break;
        }
 
        /* build the tree and the commit */
        store_tree(&b->branch_tree);
        hashcpy(b->branch_tree.versions[0].sha1,
                b->branch_tree.versions[1].sha1);
-       size_dbuf(&new_data, 114 + msglen
-               + merge_count * 49
-               + (author
-                       ? strlen(author) + strlen(committer)
-                       : 2 * strlen(committer)));
-       sp = new_data.buffer;
-       sp += sprintf(sp, "tree %s\n",
+
+       strbuf_reset(&new_data);
+       strbuf_addf(&new_data, "tree %s\n",
                sha1_to_hex(b->branch_tree.versions[1].sha1));
        if (!is_null_sha1(b->sha1))
-               sp += sprintf(sp, "parent %s\n", sha1_to_hex(b->sha1));
+               strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1));
        while (merge_list) {
                struct hash_list *next = merge_list->next;
-               sp += sprintf(sp, "parent %s\n", sha1_to_hex(merge_list->sha1));
+               strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1));
                free(merge_list);
                merge_list = next;
        }
-       sp += sprintf(sp, "author %s\n", author ? author : committer);
-       sp += sprintf(sp, "committer %s\n", committer);
-       *sp++ = '\n';
-       memcpy(sp, msg, msglen);
-       sp += msglen;
+       strbuf_addf(&new_data,
+               "author %s\n"
+               "committer %s\n"
+               "\n",
+               author ? author : committer, committer);
+       strbuf_addbuf(&new_data, &msg);
        free(author);
        free(committer);
-       free(msg);
 
-       if (!store_object(OBJ_COMMIT,
-               new_data.buffer, sp - (char*)new_data.buffer,
-               NULL, b->sha1, next_mark))
+       if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark))
                b->pack_id = pack_id;
        b->last_commit = object_count_by_type[OBJ_COMMIT];
 }
 
 static void cmd_new_tag(void)
 {
+       static struct strbuf msg = STRBUF_INIT;
        char *sp;
        const char *from;
        char *tagger;
        struct branch *s;
-       void *msg;
-       size_t msglen;
        struct tag *t;
        uintmax_t from_mark = 0;
        unsigned char sha1[20];
@@ -2213,24 +2157,21 @@ static void cmd_new_tag(void)
 
        /* tag payload/message */
        read_next_command();
-       msg = cmd_data(&msglen);
+       cmd_data(&msg);
 
        /* build the tag object */
-       size_dbuf(&new_data, 67+strlen(t->name)+strlen(tagger)+msglen);
-       sp = new_data.buffer;
-       sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1));
-       sp += sprintf(sp, "type %s\n", commit_type);
-       sp += sprintf(sp, "tag %s\n", t->name);
-       sp += sprintf(sp, "tagger %s\n", tagger);
-       *sp++ = '\n';
-       memcpy(sp, msg, msglen);
-       sp += msglen;
+       strbuf_reset(&new_data);
+       strbuf_addf(&new_data,
+               "object %s\n"
+               "type %s\n"
+               "tag %s\n"
+               "tagger %s\n"
+               "\n",
+               sha1_to_hex(sha1), commit_type, t->name, tagger);
+       strbuf_addbuf(&new_data, &msg);
        free(tagger);
-       free(msg);
 
-       if (store_object(OBJ_TAG, new_data.buffer,
-               sp - (char*)new_data.buffer,
-               NULL, t->sha1, 0))
+       if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0))
                t->pack_id = MAX_PACK_ID;
        else
                t->pack_id = pack_id;
@@ -2256,7 +2197,7 @@ static void cmd_reset_branch(void)
        else
                b = new_branch(sp);
        read_next_command();
-       if (!cmd_from(b) && command_buf.len > 1)
+       if (!cmd_from(b) && command_buf.len > 0)
                unread_command_buf = 1;
 }
 
@@ -2273,7 +2214,7 @@ static void cmd_checkpoint(void)
 
 static void cmd_progress(void)
 {
-       fwrite(command_buf.buf, 1, command_buf.len - 1, stdout);
+       fwrite(command_buf.buf, 1, command_buf.len, stdout);
        fputc('\n', stdout);
        fflush(stdout);
        skip_optional_lf();
@@ -2323,7 +2264,7 @@ int main(int argc, const char **argv)
 
        git_config(git_default_config);
        alloc_objects(object_entry_alloc);
-       strbuf_init(&command_buf);
+       strbuf_init(&command_buf, 0);
        atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*));
        branch_table = xcalloc(branch_table_sz, sizeof(struct branch*));
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
@@ -2381,11 +2322,8 @@ int main(int argc, const char **argv)
        prepare_packed_git();
        start_packfile();
        set_die_routine(die_nicely);
-       for (;;) {
-               read_next_command();
-               if (command_buf.eof)
-                       break;
-               else if (!strcmp("blob", command_buf.buf))
+       while (read_next_command() != EOF) {
+               if (!strcmp("blob", command_buf.buf))
                        cmd_new_blob();
                else if (!prefixcmp(command_buf.buf, "commit "))
                        cmd_new_commit();
diff --git a/fetch.c b/fetch.c
index 811be87a3c1e0d14d9f2b37650d56575b49caa22..b1c1f07b2a91b6abebe29ceb1d5f2f7afe2e452e 100644 (file)
--- a/fetch.c
+++ b/fetch.c
@@ -6,7 +6,6 @@
 #include "tag.h"
 #include "blob.h"
 #include "refs.h"
-#include "strbuf.h"
 
 int get_tree = 0;
 int get_history = 0;
@@ -218,13 +217,12 @@ int pull_targets_stdin(char ***target, const char ***write_ref)
        int targets = 0, targets_alloc = 0;
        struct strbuf buf;
        *target = NULL; *write_ref = NULL;
-       strbuf_init(&buf);
+       strbuf_init(&buf, 0);
        while (1) {
                char *rf_one = NULL;
                char *tg_one;
 
-               read_line(&buf, stdin, '\n');
-               if (buf.eof)
+               if (strbuf_getline(&buf, stdin, '\n') == EOF)
                        break;
                tg_one = buf.buf;
                rf_one = strchr(tg_one, '\t');
@@ -240,6 +238,7 @@ int pull_targets_stdin(char ***target, const char ***write_ref)
                (*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL;
                targets++;
        }
+       strbuf_release(&buf);
        return targets;
 }
 
index 7921cde8cbd3b58f1d7222e9828bf85e10f363e3..be6881496c88fcffc52031e482bfdd8d03faa66c 100755 (executable)
@@ -213,9 +213,13 @@ sub list_and_choose {
                        print ">> ";
                }
                my $line = <STDIN>;
-               last if (!$line);
+               if (!$line) {
+                       print "\n";
+                       $opts->{ON_EOF}->() if $opts->{ON_EOF};
+                       last;
+               }
                chomp $line;
-               my $donesomething = 0;
+               last if $line eq '';
                for my $choice (split(/[\s,]+/, $line)) {
                        my $choose = 1;
                        my ($bottom, $top);
@@ -247,12 +251,11 @@ sub list_and_choose {
                                next TOPLOOP;
                        }
                        for ($i = $bottom-1; $i <= $top-1; $i++) {
-                               next if (@stuff <= $i);
+                               next if (@stuff <= $i || $i < 0);
                                $chosen[$i] = $choose;
-                               $donesomething++;
                        }
                }
-               last if (!$donesomething || $opts->{IMMEDIATE});
+               last if ($opts->{IMMEDIATE});
        }
        for ($i = 0; $i < @stuff; $i++) {
                if ($chosen[$i]) {
@@ -791,6 +794,7 @@ sub main_loop {
                                             SINGLETON => 1,
                                             LIST_FLAT => 4,
                                             HEADER => '*** Commands ***',
+                                            ON_EOF => \&quit_cmd,
                                             IMMEDIATE => 1 }, @cmd);
                if ($it) {
                        eval {
index 32c46d7ed4b26220f4c9e7fc778bb240c85dae1c..b02ae6a76fa8265da1ea3d720df9dc29cc5d5519 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -464,6 +464,8 @@ do
                "$GIT_DIR"/hooks/post-applypatch
        fi
 
+       git gc --auto
+
        go_next
 done
 
index 17f43927aa7b766c1ba28150c4945fac7ff3e132..89939206732849e968fabaf9128597c87f18f5c7 100755 (executable)
@@ -137,6 +137,13 @@ Did you intend to checkout '$@' which can not be resolved as commit?"
        git ls-files --error-unmatch -- "$@" >/dev/null || exit
        git ls-files -- "$@" |
        git checkout-index -f -u --stdin
+
+        # Run a post-checkout hook -- the HEAD does not change so the
+        # current HEAD is passed in for both args
+       if test -x "$GIT_DIR"/hooks/post-checkout; then
+           "$GIT_DIR"/hooks/post-checkout $old $old 0
+       fi
+
        exit $?
 else
        # Make sure we did not fall back on $arg^{tree} codepath
@@ -284,3 +291,8 @@ if [ "$?" -eq 0 ]; then
 else
        exit 1
 fi
+
+# Run a post-checkout hook
+if test -x "$GIT_DIR"/hooks/post-checkout; then
+        "$GIT_DIR"/hooks/post-checkout $old $new 1
+fi
index cb14f0621651d2006b08d1eddf67ab3269df84d0..fcb8443bdfa2a87a436ae79471d91c89e944fffd 100755 (executable)
@@ -25,6 +25,7 @@ refuse_partial () {
        exit 1
 }
 
+TMP_INDEX=
 THIS_INDEX="$GIT_DIR/index"
 NEXT_INDEX="$GIT_DIR/next-index$$"
 rm -f "$NEXT_INDEX"
@@ -611,6 +612,7 @@ git rerere
 
 if test "$ret" = 0
 then
+       git gc --auto
        if test -x "$GIT_DIR"/hooks/post-commit
        then
                "$GIT_DIR"/hooks/post-commit
index 1bfbdeb94f55d57b429b91aa8762618153c34f7f..f23d934f667cc2b10ee668a3bfef8492aadbbe7d 100644 (file)
@@ -211,19 +211,20 @@ static inline void *xmalloc(size_t size)
        return ret;
 }
 
-static inline char *xstrndup(const char *str, size_t len)
+static inline void *xmemdupz(const void *data, size_t len)
 {
-       char *p;
-
-       p = memchr(str, '\0', len);
-       if (p)
-               len = p - str;
-       p = xmalloc(len + 1);
-       memcpy(p, str, len);
+       char *p = xmalloc(len + 1);
+       memcpy(p, data, len);
        p[len] = '\0';
        return p;
 }
 
+static inline char *xstrndup(const char *str, size_t len)
+{
+       char *p = memchr(str, '\0', len);
+       return xmemdupz(str, p ? p - str : len);
+}
+
 static inline void *xrealloc(void *ptr, size_t size)
 {
        void *ret = realloc(ptr, size);
index 6c513dcbdf44036b0207c276e765a87eceb7aa77..c2092a204035ad0315a3d37ed2f70097e68ed052 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
-USAGE='[-n] [--summary] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
+USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s <strategy>] [-m=<merge-message>] <commit>+'
 
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
@@ -59,7 +59,7 @@ finish_up_to_date () {
 squash_message () {
        echo Squashed commit of the following:
        echo
-       git log --no-merges ^"$head" $remote
+       git log --no-merges ^"$head" $remoteheads
 }
 
 finish () {
@@ -82,6 +82,7 @@ finish () {
                        ;;
                *)
                        git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
+                       git gc --auto
                        ;;
                esac
                ;;
@@ -132,11 +133,7 @@ merge_name () {
        fi
 }
 
-case "$#" in 0) usage ;; esac
-
-have_message=
-while test $# != 0
-do
+parse_option () {
        case "$1" in
        -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
                --no-summa|--no-summar|--no-summary)
@@ -144,9 +141,17 @@ do
        --summary)
                show_diffstat=t ;;
        --sq|--squ|--squa|--squas|--squash)
-               squash=t no_commit=t ;;
+               allow_fast_forward=t squash=t no_commit=t ;;
+       --no-sq|--no-squ|--no-squa|--no-squas|--no-squash)
+               allow_fast_forward=t squash= no_commit= ;;
+       --c|--co|--com|--comm|--commi|--commit)
+               allow_fast_forward=t squash= no_commit= ;;
        --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
-               no_commit=t ;;
+               allow_fast_forward=t squash= no_commit=t ;;
+       --ff)
+               allow_fast_forward=t squash= no_commit= ;;
+       --no-ff)
+               allow_fast_forward=false squash= no_commit= ;;
        -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
                --strateg=*|--strategy=*|\
        -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
@@ -179,9 +184,42 @@ do
                have_message=t
                ;;
        -*)     usage ;;
-       *)      break ;;
+       *)      return 1 ;;
        esac
        shift
+       args_left=$#
+}
+
+parse_config () {
+       while test $# -gt 0
+       do
+               parse_option "$@" || usage
+               while test $args_left -lt $#
+               do
+                       shift
+               done
+       done
+}
+
+test $# != 0 || usage
+
+have_message=
+
+if branch=$(git-symbolic-ref -q HEAD)
+then
+       mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions")
+       if test -n "$mergeopts"
+       then
+               parse_config $mergeopts
+       fi
+fi
+
+while parse_option "$@"
+do
+       while test $args_left -lt $#
+       do
+               shift
+       done
 done
 
 if test -z "$show_diffstat"; then
@@ -457,7 +495,13 @@ done
 # auto resolved the merge cleanly.
 if test '' != "$result_tree"
 then
-    parents=$(git show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
+    if test "$allow_fast_forward" = "t"
+    then
+        parents=$(git show-branch --independent "$head" "$@")
+    else
+        parents=$(git rev-parse "$head" "$@")
+    fi
+    parents=$(echo "$parents" | sed -e 's/^/-p /')
     result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
     finish "$result_commit" "Merge made by $wt_strategy."
     dropsave
index a0e44f71c4acd5d995bf823913ba63e119ed2e68..9f4f3134b60174495c23968cf1ca01ffdbc49bdf 100755 (executable)
@@ -12,6 +12,7 @@ USAGE='[--tool=tool] [file to merge] ...'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
 require_work_tree
+prefix=$(git rev-parse --show-prefix)
 
 # Returns true if the mode reflects a symlink
 is_symlink () {
@@ -162,9 +163,9 @@ merge_file () {
     local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
     remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
 
-    base_present   && git cat-file blob ":1:$path" > "$BASE" 2>/dev/null
-    local_present  && git cat-file blob ":2:$path" > "$LOCAL" 2>/dev/null
-    remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null
+    base_present   && git cat-file blob ":1:$prefix$path" >"$BASE" 2>/dev/null
+    local_present  && git cat-file blob ":2:$prefix$path" >"$LOCAL" 2>/dev/null
+    remote_present && git cat-file blob ":3:$prefix$path" >"$REMOTE" 2>/dev/null
 
     if test -z "$local_mode" -o -z "$remote_mode"; then
        echo "Deleted merge conflict for '$path':"
@@ -191,10 +192,10 @@ merge_file () {
     case "$merge_tool" in
        kdiff3)
            if base_present ; then
-               (kdiff3 --auto --L1 "$path (Base)" -L2 "$path (Local)" --L3 "$path (Remote)" \
+               (kdiff3 --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
                    -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
            else
-               (kdiff3 --auto -L1 "$path (Local)" --L2 "$path (Remote)" \
+               (kdiff3 --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
                    -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
            fi
            status=$?
@@ -251,9 +252,9 @@ merge_file () {
            ;;
        emerge)
            if base_present ; then
-               emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$path"
+               emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
            else
-               emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$path"
+               emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
            fi
            status=$?
            save_backup
index c3f05f56de300ad48d940def184698fb37c40028..74bfc16744d69d8394f89b6f77d81697a8336032 100755 (executable)
@@ -97,10 +97,24 @@ case "$merge_head" in
        esac
        curr_branch=${curr_branch#refs/heads/}
 
-       echo >&2 "Warning: No merge candidate found because value of config option
-         \"branch.${curr_branch}.merge\" does not match any remote branch fetched."
-       echo >&2 "No changes."
-       exit 0
+       echo >&2 "You asked me to pull without telling me which branch you"
+       echo >&2 "want to merge with, and 'branch.${curr_branch}.merge' in"
+       echo >&2 "your configuration file does not tell me either.  Please"
+       echo >&2 "name which branch you want to merge on the command line and"
+       echo >&2 "try again (e.g. 'git pull <repository> <refspec>')."
+       echo >&2 "See git-pull(1) for details on the refspec."
+       echo >&2
+       echo >&2 "If you often merge with the same branch, you may want to"
+       echo >&2 "configure the following variables in your configuration"
+       echo >&2 "file:"
+       echo >&2
+       echo >&2 "    branch.${curr_branch}.remote = <nickname>"
+       echo >&2 "    branch.${curr_branch}.merge = <remote-ref>"
+       echo >&2 "    remote.<nickname>.url = <url>"
+       echo >&2 "    remote.<nickname>.fetch = <refspec>"
+       echo >&2
+       echo >&2 "See git-config(1) for details."
+       exit 1
        ;;
 ?*' '?*)
        if test -z "$orig_head"
index 74a54d5d08f86ebbd6a4b3af1f6950dbfd91c743..880c81d121bfb9555c729bc299f8ae8baf1db32c 100755 (executable)
@@ -71,6 +71,10 @@ commit=$(git rev-parse HEAD)
 
 mkdir $tmp_dir || exit 2
 for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do
+       if ! [ -f "$QUILT_PATCHES/$patch_name" ] ; then
+               echo "$patch_name doesn't exist. Skipping."
+               continue
+       fi
        echo $patch_name
        git mailinfo "$tmp_msg" "$tmp_patch" \
                <"$QUILT_PATCHES/$patch_name" >"$tmp_info" || exit 3
index 268a629c434c3cc1bad8a59861f3f093291ec540..050140d666b2d68ab91f6d41d64779178e99c70b 100755 (executable)
@@ -232,14 +232,14 @@ do_next () {
        '#'*|'')
                mark_action_done
                ;;
-       pick)
+       pick|p)
                comment_for_reflog pick
 
                mark_action_done
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
                ;;
-       edit)
+       edit|e)
                comment_for_reflog edit
 
                mark_action_done
@@ -254,7 +254,7 @@ do_next () {
                warn
                exit 0
                ;;
-       squash)
+       squash|s)
                comment_for_reflog squash
 
                has_action "$DONE" ||
@@ -263,7 +263,7 @@ do_next () {
                mark_action_done
                make_squash_message $sha1 > "$MSG"
                case "$(peek_next_command)" in
-               squash)
+               squash|s)
                        EDIT_COMMIT=
                        USE_OUTPUT=output
                        cp "$MSG" "$SQUASH_MSG"
@@ -276,9 +276,9 @@ do_next () {
                esac
 
                failed=f
+               author_script=$(get_author_ident_from_commit HEAD)
                output git reset --soft HEAD^
                pick_one -n $sha1 || failed=t
-               author_script=$(get_author_ident_from_commit $sha1)
                echo "$author_script" > "$DOTEST"/author-script
                case $failed in
                f)
@@ -317,13 +317,18 @@ do_next () {
        else
                NEWHEAD=$(git rev-parse HEAD)
        fi &&
-       message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
-       git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
-       git symbolic-ref HEAD $HEADNAME && {
+       case $HEADNAME in
+       refs/*)
+               message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
+               git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
+               git symbolic-ref HEAD $HEADNAME
+               ;;
+       esac && {
                test ! -f "$DOTEST"/verbose ||
                        git diff --stat $(cat "$DOTEST"/head)..HEAD
        } &&
        rm -rf "$DOTEST" &&
+       git gc --auto &&
        warn "Successfully rebased and updated $HEADNAME."
 
        exit
@@ -365,7 +370,11 @@ do
 
                HEADNAME=$(cat "$DOTEST"/head-name)
                HEAD=$(cat "$DOTEST"/head)
-               git symbolic-ref HEAD $HEADNAME &&
+               case $HEADNAME in
+               refs/*)
+                       git symbolic-ref HEAD $HEADNAME
+                       ;;
+               esac &&
                output git reset --hard $HEAD &&
                rm -rf "$DOTEST"
                exit
@@ -443,8 +452,8 @@ do
                test -z "$ONTO" && ONTO=$UPSTREAM
 
                : > "$DOTEST"/interactive || die "Could not mark as interactive"
-               git symbolic-ref HEAD > "$DOTEST"/head-name ||
-                       die "Could not get HEAD"
+               git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
+                       echo "detached HEAD" > "$DOTEST"/head-name
 
                echo $HEAD > "$DOTEST"/head
                echo $UPSTREAM > "$DOTEST"/upstream
index f6f283ea4f8b45671b7d9b34687d5d7b8475e8b3..9ca3e7ef3792260e98d68e1a95a2ccfceeac5c55 100755 (executable)
@@ -218,7 +218,7 @@ sub prune_remote {
        my ($name, $ls_remote) = @_;
        if (!exists $remote->{$name}) {
                print STDERR "No such remote $name\n";
-               return;
+               return 1;
        }
        my $info = $remote->{$name};
        update_ls_remote($ls_remote, $info);
@@ -229,13 +229,14 @@ sub prune_remote {
                my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune");
                $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]);
        }
+       return 0;
 }
 
 sub show_remote {
        my ($name, $ls_remote) = @_;
        if (!exists $remote->{$name}) {
                print STDERR "No such remote $name\n";
-               return;
+               return 1;
        }
        my $info = $remote->{$name};
        update_ls_remote($ls_remote, $info);
@@ -265,6 +266,7 @@ sub show_remote {
                print "  Local branch(es) pushed with 'git push'\n";
                print "    @pushed\n";
        }
+       return 0;
 }
 
 sub add_remote {
@@ -316,6 +318,34 @@ sub update_remote {
        }
 }
 
+sub rm_remote {
+       my ($name) = @_;
+       if (!exists $remote->{$name}) {
+               print STDERR "No such remote $name\n";
+               return 1;
+       }
+
+       $git->command('config', '--remove-section', "remote.$name");
+
+       eval {
+           my @trackers = $git->command('config', '--get-regexp',
+                       'branch.*.remote', $name);
+               for (@trackers) {
+                       /^branch\.(.*)?\.remote/;
+                       $git->config('--unset', "branch.$1.remote");
+                       $git->config('--unset', "branch.$1.merge");
+               }
+       };
+
+       my @refs = $git->command('for-each-ref',
+               '--format=%(refname) %(objectname)', "refs/remotes/$name");
+       for (@refs) {
+               ($ref, $object) = split;
+               $git->command(qw(update-ref -d), $ref, $object);
+       }
+       return 0;
+}
+
 sub add_usage {
        print STDERR "Usage: git remote add [-f] [-t track]* [-m master] <name> <url>\n";
        exit(1);
@@ -353,9 +383,11 @@ elsif ($ARGV[0] eq 'show') {
                print STDERR "Usage: git remote show <remote>\n";
                exit(1);
        }
+       my $status = 0;
        for (; $i < @ARGV; $i++) {
-               show_remote($ARGV[$i], $ls_remote);
+               $status |= show_remote($ARGV[$i], $ls_remote);
        }
+       exit($status);
 }
 elsif ($ARGV[0] eq 'update') {
        if (@ARGV <= 1) {
@@ -381,9 +413,11 @@ elsif ($ARGV[0] eq 'prune') {
                print STDERR "Usage: git remote prune <remote>\n";
                exit(1);
        }
+       my $status = 0;
        for (; $i < @ARGV; $i++) {
-               prune_remote($ARGV[$i], $ls_remote);
+               $status |= prune_remote($ARGV[$i], $ls_remote);
        }
+        exit($status);
 }
 elsif ($ARGV[0] eq 'add') {
        my %opts = ();
@@ -422,9 +456,17 @@ elsif ($ARGV[0] eq 'add') {
        }
        add_remote($ARGV[1], $ARGV[2], \%opts);
 }
+elsif ($ARGV[0] eq 'rm') {
+       if (@ARGV <= 1) {
+               print STDERR "Usage: git remote rm <remote>\n";
+               exit(1);
+       }
+       exit(rm_remote($ARGV[1]));
+}
 else {
        print STDERR "Usage: git remote\n";
        print STDERR "       git remote add <name> <url>\n";
+       print STDERR "       git remote rm <name>\n";
        print STDERR "       git remote show <name>\n";
        print STDERR "       git remote prune <name>\n";
        print STDERR "       git remote update [group]\n";
index 0aae1a3ed5571a010f80438f8e8a0fc7eb0dc285..e72adc4d91efb8eb6dc96c1f431c8863c408439b 100755 (executable)
@@ -3,17 +3,19 @@
 # Copyright (c) 2005 Linus Torvalds
 #
 
-USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--max-pack-size=N] [--window=N] [--window-memory=N] [--depth=N]'
+USAGE='[-a|-A] [-d] [-f] [-l] [-n] [-q] [--max-pack-size=N] [--window=N] [--window-memory=N] [--depth=N]'
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
-no_update_info= all_into_one= remove_redundant=
+no_update_info= all_into_one= remove_redundant= keep_unreachable=
 local= quiet= no_reuse= extra=
 while test $# != 0
 do
        case "$1" in
        -n)     no_update_info=t ;;
        -a)     all_into_one=t ;;
+       -A)     all_into_one=t
+               keep_unreachable=--keep-unreachable ;;
        -d)     remove_redundant=t ;;
        -q)     quiet=-q ;;
        -f)     no_reuse=--no-reuse-object ;;
@@ -59,7 +61,13 @@ case ",$all_into_one," in
                        fi
                done
        fi
-       [ -z "$args" ] && args='--unpacked --incremental'
+       if test -z "$args"
+       then
+               args='--unpacked --incremental'
+       elif test -n "$keep_unreachable"
+       then
+               args="$args $keep_unreachable"
+       fi
        ;;
 esac
 
index 4031e86b8688e62cc3a547ffd52dfc1b8234f0f4..62e1429733375356bdeb5054c3dca2581a5524fe 100755 (executable)
@@ -77,7 +77,10 @@ Options:
                   the default section.
 
    --smtp-server  If set, specifies the outgoing SMTP server to use.
-                  Defaults to localhost.
+                  Defaults to localhost.  Port number can be specified here with
+                  hostname:port format or by using --smtp-server-port option.
+
+   --smtp-server-port Specify a port on the outgoing SMTP server to connect to.
 
    --smtp-user    The username for SMTP-AUTH.
 
@@ -172,8 +175,8 @@ my ($quiet, $dry_run) = (0, 0);
 
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
-my ($smtp_server, $smtp_authuser, $smtp_authpass, $smtp_ssl);
-my ($identity, $aliasfiletype, @alias_files);
+my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_authpass, $smtp_ssl);
+my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
 
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
@@ -185,6 +188,7 @@ my %config_bool_settings = (
 
 my %config_settings = (
     "smtpserver" => \$smtp_server,
+    "smtpserverport" => \$smtp_server_port,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
     "cccmd" => \$cc_cmd,
@@ -204,6 +208,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                    "bcc=s" => \@bcclist,
                    "chain-reply-to!" => \$chain_reply_to,
                    "smtp-server=s" => \$smtp_server,
+                   "smtp-server-port=s" => \$smtp_server_port,
                    "smtp-user=s" => \$smtp_authuser,
                    "smtp-pass=s" => \$smtp_authpass,
                    "smtp-ssl!" => \$smtp_ssl,
@@ -602,16 +607,30 @@ X-Mailer: git-send-email $gitversion
                print $sm "$header\n$message";
                close $sm or die $?;
        } else {
+
+               if (!defined $smtp_server) {
+                       die "The required SMTP server is not properly defined."
+               }
+
                if ($smtp_ssl) {
+                       $smtp_server_port ||= 465; # ssmtp
                        require Net::SMTP::SSL;
-                       $smtp ||= Net::SMTP::SSL->new( $smtp_server, Port => 465 );
+                       $smtp ||= Net::SMTP::SSL->new($smtp_server, Port => $smtp_server_port);
                }
                else {
                        require Net::SMTP;
-                       $smtp ||= Net::SMTP->new( $smtp_server );
+                       $smtp ||= Net::SMTP->new((defined $smtp_server_port)
+                                                ? "$smtp_server:$smtp_server_port"
+                                                : $smtp_server);
+               }
+
+               if (!$smtp) {
+                       die "Unable to initialize SMTP properly.  Is there something wrong with your config?";
+               }
+
+               if ((defined $smtp_authuser) && (defined $smtp_authpass)) {
+                       $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message;
                }
-               $smtp->auth( $smtp_authuser, $smtp_authpass )
-                       or die $smtp->message if (defined $smtp_authuser);
                $smtp->mail( $raw_from ) or die $smtp->message;
                $smtp->to( @recipients ) or die $smtp->message;
                $smtp->data or die $smtp->message;
index 673aa27a452b11af62d89986bf21c8a4f10e0c41..4aaaaab0d8450094efb8d2f8e82cc63383b91b48 100755 (executable)
@@ -39,6 +39,32 @@ get_repo_base() {
        ) 2>/dev/null
 }
 
+# Resolve relative url by appending to parent's url
+resolve_relative_url ()
+{
+       branch="$(git symbolic-ref HEAD 2>/dev/null)"
+       remote="$(git config branch.${branch#refs/heads/}.remote)"
+       remote="${remote:-origin}"
+       remoteurl="$(git config remote.$remote.url)" ||
+               die "remote ($remote) does not have a url in .git/config"
+       url="$1"
+       while test -n "$url"
+       do
+               case "$url" in
+               ../*)
+                       url="${url#../}"
+                       remoteurl="${remoteurl%/*}"
+                       ;;
+               ./*)
+                       url="${url#./}"
+                       ;;
+               *)
+                       break;;
+               esac
+       done
+       echo "$remoteurl/$url"
+}
+
 #
 # Map submodule path to submodule name
 #
@@ -103,11 +129,19 @@ module_add()
                usage
        fi
 
-       # Turn the source into an absolute path if
-       # it is local
-       if base=$(get_repo_base "$repo"); then
-               repo="$base"
-       fi
+       case "$repo" in
+       ./*|../*)
+               # dereference source url relative to parent's url
+               realrepo="$(resolve_relative_url $repo)" ;;
+       *)
+               # Turn the source into an absolute path if
+               # it is local
+               if base=$(get_repo_base "$repo"); then
+                       repo="$base"
+               fi
+               realrepo=$repo
+               ;;
+       esac
 
        # Guess path from repo if not specified or strip trailing slashes
        if test -z "$path"; then
@@ -122,7 +156,7 @@ module_add()
        git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
        die "'$path' already exists in the index"
 
-       module_clone "$path" "$repo" || exit
+       module_clone "$path" "$realrepo" || exit
        (unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
        die "Unable to checkout submodule '$path'"
        git add "$path" ||
@@ -153,6 +187,13 @@ modules_init()
                test -z "$url" &&
                die "No url found for submodule path '$path' in .gitmodules"
 
+               # Possibly a url relative to parent
+               case "$url" in
+               ./*|../*)
+                       url="$(resolve_relative_url "$url")"
+                       ;;
+               esac
+
                git config submodule."$name".url "$url" ||
                die "Failed to register url for submodule path '$path'"
 
index aa5b3b2c9700d9a3cfe79011499405b67da6b33e..ea8c1b2f605c3d34f12357538f0f86848b15f078 100755 (executable)
@@ -54,6 +54,7 @@ my $branch_name = $opt_b || "branches";
 my $project_name = $opt_P || "";
 $project_name = "/" . $project_name if ($project_name);
 my $repack_after = $opt_R || 1000;
+my $root_pool = SVN::Pool->new_default;
 
 @ARGV == 1 or @ARGV == 2 or usage();
 
@@ -132,7 +133,7 @@ sub conn {
        my $auth = SVN::Core::auth_open ([SVN::Client::get_simple_provider,
                          SVN::Client::get_ssl_server_trust_file_provider,
                          SVN::Client::get_username_provider]);
-       my $s = SVN::Ra->new(url => $repo, auth => $auth);
+       my $s = SVN::Ra->new(url => $repo, auth => $auth, pool => $root_pool);
        die "SVN connection to $repo: $!\n" unless defined $s;
        $self->{'svn'} = $s;
        $self->{'repo'} = $repo;
@@ -147,11 +148,10 @@ sub file {
 
        print "... $rev $path ...\n" if $opt_v;
        my (undef, $properties);
-       my $pool = SVN::Pool->new();
        $path =~ s#^/*##;
+       my $subpool = SVN::Pool::new_default_sub;
        eval { (undef, $properties)
-                  = $self->{'svn'}->get_file($path,$rev,$fh,$pool); };
-       $pool->clear;
+                  = $self->{'svn'}->get_file($path,$rev,$fh); };
        if($@) {
                return undef if $@ =~ /Attempted to get checksum/;
                die $@;
@@ -185,6 +185,7 @@ sub ignore {
 
        print "... $rev $path ...\n" if $opt_v;
        $path =~ s#^/*##;
+       my $subpool = SVN::Pool::new_default_sub;
        my (undef,undef,$properties)
            = $self->{'svn'}->get_dir($path,$rev,undef);
        if (exists $properties->{'svn:ignore'}) {
@@ -202,6 +203,7 @@ sub ignore {
 sub dir_list {
        my($self,$path,$rev) = @_;
        $path =~ s#^/*##;
+       my $subpool = SVN::Pool::new_default_sub;
        my ($dirents,undef,$properties)
            = $self->{'svn'}->get_dir($path,$rev,undef);
        return $dirents;
@@ -358,10 +360,9 @@ open BRANCHES,">>", "$git_dir/svn2git";
 
 sub node_kind($$) {
        my ($svnpath, $revision) = @_;
-       my $pool=SVN::Pool->new;
        $svnpath =~ s#^/*##;
-       my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool);
-       $pool->clear;
+       my $subpool = SVN::Pool::new_default_sub;
+       my $kind = $svn->{'svn'}->check_path($svnpath,$revision);
        return $kind;
 }
 
@@ -889,7 +890,7 @@ sub commit_all {
        # Recursive use of the SVN connection does not work
        local $svn = $svn2;
 
-       my ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
+       my ($changed_paths, $revision, $author, $date, $message) = @_;
        my %p;
        while(my($path,$action) = each %$changed_paths) {
                $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ];
@@ -925,14 +926,14 @@ print "Processing from $current_rev to $opt_l ...\n" if $opt_v;
 my $from_rev;
 my $to_rev = $current_rev - 1;
 
+my $subpool = SVN::Pool::new_default_sub;
 while ($to_rev < $opt_l) {
+       $subpool->clear;
        $from_rev = $to_rev + 1;
        $to_rev = $from_rev + $repack_after;
        $to_rev = $opt_l if $opt_l < $to_rev;
        print "Fetching from $from_rev to $to_rev ...\n" if $opt_v;
-       my $pool=SVN::Pool->new;
-       $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all,$pool);
-       $pool->clear;
+       $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all);
        my $pid = fork();
        die "Fork: $!\n" unless defined $pid;
        unless($pid) {
diff --git a/git.c b/git.c
index 56ae8ccccf6917fb27f74d56d7d96a9c4788cb54..9eaca1d67149ee60d1fe23661b6a45139987b7a2 100644 (file)
--- a/git.c
+++ b/git.c
@@ -187,19 +187,13 @@ static int handle_alias(int *argcp, const char ***argv)
        if (alias_string) {
                if (alias_string[0] == '!') {
                        if (*argcp > 1) {
-                               int i, sz = PATH_MAX;
-                               char *s = xmalloc(sz), *new_alias = s;
+                               struct strbuf buf;
 
-                               add_to_string(&s, &sz, alias_string, 0);
+                               strbuf_init(&buf, PATH_MAX);
+                               strbuf_addstr(&buf, alias_string);
+                               sq_quote_argv(&buf, (*argv) + 1, *argcp - 1, PATH_MAX);
                                free(alias_string);
-                               alias_string = new_alias;
-                               for (i = 1; i < *argcp &&
-                                       !add_to_string(&s, &sz, " ", 0) &&
-                                       !add_to_string(&s, &sz, (*argv)[i], 1)
-                                       ; i++)
-                                       ; /* do nothing */
-                               if (!sz)
-                                       die("Too many or long arguments");
+                               alias_string = buf.buf;
                        }
                        trace_printf("trace: alias to shell cmd: %s => %s\n",
                                     alias_command, alias_string + 1);
index 7c3720f602bb8f50ed54a4f7e7a85c7e08d1c07b..276e1eb1d913f5e8f274545495e39ed7530d5244 100644 (file)
@@ -1271,10 +1271,7 @@ xml_cdata(void *userData, const XML_Char *s, int len)
 {
        struct xml_ctx *ctx = (struct xml_ctx *)userData;
        free(ctx->cdata);
-       ctx->cdata = xmalloc(len + 1);
-       /* NB: 's' is not null-terminated, can not use strlcpy here */
-       memcpy(ctx->cdata, s, len);
-       ctx->cdata[len] = '\0';
+       ctx->cdata = xmemdupz(s, len);
 }
 
 static struct remote_lock *lock_remote(const char *path, long timeout)
@@ -2172,9 +2169,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
 
        /* If it's a symref, set the refname; otherwise try for a sha1 */
        if (!prefixcmp((char *)buffer.buffer, "ref: ")) {
-               *symref = xmalloc(buffer.posn - 5);
-               memcpy(*symref, (char *)buffer.buffer + 5, buffer.posn - 6);
-               (*symref)[buffer.posn - 6] = '\0';
+               *symref = xmemdupz((char *)buffer.buffer + 5, buffer.posn - 6);
        } else {
                get_sha1_hex(buffer.buffer, sha1);
        }
index a5a069608419a8ecc42be63eb7998efd262f0ddc..a429a76a6385bb7d7935cfaddec9cfc8508c77e5 100644 (file)
@@ -105,6 +105,19 @@ static void free_generic_messages( message_t * );
 
 static int nfsnprintf( char *buf, int blen, const char *fmt, ... );
 
+static int nfvasprintf(char **strp, const char *fmt, va_list ap)
+{
+       int len;
+       char tmp[8192];
+
+       len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
+       if (len < 0)
+               die("Fatal: Out of memory\n");
+       if (len >= sizeof(tmp))
+               die("imap command overflow !\n");
+       *strp = xmemdupz(tmp, len);
+       return len;
+}
 
 static void arc4_init( void );
 static unsigned char arc4_getbyte( void );
@@ -623,9 +636,7 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
                                        goto bail;
                        cur->len = s - p;
                        s++;
-                       cur->val = xmalloc( cur->len + 1 );
-                       memcpy( cur->val, p, cur->len );
-                       cur->val[cur->len] = 0;
+                       cur->val = xmemdupz(p, cur->len);
                } else {
                        /* atom */
                        p = s;
@@ -633,12 +644,10 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
                                if (level && *s == ')')
                                        break;
                        cur->len = s - p;
-                       if (cur->len == 3 && !memcmp ("NIL", p, 3))
+                       if (cur->len == 3 && !memcmp ("NIL", p, 3)) {
                                cur->val = NIL;
-                       else {
-                               cur->val = xmalloc( cur->len + 1 );
-                               memcpy( cur->val, p, cur->len );
-                               cur->val[cur->len] = 0;
+                       } else {
+                               cur->val = xmemdupz(p, cur->len);
                        }
                }
 
@@ -1160,28 +1169,18 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
 static int
 read_message( FILE *f, msg_data_t *msg )
 {
-       int len, r;
+       struct strbuf buf;
 
-       memset( msg, 0, sizeof *msg );
-       len = CHUNKSIZE;
-       msg->data = xmalloc( len+1 );
-       msg->data[0] = 0;
-
-       while(!feof( f )) {
-               if (msg->len >= len) {
-                       void *p;
-                       len += CHUNKSIZE;
-                       p = xrealloc(msg->data, len+1);
-                       if (!p)
-                               break;
-                       msg->data = p;
-               }
-               r = fread( &msg->data[msg->len], 1, len - msg->len, f );
-               if (r <= 0)
+       memset(msg, 0, sizeof(*msg));
+       strbuf_init(&buf, 0);
+
+       do {
+               if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0)
                        break;
-               msg->len += r;
-       }
-       msg->data[msg->len] = 0;
+       } while (!feof(f));
+
+       msg->len  = buf.len;
+       msg->data = strbuf_detach(&buf, NULL);
        return msg->len;
 }
 
@@ -1231,13 +1230,7 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
        if (p)
                msg->len = &p[1] - data;
 
-       msg->data = xmalloc( msg->len + 1 );
-       if (!msg->data)
-               return 0;
-
-       memcpy( msg->data, data, msg->len );
-       msg->data[ msg->len ] = 0;
-
+       msg->data = xmemdupz(data, msg->len);
        *ofs += msg->len;
        return 1;
 }
index 2f727cd05b0d071d9aa750b90fe3c16c12ccbe5e..6ef53f246511a1943e375d5d5913a4ec52e2c663 100644 (file)
@@ -44,9 +44,8 @@ void interp_clear_table(struct interp *table, int ninterps)
  *        { "%%", "%"},
  *    }
  *
- * Returns 0 on a successful substitution pass that fits in result,
- * Returns a number of bytes needed to hold the full substituted
- * string otherwise.
+ * Returns the length of the substituted string (not including the final \0).
+ * Like with snprintf, if the result is >= reslen, then it overflowed.
  */
 
 unsigned long interpolate(char *result, unsigned long reslen,
@@ -61,8 +60,6 @@ unsigned long interpolate(char *result, unsigned long reslen,
        int i;
        char c;
 
-        memset(result, 0, reslen);
-
        while ((c = *src)) {
                if (c == '%') {
                        /* Try to match an interpolation string. */
@@ -82,9 +79,9 @@ unsigned long interpolate(char *result, unsigned long reslen,
                                }
 
                                valuelen = strlen(value);
-                               if (newlen + valuelen + 1 < reslen) {
+                               if (newlen + valuelen < reslen) {
                                        /* Substitute. */
-                                       strncpy(dest, value, valuelen);
+                                       memcpy(dest, value, valuelen);
                                        dest += valuelen;
                                }
                                newlen += valuelen;
@@ -99,8 +96,9 @@ unsigned long interpolate(char *result, unsigned long reslen,
                newlen++;
        }
 
-       if (newlen + 1 < reslen)
-               return 0;
-       else
-               return newlen + 2;
+       /* XXX: the previous loop always keep room for the ending NUL,
+          we just need to check if there was room for a NUL in the first place */
+       if (reslen > 0)
+               *dest = '\0';
+       return newlen;
 }
index 79e3dee276e5b99b11c4f6986aaff74ad6ad7f5e..23191543d5324f20622048103a821ded4d13b77a 100644 (file)
@@ -79,25 +79,14 @@ static int detect_any_signoff(char *letter, int size)
        return seen_head && seen_name;
 }
 
-static unsigned long append_signoff(char **buf_p, unsigned long *buf_sz_p,
-                                   unsigned long at, const char *signoff)
+static void append_signoff(struct strbuf *sb, const char *signoff)
 {
        static const char signed_off_by[] = "Signed-off-by: ";
        size_t signoff_len = strlen(signoff);
        int has_signoff = 0;
        char *cp;
-       char *buf;
-       unsigned long buf_sz;
-
-       buf = *buf_p;
-       buf_sz = *buf_sz_p;
-       if (buf_sz <= at + strlen(signed_off_by) + signoff_len + 3) {
-               buf_sz += strlen(signed_off_by) + signoff_len + 3;
-               buf = xrealloc(buf, buf_sz);
-               *buf_p = buf;
-               *buf_sz_p = buf_sz;
-       }
-       cp = buf;
+
+       cp = sb->buf;
 
        /* First see if we already have the sign-off by the signer */
        while ((cp = strstr(cp, signed_off_by))) {
@@ -105,29 +94,25 @@ static unsigned long append_signoff(char **buf_p, unsigned long *buf_sz_p,
                has_signoff = 1;
 
                cp += strlen(signed_off_by);
-               if (cp + signoff_len >= buf + at)
+               if (cp + signoff_len >= sb->buf + sb->len)
                        break;
                if (strncmp(cp, signoff, signoff_len))
                        continue;
                if (!isspace(cp[signoff_len]))
                        continue;
                /* we already have him */
-               return at;
+               return;
        }
 
        if (!has_signoff)
-               has_signoff = detect_any_signoff(buf, at);
+               has_signoff = detect_any_signoff(sb->buf, sb->len);
 
        if (!has_signoff)
-               buf[at++] = '\n';
-
-       strcpy(buf + at, signed_off_by);
-       at += strlen(signed_off_by);
-       strcpy(buf + at, signoff);
-       at += signoff_len;
-       buf[at++] = '\n';
-       buf[at] = 0;
-       return at;
+               strbuf_addch(sb, '\n');
+
+       strbuf_addstr(sb, signed_off_by);
+       strbuf_add(sb, signoff, signoff_len);
+       strbuf_addch(sb, '\n');
 }
 
 static unsigned int digits_in_number(unsigned int number)
@@ -142,14 +127,12 @@ static unsigned int digits_in_number(unsigned int number)
 
 void show_log(struct rev_info *opt, const char *sep)
 {
-       char *msgbuf = NULL;
-       unsigned long msgbuf_len = 0;
+       struct strbuf msgbuf;
        struct log_info *log = opt->loginfo;
        struct commit *commit = log->commit, *parent = log->parent;
        int abbrev = opt->diffopt.abbrev;
        int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
        const char *extra;
-       int len;
        const char *subject = NULL, *extra_headers = opt->extra_headers;
 
        opt->loginfo = NULL;
@@ -288,19 +271,18 @@ void show_log(struct rev_info *opt, const char *sep)
        /*
         * And then the pretty-printed message itself
         */
-       len = pretty_print_commit(opt->commit_format, commit, ~0u,
-                                 &msgbuf, &msgbuf_len, abbrev, subject,
-                                 extra_headers, opt->date_mode);
+       strbuf_init(&msgbuf, 0);
+       pretty_print_commit(opt->commit_format, commit, &msgbuf,
+                                 abbrev, subject, extra_headers, opt->date_mode);
 
        if (opt->add_signoff)
-               len = append_signoff(&msgbuf, &msgbuf_len, len,
-                                    opt->add_signoff);
+               append_signoff(&msgbuf, opt->add_signoff);
        if (opt->show_log_size)
-               printf("log size %i\n", len);
+               printf("log size %i\n", (int)msgbuf.len);
 
-       if (*msgbuf)
-               printf("%s%s%s", msgbuf, extra, sep);
-       free(msgbuf);
+       if (msgbuf.len)
+               printf("%s%s%s", msgbuf.buf, extra, sep);
+       strbuf_release(&msgbuf);
 }
 
 int log_tree_diff_flush(struct rev_info *opt)
index 97dcf9bf02d8f382ba63cfeef9d6dcb792c62f76..4a5c77c3b632a9c0636848b933ef5395b01b7103 100644 (file)
@@ -85,12 +85,6 @@ struct stage_data
        unsigned processed:1;
 };
 
-struct output_buffer
-{
-       struct output_buffer *next;
-       char *str;
-};
-
 static struct path_list current_file_set = {NULL, 0, 0, 1};
 static struct path_list current_directory_set = {NULL, 0, 0, 1};
 
@@ -98,51 +92,52 @@ static int call_depth = 0;
 static int verbosity = 2;
 static int rename_limit = -1;
 static int buffer_output = 1;
-static struct output_buffer *output_list, *output_end;
+static struct strbuf obuf = STRBUF_INIT;
 
-static int show (int v)
+static int show(int v)
 {
        return (!call_depth && verbosity >= v) || verbosity >= 5;
 }
 
-static void output(int v, const char *fmt, ...)
+static void flush_output(void)
 {
-       va_list args;
-       va_start(args, fmt);
-       if (buffer_output && show(v)) {
-               struct output_buffer *b = xmalloc(sizeof(*b));
-               nfvasprintf(&b->str, fmt, args);
-               b->next = NULL;
-               if (output_end)
-                       output_end->next = b;
-               else
-                       output_list = b;
-               output_end = b;
-       } else if (show(v)) {
-               int i;
-               for (i = call_depth; i--;)
-                       fputs("  ", stdout);
-               vfprintf(stdout, fmt, args);
-               fputc('\n', stdout);
+       if (obuf.len) {
+               fputs(obuf.buf, stdout);
+               strbuf_reset(&obuf);
        }
-       va_end(args);
 }
 
-static void flush_output(void)
+static void output(int v, const char *fmt, ...)
 {
-       struct output_buffer *b, *n;
-       for (b = output_list; b; b = n) {
-               int i;
-               for (i = call_depth; i--;)
-                       fputs("  ", stdout);
-               fputs(b->str, stdout);
-               fputc('\n', stdout);
-               n = b->next;
-               free(b->str);
-               free(b);
+       int len;
+       va_list ap;
+
+       if (!show(v))
+               return;
+
+       strbuf_grow(&obuf, call_depth * 2 + 2);
+       memset(obuf.buf + obuf.len, ' ', call_depth * 2);
+       strbuf_setlen(&obuf, obuf.len + call_depth * 2);
+
+       va_start(ap, fmt);
+       len = vsnprintf(obuf.buf + obuf.len, strbuf_avail(&obuf), fmt, ap);
+       va_end(ap);
+
+       if (len < 0)
+               len = 0;
+       if (len >= strbuf_avail(&obuf)) {
+               strbuf_grow(&obuf, len + 2);
+               va_start(ap, fmt);
+               len = vsnprintf(obuf.buf + obuf.len, strbuf_avail(&obuf), fmt, ap);
+               va_end(ap);
+               if (len >= strbuf_avail(&obuf)) {
+                       die("this should not happen, your snprintf is broken");
+               }
        }
-       output_list = NULL;
-       output_end = NULL;
+       strbuf_setlen(&obuf, obuf.len + len);
+       strbuf_add(&obuf, "\n", 1);
+       if (!buffer_output)
+               flush_output();
 }
 
 static void output_commit_title(struct commit *commit)
@@ -434,19 +429,15 @@ static int update_stages(const char *path, struct diff_filespec *o,
 
 static int remove_path(const char *name)
 {
-       int ret, len;
+       int ret;
        char *slash, *dirs;
 
        ret = unlink(name);
        if (ret)
                return ret;
-       len = strlen(name);
-       dirs = xmalloc(len+1);
-       memcpy(dirs, name, len);
-       dirs[len] = '\0';
+       dirs = xstrdup(name);
        while ((slash = strrchr(name, '/'))) {
                *slash = '\0';
-               len = slash - name;
                if (rmdir(name) != 0)
                        break;
        }
@@ -580,9 +571,7 @@ static void update_file_flags(const unsigned char *sha,
                        flush_buffer(fd, buf, size);
                        close(fd);
                } else if (S_ISLNK(mode)) {
-                       char *lnk = xmalloc(size + 1);
-                       memcpy(lnk, buf, size);
-                       lnk[size] = '\0';
+                       char *lnk = xmemdupz(buf, size);
                        mkdir_p(path, 0777);
                        unlink(path);
                        symlink(lnk, path);
@@ -874,14 +863,9 @@ static int read_merge_config(const char *var, const char *value)
                if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
                        break;
        if (!fn) {
-               char *namebuf;
                fn = xcalloc(1, sizeof(struct ll_merge_driver));
-               namebuf = xmalloc(namelen + 1);
-               memcpy(namebuf, name, namelen);
-               namebuf[namelen] = 0;
-               fn->name = namebuf;
+               fn->name = xmemdupz(name, namelen);
                fn->fn = ll_ext_merge;
-               fn->next = NULL;
                *ll_user_merge_tail = fn;
                ll_user_merge_tail = &(fn->next);
        }
diff --git a/mktag.c b/mktag.c
index 38acd5a295d5f06fb2db60633e89a06ba634beb8..b05260c83fd8ef766eb2e16fa355501bf1f62fb5 100644 (file)
--- a/mktag.c
+++ b/mktag.c
@@ -111,8 +111,7 @@ static int verify_tag(char *buffer, unsigned long size)
 
 int main(int argc, char **argv)
 {
-       unsigned long size = 4096;
-       char *buffer = xmalloc(size);
+       struct strbuf buf;
        unsigned char result_sha1[20];
 
        if (argc != 1)
@@ -120,21 +119,20 @@ int main(int argc, char **argv)
 
        setup_git_directory();
 
-       if (read_fd(0, &buffer, &size)) {
-               free(buffer);
+       strbuf_init(&buf, 0);
+       if (strbuf_read(&buf, 0, 4096) < 0) {
                die("could not read from stdin");
        }
 
        /* Verify it for some basic sanity: it needs to start with
           "object <sha1>\ntype\ntagger " */
-       if (verify_tag(buffer, size) < 0)
+       if (verify_tag(buf.buf, buf.len) < 0)
                die("invalid tag signature file");
 
-       if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0)
+       if (write_sha1_file(buf.buf, buf.len, tag_type, result_sha1) < 0)
                die("unable to write tag file");
 
-       free(buffer);
-
+       strbuf_release(&buf);
        printf("%s\n", sha1_to_hex(result_sha1));
        return 0;
 }
index d86dde89d63e21994fd2538d5ac3a21ead3a7338..e0da110a98d3a7376dc78df71fabc10fc5664296 100644 (file)
--- a/mktree.c
+++ b/mktree.c
@@ -4,7 +4,6 @@
  * Copyright (c) Junio C Hamano, 2006
  */
 #include "cache.h"
-#include "strbuf.h"
 #include "quote.h"
 #include "tree.h"
 
@@ -44,30 +43,22 @@ static int ent_compare(const void *a_, const void *b_)
 
 static void write_tree(unsigned char *sha1)
 {
-       char *buffer;
-       unsigned long size, offset;
+       struct strbuf buf;
+       size_t size;
        int i;
 
        qsort(entries, used, sizeof(*entries), ent_compare);
        for (size = i = 0; i < used; i++)
                size += 32 + entries[i]->len;
-       buffer = xmalloc(size);
-       offset = 0;
 
+       strbuf_init(&buf, size);
        for (i = 0; i < used; i++) {
                struct treeent *ent = entries[i];
-
-               if (offset + ent->len + 100 < size) {
-                       size = alloc_nr(offset + ent->len + 100);
-                       buffer = xrealloc(buffer, size);
-               }
-               offset += sprintf(buffer + offset, "%o ", ent->mode);
-               offset += sprintf(buffer + offset, "%s", ent->name);
-               buffer[offset++] = 0;
-               hashcpy((unsigned char*)buffer + offset, ent->sha1);
-               offset += 20;
+               strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0');
+               strbuf_add(&buf, ent->sha1, 20);
        }
-       write_sha1_file(buffer, offset, tree_type, sha1);
+
+       write_sha1_file(buf.buf, buf.len, tree_type, sha1);
 }
 
 static const char mktree_usage[] = "git-mktree [-z]";
@@ -75,6 +66,7 @@ static const char mktree_usage[] = "git-mktree [-z]";
 int main(int ac, char **av)
 {
        struct strbuf sb;
+       struct strbuf p_uq;
        unsigned char sha1[20];
        int line_termination = '\n';
 
@@ -90,18 +82,14 @@ int main(int ac, char **av)
                av++;
        }
 
-       strbuf_init(&sb);
-       while (1) {
-               int len;
+       strbuf_init(&sb, 0);
+       strbuf_init(&p_uq, 0);
+       while (strbuf_getline(&sb, stdin, line_termination) != EOF) {
                char *ptr, *ntr;
                unsigned mode;
                enum object_type type;
                char *path;
 
-               read_line(&sb, stdin, line_termination);
-               if (sb.eof)
-                       break;
-               len = sb.len;
                ptr = sb.buf;
                /* Input is non-recursive ls-tree output format
                 * mode SP type SP sha1 TAB name
@@ -111,7 +99,7 @@ int main(int ac, char **av)
                        die("input format error: %s", sb.buf);
                ptr = ntr + 1; /* type */
                ntr = strchr(ptr, ' ');
-               if (!ntr || sb.buf + len <= ntr + 41 ||
+               if (!ntr || sb.buf + sb.len <= ntr + 40 ||
                    ntr[41] != '\t' ||
                    get_sha1_hex(ntr + 1, sha1))
                        die("input format error: %s", sb.buf);
@@ -121,17 +109,21 @@ int main(int ac, char **av)
                *ntr++ = 0; /* now at the beginning of SHA1 */
                if (type != type_from_string(ptr))
                        die("object type %s mismatch (%s)", ptr, typename(type));
-               ntr += 41; /* at the beginning of name */
-               if (line_termination && ntr[0] == '"')
-                       path = unquote_c_style(ntr, NULL);
-               else
-                       path = ntr;
 
-               append_to_tree(mode, sha1, path);
+               path = ntr + 41;  /* at the beginning of name */
+               if (line_termination && path[0] == '"') {
+                       strbuf_reset(&p_uq);
+                       if (unquote_c_style(&p_uq, path, NULL)) {
+                               die("invalid quoting");
+                       }
+                       path = p_uq.buf;
+               }
 
-               if (path != ntr)
-                       free(path);
+               append_to_tree(mode, sha1, path);
        }
+       strbuf_release(&p_uq);
+       strbuf_release(&sb);
+
        write_tree(sha1);
        puts(sha1_to_hex(sha1));
        exit(0);
diff --git a/quote.c b/quote.c
index d88bf7515932bba96c694478c3b51c85549fa92a..482be05b7af159b9b47095fedfbdfa3bda65c748 100644 (file)
--- a/quote.c
+++ b/quote.c
  *  a'b      ==> a'\''b    ==> 'a'\''b'
  *  a!b      ==> a'\!'b    ==> 'a'\!'b'
  */
-#undef EMIT
-#define EMIT(x) do { if (++len < n) *bp++ = (x); } while(0)
-
 static inline int need_bs_quote(char c)
 {
        return (c == '\'' || c == '!');
 }
 
-static size_t sq_quote_buf(char *dst, size_t n, const char *src)
+void sq_quote_buf(struct strbuf *dst, const char *src)
 {
-       char c;
-       char *bp = dst;
-       size_t len = 0;
-
-       EMIT('\'');
-       while ((c = *src++)) {
-               if (need_bs_quote(c)) {
-                       EMIT('\'');
-                       EMIT('\\');
-                       EMIT(c);
-                       EMIT('\'');
-               } else {
-                       EMIT(c);
+       char *to_free = NULL;
+
+       if (dst->buf == src)
+               to_free = strbuf_detach(dst, NULL);
+
+       strbuf_addch(dst, '\'');
+       while (*src) {
+               size_t len = strcspn(src, "'\\");
+               strbuf_add(dst, src, len);
+               src += len;
+               while (need_bs_quote(*src)) {
+                       strbuf_addstr(dst, "'\\");
+                       strbuf_addch(dst, *src++);
+                       strbuf_addch(dst, '\'');
                }
        }
-       EMIT('\'');
-
-       if ( n )
-               *bp = 0;
-
-       return len;
+       strbuf_addch(dst, '\'');
+       free(to_free);
 }
 
 void sq_quote_print(FILE *stream, const char *src)
@@ -62,11 +56,10 @@ void sq_quote_print(FILE *stream, const char *src)
        fputc('\'', stream);
 }
 
-char *sq_quote_argv(const char** argv, int count)
+void sq_quote_argv(struct strbuf *dst, const char** argv, int count,
+                   size_t maxlen)
 {
-       char *buf, *to;
        int i;
-       size_t len = 0;
 
        /* Count argv if needed. */
        if (count < 0) {
@@ -74,53 +67,14 @@ char *sq_quote_argv(const char** argv, int count)
                        ; /* just counting */
        }
 
-       /* Special case: no argv. */
-       if (!count)
-               return xcalloc(1,1);
-
-       /* Get destination buffer length. */
-       for (i = 0; i < count; i++)
-               len += sq_quote_buf(NULL, 0, argv[i]) + 1;
-
-       /* Alloc destination buffer. */
-       to = buf = xmalloc(len + 1);
-
        /* Copy into destination buffer. */
+       strbuf_grow(dst, 32 * count);
        for (i = 0; i < count; ++i) {
-               *to++ = ' ';
-               to += sq_quote_buf(to, len, argv[i]);
+               strbuf_addch(dst, ' ');
+               sq_quote_buf(dst, argv[i]);
+               if (maxlen && dst->len > maxlen)
+                       die("Too many or long arguments");
        }
-
-       return buf;
-}
-
-/*
- * Append a string to a string buffer, with or without shell quoting.
- * Return true if the buffer overflowed.
- */
-int add_to_string(char **ptrp, int *sizep, const char *str, int quote)
-{
-       char *p = *ptrp;
-       int size = *sizep;
-       int oc;
-       int err = 0;
-
-       if (quote)
-               oc = sq_quote_buf(p, size, str);
-       else {
-               oc = strlen(str);
-               memcpy(p, str, (size <= oc) ? size - 1 : oc);
-       }
-
-       if (size <= oc) {
-               err = 1;
-               oc = size - 1;
-       }
-
-       *ptrp += oc;
-       **ptrp = '\0';
-       *sizep -= oc;
-       return err;
 }
 
 char *sq_dequote(char *arg)
@@ -157,186 +111,213 @@ char *sq_dequote(char *arg)
        }
 }
 
+/* 1 means: quote as octal
+ * 0 means: quote as octal if (quote_path_fully)
+ * -1 means: never quote
+ * c: quote as "\\c"
+ */
+#define X8(x)   x, x, x, x, x, x, x, x
+#define X16(x)  X8(x), X8(x)
+static signed char const sq_lookup[256] = {
+       /*           0    1    2    3    4    5    6    7 */
+       /* 0x00 */   1,   1,   1,   1,   1,   1,   1, 'a',
+       /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r',   1,   1,
+       /* 0x10 */ X16(1),
+       /* 0x20 */  -1,  -1, '"',  -1,  -1,  -1,  -1,  -1,
+       /* 0x28 */ X16(-1), X16(-1), X16(-1),
+       /* 0x58 */  -1,  -1,  -1,  -1,'\\',  -1,  -1,  -1,
+       /* 0x60 */ X16(-1), X8(-1),
+       /* 0x78 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,
+       /* 0x80 */ /* set to 0 */
+};
+
+static inline int sq_must_quote(char c) {
+       return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
+}
+
+/* returns the longest prefix not needing a quote up to maxlen if positive.
+   This stops at the first \0 because it's marked as a character needing an
+   escape */
+static size_t next_quote_pos(const char *s, ssize_t maxlen)
+{
+       size_t len;
+       if (maxlen < 0) {
+               for (len = 0; !sq_must_quote(s[len]); len++);
+       } else {
+               for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++);
+       }
+       return len;
+}
+
 /*
  * C-style name quoting.
  *
- * Does one of three things:
- *
- * (1) if outbuf and outfp are both NULL, inspect the input name and
- *     counts the number of bytes that are needed to hold c_style
- *     quoted version of name, counting the double quotes around
- *     it but not terminating NUL, and returns it.  However, if name
- *     does not need c_style quoting, it returns 0.
- *
- * (2) if outbuf is not NULL, it must point at a buffer large enough
- *     to hold the c_style quoted version of name, enclosing double
- *     quotes, and terminating NUL.  Fills outbuf with c_style quoted
- *     version of name enclosed in double-quote pair.  Return value
- *     is undefined.
+ * (1) if sb and fp are both NULL, inspect the input name and counts the
+ *     number of bytes that are needed to hold c_style quoted version of name,
+ *     counting the double quotes around it but not terminating NUL, and
+ *     returns it.
+ *     However, if name does not need c_style quoting, it returns 0.
  *
- * (3) if outfp is not NULL, outputs c_style quoted version of name,
- *     but not enclosed in double-quote pair.  Return value is undefined.
+ * (2) if sb or fp are not NULL, it emits the c_style quoted version
+ *     of name, enclosed with double quotes if asked and needed only.
+ *     Return value is the same as in (1).
  */
-
-static int quote_c_style_counted(const char *name, int namelen,
-                                char *outbuf, FILE *outfp, int no_dq)
+static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
+                                    struct strbuf *sb, FILE *fp, int no_dq)
 {
 #undef EMIT
-#define EMIT(c) \
-       (outbuf ? (*outbuf++ = (c)) : outfp ? fputc(c, outfp) : (count++))
-
-#define EMITQ() EMIT('\\')
+#define EMIT(c)                                 \
+       do {                                        \
+               if (sb) strbuf_addch(sb, (c));          \
+               if (fp) fputc((c), fp);                 \
+               count++;                                \
+       } while (0)
+#define EMITBUF(s, l)                           \
+       do {                                        \
+               if (sb) strbuf_add(sb, (s), (l));       \
+               if (fp) fwrite((s), (l), 1, fp);        \
+               count += (l);                           \
+       } while (0)
+
+       size_t len, count = 0;
+       const char *p = name;
 
-       const char *sp;
-       unsigned char ch;
-       int count = 0, needquote = 0;
+       for (;;) {
+               int ch;
 
-       if (!no_dq)
-               EMIT('"');
-       for (sp = name; sp < name + namelen; sp++) {
-               ch = *sp;
-               if (!ch)
+               len = next_quote_pos(p, maxlen);
+               if (len == maxlen || !p[len])
                        break;
-               if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
-                   (quote_path_fully && (ch >= 0177))) {
-                       needquote = 1;
-                       switch (ch) {
-                       case '\a': EMITQ(); ch = 'a'; break;
-                       case '\b': EMITQ(); ch = 'b'; break;
-                       case '\f': EMITQ(); ch = 'f'; break;
-                       case '\n': EMITQ(); ch = 'n'; break;
-                       case '\r': EMITQ(); ch = 'r'; break;
-                       case '\t': EMITQ(); ch = 't'; break;
-                       case '\v': EMITQ(); ch = 'v'; break;
-
-                       case '\\': /* fallthru */
-                       case '"': EMITQ(); break;
-                       default:
-                               /* octal */
-                               EMITQ();
-                               EMIT(((ch >> 6) & 03) + '0');
-                               EMIT(((ch >> 3) & 07) + '0');
-                               ch = (ch & 07) + '0';
-                               break;
-                       }
+
+               if (!no_dq && p == name)
+                       EMIT('"');
+
+               EMITBUF(p, len);
+               EMIT('\\');
+               p += len;
+               ch = (unsigned char)*p++;
+               if (sq_lookup[ch] >= ' ') {
+                       EMIT(sq_lookup[ch]);
+               } else {
+                       EMIT(((ch >> 6) & 03) + '0');
+                       EMIT(((ch >> 3) & 07) + '0');
+                       EMIT(((ch >> 0) & 07) + '0');
                }
-               EMIT(ch);
        }
+
+       EMITBUF(p, len);
+       if (p == name)   /* no ending quote needed */
+               return 0;
+
        if (!no_dq)
                EMIT('"');
-       if (outbuf)
-               *outbuf = 0;
+       return count;
+}
 
-       return needquote ? count : 0;
+size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq)
+{
+       return quote_c_style_counted(name, -1, sb, fp, nodq);
 }
 
-int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+void write_name_quoted(const char *name, FILE *fp, int terminator)
 {
-       int cnt = strlen(name);
-       return quote_c_style_counted(name, cnt, outbuf, outfp, no_dq);
+       if (terminator) {
+               quote_c_style(name, NULL, fp, 0);
+       } else {
+               fputs(name, fp);
+       }
+       fputc(terminator, fp);
+}
+
+extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
+                                 const char *name, FILE *fp, int terminator)
+{
+       int needquote = 0;
+
+       if (terminator) {
+               needquote = next_quote_pos(pfx, pfxlen) < pfxlen
+                       || name[next_quote_pos(name, -1)];
+       }
+       if (needquote) {
+               fputc('"', fp);
+               quote_c_style_counted(pfx, pfxlen, NULL, fp, 1);
+               quote_c_style(name, NULL, fp, 1);
+               fputc('"', fp);
+       } else {
+               fwrite(pfx, pfxlen, 1, fp);
+               fputs(name, fp);
+       }
+       fputc(terminator, fp);
 }
 
 /*
  * C-style name unquoting.
  *
- * Quoted should point at the opening double quote.  Returns
- * an allocated memory that holds unquoted name, which the caller
- * should free when done.  Updates endp pointer to point at
- * one past the ending double quote if given.
+ * Quoted should point at the opening double quote.
+ * + Returns 0 if it was able to unquote the string properly, and appends the
+ *   result in the strbuf `sb'.
+ * + Returns -1 in case of error, and doesn't touch the strbuf. Though note
+ *   that this function will allocate memory in the strbuf, so calling
+ *   strbuf_release is mandatory whichever result unquote_c_style returns.
+ *
+ * Updates endp pointer to point at one past the ending double quote if given.
  */
-
-char *unquote_c_style(const char *quoted, const char **endp)
+int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
 {
-       const char *sp;
-       char *name = NULL, *outp = NULL;
-       int count = 0, ch, ac;
-
-#undef EMIT
-#define EMIT(c) (outp ? (*outp++ = (c)) : (count++))
+       size_t oldlen = sb->len, len;
+       int ch, ac;
 
        if (*quoted++ != '"')
-               return NULL;
+               return -1;
+
+       for (;;) {
+               len = strcspn(quoted, "\"\\");
+               strbuf_add(sb, quoted, len);
+               quoted += len;
+
+               switch (*quoted++) {
+                 case '"':
+                       if (endp)
+                               *endp = quoted + 1;
+                       return 0;
+                 case '\\':
+                       break;
+                 default:
+                       goto error;
+               }
+
+               switch ((ch = *quoted++)) {
+               case 'a': ch = '\a'; break;
+               case 'b': ch = '\b'; break;
+               case 'f': ch = '\f'; break;
+               case 'n': ch = '\n'; break;
+               case 'r': ch = '\r'; break;
+               case 't': ch = '\t'; break;
+               case 'v': ch = '\v'; break;
 
-       while (1) {
-               /* first pass counts and allocates, second pass fills */
-               for (sp = quoted; (ch = *sp++) != '"'; ) {
-                       if (ch == '\\') {
-                               switch (ch = *sp++) {
-                               case 'a': ch = '\a'; break;
-                               case 'b': ch = '\b'; break;
-                               case 'f': ch = '\f'; break;
-                               case 'n': ch = '\n'; break;
-                               case 'r': ch = '\r'; break;
-                               case 't': ch = '\t'; break;
-                               case 'v': ch = '\v'; break;
-
-                               case '\\': case '"':
-                                       break; /* verbatim */
-
-                               case '0':
-                               case '1':
-                               case '2':
-                               case '3':
-                               case '4':
-                               case '5':
-                               case '6':
-                               case '7':
-                                       /* octal */
+               case '\\': case '"':
+                       break; /* verbatim */
+
+               /* octal values with first digit over 4 overflow */
+               case '0': case '1': case '2': case '3':
                                        ac = ((ch - '0') << 6);
-                                       if ((ch = *sp++) < '0' || '7' < ch)
-                                               return NULL;
+                       if ((ch = *quoted++) < '0' || '7' < ch)
+                               goto error;
                                        ac |= ((ch - '0') << 3);
-                                       if ((ch = *sp++) < '0' || '7' < ch)
-                                               return NULL;
+                       if ((ch = *quoted++) < '0' || '7' < ch)
+                               goto error;
                                        ac |= (ch - '0');
                                        ch = ac;
                                        break;
                                default:
-                                       return NULL; /* malformed */
-                               }
+                       goto error;
                        }
-                       EMIT(ch);
+               strbuf_addch(sb, ch);
                }
 
-               if (name) {
-                       *outp = 0;
-                       if (endp)
-                               *endp = sp;
-                       return name;
-               }
-               outp = name = xmalloc(count + 1);
-       }
-}
-
-void write_name_quoted(const char *prefix, int prefix_len,
-                      const char *name, int quote, FILE *out)
-{
-       int needquote;
-
-       if (!quote) {
-       no_quote:
-               if (prefix_len)
-                       fprintf(out, "%.*s", prefix_len, prefix);
-               fputs(name, out);
-               return;
-       }
-
-       needquote = 0;
-       if (prefix_len)
-               needquote = quote_c_style_counted(prefix, prefix_len,
-                                                 NULL, NULL, 0);
-       if (!needquote)
-               needquote = quote_c_style(name, NULL, NULL, 0);
-       if (needquote) {
-               fputc('"', out);
-               if (prefix_len)
-                       quote_c_style_counted(prefix, prefix_len,
-                                             NULL, out, 1);
-               quote_c_style(name, NULL, out, 1);
-               fputc('"', out);
-       }
-       else
-               goto no_quote;
+  error:
+       strbuf_setlen(sb, oldlen);
+       return -1;
 }
 
 /* quoting as a string literal for other languages */
diff --git a/quote.h b/quote.h
index 8a59cc55d1dcfba728614b2d6494272ceafbf3a1..42879909983679f31b9ac6d7e5bfc330d8167a91 100644 (file)
--- a/quote.h
+++ b/quote.h
  */
 
 extern void sq_quote_print(FILE *stream, const char *src);
-extern char *sq_quote_argv(const char** argv, int count);
 
-/*
- * Append a string to a string buffer, with or without shell quoting.
- * Return true if the buffer overflowed.
- */
-extern int add_to_string(char **ptrp, int *sizep, const char *str, int quote);
+extern void sq_quote_buf(struct strbuf *, const char *src);
+extern void sq_quote_argv(struct strbuf *, const char **argv, int count,
+                          size_t maxlen);
 
 /* This unwraps what sq_quote() produces in place, but returns
  * NULL if the input does not look like what sq_quote would have
@@ -43,12 +40,12 @@ extern int add_to_string(char **ptrp, int *sizep, const char *str, int quote);
  */
 extern char *sq_dequote(char *);
 
-extern int quote_c_style(const char *name, char *outbuf, FILE *outfp,
-                        int nodq);
-extern char *unquote_c_style(const char *quoted, const char **endp);
+extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
+extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
 
-extern void write_name_quoted(const char *prefix, int prefix_len,
-                             const char *name, int quote, FILE *out);
+extern void write_name_quoted(const char *name, FILE *, int terminator);
+extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
+                                 const char *name, FILE *, int terminator);
 
 /* quoting as a string literal for other languages */
 extern void perl_quote_print(FILE *stream, const char *src);
index 2e40a344209e010e664758865846b63dc9546c1f..56202d13dfcfe4ed583fee7b4063596bc1ef417e 100644 (file)
@@ -1136,7 +1136,7 @@ int write_index(struct index_state *istate, int newfd)
 {
        SHA_CTX c;
        struct cache_header hdr;
-       int i, removed;
+       int i, err, removed;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
 
@@ -1165,16 +1165,15 @@ int write_index(struct index_state *istate, int newfd)
 
        /* Write extension data here */
        if (istate->cache_tree) {
-               unsigned long sz;
-               void *data = cache_tree_write(istate->cache_tree, &sz);
-               if (data &&
-                   !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
-                   !ce_write(&c, newfd, data, sz))
-                       free(data);
-               else {
-                       free(data);
+               struct strbuf sb;
+
+               strbuf_init(&sb, 0);
+               cache_tree_write(&sb, istate->cache_tree);
+               err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0
+                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               strbuf_release(&sb);
+               if (err)
                        return -1;
-               }
        }
        return ce_flush(&c, newfd);
 }
diff --git a/refs.c b/refs.c
index 7fb3350789a407e36dc74418b79508e9bf916594..07e260c8a15c9118ee829964aed65b209900dc36 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1246,15 +1246,11 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
 static char *ref_msg(const char *line, const char *endp)
 {
        const char *ep;
-       char *msg;
-
        line += 82;
-       for (ep = line; ep < endp && *ep != '\n'; ep++)
-               ;
-       msg = xmalloc(ep - line + 1);
-       memcpy(msg, line, ep - line);
-       msg[ep - line] = 0;
-       return msg;
+       ep = memchr(line, '\n', endp - line);
+       if (!ep)
+               ep = endp;
+       return xmemdupz(line, ep - line);
 }
 
 int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
index 33d092c3c4b4c08fb6f39228e464219df4cb4f1c..5d294be308eed42c244c4424618b43d11048d4e6 100644 (file)
@@ -1134,22 +1134,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                continue;
                        }
                        if (!strncmp(arg, "--date=", 7)) {
-                               if (!strcmp(arg + 7, "relative"))
-                                       revs->date_mode = DATE_RELATIVE;
-                               else if (!strcmp(arg + 7, "iso8601") ||
-                                        !strcmp(arg + 7, "iso"))
-                                       revs->date_mode = DATE_ISO8601;
-                               else if (!strcmp(arg + 7, "rfc2822") ||
-                                        !strcmp(arg + 7, "rfc"))
-                                       revs->date_mode = DATE_RFC2822;
-                               else if (!strcmp(arg + 7, "short"))
-                                       revs->date_mode = DATE_SHORT;
-                               else if (!strcmp(arg + 7, "local"))
-                                       revs->date_mode = DATE_LOCAL;
-                               else if (!strcmp(arg + 7, "default"))
-                                       revs->date_mode = DATE_NORMAL;
-                               else
-                                       die("unknown date format %s", arg);
+                               revs->date_mode = parse_date_format(arg + 7);
                                continue;
                        }
                        if (!strcmp(arg, "--log-size")) {
@@ -1209,8 +1194,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 
                        opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
                        if (opts > 0) {
-                               if (strcmp(argv[i], "-z"))
-                                       revs->diff = 1;
                                i += opts - 1;
                                continue;
                        }
@@ -1254,6 +1237,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                add_pending_object_with_mode(revs, object, def, mode);
        }
 
+       /* Did the user ask for any diff output? Run the diff! */
+       if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT)
+               revs->diff = 1;
+
+       /* Pickaxe needs diffs */
+       if (revs->diffopt.pickaxe)
+               revs->diff = 1;
+
        if (revs->topo_order)
                revs->limited = 1;
 
diff --git a/rsh.c b/rsh.c
index 5754a230e2c23ce3fea255fccd8726af76c13317..016d72ead71f34196b374a088bdf6e4534326003 100644 (file)
--- a/rsh.c
+++ b/rsh.c
@@ -10,12 +10,9 @@ int setup_connection(int *fd_in, int *fd_out, const char *remote_prog,
        char *host;
        char *path;
        int sv[2];
-       char command[COMMAND_SIZE];
-       char *posn;
-       int sizen;
-       int of;
        int i;
        pid_t pid;
+       struct strbuf cmd;
 
        if (!strcmp(url, "-")) {
                *fd_in = 0;
@@ -36,24 +33,23 @@ int setup_connection(int *fd_in, int *fd_out, const char *remote_prog,
        if (!path) {
                return error("Bad URL: %s", url);
        }
+
        /* $GIT_RSH <host> "env GIT_DIR=<path> <remote_prog> <args...>" */
-       sizen = COMMAND_SIZE;
-       posn = command;
-       of = 0;
-       of |= add_to_string(&posn, &sizen, "env ", 0);
-       of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT "=", 0);
-       of |= add_to_string(&posn, &sizen, path, 1);
-       of |= add_to_string(&posn, &sizen, " ", 0);
-       of |= add_to_string(&posn, &sizen, remote_prog, 1);
+       strbuf_init(&cmd, COMMAND_SIZE);
+       strbuf_addstr(&cmd, "env ");
+       strbuf_addstr(&cmd, GIT_DIR_ENVIRONMENT "=");
+       sq_quote_buf(&cmd, path);
+       strbuf_addch(&cmd, ' ');
+       sq_quote_buf(&cmd, remote_prog);
 
-       for ( i = 0 ; i < rmt_argc ; i++ ) {
-               of |= add_to_string(&posn, &sizen, " ", 0);
-               of |= add_to_string(&posn, &sizen, rmt_argv[i], 1);
+       for (i = 0 ; i < rmt_argc ; i++) {
+               strbuf_addch(&cmd, ' ');
+               sq_quote_buf(&cmd, rmt_argv[i]);
        }
 
-       of |= add_to_string(&posn, &sizen, " -", 0);
+       strbuf_addstr(&cmd, " -");
 
-       if ( of )
+       if (cmd.len >= COMMAND_SIZE)
                return error("Command line too long");
 
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
@@ -74,7 +70,7 @@ int setup_connection(int *fd_in, int *fd_out, const char *remote_prog,
                close(sv[1]);
                dup2(sv[0], 0);
                dup2(sv[0], 1);
-               execlp(ssh, ssh_basename, host, command, NULL);
+               execlp(ssh, ssh_basename, host, cmd.buf, NULL);
        }
        close(sv[0]);
        *fd_in = sv[1];
index 9978a58da68bbf6f3482545d9f290fbfa3f3fe34..83a06a7aed84715db191b703e529d3501df0a8f2 100644 (file)
@@ -1491,11 +1491,8 @@ found_cache_entry:
                ent->lru.next->prev = ent->lru.prev;
                ent->lru.prev->next = ent->lru.next;
                delta_base_cached -= ent->size;
-       }
-       else {
-               ret = xmalloc(ent->size + 1);
-               memcpy(ret, ent->data, ent->size);
-               ((char *)ret)[ent->size] = 0;
+       } else {
+               ret = xmemdupz(ent->data, ent->size);
        }
        *type = ent->type;
        *base_size = ent->size;
@@ -1684,22 +1681,22 @@ off_t find_pack_entry_one(const unsigned char *sha1,
        return 0;
 }
 
-static int matches_pack_name(struct packed_git *p, const char *ig)
+int matches_pack_name(struct packed_git *p, const char *name)
 {
        const char *last_c, *c;
 
-       if (!strcmp(p->pack_name, ig))
-               return 0;
+       if (!strcmp(p->pack_name, name))
+               return 1;
 
        for (c = p->pack_name, last_c = c; *c;)
                if (*c == '/')
                        last_c = ++c;
                else
                        ++c;
-       if (!strcmp(last_c, ig))
-               return 0;
+       if (!strcmp(last_c, name))
+               return 1;
 
-       return 1;
+       return 0;
 }
 
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
@@ -1717,7 +1714,7 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
                if (ignore_packed) {
                        const char **ig;
                        for (ig = ignore_packed; *ig; ig++)
-                               if (!matches_pack_name(p, *ig))
+                               if (matches_pack_name(p, *ig))
                                        break;
                        if (*ig)
                                goto next;
@@ -1872,12 +1869,9 @@ void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
 
        co = find_cached_object(sha1);
        if (co) {
-               buf = xmalloc(co->size + 1);
-               memcpy(buf, co->buf, co->size);
-               ((char*)buf)[co->size] = 0;
                *type = co->type;
                *size = co->size;
-               return buf;
+               return xmemdupz(co->buf, co->size);
        }
 
        buf = read_packed_sha1(sha1, type, size);
@@ -2302,68 +2296,25 @@ int has_sha1_file(const unsigned char *sha1)
        return find_sha1_file(sha1, &st) ? 1 : 0;
 }
 
-/*
- * reads from fd as long as possible into a supplied buffer of size bytes.
- * If necessary the buffer's size is increased using realloc()
- *
- * returns 0 if anything went fine and -1 otherwise
- *
- * The buffer is always NUL-terminated, not including it in returned size.
- *
- * NOTE: both buf and size may change, but even when -1 is returned
- * you still have to free() it yourself.
- */
-int read_fd(int fd, char **return_buf, unsigned long *return_size)
-{
-       char *buf = *return_buf;
-       unsigned long size = *return_size;
-       ssize_t iret;
-       unsigned long off = 0;
-
-       if (!buf || size <= 1) {
-               size = 1024;
-               buf = xrealloc(buf, size);
-       }
-
-       do {
-               iret = xread(fd, buf + off, (size - 1) - off);
-               if (iret > 0) {
-                       off += iret;
-                       if (off == size - 1) {
-                               size = alloc_nr(size);
-                               buf = xrealloc(buf, size);
-                       }
-               }
-       } while (iret > 0);
-
-       buf[off] = '\0';
-
-       *return_buf = buf;
-       *return_size = off;
-
-       if (iret < 0)
-               return -1;
-       return 0;
-}
-
 int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
 {
-       unsigned long size = 4096;
-       char *buf = xmalloc(size);
+       struct strbuf buf;
        int ret;
 
-       if (read_fd(fd, &buf, &size)) {
-               free(buf);
+       strbuf_init(&buf, 0);
+       if (strbuf_read(&buf, fd, 4096) < 0) {
+               strbuf_release(&buf);
                return -1;
        }
 
        if (!type)
                type = blob_type;
        if (write_object)
-               ret = write_sha1_file(buf, size, type, sha1);
+               ret = write_sha1_file(buf.buf, buf.len, type, sha1);
        else
-               ret = hash_sha1_file(buf, size, type, sha1);
-       free(buf);
+               ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
+       strbuf_release(&buf);
+
        return ret;
 }
 
@@ -2385,12 +2336,11 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
         * Convert blobs to git internal format
         */
        if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
-               unsigned long nsize = size;
-               char *nbuf = convert_to_git(path, buf, &nsize);
-               if (nbuf) {
+               struct strbuf nbuf;
+               strbuf_init(&nbuf, 0);
+               if (convert_to_git(path, buf, size, &nbuf)) {
                        munmap(buf, size);
-                       size = nsize;
-                       buf = nbuf;
+                       buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
                }
        }
index e33d06b87c978ad7484c9d6bf972681c3d6cdeb0..f4201e160de2ccb9f2d9adef695c73a124e676d5 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
 #include "cache.h"
-#include "strbuf.h"
 
-void strbuf_init(struct strbuf *sb) {
-       sb->buf = NULL;
-       sb->eof = sb->alloc = sb->len = 0;
+/*
+ * Used as the default ->buf value, so that people can always assume
+ * buf is non NULL and ->buf is NUL terminated even for a freshly
+ * initialized strbuf.
+ */
+char strbuf_slopbuf[1];
+
+void strbuf_init(struct strbuf *sb, size_t hint)
+{
+       sb->alloc = sb->len = 0;
+       sb->buf = strbuf_slopbuf;
+       if (hint)
+               strbuf_grow(sb, hint);
+}
+
+void strbuf_release(struct strbuf *sb)
+{
+       if (sb->alloc) {
+               free(sb->buf);
+               strbuf_init(sb, 0);
+       }
+}
+
+char *strbuf_detach(struct strbuf *sb, size_t *sz)
+{
+       char *res = sb->alloc ? sb->buf : NULL;
+       if (sz)
+               *sz = sb->len;
+       strbuf_init(sb, 0);
+       return res;
 }
 
-static void strbuf_begin(struct strbuf *sb) {
-       free(sb->buf);
-       strbuf_init(sb);
+void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc)
+{
+       strbuf_release(sb);
+       sb->buf   = buf;
+       sb->len   = len;
+       sb->alloc = alloc;
+       strbuf_grow(sb, 0);
+       sb->buf[sb->len] = '\0';
 }
 
-static void inline strbuf_add(struct strbuf *sb, int ch) {
-       if (sb->alloc <= sb->len) {
-               sb->alloc = sb->alloc * 3 / 2 + 16;
-               sb->buf = xrealloc(sb->buf, sb->alloc);
+void strbuf_grow(struct strbuf *sb, size_t extra)
+{
+       if (sb->len + extra + 1 <= sb->len)
+               die("you want to use way too much memory");
+       if (!sb->alloc)
+               sb->buf = NULL;
+       ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
+}
+
+void strbuf_rtrim(struct strbuf *sb)
+{
+       while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
+               sb->len--;
+       sb->buf[sb->len] = '\0';
+}
+
+int strbuf_cmp(struct strbuf *a, struct strbuf *b)
+{
+       int cmp;
+       if (a->len < b->len) {
+               cmp = memcmp(a->buf, b->buf, a->len);
+               return cmp ? cmp : -1;
+       } else {
+               cmp = memcmp(a->buf, b->buf, b->len);
+               return cmp ? cmp : a->len != b->len;
        }
-       sb->buf[sb->len++] = ch;
 }
 
-static void strbuf_end(struct strbuf *sb) {
-       strbuf_add(sb, 0);
+void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
+                                  const void *data, size_t dlen)
+{
+       if (pos + len < pos)
+               die("you want to use way too much memory");
+       if (pos > sb->len)
+               die("`pos' is too far after the end of the buffer");
+       if (pos + len > sb->len)
+               die("`pos + len' is too far after the end of the buffer");
+
+       if (dlen >= len)
+               strbuf_grow(sb, dlen - len);
+       memmove(sb->buf + pos + dlen,
+                       sb->buf + pos + len,
+                       sb->len - pos - len);
+       memcpy(sb->buf + pos, data, dlen);
+       strbuf_setlen(sb, sb->len + dlen - len);
 }
 
-void read_line(struct strbuf *sb, FILE *fp, int term) {
-       int ch;
-       strbuf_begin(sb);
-       if (feof(fp)) {
-               sb->eof = 1;
-               return;
+void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)
+{
+       strbuf_splice(sb, pos, 0, data, len);
+}
+
+void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
+{
+       strbuf_splice(sb, pos, len, NULL, 0);
+}
+
+void strbuf_add(struct strbuf *sb, const void *data, size_t len)
+{
+       strbuf_grow(sb, len);
+       memcpy(sb->buf + sb->len, data, len);
+       strbuf_setlen(sb, sb->len + len);
+}
+
+void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
+{
+       int len;
+       va_list ap;
+
+       va_start(ap, fmt);
+       len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+       va_end(ap);
+       if (len < 0) {
+               len = 0;
        }
+       if (len > strbuf_avail(sb)) {
+               strbuf_grow(sb, len);
+               va_start(ap, fmt);
+               len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+               va_end(ap);
+               if (len > strbuf_avail(sb)) {
+                       die("this should not happen, your snprintf is broken");
+               }
+       }
+       strbuf_setlen(sb, sb->len + len);
+}
+
+size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
+{
+       size_t res;
+
+       strbuf_grow(sb, size);
+       res = fread(sb->buf + sb->len, 1, size, f);
+       if (res > 0) {
+               strbuf_setlen(sb, sb->len + res);
+       }
+       return res;
+}
+
+ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
+{
+       size_t oldlen = sb->len;
+
+       strbuf_grow(sb, hint ? hint : 8192);
+       for (;;) {
+               ssize_t cnt;
+
+               cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
+               if (cnt < 0) {
+                       strbuf_setlen(sb, oldlen);
+                       return -1;
+               }
+               if (!cnt)
+                       break;
+               sb->len += cnt;
+               strbuf_grow(sb, 8192);
+       }
+
+       sb->buf[sb->len] = '\0';
+       return sb->len - oldlen;
+}
+
+int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+{
+       int ch;
+
+       strbuf_grow(sb, 0);
+       if (feof(fp))
+               return EOF;
+
+       strbuf_reset(sb);
        while ((ch = fgetc(fp)) != EOF) {
                if (ch == term)
                        break;
-               strbuf_add(sb, ch);
+               strbuf_grow(sb, 1);
+               sb->buf[sb->len++] = ch;
        }
        if (ch == EOF && sb->len == 0)
-               sb->eof = 1;
-       strbuf_end(sb);
+               return EOF;
+
+       sb->buf[sb->len] = '\0';
+       return 0;
+}
+
+int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
+{
+       int fd, len;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return -1;
+       len = strbuf_read(sb, fd, hint);
+       close(fd);
+       if (len < 0)
+               return -1;
+
+       return len;
 }
index 74cc012c2c62d05cb773c6dd4776af0fdc237dfb..9b9e861d3d5e24477459eec1ab4007cfb35f52b9 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
 #ifndef STRBUF_H
 #define STRBUF_H
+
+/*
+ * Strbuf's can be use in many ways: as a byte array, or to store arbitrary
+ * long, overflow safe strings.
+ *
+ * Strbufs has some invariants that are very important to keep in mind:
+ *
+ * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to
+ *    build complex strings/buffers whose final size isn't easily known.
+ *
+ *    It is NOT legal to copy the ->buf pointer away.
+ *    `strbuf_detach' is the operation that detachs a buffer from its shell
+ *    while keeping the shell valid wrt its invariants.
+ *
+ * 2. the ->buf member is a byte array that has at least ->len + 1 bytes
+ *    allocated. The extra byte is used to store a '\0', allowing the ->buf
+ *    member to be a valid C-string. Every strbuf function ensure this
+ *    invariant is preserved.
+ *
+ *    Note that it is OK to "play" with the buffer directly if you work it
+ *    that way:
+ *
+ *    strbuf_grow(sb, SOME_SIZE);
+ *    // ... here the memory areay starting at sb->buf, and of length
+ *    // sb_avail(sb) is all yours, and you are sure that sb_avail(sb) is at
+ *    // least SOME_SIZE
+ *    strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE);
+ *
+ *    Of course, SOME_OTHER_SIZE must be smaller or equal to sb_avail(sb).
+ *
+ *    Doing so is safe, though if it has to be done in many places, adding the
+ *    missing API to the strbuf module is the way to go.
+ *
+ *    XXX: do _not_ assume that the area that is yours is of size ->alloc - 1
+ *         even if it's true in the current implementation. Alloc is somehow a
+ *         "private" member that should not be messed with.
+ */
+
+#include <assert.h>
+
+extern char strbuf_slopbuf[];
 struct strbuf {
-       int alloc;
-       int len;
-       int eof;
+       size_t alloc;
+       size_t len;
        char *buf;
 };
 
-extern void strbuf_init(struct strbuf *);
-extern void read_line(struct strbuf *, FILE *, int);
+#define STRBUF_INIT  { 0, 0, strbuf_slopbuf }
+
+/*----- strbuf life cycle -----*/
+extern void strbuf_init(struct strbuf *, size_t);
+extern void strbuf_release(struct strbuf *);
+extern char *strbuf_detach(struct strbuf *, size_t *);
+extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
+static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) {
+       struct strbuf tmp = *a;
+       *a = *b;
+       *b = tmp;
+}
+
+/*----- strbuf size related -----*/
+static inline size_t strbuf_avail(struct strbuf *sb) {
+       return sb->alloc ? sb->alloc - sb->len - 1 : 0;
+}
+
+extern void strbuf_grow(struct strbuf *, size_t);
+
+static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
+       if (!sb->alloc)
+               strbuf_grow(sb, 0);
+       assert(len < sb->alloc);
+       sb->len = len;
+       sb->buf[len] = '\0';
+}
+#define strbuf_reset(sb)  strbuf_setlen(sb, 0)
+
+/*----- content related -----*/
+extern void strbuf_rtrim(struct strbuf *);
+extern int strbuf_cmp(struct strbuf *, struct strbuf *);
+
+/*----- add data in your buffer -----*/
+static inline void strbuf_addch(struct strbuf *sb, int c) {
+       strbuf_grow(sb, 1);
+       sb->buf[sb->len++] = c;
+       sb->buf[sb->len] = '\0';
+}
+
+extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t);
+extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
+
+/* splice pos..pos+len with given data */
+extern void strbuf_splice(struct strbuf *, size_t pos, size_t len,
+                          const void *, size_t);
+
+extern void strbuf_add(struct strbuf *, const void *, size_t);
+static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
+       strbuf_add(sb, s, strlen(s));
+}
+static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) {
+       strbuf_add(sb, sb2->buf, sb2->len);
+}
+
+__attribute__((format(printf,2,3)))
+extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
+
+extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
+/* XXX: if read fails, any partial read is undone */
+extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
+extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
+
+extern int strbuf_getline(struct strbuf *, FILE *, int);
+
+extern void stripspace(struct strbuf *buf, int skip_comments);
 
 #endif /* STRBUF_H */
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
new file mode 100755 (executable)
index 0000000..68eb266
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carl D. Worth
+#
+
+test_description='git ls-files test (--with-tree).
+
+This test runs git ls-files --with-tree and in particular in
+a scenario known to trigger a crash with some versions of git.
+'
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       # The bug we are exercising requires a fair number of entries
+       # in a sub-directory so that add_index_entry will trigger a
+       # realloc.
+
+       echo file >expected &&
+       mkdir sub &&
+       bad= &&
+       for n in 0 1 2 3 4 5
+       do
+               for m in 0 1 2 3 4 5 6 7 8 9
+               do
+                       num=00$n$m &&
+                       >sub/file-$num &&
+                       echo file-$num >>expected || {
+                               bad=t
+                               break
+                       }
+               done && test -z "$bad" || {
+                       bad=t
+                       break
+               }
+       done && test -z "$bad" &&
+       git add . &&
+       git commit -m "add a bunch of files" &&
+
+       # We remove them all so that we will have something to add
+       # back with --with-tree and so that we will definitely be
+       # under the realloc size to trigger the bug.
+       rm -rf sub &&
+       git commit -a -m "remove them all" &&
+
+       # The bug also requires some entry before our directory so that
+       # prune_path will modify the_index.cache
+
+       mkdir a_directory_that_sorts_before_sub &&
+       >a_directory_that_sorts_before_sub/file &&
+       mkdir sub &&
+       >sub/file &&
+       git add .
+'
+
+# We have to run from a sub-directory to trigger prune_path
+# Then we finally get to run our --with-tree test
+cd sub
+
+test_expect_success 'git -ls-files --with-tree should succeed from subdir' '
+
+       git ls-files --with-tree=HEAD~1 >../output
+
+'
+
+cd ..
+test_expect_success \
+    'git -ls-files --with-tree should add entries from named tree.' \
+    'diff -u expected output'
+
+test_done
index 1af73a47c6af80c51a6ac6df4c5b20529c1a3565..11139048fe2238a3e06972252d7410da4058dccb 100755 (executable)
@@ -180,7 +180,7 @@ test_expect_success 'squash' '
 '
 
 test_expect_success 'retain authorship when squashing' '
-       git show HEAD | grep "^Author: Nitfol"
+       git show HEAD | grep "^Author: Twerp Snog"
 '
 
 test_expect_success 'preserve merges with -p' '
@@ -309,4 +309,12 @@ test_expect_success '--continue tries to commit, even for "edit"' '
        test $parent = $(git rev-parse HEAD^)
 '
 
+test_expect_success 'rebase a detached HEAD' '
+       grandparent=$(git rev-parse HEAD~2) &&
+       git checkout $(git rev-parse HEAD) &&
+       test_tick &&
+       FAKE_LINES="2 1" git rebase -i HEAD~2 &&
+       test $grandparent = $(git rev-parse HEAD~2)
+'
+
 test_done
index 42e28ab758358500183ee8ade35d7052c69ce9e1..dca2067b2d0bcd4423d843561b9275be50fe0da3 100755 (executable)
@@ -36,7 +36,8 @@ test_expect_success \
      echo simple textfile >a/a &&
      mkdir a/bin &&
      cp /bin/sh a/bin &&
-     printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile &&
+     printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
+     printf "A not substituted O" >a/substfile2 &&
      ln -s a a/l1 &&
      (p=long_path_to_a_file && cd a &&
       for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
@@ -108,20 +109,22 @@ test_expect_success \
     'diff -r a c/prefix/a'
 
 test_expect_success \
-    'create an archive with a substfile' \
-    'echo substfile export-subst >a/.gitattributes &&
+    'create an archive with a substfiles' \
+    'echo "substfile?" export-subst >a/.gitattributes &&
      git archive HEAD >f.tar &&
      rm a/.gitattributes'
 
 test_expect_success \
-    'extract substfile' \
+    'extract substfiles' \
     '(mkdir f && cd f && $TAR xf -) <f.tar'
 
 test_expect_success \
      'validate substfile contents' \
      'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
-      >f/a/substfile.expected &&
-      diff f/a/substfile.expected f/a/substfile'
+      >f/a/substfile1.expected &&
+      diff f/a/substfile1.expected f/a/substfile1 &&
+      diff a/substfile2 f/a/substfile2
+'
 
 test_expect_success \
     'git archive --format=zip' \
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
new file mode 100755 (executable)
index 0000000..487abf3
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Josh England
+#
+
+test_description='Test the post-checkout hook.'
+. ./test-lib.sh
+
+test_expect_success setup '
+        echo Data for commit0. >a &&
+        echo Data for commit0. >b &&
+        git update-index --add a &&
+        git update-index --add b &&
+        tree0=$(git write-tree) &&
+        commit0=$(echo setup | git commit-tree $tree0) &&
+        git update-ref refs/heads/master $commit0 &&
+        git-clone ./. clone1 &&
+        git-clone ./. clone2 &&
+        GIT_DIR=clone2/.git git branch -a new2 &&
+        echo Data for commit1. >clone2/b &&
+        GIT_DIR=clone2/.git git add clone2/b &&
+        GIT_DIR=clone2/.git git commit -m new2
+'
+
+for clone in 1 2; do
+    cat >clone${clone}/.git/hooks/post-checkout <<'EOF'
+#!/bin/sh
+echo $@ > $GIT_DIR/post-checkout.args
+EOF
+    chmod u+x clone${clone}/.git/hooks/post-checkout
+done
+
+test_expect_success 'post-checkout runs as expected ' '
+        GIT_DIR=clone1/.git git checkout master &&
+        test -e clone1/.git/post-checkout.args
+'
+
+test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' '
+        old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
+        new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
+        flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
+        test $old = $new -a $flag == 1
+'
+
+test_expect_success 'post-checkout runs as expected ' '
+        GIT_DIR=clone1/.git git checkout master &&
+        test -e clone1/.git/post-checkout.args
+'
+
+test_expect_success 'post-checkout args are correct with git checkout -b ' '
+        GIT_DIR=clone1/.git git checkout -b new1 &&
+        old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
+        new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
+        flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
+        test $old = $new -a $flag == 1
+'
+
+test_expect_success 'post-checkout receives the right args with HEAD changed ' '
+        GIT_DIR=clone2/.git git checkout new2 &&
+        old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
+        new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
+        flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
+        test $old != $new -a $flag == 1
+'
+
+test_expect_success 'post-checkout receives the right args when not switching branches ' '
+        GIT_DIR=clone2/.git git checkout master b &&
+        old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
+        new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
+        flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
+        test $old == $new -a $flag == 0
+'
+
+test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
new file mode 100755 (executable)
index 0000000..636aec2
--- /dev/null
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='git remote porcelain-ish'
+
+. ./test-lib.sh
+
+GIT_CONFIG=.git/config
+export GIT_CONFIG
+
+setup_repository () {
+       mkdir "$1" && (
+       cd "$1" &&
+       git init &&
+       >file &&
+       git add file &&
+       git commit -m "Initial" &&
+       git checkout -b side &&
+       >elif &&
+       git add elif &&
+       git commit -m "Second" &&
+       git checkout master
+       )
+}
+
+tokens_match () {
+       echo "$1" | tr ' ' '\012' | sort | sed -e '/^$/d' >expect &&
+       echo "$2" | tr ' ' '\012' | sort | sed -e '/^$/d' >actual &&
+       diff -u expect actual
+}
+
+check_remote_track () {
+       actual=$(git remote show "$1" | sed -n -e '$p') &&
+       shift &&
+       tokens_match "$*" "$actual"
+}
+
+check_tracking_branch () {
+       f="" &&
+       r=$(git for-each-ref "--format=%(refname)" |
+               sed -ne "s|^refs/remotes/$1/||p") &&
+       shift &&
+       tokens_match "$*" "$r"
+}
+
+test_expect_success setup '
+
+       setup_repository one &&
+       setup_repository two &&
+       (
+               cd two && git branch another
+       ) &&
+       git clone one test
+
+'
+
+test_expect_success 'remote information for the origin' '
+(
+       cd test &&
+       tokens_match origin "$(git remote)" &&
+       check_remote_track origin master side &&
+       check_tracking_branch origin HEAD master side
+)
+'
+
+test_expect_success 'add another remote' '
+(
+       cd test &&
+       git remote add -f second ../two &&
+       tokens_match "origin second" "$(git remote)" &&
+       check_remote_track origin master side &&
+       check_remote_track second master side another &&
+       check_tracking_branch second master side another &&
+       git for-each-ref "--format=%(refname)" refs/remotes |
+       sed -e "/^refs\/remotes\/origin\//d" \
+           -e "/^refs\/remotes\/second\//d" >actual &&
+       >expect &&
+       diff -u expect actual
+)
+'
+
+test_expect_success 'remove remote' '
+(
+       cd test &&
+       git remote rm second
+)
+'
+
+test_expect_success 'remove remote' '
+(
+       cd test &&
+       tokens_match origin "$(git remote)" &&
+       check_remote_track origin master side &&
+       git for-each-ref "--format=%(refname)" refs/remotes |
+       sed -e "/^refs\/remotes\/origin\//d" >actual &&
+       >expect &&
+       diff -u expect actual
+)
+'
+
+test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
new file mode 100644 (file)
index 0000000..d0809eb
--- /dev/null
@@ -0,0 +1,151 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Andy Parkins
+#
+
+test_description='for-each-ref test'
+
+. ./test-lib.sh
+
+# Mon Jul 3 15:18:43 2006 +0000
+datestamp=1151939923
+setdate_and_increment () {
+    GIT_COMMITTER_DATE="$datestamp +0200"
+    datestamp=$(expr "$datestamp" + 1)
+    GIT_AUTHOR_DATE="$datestamp +0200"
+    datestamp=$(expr "$datestamp" + 1)
+    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+test_expect_success 'Create sample commit with known timestamp' '
+       setdate_and_increment &&
+       echo "Using $datestamp" > one &&
+       git add one &&
+       git commit -m "Initial" &&
+       setdate_and_increment &&
+       git tag -a -m "Tagging at $datestamp" testtag
+'
+
+test_expect_success 'Check atom names are valid' '
+       bad=
+       for token in \
+               refname objecttype objectsize objectname tree parent \
+               numparent object type author authorname authoremail \
+               authordate committer committername committeremail \
+               committerdate tag tagger taggername taggeremail \
+               taggerdate creator creatordate subject body contents
+       do
+               git for-each-ref --format="$token=%($token)" refs/heads || {
+                       bad=$token
+                       break
+               }
+       done
+       test -z "$bad"
+'
+
+test_expect_failure 'Check invalid atoms names are errors' '
+       git-for-each-ref --format="%(INVALID)" refs/heads
+'
+
+test_expect_success 'Check format specifiers are ignored in naming date atoms' '
+       git-for-each-ref --format="%(authordate)" refs/heads &&
+       git-for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
+       git-for-each-ref --format="%(authordate) %(authordate:default)" refs/heads &&
+       git-for-each-ref --format="%(authordate:default) %(authordate:default)" refs/heads
+'
+
+test_expect_success 'Check valid format specifiers for date fields' '
+       git-for-each-ref --format="%(authordate:default)" refs/heads &&
+       git-for-each-ref --format="%(authordate:relative)" refs/heads &&
+       git-for-each-ref --format="%(authordate:short)" refs/heads &&
+       git-for-each-ref --format="%(authordate:local)" refs/heads &&
+       git-for-each-ref --format="%(authordate:iso8601)" refs/heads &&
+       git-for-each-ref --format="%(authordate:rfc2822)" refs/heads
+'
+
+test_expect_failure 'Check invalid format specifiers are errors' '
+       git-for-each-ref --format="%(authordate:INVALID)" refs/heads
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon Jul 3 17:18:43 2006 +0200' 'Mon Jul 3 17:18:44 2006 +0200'
+'refs/tags/testtag' 'Mon Jul 3 17:18:45 2006 +0200'
+EOF
+
+test_expect_success 'Check unformatted date fields output' '
+       (git for-each-ref --shell --format="%(refname) %(committerdate) %(authordate)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+test_expect_success 'Check format "default" formatted date fields output' '
+       f=default &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+# Don't know how to do relative check because I can't know when this script
+# is going to be run and can't fake the current time to git, and hence can't
+# provide expected output.  Instead, I'll just make sure that "relative"
+# doesn't exit in error
+#
+#cat >expected <<\EOF
+#
+#EOF
+#
+test_expect_success 'Check format "relative" date fields output' '
+       f=relative &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' '2006-07-03' '2006-07-03'
+'refs/tags/testtag' '2006-07-03'
+EOF
+
+test_expect_success 'Check format "short" date fields output' '
+       f=short &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon Jul 3 15:18:43 2006' 'Mon Jul 3 15:18:44 2006'
+'refs/tags/testtag' 'Mon Jul 3 15:18:45 2006'
+EOF
+
+test_expect_success 'Check format "local" date fields output' '
+       f=local &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' '2006-07-03 17:18:43 +0200' '2006-07-03 17:18:44 +0200'
+'refs/tags/testtag' '2006-07-03 17:18:45 +0200'
+EOF
+
+test_expect_success 'Check format "iso8601" date fields output' '
+       f=iso8601 &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon, 3 Jul 2006 17:18:43 +0200' 'Mon, 3 Jul 2006 17:18:44 +0200'
+'refs/tags/testtag' 'Mon, 3 Jul 2006 17:18:45 +0200'
+EOF
+
+test_expect_success 'Check format "rfc2822" date fields output' '
+       f=rfc2822 &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
new file mode 100755 (executable)
index 0000000..6424c6e
--- /dev/null
@@ -0,0 +1,440 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Lars Hjemli
+#
+
+test_description='git-merge
+
+Testing basic merge operations/option parsing.'
+
+. ./test-lib.sh
+
+cat >file <<EOF
+1
+2
+3
+4
+5
+6
+7
+8
+9
+EOF
+
+cat >file.1 <<EOF
+1 X
+2
+3
+4
+5
+6
+7
+8
+9
+EOF
+
+cat >file.5 <<EOF
+1
+2
+3
+4
+5 X
+6
+7
+8
+9
+EOF
+
+cat >file.9 <<EOF
+1
+2
+3
+4
+5
+6
+7
+8
+9 X
+EOF
+
+cat  >result.1 <<EOF
+1 X
+2
+3
+4
+5
+6
+7
+8
+9
+EOF
+
+cat >result.1-5 <<EOF
+1 X
+2
+3
+4
+5 X
+6
+7
+8
+9
+EOF
+
+cat >result.1-5-9 <<EOF
+1 X
+2
+3
+4
+5 X
+6
+7
+8
+9 X
+EOF
+
+create_merge_msgs() {
+       echo "Merge commit 'c2'" >msg.1-5 &&
+       echo "Merge commit 'c2'; commit 'c3'" >msg.1-5-9 &&
+       echo "Squashed commit of the following:" >squash.1 &&
+       echo >>squash.1 &&
+       git log --no-merges ^HEAD c1 >>squash.1 &&
+       echo "Squashed commit of the following:" >squash.1-5 &&
+       echo >>squash.1-5 &&
+       git log --no-merges ^HEAD c2 >>squash.1-5 &&
+       echo "Squashed commit of the following:" >squash.1-5-9 &&
+       echo >>squash.1-5-9 &&
+       git log --no-merges ^HEAD c2 c3 >>squash.1-5-9
+}
+
+verify_diff() {
+       if ! diff -u "$1" "$2"
+       then
+               echo "$3"
+               false
+       fi
+}
+
+verify_merge() {
+       verify_diff "$2" "$1" "[OOPS] bad merge result" &&
+       if test $(git ls-files -u | wc -l) -gt 0
+       then
+               echo "[OOPS] unmerged files"
+               false
+       fi &&
+       if ! git diff --exit-code
+       then
+               echo "[OOPS] working tree != index"
+               false
+       fi &&
+       if test -n "$3"
+       then
+               git show -s --pretty=format:%s HEAD >msg.act &&
+               verify_diff "$3" msg.act "[OOPS] bad merge message"
+       fi
+}
+
+verify_head() {
+       if test "$1" != "$(git rev-parse HEAD)"
+       then
+               echo "[OOPS] HEAD != $1"
+               false
+       fi
+}
+
+verify_parents() {
+       i=1
+       while test $# -gt 0
+       do
+               if test "$1" != "$(git rev-parse HEAD^$i)"
+               then
+                       echo "[OOPS] HEAD^$i != $1"
+                       return 1
+               fi
+               i=$(expr $i + 1)
+               shift
+       done
+}
+
+verify_mergeheads() {
+       i=1
+       if ! test -f .git/MERGE_HEAD
+       then
+               echo "[OOPS] MERGE_HEAD is missing"
+               false
+       fi &&
+       while test $# -gt 0
+       do
+               head=$(head -n $i .git/MERGE_HEAD | tail -n 1)
+               if test "$1" != "$head"
+               then
+                       echo "[OOPS] MERGE_HEAD $i != $1"
+                       return 1
+               fi
+               i=$(expr $i + 1)
+               shift
+       done
+}
+
+verify_no_mergehead() {
+       if test -f .git/MERGE_HEAD
+       then
+               echo "[OOPS] MERGE_HEAD exists"
+               false
+       fi
+}
+
+
+test_expect_success 'setup' '
+       git add file &&
+       test_tick &&
+       git commit -m "commit 0" &&
+       git tag c0 &&
+       c0=$(git rev-parse HEAD) &&
+       cp file.1 file &&
+       git add file &&
+       test_tick &&
+       git commit -m "commit 1" &&
+       git tag c1 &&
+       c1=$(git rev-parse HEAD) &&
+       git reset --hard "$c0" &&
+       cp file.5 file &&
+       git add file &&
+       test_tick &&
+       git commit -m "commit 2" &&
+       git tag c2 &&
+       c2=$(git rev-parse HEAD) &&
+       git reset --hard "$c0" &&
+       cp file.9 file &&
+       git add file &&
+       test_tick &&
+       git commit -m "commit 3" &&
+       git tag c3 &&
+       c3=$(git rev-parse HEAD)
+       git reset --hard "$c0" &&
+       create_merge_msgs
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'test option parsing' '
+       if git merge -$ c1
+       then
+               echo "[OOPS] -$ accepted"
+               false
+       fi &&
+       if git merge --no-such c1
+       then
+               echo "[OOPS] --no-such accepted"
+               false
+       fi &&
+       if git merge -s foobar c1
+       then
+               echo "[OOPS] -s foobar accepted"
+               false
+       fi &&
+       if git merge -s=foobar c1
+       then
+               echo "[OOPS] -s=foobar accepted"
+               false
+       fi &&
+       if git merge -m
+       then
+               echo "[OOPS] missing commit msg accepted"
+               false
+       fi &&
+       if git merge
+       then
+               echo "[OOPS] missing commit references accepted"
+               false
+       fi
+'
+
+test_expect_success 'merge c0 with c1' '
+       git reset --hard c0 &&
+       git merge c1 &&
+       verify_merge file result.1 &&
+       verify_head "$c1"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2' '
+       git reset --hard c1 &&
+       test_tick &&
+       git merge c2 &&
+       verify_merge file result.1-5 msg.1-5 &&
+       verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 and c3' '
+       git reset --hard c1 &&
+       test_tick &&
+       git merge c2 c3 &&
+       verify_merge file result.1-5-9 msg.1-5-9 &&
+       verify_parents $c1 $c2 $c3
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c0 with c1 (no-commit)' '
+       git reset --hard c0 &&
+       git merge --no-commit c1 &&
+       verify_merge file result.1 &&
+       verify_head $c1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (no-commit)' '
+       git reset --hard c1 &&
+       git merge --no-commit c2 &&
+       verify_merge file result.1-5 &&
+       verify_head $c1 &&
+       verify_mergeheads $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
+       git reset --hard c1 &&
+       git merge --no-commit c2 c3 &&
+       verify_merge file result.1-5-9 &&
+       verify_head $c1 &&
+       verify_mergeheads $c2 $c3
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c0 with c1 (squash)' '
+       git reset --hard c0 &&
+       git merge --squash c1 &&
+       verify_merge file result.1 &&
+       verify_head $c0 &&
+       verify_no_mergehead &&
+       verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (squash)' '
+       git reset --hard c1 &&
+       git merge --squash c2 &&
+       verify_merge file result.1-5 &&
+       verify_head $c1 &&
+       verify_no_mergehead &&
+       verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 and c3 (squash)' '
+       git reset --hard c1 &&
+       git merge --squash c2 c3 &&
+       verify_merge file result.1-5-9 &&
+       verify_head $c1 &&
+       verify_no_mergehead &&
+       verify_diff squash.1-5-9 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (no-commit in config)' '
+       git reset --hard c1 &&
+       git config branch.master.mergeoptions "--no-commit" &&
+       git merge c2 &&
+       verify_merge file result.1-5 &&
+       verify_head $c1 &&
+       verify_mergeheads $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (squash in config)' '
+       git reset --hard c1 &&
+       git config branch.master.mergeoptions "--squash" &&
+       git merge c2 &&
+       verify_merge file result.1-5 &&
+       verify_head $c1 &&
+       verify_no_mergehead &&
+       verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'override config option -n' '
+       git reset --hard c1 &&
+       git config branch.master.mergeoptions "-n" &&
+       test_tick &&
+       git merge --summary c2 >diffstat.txt &&
+       verify_merge file result.1-5 msg.1-5 &&
+       verify_parents $c1 $c2 &&
+       if ! grep -e "^ file | \+2 +-$" diffstat.txt
+       then
+               echo "[OOPS] diffstat was not generated"
+       fi
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'override config option --summary' '
+       git reset --hard c1 &&
+       git config branch.master.mergeoptions "--summary" &&
+       test_tick &&
+       git merge -n c2 >diffstat.txt &&
+       verify_merge file result.1-5 msg.1-5 &&
+       verify_parents $c1 $c2 &&
+       if grep -e "^ file | \+2 +-$" diffstat.txt
+       then
+               echo "[OOPS] diffstat was generated"
+               false
+       fi
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (override --no-commit)' '
+       git reset --hard c1 &&
+       git config branch.master.mergeoptions "--no-commit" &&
+       test_tick &&
+       git merge --commit c2 &&
+       verify_merge file result.1-5 msg.1-5 &&
+       verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (override --squash)' '
+       git reset --hard c1 &&
+       git config branch.master.mergeoptions "--squash" &&
+       test_tick &&
+       git merge --no-squash c2 &&
+       verify_merge file result.1-5 msg.1-5 &&
+       verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c0 with c1 (no-ff)' '
+       git reset --hard c0 &&
+       test_tick &&
+       git merge --no-ff c1 &&
+       verify_merge file result.1 &&
+       verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
+       git reset --hard c0 &&
+       git config branch.master.mergeoptions "--no-ff" &&
+       git merge --ff c1 &&
+       verify_merge file result.1 &&
+       verify_head $c1
+'
+
+test_debug 'gitk --all'
+
+test_done
index 622ea1c0df1cdfcbabcd9a884abe151c4d0dff53..5aac644223cf217255a0fdbbb1238b4125fd2e6e 100755 (executable)
@@ -140,6 +140,7 @@ test_expect_success 'test show-ignore' "
        cd test_wc &&
        mkdir -p deeply/nested/directory &&
        svn add deeply &&
+       svn up &&
        svn propset -R svn:ignore 'no-such-file*' .
        svn commit -m 'propset svn:ignore'
        cd .. &&
index d8f9cab35dcff89469f1974dcc7130e3d38f471e..7ba76309ac9e57f9e5379bf93ecac4e6a4e4ad96 100755 (executable)
@@ -19,8 +19,7 @@ test_expect_success 'initialize repo' "
        poke trunk/readme &&
        svn commit -m 'another commit' &&
        svn up &&
-       svn mv -m 'rename to thunk' trunk thunk &&
-       svn up &&
+       svn mv trunk thunk &&
        echo goodbye >> thunk/readme &&
        poke thunk/readme &&
        svn commit -m 'bye now' &&
@@ -52,8 +51,10 @@ test_expect_success 'init and fetch from one svn-remote' "
         "
 
 test_expect_success 'follow deleted parent' "
-        svn cp -m 'resurrecting trunk as junk' \
-               -r2 $svnrepo/trunk $svnrepo/junk &&
+        (svn cp -m 'resurrecting trunk as junk' \
+               $svnrepo/trunk@2 $svnrepo/junk ||
+         svn cp -m 'resurrecting trunk as junk' \
+               -r2 $svnrepo/trunk $svnrepo/junk) &&
         git config --add svn-remote.svn.fetch \
           junk:refs/remotes/svn/junk &&
         git-svn fetch -i svn/thunk &&
diff --git a/tag.c b/tag.c
index bbacd59a23f7994980f4bf017324833ca3d4adb3..f62bcdd994509323080683ce19c1a4d8241f9dec 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -68,9 +68,7 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
        memcpy(type, type_line + 5, typelen);
        type[typelen] = '\0';
        taglen = sig_line - tag_line - strlen("tag \n");
-       item->tag = xmalloc(taglen + 1);
-       memcpy(item->tag, tag_line + 4, taglen);
-       item->tag[taglen] = '\0';
+       item->tag = xmemdupz(tag_line + 4, taglen);
 
        if (!strcmp(type, blob_type)) {
                item->tagged = &lookup_blob(sha1)->object;
diff --git a/trace.c b/trace.c
index 7961a27a2ed4f32c766dabdf12c4115c3d3b36ba..69fa05e6446355ed8e5bb7f560c83f61b9bc3aff 100644 (file)
--- a/trace.c
+++ b/trace.c
 #include "cache.h"
 #include "quote.h"
 
-/* Stolen from "imap-send.c". */
-int nfvasprintf(char **strp, const char *fmt, va_list ap)
-{
-       int len;
-       char tmp[1024];
-
-       if ((len = vsnprintf(tmp, sizeof(tmp), fmt, ap)) < 0 ||
-           !(*strp = xmalloc(len + 1)))
-               die("Fatal: Out of memory\n");
-       if (len >= (int)sizeof(tmp))
-               vsprintf(*strp, fmt, ap);
-       else
-               memcpy(*strp, tmp, len + 1);
-       return len;
-}
-
-int nfasprintf(char **str, const char *fmt, ...)
-{
-       int rc;
-       va_list args;
-
-       va_start(args, fmt);
-       rc = nfvasprintf(str, fmt, args);
-       va_end(args);
-       return rc;
-}
-
 /* Get a trace file descriptor from GIT_TRACE env variable. */
 static int get_trace_fd(int *need_close)
 {
@@ -89,63 +62,65 @@ static int get_trace_fd(int *need_close)
 static const char err_msg[] = "Could not trace into fd given by "
        "GIT_TRACE environment variable";
 
-void trace_printf(const char *format, ...)
+void trace_printf(const char *fmt, ...)
 {
-       char *trace_str;
-       va_list rest;
-       int need_close = 0;
-       int fd = get_trace_fd(&need_close);
+       struct strbuf buf;
+       va_list ap;
+       int fd, len, need_close = 0;
 
+       fd = get_trace_fd(&need_close);
        if (!fd)
                return;
 
-       va_start(rest, format);
-       nfvasprintf(&trace_str, format, rest);
-       va_end(rest);
-
-       write_or_whine_pipe(fd, trace_str, strlen(trace_str), err_msg);
+       strbuf_init(&buf, 0);
+       va_start(ap, fmt);
+       len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
+       va_end(ap);
+       if (len >= strbuf_avail(&buf)) {
+               strbuf_grow(&buf, len - strbuf_avail(&buf) + 128);
+               va_start(ap, fmt);
+               len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
+               va_end(ap);
+               if (len >= strbuf_avail(&buf))
+                       die("broken vsnprintf");
+       }
+       strbuf_setlen(&buf, len);
 
-       free(trace_str);
+       write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
+       strbuf_release(&buf);
 
        if (need_close)
                close(fd);
 }
 
-void trace_argv_printf(const char **argv, int count, const char *format, ...)
+void trace_argv_printf(const char **argv, int count, const char *fmt, ...)
 {
-       char *argv_str, *format_str, *trace_str;
-       size_t argv_len, format_len, trace_len;
-       va_list rest;
-       int need_close = 0;
-       int fd = get_trace_fd(&need_close);
+       struct strbuf buf;
+       va_list ap;
+       int fd, len, need_close = 0;
 
+       fd = get_trace_fd(&need_close);
        if (!fd)
                return;
 
-       /* Get the argv string. */
-       argv_str = sq_quote_argv(argv, count);
-       argv_len = strlen(argv_str);
-
-       /* Get the formated string. */
-       va_start(rest, format);
-       nfvasprintf(&format_str, format, rest);
-       va_end(rest);
-
-       /* Allocate buffer for trace string. */
-       format_len = strlen(format_str);
-       trace_len = argv_len + format_len + 1; /* + 1 for \n */
-       trace_str = xmalloc(trace_len + 1);
-
-       /* Copy everything into the trace string. */
-       strncpy(trace_str, format_str, format_len);
-       strncpy(trace_str + format_len, argv_str, argv_len);
-       strcpy(trace_str + trace_len - 1, "\n");
-
-       write_or_whine_pipe(fd, trace_str, trace_len, err_msg);
+       strbuf_init(&buf, 0);
+       va_start(ap, fmt);
+       len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
+       va_end(ap);
+       if (len >= strbuf_avail(&buf)) {
+               strbuf_grow(&buf, len - strbuf_avail(&buf) + 128);
+               va_start(ap, fmt);
+               len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
+               va_end(ap);
+               if (len >= strbuf_avail(&buf))
+                       die("broken vsnprintf");
+       }
+       strbuf_setlen(&buf, len);
 
-       free(argv_str);
-       free(format_str);
-       free(trace_str);
+       sq_quote_argv(&buf, argv, count, 0);
+       strbuf_addch(&buf, '\n');
+       write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
+       strbuf_release(&buf);
 
        if (need_close)
                close(fd);