Compare commits

..

49 Commits

Author SHA1 Message Date
ebc5da3208 Git 2.0.2
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-16 11:19:56 -07:00
2e931843ad Merge branch 'jc/fix-clone-single-starting-at-a-tag' into maint
"git clone -b brefs/tags/bar" would have mistakenly thought we were
following a single tag, even though it was a name of the branch,
because it incorrectly used strstr().

* jc/fix-clone-single-starting-at-a-tag:
  builtin/clone.c: detect a clone starting at a tag correctly
2014-07-16 11:17:36 -07:00
588de86f06 Merge branch 'jk/pretty-G-format-fixes' into maint
"%G" (nothing after G) is an invalid pretty format specifier, but
the parser did not notice it as garbage.

* jk/pretty-G-format-fixes:
  move "%G" format test from t7510 to t6006
  pretty: avoid reading past end-of-string with "%G"
  t7510: check %G* pretty-format output
  t7510: test a commit signed by an unknown key
  t7510: use consistent &&-chains in loop
  t7510: stop referring to master in later tests
2014-07-16 11:17:21 -07:00
5a3db94539 Merge branch 'rs/fix-alt-odb-path-comparison' into maint
Code to avoid adding the same alternate object store twice was
subtly broken for a long time, but nobody seems to have noticed.

* rs/fix-alt-odb-path-comparison:
  sha1_file: avoid overrunning alternate object base string
2014-07-16 11:17:08 -07:00
5c18fde0d9 Merge branch 'jk/commit-buffer-length' into maint
A handful of code paths had to read the commit object more than
once when showing header fields that are usually not parsed.  The
internal data structure to keep track of the contents of the commit
object has been updated to reduce the need for this double-reading,
and to allow the caller find the length of the object.

* jk/commit-buffer-length:
  reuse cached commit buffer when parsing signatures
  commit: record buffer length in cache
  commit: convert commit->buffer to a slab
  commit-slab: provide a static initializer
  use get_commit_buffer everywhere
  convert logmsg_reencode to get_commit_buffer
  use get_commit_buffer to avoid duplicate code
  use get_cached_commit_buffer where appropriate
  provide helpers to access the commit buffer
  provide a helper to set the commit buffer
  provide a helper to free commit buffer
  sequencer: use logmsg_reencode in get_message
  logmsg_reencode: return const buffer
  do not create "struct commit" with xcalloc
  commit: push commit_index update into alloc_commit_node
  alloc: include any-object allocations in alloc_report
  replace dangerous uses of strbuf_attach
  commit_tree: take a pointer/len pair rather than a const strbuf
2014-07-16 11:16:38 -07:00
64630d807a Merge branch 'bc/fix-rebase-merge-skip' into maint
During "git rebase --merge", a conflicted patch could not be
skipped with "--skip" if the next one also conflicted.

* bc/fix-rebase-merge-skip:
  rebase--merge: fix --skip with two conflicts in a row
2014-07-16 11:16:16 -07:00
9092a9696b Merge branch 'maint-1.9' into maint
* maint-1.9:
  annotate: use argv_array
2014-07-16 11:11:06 -07:00
d22acacf81 Merge branch 'maint-1.8.5' into maint-1.9
* maint-1.8.5:
  annotate: use argv_array
  t7300: repair filesystem permissions with test_when_finished
  enums: remove trailing ',' after last item in enum
2014-07-16 11:10:30 -07:00
8c2cfa5544 annotate: use argv_array
Simplify the code and get rid of some magic constants by using
argv_array to build the argument list for cmd_blame.  Be lazy and let
the OS release our allocated memory, as before.

Signed-off-by: Rene Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-16 11:10:11 -07:00
8693e1cc2f Start preparing for 2.0.2
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-10 11:15:10 -07:00
cbf4e024ad Merge branch 'pb/trim-trailing-spaces' into maint
* pb/trim-trailing-spaces:
  t0008: do not depend on 'echo' handling backslashes specially
  dir.c:trim_trailing_spaces(): fix for " \ " sequence
2014-07-10 11:10:52 -07:00
f35392b018 Merge branch 'jk/repack-pack-keep-objects' into maint
* jk/repack-pack-keep-objects:
  repack: s/write_bitmap/&s/ in code
  repack: respect pack.writebitmaps
  repack: do not accidentally pack kept objects by default
2014-07-10 11:10:05 -07:00
3fea9ebdff Merge branch 'mc/doc-submodule-sync-recurse' into maint
* mc/doc-submodule-sync-recurse:
  submodule: document "sync --recursive"
2014-07-10 11:08:31 -07:00
c2f7b1026e Merge branch 'maint-1.8.5' into maint
* maint-1.8.5:
  t7300: repair filesystem permissions with test_when_finished
  enums: remove trailing ',' after last item in enum
2014-07-02 12:51:50 -07:00
45067fc973 t7300: repair filesystem permissions with test_when_finished
We create a directory that cannot be removed, confirm that
it cannot be removed, and then fix it like:

  chmod 0 foo &&
  test_must_fail git clean -d -f &&
  chmod 755 foo

If the middle step fails but leaves the directory (e.g., the
bug is that clean does not notice the failure), this
pollutes the test repo with an unremovable directory. Not
only does this cause further tests to fail, but it means
that "rm -rf" fails on the whole trash directory, and the
user has to intervene manually to even re-run the test script.

We can bump the "chmod 755" recovery to a test_when_finished
block to be sure that it always runs.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-02 12:51:38 -07:00
782735203c enums: remove trailing ',' after last item in enum
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-02 12:37:05 -07:00
80b47854ca sha1_file: avoid overrunning alternate object base string
While checking if a new alternate object database is a duplicate make
sure that old and new base paths have the same length before comparing
them with memcmp.  This avoids overrunning the buffer of the existing
entry if the new one is longer and it stops rejecting foobar/ after
foo/ was already added.

Signed-off-by: Rene Scharfe <ls.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-01 13:30:50 -07:00
958b2eb26c move "%G" format test from t7510 to t6006
The final test in t7510 checks that "--format" placeholders
that look similar to GPG placeholders (but that we don't
actually understand) are passed through. That test was
placed in t7510, since the other GPG placeholder tests are
there. However, it does not have a GPG prerequisite, because
it is not actually checking any signed commits.

This causes the test to erroneously fail when gpg is not
installed on a system, however. Not because we need signed
commits, but because we need _any_ commit to run "git log".
If we don't have gpg installed, t7510 doesn't create any
commits at all.

We can fix this by moving the test into t6006. This is
arguably a better place anyway, because it is where we test
most of the other placeholders (we do not test GPG
placeholders there because of the infrastructure needed to
make signed commits).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-25 15:01:06 -07:00
60a5f5fc79 builtin/clone.c: detect a clone starting at a tag correctly
31b808a0 (clone --single: limit the fetch refspec to fetched branch,
2012-09-20) tried to see if the given "branch" to follow is actually
a tag at the remote repository by checking with "refs/tags/" but it
incorrectly used strstr(3); it is actively wrong to treat a "branch"
"refs/heads/refs/tags/foo" and use the logic for the "refs/tags/"
ref hierarchy.  What the code really wanted to do is to see if it
starts with "refs/tags/".

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-23 14:31:35 -07:00
aa4b78d483 pretty: avoid reading past end-of-string with "%G"
If the user asks for --format=%G with nothing else, we
correctly realize that "%G" is not a valid placeholder (it
should be "%G?", "%GK", etc). But we still tell the
strbuf_expand code that we consumed 2 characters, causing it
to jump over the trailing NUL and output garbage.

This also fixes the case where "%GX" would be consumed (and
produce no output). In other cases, we pass unrecognized
placeholders through to the final string.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-17 13:41:41 -07:00
06ca0f45a0 t7510: check %G* pretty-format output
We do not check these along with the other pretty-format
placeholders in t6006, because we need signed commits to
make them interesting. t7510 has such commits, and can
easily exercise them in addition to the regular
--show-signature code path.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-17 13:41:39 -07:00
4baf839fe0 t7510: test a commit signed by an unknown key
We tested both good and bad signatures, but not ones made
correctly but with a key for which we have no trust.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-17 13:41:28 -07:00
7b1732c116 t7510: use consistent &&-chains in loop
We check multiple commits in a loop. Because we want to
break out of the loop if any single iteration fails, we use
a subshell/exit like:

  (
	for i in $stuff
	do
		do-something $i || exit 1
	done
  )

However, we are inconsistent in our loop body. Some commands
get their own "|| exit 1", and others try to chain to the
next command with "&&", like:

  X &&
  Y || exit 1
  Z || exit 1

This is a little hard to read and follow, because X and Y
are treated differently for no good reason. But much worse,
the second loop follows a similar pattern and gets it wrong.
"Y" is expected to fail, so we use "&& exit 1", giving us:

  X &&
  Y && exit 1
  Z || exit 1

That gets the test for X wrong (we do not exit unless both X
fails and Y unexpectedly succeeds, but we would want to exit
if _either_ is wrong). We can write this clearly and
correctly by consistently using "&&", followed by a single
"|| exit 1", and negating Y with "!" (as we would in a
normal &&-chain). Like:

  X &&
  ! Y &&
  Z || exit 1

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-17 13:39:52 -07:00
526d56e072 t7510: stop referring to master in later tests
Our setup creates a sequence of commits, each with its own
tag. However, we sometimes refer to "seventh-signed" as
"master". This works, since it is at the tip of the created
branch, but is brittle if new tests need to add more
commits. Let's use its tag name to be unambiguous.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-17 13:39:12 -07:00
95104c7e25 rebase--merge: fix --skip with two conflicts in a row
If git rebase --merge encountered a conflict, --skip would not work if the
next commit also conflicted.  The msgnum file would never be updated with
the new patch number, so no patch would actually be skipped, resulting in an
inescapable loop.

Update the msgnum file's value as the first thing in call_merge.  This also
avoids an "Already applied" message when skipping a commit.  There is no
visible change for the other contexts in which call_merge is invoked, as the
msgnum file's value remains unchanged in those situations.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-16 13:29:16 -07:00
9393ae79c9 submodule: document "sync --recursive"
The "git submodule sync" command supports the --recursive flag, but
the documentation does not mention this.  That flag is useful, for
example when a remote is changed in a submodule of a submodule.

Signed-off-by: Matthew Chen <charlesmchen@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 15:00:17 -07:00
97c1364be6 t0008: do not depend on 'echo' handling backslashes specially
The original used to pass with /bin/dash but not with /bin/bash set
to $SHELL_PATH.  The former turns "\\" into "\", but the latter does
not.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 13:29:03 -07:00
218aa3a616 reuse cached commit buffer when parsing signatures
When we call show_signature or show_mergetag, we read the
commit object fresh via read_sha1_file and reparse its
headers. However, in most cases we already have the object
data available, attached to the "struct commit". This is
partially laziness in dealing with the memory allocation
issues, but partially defensive programming, in that we
would always want to verify a clean version of the buffer
(not one that might have been munged by other users of the
commit).

However, we do not currently ever munge the commit buffer,
and not using the already-available buffer carries a fairly
big performance penalty when we are looking at a large
number of commits. Here are timings on linux.git:

  [baseline, no signatures]
  $ time git log >/dev/null
  real    0m4.902s
  user    0m4.784s
  sys     0m0.120s

  [before]
  $ time git log --show-signature >/dev/null
  real    0m14.735s
  user    0m9.964s
  sys     0m0.944s

  [after]
  $ time git log --show-signature >/dev/null
  real    0m9.981s
  user    0m5.260s
  sys     0m0.936s

Note that our user CPU time drops almost in half, close to
the non-signature case, but we do still spend more
wall-clock and system time, presumably from dealing with
gpg.

An alternative to this is to note that most commits do not
have signatures (less than 1% in this repo), yet we pay the
re-parsing cost for every commit just to find out if it has
a mergetag or signature. If we checked that when parsing the
commit initially, we could avoid re-examining most commits
later on. Even if we did pursue that direction, however,
this would still speed up the cases where we _do_ have
signatures. So it's probably worth doing either way.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:10:13 -07:00
8597ea3afe commit: record buffer length in cache
Most callsites which use the commit buffer try to use the
cached version attached to the commit, rather than
re-reading from disk. Unfortunately, that interface provides
only a pointer to the NUL-terminated buffer, with no
indication of the original length.

For the most part, this doesn't matter. People do not put
NULs in their commit messages, and the log code is happy to
treat it all as a NUL-terminated string. However, some code
paths do care. For example, when checking signatures, we
want to be very careful that we verify all the bytes to
avoid malicious trickery.

This patch just adds an optional "size" out-pointer to
get_commit_buffer and friends. The existing callers all pass
NULL (there did not seem to be any obvious sites where we
could avoid an immediate strlen() call, though perhaps with
some further refactoring we could).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:09:38 -07:00
c1b3c71f4b commit: convert commit->buffer to a slab
This will make it easier to manage the buffer cache
independently of the "struct commit" objects. It also
shrinks "struct commit" by one pointer, which may be
helpful.

Unfortunately it does not reduce the max memory size of
something like "rev-list", because rev-list uses
get_cached_commit_buffer() to decide not to show each
commit's output (and due to the design of slab_at, accessing
the slab requires us to extend it, allocating exactly the
same number of buffer pointers we dropped from the commit
structs).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:08:17 -07:00
80cdaba569 commit-slab: provide a static initializer
Callers currently must use init_foo_slab() at runtime before
accessing a slab. For global slabs, it's much nicer if we
can initialize them in BSS, so that each user does not have
to add code to check-and-initialize.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:08:17 -07:00
bc6b8fc130 use get_commit_buffer everywhere
Each of these sites assumes that commit->buffer is valid.
Since they would segfault if this was not the case, they are
likely to be correct in practice. However, we can
future-proof them by using get_commit_buffer.

And as a side effect, we abstract away the final bare uses
of commit->buffer.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:08:17 -07:00
b66103c3ba convert logmsg_reencode to get_commit_buffer
Like the callsites in the previous commit, logmsg_reencode
already falls back to read_sha1_file when necessary.
However, I split its conversion out into its own commit
because it's a bit more complex.

We return either:

  1. The original commit->buffer

  2. A newly allocated buffer from read_sha1_file

  3. A reencoded buffer (based on either 1 or 2 above).

while trying to do as few extra reads/allocations as
possible. Callers currently free the result with
logmsg_free, but we can simplify this by pointing them
straight to unuse_commit_buffer. This is a slight layering
violation, in that we may be passing a buffer from (3).
However, since the end result is to free() anything except
(1), which is unlikely to change, and because this makes the
interface much simpler, it's a reasonable bending of the
rules.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:08:17 -07:00
ba41c1c93f use get_commit_buffer to avoid duplicate code
For both of these sites, we already do the "fallback to
read_sha1_file" trick. But we can shorten the code by just
using get_commit_buffer.

Note that the error cases are slightly different when
read_sha1_file fails. get_commit_buffer will die() if the
object cannot be loaded, or is a non-commit.

For get_sha1_oneline, this will almost certainly never
happen, as we will have just called parse_object (and if it
does, it's probably worth complaining about).

For record_author_date, the new behavior is probably better;
we notify the user of the error instead of silently ignoring
it. And because it's used only for sorting by author-date,
somebody examining a corrupt repo can fallback to the
regular traversal order.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:08:17 -07:00
a97934d820 use get_cached_commit_buffer where appropriate
Some call sites check commit->buffer to see whether we have
a cached buffer, and if so, do some work with it. In the
long run we may want to switch these code paths to make
their decision on a different boolean flag (because checking
the cache may get a little more expensive in the future).
But for now, we can easily support them by converting the
calls to use get_cached_commit_buffer.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:08:17 -07:00
152ff1cceb provide helpers to access the commit buffer
Many sites look at commit->buffer to get more detailed
information than what is in the parsed commit struct.
However, we sometimes drop commit->buffer to save memory,
in which case the caller would need to read the object
afresh. Some callers do this (leading to duplicated code),
and others do not (which opens the possibility of a segfault
if somebody else frees the buffer).

Let's provide a pair of helpers, "get" and "unuse", that let
callers easily get the buffer. They will use the cached
buffer when possible, and otherwise load from disk using
read_sha1_file.

Note that we also need to add a "get_cached" variant which
returns NULL when we do not have a cached buffer. At first
glance this seems to defeat the purpose of "get", which is
to always provide a return value. However, some log code
paths actually use the NULL-ness of commit->buffer as a
boolean flag to decide whether to try printing the
commit. At least for now, we want to continue supporting
that use.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:08:17 -07:00
66c2827ea4 provide a helper to set the commit buffer
Right now this is just a one-liner, but abstracting it will
make it easier to change later.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:08:17 -07:00
0fb370da9c provide a helper to free commit buffer
This converts two lines into one at each caller. But more
importantly, it abstracts the concept of freeing the buffer,
which will make it easier to change later.

Note that we also need to provide a "detach" mechanism for a
tricky case in index-pack. We are passed a buffer for the
object generated by processing the incoming pack. If we are
not using --strict, we just calculate the sha1 on that
buffer and return, leaving the caller to free it.  But if we
are using --strict, we actually attach that buffer to an
object, pass the object to the fsck functions, and then
detach the buffer from the object again (so that the caller
can free it as usual).  In this case, we don't want to free
the buffer ourselves, but just make sure it is no longer
associated with the commit.

Note that we are making the assumption here that the
attach/detach process does not impact the buffer at all
(e.g., it is never reallocated or modified). That holds true
now, and we have no plans to change that. However, as we
abstract the commit_buffer code, this dependency becomes
less obvious. So when we detach, let's also make sure that
we get back the same buffer that we gave to the
commit_buffer code.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-13 12:07:47 -07:00
d74a4e57d2 sequencer: use logmsg_reencode in get_message
This simplifies the code, as logmsg_reencode handles the
reencoding for us in a single call. It also means we learn
logmsg_reencode's trick of pulling the buffer from disk when
commit->buffer is NULL (we currently just silently return!).
It is doubtful this matters in practice, though, as
sequencer operations would not generally turn off
save_commit_buffer.

Note that we may be fixing a bug here. The existing code
does:

  if (same_encoding(to, from))
	  reencode_string(buf, to, from);

That probably should have been "!same_encoding".

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-12 10:29:43 -07:00
b000c59b0c logmsg_reencode: return const buffer
The return value from logmsg_reencode may be either a newly
allocated buffer or a pointer to the existing commit->buffer.
We would not want the caller to accidentally free() or
modify the latter, so let's mark it as const.  We can cast
away the constness in logmsg_free, but only once we have
determined that it is a free-able buffer.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-12 10:29:43 -07:00
10322a0aaf do not create "struct commit" with xcalloc
In both blame and merge-recursive, we sometimes create a
"fake" commit struct for convenience (e.g., to represent the
HEAD state as if we would commit it). By allocating
ourselves rather than using alloc_commit_node, we do not
properly set the "index" field of the commit. This can
produce subtle bugs if we then use commit-slab on the
resulting commit, as we will share the "0" index with
another commit.

We can fix this by using alloc_commit_node() to allocate.
Note that we cannot free the result, as it is part of our
commit allocator. However, both cases were already leaking
the allocated commit anyway, so there's nothing to fix up.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-12 10:29:42 -07:00
969eba6341 commit: push commit_index update into alloc_commit_node
Whenever we create a commit object via lookup_commit, we
give it a unique index to be used with the commit-slab API.
The theory is that any "struct commit" we create would
follow this code path, so any such struct would get an
index. However, callers could use alloc_commit_node()
directly (and get multiple commits with index 0).

Let's push the indexing into alloc_commit_node so that it's
hard for callers to get it wrong.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-12 10:29:42 -07:00
c335d74d34 alloc: include any-object allocations in alloc_report
When 2c1cbec (Use proper object allocators for unknown
object nodes too, 2007-04-16), added a special "any_object"
allocator, it never taught alloc_report to report on it. To
do so we need to add an extra type argument to the REPORT
macro, as that commit did for DEFINE_ALLOCATOR.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-12 10:29:42 -07:00
e6dfcd6767 replace dangerous uses of strbuf_attach
It is not a good idea to strbuf_attach an arbitrary pointer
just because a function you are calling wants a strbuf.
Attaching implies a transfer of memory ownership; if anyone
were to modify or release the resulting strbuf, we would
free() the pointer, leading to possible problems:

  1. Other users of the original pointer might access freed
     memory.

  2. The pointer might not be the start of a malloc'd
     area, so calling free() on it in the first place would
     be wrong.

In the two cases modified here, we are fortunate that nobody
touches the strbuf once it is attached, but it is an
accident waiting to happen.  Since the previous commit,
commit_tree and friends take a pointer/buf pair, so we can
just do away with the strbufs entirely.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-12 10:29:42 -07:00
3ffefb54c0 commit_tree: take a pointer/len pair rather than a const strbuf
While strbufs are pretty common throughout our code, it is
more flexible for functions to take a pointer/len pair than
a strbuf. It's easy to turn a strbuf into such a pair (by
dereferencing its members), but less easy to go the other
way (you can strbuf_attach, but that has implications about
memory ownership).

This patch teaches commit_tree (and its associated callers
and sub-functions) to take such a pair for the commit
message rather than a strbuf.  This makes passing the buffer
around slightly more verbose, but means we can get rid of
some dangerous strbuf_attach calls in the next patch.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-12 10:29:41 -07:00
d078d85bc3 repack: s/write_bitmap/&s/ in code
The config name is "writeBitmaps", so the internal variable
missing the plural is unnecessarily confusing to write.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-10 14:01:30 -07:00
3198b89fb2 repack: respect pack.writebitmaps
The config option to turn on bitmaps is read all the way
down in the plumbing of pack-objects. This makes it hard for
other options in the porcelain of repack to make decisions
based on the bitmap setting. For example,
repack.packKeptObjects tries to kick in by default only when
bitmaps are turned on. But it can't do so reliably because
it doesn't yet know whether we are using bitmaps.

This patch teaches repack to respect pack.writebitmaps. It
means we pass a redundant command-line flag to pack-objects,
but that's OK; it shouldn't affect the outcome.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-10 14:01:08 -07:00
64d3dc9468 repack: do not accidentally pack kept objects by default
Commit ee34a2b (repack: add `repack.packKeptObjects` config
var, 2014-03-03) added a flag which could duplicate kept
objects, but did not mean to turn it on by default. Instead,
the option is tied by default to the decision to write
bitmaps, like:

  if (pack_kept_objects < 0)
	  pack_kept_objects = write_bitmap;

after which we expect pack_kept_objects to be a boolean 0 or
1.  However, that assignment neglects that write_bitmap is
_also_ a tri-state with "-1" as the default, and with
neither option given, we accidentally turn the option on.

This patch is the minimal fix to restore the desired
behavior for the default state. Further patches will fix the
more complicated cases.

Note the update to t7700. It failed to turn on bitmaps,
meaning we were actually confirming the wrong behavior!

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-10 13:58:43 -07:00
e61a6c1d82 dir.c:trim_trailing_spaces(): fix for " \ " sequence
Discard the unnecessary 'nr_spaces' variable, remove 'strlen()' and
improve the 'if' structure.  Switch to pointers instead of integers
to control the loop.

Slightly more rare occurrences of 'text  \    ' with a backslash
in between spaces are handled correctly.  Namely, the code in
7e2e4b37 (dir: ignore trailing spaces in exclude patterns, 2014-02-09)
does not reset 'last_space' when a backslash is encountered and the above
line stays intact as a result.

Add a test at the end of t/t0008-ignores.sh to exhibit this behavior.

Signed-off-by: Pasha Bolokhov <pasha.bolokhov@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-02 15:48:48 -07:00
45 changed files with 492 additions and 247 deletions

View File

@ -0,0 +1,32 @@
Git v2.0.2 Release Notes
========================
* Documentation for "git submodule sync" forgot to say that the subcommand
can take the "--recursive" option.
* Mishandling of patterns in .gitignore that has trailing SPs quoted
with backslashes (e.g. ones that end with "\ ") have been
corrected.
* Recent updates to "git repack" started to duplicate objects that
are in packfiles marked with .keep flag into the new packfile by
mistake.
* "git clone -b brefs/tags/bar" would have mistakenly thought we were
following a single tag, even though it was a name of the branch,
because it incorrectly used strstr().
* "%G" (nothing after G) is an invalid pretty format specifier, but
the parser did not notice it as garbage.
* Code to avoid adding the same alternate object store twice was
subtly broken for a long time, but nobody seems to have noticed.
* A handful of code paths had to read the commit object more than
once when showing header fields that are usually not parsed. The
internal data structure to keep track of the contents of the commit
object has been updated to reduce the need for this double-reading,
and to allow the caller find the length of the object.
* During "git rebase --merge", a conflicted patch could not be
skipped with "--skip" if the next one also conflicted.

View File

@ -20,7 +20,7 @@ SYNOPSIS
'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
[commit] [--] [<path>...]
'git submodule' [--quiet] foreach [--recursive] <command>
'git submodule' [--quiet] sync [--] [<path>...]
'git submodule' [--quiet] sync [--recursive] [--] [<path>...]
DESCRIPTION

View File

@ -43,9 +43,10 @@ unreleased) version of Git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
* link:v2.0.1/git.html[documentation for release 2.0.1]
* link:v2.0.2/git.html[documentation for release 2.0.2]
* release notes for
link:RelNotes/2.0.2.txt[2.0.2],
link:RelNotes/2.0.1.txt[2.0.1],
link:RelNotes/2.0.0.txt[2.0.0].

View File

@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
DEF_VER=v2.0.1
DEF_VER=v2.0.2
LF='
'

View File

@ -1 +1 @@
Documentation/RelNotes/2.0.1.txt
Documentation/RelNotes/2.0.2.txt

23
alloc.c
View File

@ -47,23 +47,32 @@ union any_object {
DEFINE_ALLOCATOR(blob, struct blob)
DEFINE_ALLOCATOR(tree, struct tree)
DEFINE_ALLOCATOR(commit, struct commit)
DEFINE_ALLOCATOR(raw_commit, struct commit)
DEFINE_ALLOCATOR(tag, struct tag)
DEFINE_ALLOCATOR(object, union any_object)
void *alloc_commit_node(void)
{
static int commit_count;
struct commit *c = alloc_raw_commit_node();
c->index = commit_count++;
return c;
}
static void report(const char *name, unsigned int count, size_t size)
{
fprintf(stderr, "%10s: %8u (%"PRIuMAX" kB)\n",
name, count, (uintmax_t) size);
}
#define REPORT(name) \
report(#name, name##_allocs, name##_allocs * sizeof(struct name) >> 10)
#define REPORT(name, type) \
report(#name, name##_allocs, name##_allocs * sizeof(type) >> 10)
void alloc_report(void)
{
REPORT(blob);
REPORT(tree);
REPORT(commit);
REPORT(tag);
REPORT(blob, struct blob);
REPORT(tree, struct tree);
REPORT(raw_commit, struct commit);
REPORT(tag, struct tag);
REPORT(object, union any_object);
}

View File

@ -5,20 +5,18 @@
*/
#include "git-compat-util.h"
#include "builtin.h"
#include "argv-array.h"
int cmd_annotate(int argc, const char **argv, const char *prefix)
{
const char **nargv;
struct argv_array args = ARGV_ARRAY_INIT;
int i;
nargv = xmalloc(sizeof(char *) * (argc + 2));
nargv[0] = "annotate";
nargv[1] = "-c";
argv_array_pushl(&args, "annotate", "-c", NULL);
for (i = 1; i < argc; i++) {
nargv[i+1] = argv[i];
argv_array_push(&args, argv[i]);
}
nargv[argc + 1] = NULL;
return cmd_blame(argc + 1, nargv, prefix);
return cmd_blame(args.argc, args.argv, prefix);
}

View File

@ -1405,7 +1405,7 @@ static void get_commit_info(struct commit *commit,
{
int len;
const char *subject, *encoding;
char *message;
const char *message;
commit_info_init(ret);
@ -1416,7 +1416,7 @@ static void get_commit_info(struct commit *commit,
&ret->author_time, &ret->author_tz);
if (!detailed) {
logmsg_free(message, commit);
unuse_commit_buffer(commit, message);
return;
}
@ -1430,7 +1430,7 @@ static void get_commit_info(struct commit *commit,
else
strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
logmsg_free(message, commit);
unuse_commit_buffer(commit, message);
}
/*
@ -2005,6 +2005,18 @@ static void append_merge_parents(struct commit_list **tail)
strbuf_release(&line);
}
/*
* This isn't as simple as passing sb->buf and sb->len, because we
* want to transfer ownership of the buffer to the commit (so we
* must use detach).
*/
static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
{
size_t len;
void *buf = strbuf_detach(sb, &len);
set_commit_buffer(c, buf, len);
}
/*
* Prepare a dummy commit that represents the work tree (or staged) item.
* Note that annotating work tree item never works in the reverse.
@ -2026,7 +2038,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
struct strbuf msg = STRBUF_INIT;
time(&now);
commit = xcalloc(1, sizeof(*commit));
commit = alloc_commit_node();
commit->object.parsed = 1;
commit->date = now;
commit->object.type = OBJ_COMMIT;
@ -2053,7 +2065,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
ident, ident, path,
(!contents_from ? path :
(!strcmp(contents_from, "-") ? "standard input" : contents_from)));
commit->buffer = strbuf_detach(&msg, NULL);
set_commit_buffer_from_strbuf(commit, &msg);
if (!contents_from || strcmp("-", contents_from)) {
struct stat st;

View File

@ -48,7 +48,7 @@ enum color_clean {
CLEAN_COLOR_PROMPT = 2,
CLEAN_COLOR_HEADER = 3,
CLEAN_COLOR_HELP = 4,
CLEAN_COLOR_ERROR = 5,
CLEAN_COLOR_ERROR = 5
};
#define MENU_OPTS_SINGLETON 01

View File

@ -695,7 +695,7 @@ static void write_refspec_config(const char* src_ref_prefix,
if (option_mirror || !option_bare) {
if (option_single_branch && !option_mirror) {
if (option_branch) {
if (strstr(our_head_points_at->name, "refs/tags/"))
if (starts_with(our_head_points_at->name, "refs/tags/"))
strbuf_addf(&value, "+%s:%s", our_head_points_at->name,
our_head_points_at->name);
else

View File

@ -123,8 +123,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
die_errno("git commit-tree: failed to read");
}
if (commit_tree(&buffer, tree_sha1, parents, commit_sha1,
NULL, sign_commit)) {
if (commit_tree(buffer.buf, buffer.len, tree_sha1, parents,
commit_sha1, NULL, sign_commit)) {
strbuf_release(&buffer);
return 1;
}

View File

@ -1672,8 +1672,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
append_merge_tag_headers(parents, &tail);
}
if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1,
author_ident.buf, sign_commit, extra)) {
if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
parents, sha1, author_ident.buf, sign_commit, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
}

View File

@ -279,6 +279,7 @@ static const char *find_encoding(const char *begin, const char *end)
static void handle_commit(struct commit *commit, struct rev_info *rev)
{
int saved_output_format = rev->diffopt.output_format;
const char *commit_buffer;
const char *author, *author_end, *committer, *committer_end;
const char *encoding, *message;
char *reencoded = NULL;
@ -288,7 +289,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
parse_commit_or_die(commit);
author = strstr(commit->buffer, "\nauthor ");
commit_buffer = get_commit_buffer(commit, NULL);
author = strstr(commit_buffer, "\nauthor ");
if (!author)
die ("Could not find author in commit %s",
sha1_to_hex(commit->object.sha1));
@ -335,6 +337,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
? strlen(message) : 0),
reencoded ? reencoded : message ? message : "");
free(reencoded);
unuse_commit_buffer(commit, commit_buffer);
for (i = 0, p = commit->parents; p; p = p->next) {
int mark = get_object_mark(&p->item->object);

View File

@ -230,12 +230,14 @@ static void add_branch_desc(struct strbuf *out, const char *name)
static void record_person(int which, struct string_list *people,
struct commit *commit)
{
const char *buffer;
char *name_buf, *name, *name_end;
struct string_list_item *elem;
const char *field;
field = (which == 'a') ? "\nauthor " : "\ncommitter ";
name = strstr(commit->buffer, field);
buffer = get_commit_buffer(commit, NULL);
name = strstr(buffer, field);
if (!name)
return;
name += strlen(field);
@ -247,6 +249,7 @@ static void record_person(int which, struct string_list *people,
if (name_end < name)
return;
name_buf = xmemdupz(name, name_end - name + 1);
unuse_commit_buffer(commit, buffer);
elem = string_list_lookup(people, name_buf);
if (!elem) {

View File

@ -310,8 +310,7 @@ static int fsck_obj(struct object *obj)
if (obj->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *) obj;
free(commit->buffer);
commit->buffer = NULL;
free_commit_buffer(commit);
if (!commit->parents && show_root)
printf("root %s\n", sha1_to_hex(commit->object.sha1));

View File

@ -786,7 +786,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
}
if (obj->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *) obj;
commit->buffer = NULL;
if (detach_commit_buffer(commit, NULL) != data)
die("BUG: parse_object_buffer transmogrified our buffer");
}
obj->flags |= FLAG_CHECKED;
}

View File

@ -345,8 +345,7 @@ static int cmd_log_walk(struct rev_info *rev)
rev->max_count++;
if (!rev->reflog_info) {
/* we allow cycles in reflog ancestry */
free(commit->buffer);
commit->buffer = NULL;
free_commit_buffer(commit);
}
free_commit_list(commit->parents);
commit->parents = NULL;
@ -915,9 +914,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
&need_8bit_cte);
for (i = 0; !need_8bit_cte && i < nr; i++)
if (has_non_ascii(list[i]->buffer))
for (i = 0; !need_8bit_cte && i < nr; i++) {
const char *buf = get_commit_buffer(list[i], NULL);
if (has_non_ascii(buf))
need_8bit_cte = 1;
unuse_commit_buffer(list[i], buf);
}
if (!branch_name)
branch_name = find_branch_name(rev);
@ -1504,8 +1506,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
die(_("Failed to create output files"));
shown = log_tree_commit(&rev, commit);
free(commit->buffer);
commit->buffer = NULL;
free_commit_buffer(commit);
/* We put one extra blank line between formatted
* patches and this flag is used by log-tree code

View File

@ -852,8 +852,8 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
parent->next->item = remoteheads->item;
parent->next->next = NULL;
prepare_to_commit(remoteheads);
if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
sign_commit))
if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parent,
result_commit, NULL, sign_commit))
die(_("failed to write commit object"));
finish(head, remoteheads, result_commit, "In-index merge");
drop_save();
@ -877,8 +877,8 @@ static int finish_automerge(struct commit *head,
commit_list_insert(head, &parents);
strbuf_addch(&merge_msg, '\n');
prepare_to_commit(remoteheads);
if (commit_tree(&merge_msg, result_tree, parents, result_commit,
NULL, sign_commit))
if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
result_commit, NULL, sign_commit))
die(_("failed to write commit object"));
strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
finish(head, remoteheads, result_commit, buf.buf);

View File

@ -10,6 +10,7 @@
static int delta_base_offset = 1;
static int pack_kept_objects = -1;
static int write_bitmaps = -1;
static char *packdir, *packtmp;
static const char *const git_repack_usage[] = {
@ -27,6 +28,10 @@ static int repack_config(const char *var, const char *value, void *cb)
pack_kept_objects = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "pack.writebitmaps")) {
write_bitmaps = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value, cb);
}
@ -149,7 +154,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int no_update_server_info = 0;
int quiet = 0;
int local = 0;
int write_bitmap = -1;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@ -168,7 +172,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
OPT__QUIET(&quiet, N_("be quiet")),
OPT_BOOL('l', "local", &local,
N_("pass --local to git-pack-objects")),
OPT_BOOL('b', "write-bitmap-index", &write_bitmap,
OPT_BOOL('b', "write-bitmap-index", &write_bitmaps,
N_("write bitmap index")),
OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
N_("with -A, do not loosen objects older than this")),
@ -191,7 +195,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
git_repack_usage, 0);
if (pack_kept_objects < 0)
pack_kept_objects = write_bitmap;
pack_kept_objects = write_bitmaps > 0;
packdir = mkpathdup("%s/pack", get_object_directory());
packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
@ -217,9 +221,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_pushf(&cmd_args, "--no-reuse-delta");
if (no_reuse_object)
argv_array_pushf(&cmd_args, "--no-reuse-object");
if (write_bitmap >= 0)
if (write_bitmaps >= 0)
argv_array_pushf(&cmd_args, "--%swrite-bitmap-index",
write_bitmap ? "" : "no-");
write_bitmaps ? "" : "no-");
if (pack_everything & ALL_INTO_ONE) {
get_non_kept_pack_filenames(&existing_packs);

View File

@ -93,7 +93,7 @@ static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
static void print_new_head_line(struct commit *commit)
{
const char *hex, *body;
char *msg;
const char *msg;
hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
printf(_("HEAD is now at %s"), hex);
@ -109,7 +109,7 @@ static void print_new_head_line(struct commit *commit)
}
else
printf("\n");
logmsg_free(msg, commit);
unuse_commit_buffer(commit, msg);
}
static void update_index_from_diff(struct diff_queue_struct *q,

View File

@ -106,7 +106,7 @@ static void show_commit(struct commit *commit, void *data)
else
putchar('\n');
if (revs->verbose_header && commit->buffer) {
if (revs->verbose_header && get_cached_commit_buffer(commit, NULL)) {
struct strbuf buf = STRBUF_INIT;
struct pretty_print_context ctx = {0};
ctx.abbrev = revs->abbrev;
@ -173,8 +173,7 @@ static void finish_commit(struct commit *commit, void *data)
free_commit_list(commit->parents);
commit->parents = NULL;
}
free(commit->buffer);
commit->buffer = NULL;
free_commit_buffer(commit);
}
static void finish_object(struct object *obj,

View File

@ -117,4 +117,16 @@ static int stat_ ##slabname## realloc
* catch because GCC silently parses it by default.
*/
/*
* Statically initialize a commit slab named "var". Note that this
* evaluates "stride" multiple times! Example:
*
* struct indegree indegrees = COMMIT_SLAB_INIT(1, indegrees);
*
*/
#define COMMIT_SLAB_INIT(stride, var) { \
COMMIT_SLAB_SIZE / sizeof(**((var).slab)) / (stride), \
(stride), 0, NULL \
}
#endif /* COMMIT_SLAB_H */

131
commit.c
View File

@ -17,7 +17,6 @@ static struct commit_extra_header *read_commit_extra_header_lines(const char *bu
int save_commit_buffer = 1;
const char *commit_type = "commit";
static int commit_count;
static struct commit *check_commit(struct object *obj,
const unsigned char *sha1,
@ -64,7 +63,6 @@ struct commit *lookup_commit(const unsigned char *sha1)
struct object *obj = lookup_object(sha1);
if (!obj) {
struct commit *c = alloc_commit_node();
c->index = commit_count++;
return create_object(sha1, OBJ_COMMIT, c);
}
if (!obj->type)
@ -247,6 +245,76 @@ int unregister_shallow(const unsigned char *sha1)
return 0;
}
struct commit_buffer {
void *buffer;
unsigned long size;
};
define_commit_slab(buffer_slab, struct commit_buffer);
static struct buffer_slab buffer_slab = COMMIT_SLAB_INIT(1, buffer_slab);
void set_commit_buffer(struct commit *commit, void *buffer, unsigned long size)
{
struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
v->buffer = buffer;
v->size = size;
}
const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
{
struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
if (sizep)
*sizep = v->size;
return v->buffer;
}
const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
{
const void *ret = get_cached_commit_buffer(commit, sizep);
if (!ret) {
enum object_type type;
unsigned long size;
ret = read_sha1_file(commit->object.sha1, &type, &size);
if (!ret)
die("cannot read commit object %s",
sha1_to_hex(commit->object.sha1));
if (type != OBJ_COMMIT)
die("expected commit for %s, got %s",
sha1_to_hex(commit->object.sha1), typename(type));
if (sizep)
*sizep = size;
}
return ret;
}
void unuse_commit_buffer(const struct commit *commit, const void *buffer)
{
struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
if (v->buffer != buffer)
free((void *)buffer);
}
void free_commit_buffer(struct commit *commit)
{
struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
free(v->buffer);
v->buffer = NULL;
v->size = 0;
}
const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
{
struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
void *ret;
ret = v->buffer;
if (sizep)
*sizep = v->size;
v->buffer = NULL;
v->size = 0;
return ret;
}
int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
{
const char *tail = buffer;
@ -324,7 +392,7 @@ int parse_commit(struct commit *item)
}
ret = parse_commit_buffer(item, buffer, size);
if (save_commit_buffer && !ret) {
item->buffer = buffer;
set_commit_buffer(item, buffer, size);
return 0;
}
free(buffer);
@ -539,22 +607,12 @@ static void record_author_date(struct author_date_slab *author_date,
struct commit *commit)
{
const char *buf, *line_end, *ident_line;
char *buffer = NULL;
const char *buffer = get_commit_buffer(commit, NULL);
struct ident_split ident;
char *date_end;
unsigned long date;
if (!commit->buffer) {
unsigned long size;
enum object_type type;
buffer = read_sha1_file(commit->object.sha1, &type, &size);
if (!buffer)
return;
}
for (buf = commit->buffer ? commit->buffer : buffer;
buf;
buf = line_end + 1) {
for (buf = buffer; buf; buf = line_end + 1) {
line_end = strchrnul(buf, '\n');
ident_line = skip_prefix(buf, "author ");
if (!ident_line) {
@ -575,7 +633,7 @@ static void record_author_date(struct author_date_slab *author_date,
*(author_date_slab_at(author_date, commit)) = date;
fail_exit:
free(buffer);
unuse_commit_buffer(commit, buffer);
}
static int compare_commits_by_author_date(const void *a_, const void *b_,
@ -1080,17 +1138,14 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid)
return 0;
}
int parse_signed_commit(const unsigned char *sha1,
int parse_signed_commit(const struct commit *commit,
struct strbuf *payload, struct strbuf *signature)
{
unsigned long size;
enum object_type type;
char *buffer = read_sha1_file(sha1, &type, &size);
int in_signature, saw_signature = -1;
char *line, *tail;
if (!buffer || type != OBJ_COMMIT)
goto cleanup;
unsigned long size;
const char *buffer = get_commit_buffer(commit, &size);
int in_signature, saw_signature = -1;
const char *line, *tail;
line = buffer;
tail = buffer + size;
@ -1098,7 +1153,7 @@ int parse_signed_commit(const unsigned char *sha1,
saw_signature = 0;
while (line < tail) {
const char *sig = NULL;
char *next = memchr(line, '\n', tail - line);
const char *next = memchr(line, '\n', tail - line);
next = next ? next + 1 : tail;
if (in_signature && line[0] == ' ')
@ -1119,8 +1174,7 @@ int parse_signed_commit(const unsigned char *sha1,
}
line = next;
}
cleanup:
free(buffer);
unuse_commit_buffer(commit, buffer);
return saw_signature;
}
@ -1211,8 +1265,7 @@ void check_commit_signature(const struct commit* commit, struct signature_check
sigc->result = 'N';
if (parse_signed_commit(commit->object.sha1,
&payload, &signature) <= 0)
if (parse_signed_commit(commit, &payload, &signature) <= 0)
goto out;
status = verify_signed_buffer(payload.buf, payload.len,
signature.buf, signature.len,
@ -1257,11 +1310,9 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
{
struct commit_extra_header *extra = NULL;
unsigned long size;
enum object_type type;
char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
if (buffer && type == OBJ_COMMIT)
extra = read_commit_extra_header_lines(buffer, size, exclude);
free(buffer);
const char *buffer = get_commit_buffer(commit, &size);
extra = read_commit_extra_header_lines(buffer, size, exclude);
unuse_commit_buffer(commit, buffer);
return extra;
}
@ -1344,7 +1395,8 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
}
}
int commit_tree(const struct strbuf *msg, const unsigned char *tree,
int commit_tree(const char *msg, size_t msg_len,
const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit)
{
@ -1352,7 +1404,7 @@ int commit_tree(const struct strbuf *msg, const unsigned char *tree,
int result;
append_merge_tag_headers(parents, &tail);
result = commit_tree_extended(msg, tree, parents, ret,
result = commit_tree_extended(msg, msg_len, tree, parents, ret,
author, sign_commit, extra);
free_commit_extra_headers(extra);
return result;
@ -1473,7 +1525,8 @@ static const char commit_utf8_warn[] =
"You may want to amend it after fixing the message, or set the config\n"
"variable i18n.commitencoding to the encoding your project uses.\n";
int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
int commit_tree_extended(const char *msg, size_t msg_len,
const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit,
struct commit_extra_header *extra)
@ -1484,7 +1537,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
assert_sha1_type(tree, OBJ_TREE);
if (memchr(msg->buf, '\0', msg->len))
if (memchr(msg, '\0', msg_len))
return error("a NUL byte in commit log message not allowed.");
/* Not having i18n.commitencoding is the same as having utf-8 */
@ -1523,7 +1576,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
strbuf_addch(&buffer, '\n');
/* And add the comment */
strbuf_addbuf(&buffer, msg);
strbuf_add(&buffer, msg, msg_len);
/* And check the encoding */
if (encoding_is_utf8 && !verify_utf8(&buffer))

View File

@ -20,7 +20,6 @@ struct commit {
unsigned long date;
struct commit_list *parents;
struct tree *tree;
char *buffer;
};
extern int save_commit_buffer;
@ -51,6 +50,44 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
int parse_commit(struct commit *item);
void parse_commit_or_die(struct commit *item);
/*
* Associate an object buffer with the commit. The ownership of the
* memory is handed over to the commit, and must be free()-able.
*/
void set_commit_buffer(struct commit *, void *buffer, unsigned long size);
/*
* Get any cached object buffer associated with the commit. Returns NULL
* if none. The resulting memory should not be freed.
*/
const void *get_cached_commit_buffer(const struct commit *, unsigned long *size);
/*
* Get the commit's object contents, either from cache or by reading the object
* from disk. The resulting memory should not be modified, and must be given
* to unuse_commit_buffer when the caller is done.
*/
const void *get_commit_buffer(const struct commit *, unsigned long *size);
/*
* Tell the commit subsytem that we are done with a particular commit buffer.
* The commit and buffer should be the input and return value, respectively,
* from an earlier call to get_commit_buffer. The buffer may or may not be
* freed by this call; callers should not access the memory afterwards.
*/
void unuse_commit_buffer(const struct commit *, const void *buffer);
/*
* Free any cached object buffer associated with the commit.
*/
void free_commit_buffer(struct commit *);
/*
* Disassociate any cached object buffer from the commit, but do not free it.
* The buffer (or NULL, if none) is returned.
*/
const void *detach_commit_buffer(struct commit *, unsigned long *sizep);
/* Find beginning and length of commit subject. */
int find_commit_subject(const char *commit_buffer, const char **subject);
@ -115,10 +152,9 @@ struct userformat_want {
extern int has_non_ascii(const char *text);
struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
extern char *logmsg_reencode(const struct commit *commit,
char **commit_encoding,
const char *output_encoding);
extern void logmsg_free(char *msg, const struct commit *commit);
extern const char *logmsg_reencode(const struct commit *commit,
char **commit_encoding,
const char *output_encoding);
extern void get_commit_format(const char *arg, struct rev_info *);
extern const char *format_subject(struct strbuf *sb, const char *msg,
const char *line_separator);
@ -261,11 +297,13 @@ struct commit_extra_header {
extern void append_merge_tag_headers(struct commit_list *parents,
struct commit_extra_header ***tail);
extern int commit_tree(const struct strbuf *msg, const unsigned char *tree,
extern int commit_tree(const char *msg, size_t msg_len,
const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit);
extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
extern int commit_tree_extended(const char *msg, size_t msg_len,
const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit,
struct commit_extra_header *);
@ -287,7 +325,7 @@ struct merge_remote_desc {
*/
struct commit *get_merge_parent(const char *name);
extern int parse_signed_commit(const unsigned char *sha1,
extern int parse_signed_commit(const struct commit *commit,
struct strbuf *message, struct strbuf *signature);
extern void print_commit_list(struct commit_list *list,
const char *format_cur,

32
dir.c
View File

@ -508,21 +508,25 @@ void clear_exclude_list(struct exclude_list *el)
static void trim_trailing_spaces(char *buf)
{
int i, last_space = -1, nr_spaces, len = strlen(buf);
for (i = 0; i < len; i++)
if (buf[i] == '\\')
i++;
else if (buf[i] == ' ') {
if (last_space == -1) {
last_space = i;
nr_spaces = 1;
} else
nr_spaces++;
} else
last_space = -1;
char *p, *last_space = NULL;
if (last_space != -1 && last_space + nr_spaces == len)
buf[last_space] = '\0';
for (p = buf; *p; p++)
switch (*p) {
case ' ':
if (!last_space)
last_space = p;
break;
case '\\':
p++;
if (!*p)
return;
/* fallthrough */
default:
last_space = NULL;
}
if (last_space)
*last_space = '\0';
}
int add_excludes_from_file_to_list(const char *fname,

13
fsck.c
View File

@ -276,9 +276,10 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f
return 0;
}
static int fsck_commit(struct commit *commit, fsck_error error_func)
static int fsck_commit_buffer(struct commit *commit, const char *buffer,
fsck_error error_func)
{
const char *buffer = commit->buffer, *tmp;
const char *tmp;
unsigned char tree_sha1[20], sha1[20];
struct commit_graft *graft;
int parents = 0;
@ -336,6 +337,14 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
return 0;
}
static int fsck_commit(struct commit *commit, fsck_error error_func)
{
const char *buffer = get_commit_buffer(commit, NULL);
int ret = fsck_commit_buffer(commit, buffer, error_func);
unuse_commit_buffer(commit, buffer);
return ret;
}
static int fsck_tag(struct tag *tag, fsck_error error_func)
{
struct object *tagged = tag->tagged;

View File

@ -53,11 +53,12 @@ continue_merge () {
}
call_merge () {
cmt="$(cat "$state_dir/cmt.$1")"
msgnum="$1"
echo "$msgnum" >"$state_dir/msgnum"
cmt="$(cat "$state_dir/cmt.$msgnum")"
echo "$cmt" > "$state_dir/current"
hd=$(git rev-parse --verify HEAD)
cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
msgnum=$(cat "$state_dir/msgnum")
eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
eval GITHEAD_$hd='$onto_name'
export GITHEAD_$cmt GITHEAD_$hd

View File

@ -376,7 +376,7 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
struct strbuf gpg_output = STRBUF_INIT;
int status;
if (parse_signed_commit(commit->object.sha1, &payload, &signature) <= 0)
if (parse_signed_commit(commit, &payload, &signature) <= 0)
goto out;
status = verify_signed_buffer(payload.buf, payload.len,
@ -588,7 +588,7 @@ void show_log(struct rev_info *opt)
show_mergetag(opt, commit);
}
if (!commit->buffer)
if (!get_cached_commit_buffer(commit, NULL))
return;
if (opt->show_notes) {

View File

@ -40,7 +40,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
{
struct commit *commit = xcalloc(1, sizeof(struct commit));
struct commit *commit = alloc_commit_node();
struct merge_remote_desc *desc = xmalloc(sizeof(*desc));
desc->name = comment;
@ -190,9 +190,11 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
printf(_("(bad commit)\n"));
else {
const char *title;
int len = find_commit_subject(commit->buffer, &title);
const char *msg = get_commit_buffer(commit, NULL);
int len = find_commit_subject(msg, &title);
if (len)
printf("%.*s\n", len, title);
unuse_commit_buffer(commit, msg);
}
}
}

View File

@ -48,7 +48,6 @@ int notes_cache_write(struct notes_cache *c)
{
unsigned char tree_sha1[20];
unsigned char commit_sha1[20];
struct strbuf msg = STRBUF_INIT;
if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref)
return -1;
@ -57,9 +56,8 @@ int notes_cache_write(struct notes_cache *c)
if (write_notes_tree(&c->tree, tree_sha1))
return -1;
strbuf_attach(&msg, c->validity,
strlen(c->validity), strlen(c->validity) + 1);
if (commit_tree(&msg, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0)
if (commit_tree(c->validity, strlen(c->validity), tree_sha1, NULL,
commit_sha1, NULL, NULL) < 0)
return -1;
if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
0, QUIET_ON_ERR) < 0)

View File

@ -644,7 +644,8 @@ int notes_merge(struct notes_merge_options *o,
struct commit_list *parents = NULL;
commit_list_insert(remote, &parents); /* LIFO order */
commit_list_insert(local, &parents);
create_notes_commit(local_tree, parents, &o->commit_msg,
create_notes_commit(local_tree, parents,
o->commit_msg.buf, o->commit_msg.len,
result_sha1);
}
@ -671,8 +672,8 @@ int notes_merge_commit(struct notes_merge_options *o,
DIR *dir;
struct dirent *e;
struct strbuf path = STRBUF_INIT;
char *msg = strstr(partial_commit->buffer, "\n\n");
struct strbuf sb_msg = STRBUF_INIT;
const char *buffer = get_commit_buffer(partial_commit, NULL);
const char *msg = strstr(buffer, "\n\n");
int baselen;
strbuf_addstr(&path, git_path(NOTES_MERGE_WORKTREE));
@ -719,9 +720,9 @@ int notes_merge_commit(struct notes_merge_options *o,
strbuf_setlen(&path, baselen);
}
strbuf_attach(&sb_msg, msg, strlen(msg), strlen(msg) + 1);
create_notes_commit(partial_tree, partial_commit->parents, &sb_msg,
result_sha1);
create_notes_commit(partial_tree, partial_commit->parents,
msg, strlen(msg), result_sha1);
unuse_commit_buffer(partial_commit, buffer);
if (o->verbosity >= 4)
printf("Finalized notes merge commit: %s\n",
sha1_to_hex(result_sha1));

View File

@ -4,7 +4,8 @@
#include "notes-utils.h"
void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
const struct strbuf *msg, unsigned char *result_sha1)
const char *msg, size_t msg_len,
unsigned char *result_sha1)
{
unsigned char tree_sha1[20];
@ -25,7 +26,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
/* else: t->ref points to nothing, assume root/orphan commit */
}
if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
if (commit_tree(msg, msg_len, tree_sha1, parents, result_sha1, NULL, NULL))
die("Failed to commit notes tree to database");
}
@ -46,7 +47,7 @@ void commit_notes(struct notes_tree *t, const char *msg)
if (buf.buf[buf.len - 1] != '\n')
strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
create_notes_commit(t, NULL, &buf, commit_sha1);
create_notes_commit(t, NULL, buf.buf, buf.len, commit_sha1);
strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);

View File

@ -15,7 +15,7 @@
* The resulting commit SHA1 is stored in result_sha1.
*/
void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
const struct strbuf *msg, unsigned char *result_sha1);
const char *msg, size_t msg_len, unsigned char *result_sha1);
void commit_notes(struct notes_tree *t, const char *msg);

View File

@ -197,8 +197,8 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
if (commit) {
if (parse_commit_buffer(commit, buffer, size))
return NULL;
if (!commit->buffer) {
commit->buffer = buffer;
if (!get_cached_commit_buffer(commit, NULL)) {
set_commit_buffer(commit, buffer, size);
*eaten_p = 1;
}
obj = &commit->object;

View File

@ -274,7 +274,7 @@ static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
enum rfc2047_type {
RFC2047_SUBJECT,
RFC2047_ADDRESS,
RFC2047_ADDRESS
};
static int is_rfc2047_special(char ch, enum rfc2047_type type)
@ -606,29 +606,16 @@ static char *replace_encoding_header(char *buf, const char *encoding)
return strbuf_detach(&tmp, NULL);
}
char *logmsg_reencode(const struct commit *commit,
char **commit_encoding,
const char *output_encoding)
const char *logmsg_reencode(const struct commit *commit,
char **commit_encoding,
const char *output_encoding)
{
static const char *utf8 = "UTF-8";
const char *use_encoding;
char *encoding;
char *msg = commit->buffer;
const char *msg = get_commit_buffer(commit, NULL);
char *out;
if (!msg) {
enum object_type type;
unsigned long size;
msg = read_sha1_file(commit->object.sha1, &type, &size);
if (!msg)
die("Cannot read commit object %s",
sha1_to_hex(commit->object.sha1));
if (type != OBJ_COMMIT)
die("Expected commit for '%s', got %s",
sha1_to_hex(commit->object.sha1), typename(type));
}
if (!output_encoding || !*output_encoding) {
if (commit_encoding)
*commit_encoding =
@ -652,12 +639,13 @@ char *logmsg_reencode(const struct commit *commit,
* Otherwise, we still want to munge the encoding header in the
* result, which will be done by modifying the buffer. If we
* are using a fresh copy, we can reuse it. But if we are using
* the cached copy from commit->buffer, we need to duplicate it
* to avoid munging commit->buffer.
* the cached copy from get_commit_buffer, we need to duplicate it
* to avoid munging the cached copy.
*/
out = msg;
if (out == commit->buffer)
out = xstrdup(out);
if (msg == get_cached_commit_buffer(commit, NULL))
out = xstrdup(msg);
else
out = (char *)msg;
}
else {
/*
@ -667,8 +655,8 @@ char *logmsg_reencode(const struct commit *commit,
* copy, we can free it.
*/
out = reencode_string(msg, output_encoding, use_encoding);
if (out && msg != commit->buffer)
free(msg);
if (out)
unuse_commit_buffer(commit, msg);
}
/*
@ -687,12 +675,6 @@ char *logmsg_reencode(const struct commit *commit,
return out ? out : msg;
}
void logmsg_free(char *msg, const struct commit *commit)
{
if (msg != commit->buffer)
free(msg);
}
static int mailmap_name(const char **email, size_t *email_len,
const char **name, size_t *name_len)
{
@ -796,7 +778,7 @@ struct format_commit_context {
struct signature_check signature_check;
enum flush_type flush_type;
enum trunc_type truncate;
char *message;
const char *message;
char *commit_encoding;
size_t width, indent1, indent2;
int auto_color;
@ -1267,6 +1249,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
if (c->signature_check.key)
strbuf_addstr(sb, c->signature_check.key);
break;
default:
return 0;
}
return 2;
}
@ -1536,7 +1520,7 @@ void format_commit_message(const struct commit *commit,
}
free(context.commit_encoding);
logmsg_free(context.message, commit);
unuse_commit_buffer(commit, context.message);
free(context.signature_check.gpg_output);
free(context.signature_check.signer);
}
@ -1705,7 +1689,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
unsigned long beginning_of_body;
int indent = 4;
const char *msg;
char *reencoded;
const char *reencoded;
const char *encoding;
int need_8bit_cte = pp->need_8bit_cte;
@ -1772,7 +1756,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
strbuf_addch(sb, '\n');
logmsg_free(reencoded, commit);
unuse_commit_buffer(commit, reencoded);
}
void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,

View File

@ -2791,7 +2791,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
{
int retval;
const char *encoding;
char *message;
const char *message;
struct strbuf buf = STRBUF_INIT;
if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
@ -2833,14 +2833,21 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
format_display_notes(commit->object.sha1, &buf, encoding, 1);
}
/* Find either in the original commit message, or in the temporary */
/*
* Find either in the original commit message, or in the temporary.
* Note that we cast away the constness of "message" here. It is
* const because it may come from the cached commit buffer. That's OK,
* because we know that it is modifiable heap memory, and that while
* grep_buffer may modify it for speed, it will restore any
* changes before returning.
*/
if (buf.len)
retval = grep_buffer(&opt->grep_filter, buf.buf, buf.len);
else
retval = grep_buffer(&opt->grep_filter,
message, strlen(message));
(char *)message, strlen(message));
strbuf_release(&buf);
logmsg_free(message, commit);
unuse_commit_buffer(commit, message);
return retval;
}

View File

@ -116,39 +116,23 @@ static const char *action_name(const struct replay_opts *opts)
return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
}
static char *get_encoding(const char *message);
struct commit_message {
char *parent_label;
const char *label;
const char *subject;
char *reencoded_message;
const char *message;
};
static int get_message(struct commit *commit, struct commit_message *out)
{
const char *encoding;
const char *abbrev, *subject;
int abbrev_len, subject_len;
char *q;
if (!commit->buffer)
return -1;
encoding = get_encoding(commit->buffer);
if (!encoding)
encoding = "UTF-8";
if (!git_commit_encoding)
git_commit_encoding = "UTF-8";
out->reencoded_message = NULL;
out->message = commit->buffer;
if (same_encoding(encoding, git_commit_encoding))
out->reencoded_message = reencode_string(commit->buffer,
git_commit_encoding, encoding);
if (out->reencoded_message)
out->message = out->reencoded_message;
out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
abbrev_len = strlen(abbrev);
@ -167,29 +151,10 @@ static int get_message(struct commit *commit, struct commit_message *out)
return 0;
}
static void free_message(struct commit_message *msg)
static void free_message(struct commit *commit, struct commit_message *msg)
{
free(msg->parent_label);
free(msg->reencoded_message);
}
static char *get_encoding(const char *message)
{
const char *p = message, *eol;
while (*p && *p != '\n') {
for (eol = p + 1; *eol && *eol != '\n'; eol++)
; /* do nothing */
if (starts_with(p, "encoding ")) {
char *result = xmalloc(eol - 8 - p);
strlcpy(result, p + 9, eol - 8 - p);
return result;
}
p = eol;
if (*p == '\n')
p++;
}
return NULL;
unuse_commit_buffer(commit, msg->message);
}
static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
@ -485,7 +450,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
unsigned char head[20];
struct commit *base, *next, *parent;
const char *base_label, *next_label;
struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
struct commit_message msg = { NULL, NULL, NULL, NULL };
char *defmsg = NULL;
struct strbuf msgbuf = STRBUF_INIT;
int res, unborn = 0, allow;
@ -650,7 +615,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
res = run_git_commit(defmsg, opts, allow);
leave:
free_message(&msg);
free_message(commit, &msg);
free(defmsg);
return res;
@ -697,10 +662,12 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
int subject_len;
for (cur = todo_list; cur; cur = cur->next) {
const char *commit_buffer = get_commit_buffer(cur->item, NULL);
sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
subject_len = find_commit_subject(cur->item->buffer, &subject);
subject_len = find_commit_subject(commit_buffer, &subject);
strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
subject_len, subject);
unuse_commit_buffer(cur->item, commit_buffer);
}
return 0;
}

View File

@ -315,7 +315,8 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base, int
* thing twice, or object directory itself.
*/
for (alt = alt_odb_list; alt; alt = alt->next) {
if (!memcmp(ent->base, alt->base, pfxlen)) {
if (pfxlen == alt->name - alt->base - 1 &&
!memcmp(ent->base, alt->base, pfxlen)) {
free(ent);
return -1;
}

View File

@ -862,27 +862,17 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
commit_list_insert(l->item, &backup);
}
while (list) {
char *p, *to_free = NULL;
const char *p, *buf;
struct commit *commit;
enum object_type type;
unsigned long size;
int matches;
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
if (!parse_object(commit->object.sha1))
continue;
if (commit->buffer)
p = commit->buffer;
else {
p = read_sha1_file(commit->object.sha1, &type, &size);
if (!p)
continue;
to_free = p;
}
p = strstr(p, "\n\n");
buf = get_commit_buffer(commit, NULL);
p = strstr(buf, "\n\n");
matches = p && !regexec(&regex, p + 2, 0, NULL, 0);
free(to_free);
unuse_commit_buffer(commit, buf);
if (matches) {
hashcpy(sha1, commit->object.sha1);

View File

@ -806,4 +806,29 @@ test_expect_success !MINGW 'quoting allows trailing whitespace' '
test_cmp err.expect err
'
test_expect_success NOT_MINGW,NOT_CYGWIN 'correct handling of backslashes' '
rm -rf whitespace &&
mkdir whitespace &&
>"whitespace/trailing 1 " &&
>"whitespace/trailing 2 \\\\" &&
>"whitespace/trailing 3 \\\\" &&
>"whitespace/trailing 4 \\ " &&
>"whitespace/trailing 5 \\ \\ " &&
>"whitespace/trailing 6 \\a\\" &&
>whitespace/untracked &&
sed -e "s/Z$//" >ignore <<-\EOF &&
whitespace/trailing 1 \ Z
whitespace/trailing 2 \\\\Z
whitespace/trailing 3 \\\\ Z
whitespace/trailing 4 \\\ Z
whitespace/trailing 5 \\ \\\ Z
whitespace/trailing 6 \\a\\Z
EOF
echo whitespace/untracked >expect &&
>err.expect &&
git ls-files -o -X ignore whitespace >actual 2>err &&
test_cmp expect actual &&
test_cmp err.expect err
'
test_done

View File

@ -33,6 +33,7 @@ test_expect_success setup '
tr "[a-z]" "[A-Z]" <original >newfile &&
git add newfile &&
git commit -a -m"side edits further." &&
git branch second-side &&
tr "[a-m]" "[A-M]" <original >newfile &&
rm -f original &&
@ -41,6 +42,7 @@ test_expect_success setup '
git branch test-rebase side &&
git branch test-rebase-pick side &&
git branch test-reference-pick side &&
git branch test-conflicts side &&
git checkout -b test-merge side
'
@ -138,4 +140,17 @@ test_expect_success 'rebase -s funny -Xopt' '
test -f funny.was.run
'
test_expect_success 'rebase --skip works with two conflicts in a row' '
git checkout second-side &&
tr "[A-Z]" "[a-z]" <newfile >tmp &&
mv tmp newfile &&
git commit -a -m"edit conflicting with side" &&
tr "[d-f]" "[D-F]" <newfile >tmp &&
mv tmp newfile &&
git commit -a -m"another edit conflicting with side" &&
test_must_fail git rebase --merge test-conflicts &&
test_must_fail git rebase --skip &&
git rebase --skip
'
test_done

View File

@ -468,4 +468,10 @@ test_expect_success 'single-character name is parsed correctly' '
test_cmp expect actual
'
test_expect_success 'unused %G placeholders are passed through' '
echo "%GX %G" >expect &&
git log -1 --format="%GX %G" >actual &&
test_cmp expect actual
'
test_done

View File

@ -426,10 +426,10 @@ test_expect_success SANITY 'removal failure' '
mkdir foo &&
touch foo/bar &&
test_when_finished "chmod 755 foo" &&
(exec <foo/bar &&
chmod 0 foo &&
test_must_fail git clean -f -d &&
chmod 755 foo)
test_must_fail git clean -f -d)
'
test_expect_success 'nested git work tree' '

View File

@ -43,31 +43,44 @@ test_expect_success GPG 'create signed commits' '
test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
git tag seventh-signed
echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 &&
git tag eighth-signed-alt
'
test_expect_success GPG 'show signatures' '
(
for commit in initial second merge fourth-signed fifth-signed sixth-signed master
for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
do
git show --pretty=short --show-signature $commit >actual &&
grep "Good signature from" actual || exit 1
! grep "BAD signature from" actual || exit 1
echo $commit OK
grep "Good signature from" actual &&
! grep "BAD signature from" actual &&
echo $commit OK || exit 1
done
) &&
(
for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
do
git show --pretty=short --show-signature $commit >actual &&
grep "Good signature from" actual && exit 1
! grep "BAD signature from" actual || exit 1
echo $commit OK
! grep "Good signature from" actual &&
! grep "BAD signature from" actual &&
echo $commit OK || exit 1
done
) &&
(
for commit in eighth-signed-alt
do
git show --pretty=short --show-signature $commit >actual &&
grep "Good signature from" actual &&
! grep "BAD signature from" actual &&
grep "not certified" actual &&
echo $commit OK || exit 1
done
)
'
test_expect_success GPG 'detect fudged signature' '
git cat-file commit master >raw &&
git cat-file commit seventh-signed >raw &&
sed -e "s/seventh/7th forged/" raw >forged1 &&
git hash-object -w -t commit forged1 >forged1.commit &&
@ -77,7 +90,7 @@ test_expect_success GPG 'detect fudged signature' '
'
test_expect_success GPG 'detect fudged signature with NUL' '
git cat-file commit master >raw &&
git cat-file commit seventh-signed >raw &&
cat raw >forged2 &&
echo Qwik | tr "Q" "\000" >>forged2 &&
git hash-object -w -t commit forged2 >forged2.commit &&
@ -94,4 +107,44 @@ test_expect_success GPG 'amending already signed commit' '
! grep "BAD signature from" actual
'
test_expect_success GPG 'show good signature with custom format' '
cat >expect <<-\EOF &&
G
13B6F51ECDDE430D
C O Mitter <committer@example.com>
EOF
git log -1 --format="%G?%n%GK%n%GS" sixth-signed >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show bad signature with custom format' '
cat >expect <<-\EOF &&
B
13B6F51ECDDE430D
C O Mitter <committer@example.com>
EOF
git log -1 --format="%G?%n%GK%n%GS" $(cat forged1.commit) >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show unknown signature with custom format' '
cat >expect <<-\EOF &&
U
61092E85B7227189
Eris Discordia <discord@example.net>
EOF
git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show lack of signature with custom format' '
cat >expect <<-\EOF &&
N
EOF
git log -1 --format="%G?%n%GK%n%GS" seventh-unsigned >actual &&
test_cmp expect actual
'
test_done

View File

@ -35,9 +35,25 @@ test_expect_success 'objects in packs marked .keep are not repacked' '
test -z "$found_duplicate_object"
'
test_expect_success 'writing bitmaps can duplicate .keep objects' '
test_expect_success 'writing bitmaps via command-line can duplicate .keep objects' '
# build on $objsha1, $packsha1, and .keep state from previous
git repack -Adl &&
git repack -Adbl &&
test_when_finished "found_duplicate_object=" &&
for p in .git/objects/pack/*.idx; do
idx=$(basename $p)
test "pack-$packsha1.idx" = "$idx" && continue
if git verify-pack -v $p | egrep "^$objsha1"; then
found_duplicate_object=1
echo "DUPLICATE OBJECT FOUND"
break
fi
done &&
test "$found_duplicate_object" = 1
'
test_expect_success 'writing bitmaps via config can duplicate .keep objects' '
# build on $objsha1, $packsha1, and .keep state from previous
git -c pack.writebitmaps=true repack -Adl &&
test_when_finished "found_duplicate_object=" &&
for p in .git/objects/pack/*.idx; do
idx=$(basename $p)