Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
6dc78e696b | |||
183bdb2ccc | |||
5e8dc750ee | |||
b2504a0d2f | |||
d64e6b0429 | |||
589e4f93c7 | |||
2fb4a21074 | |||
15b4d577ae | |||
4181bda156 | |||
ab7cd7bb8c | |||
3f9ac8d259 | |||
26125f6b9b | |||
aa064743fa | |||
fab5de7936 | |||
60ace8790f |
@ -7,7 +7,7 @@ git-add - Add files to the index file.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-add' [-n] [-v] <file>...
|
||||
'git-add' [-n] [-v] [--] <file>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -26,6 +26,11 @@ OPTIONS
|
||||
-v::
|
||||
Be verbose.
|
||||
|
||||
--::
|
||||
This option can be used to separate command-line options from
|
||||
the list of files, (useful when filenames might be mistaken
|
||||
for command-line options).
|
||||
|
||||
|
||||
DISCUSSION
|
||||
----------
|
||||
|
@ -8,7 +8,10 @@ git-pack-objects - Create a packed archive of objects.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-pack-objects' [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list
|
||||
[verse]
|
||||
'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
|
||||
[--local] [--incremental] [--window=N] [--depth=N]
|
||||
{--stdout | base-name} < object-list
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@ -32,6 +35,10 @@ Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
|
||||
any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
|
||||
enables git to read from such an archive.
|
||||
|
||||
In a packed archive, an object is either stored as a compressed
|
||||
whole, or as a difference from some other object. The latter is
|
||||
often called a delta.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@ -74,6 +81,18 @@ base-name::
|
||||
Only create a packed archive if it would contain at
|
||||
least one object.
|
||||
|
||||
-q::
|
||||
This flag makes the command not to report its progress
|
||||
on the standard error stream.
|
||||
|
||||
--no-reuse-delta::
|
||||
When creating a packed archive in a repository that
|
||||
has existing packs, the command reuses existing deltas.
|
||||
This sometimes results in a slightly suboptimal pack.
|
||||
This flag tells the command not to reuse existing deltas
|
||||
but compute them from scratch.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>
|
||||
|
@ -43,6 +43,12 @@ to fast forward the remote ref that matches <dst>. If
|
||||
the optional plus `+` is used, the remote ref is updated
|
||||
even if it does not result in a fast forward update.
|
||||
+
|
||||
Note: If no explicit refspec is found, (that is neither
|
||||
on the command line nor in any Push line of the
|
||||
corresponding remotes file---see below), then all the
|
||||
refs that exist both on the local side and on the remote
|
||||
side are updated.
|
||||
+
|
||||
Some short-cut notations are also supported.
|
||||
+
|
||||
* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
|
||||
|
@ -9,7 +9,7 @@ objects into pack files.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-repack' [-a] [-d] [-l] [-n]
|
||||
'git-repack' [-a] [-d] [-f] [-l] [-n] [-q]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -43,6 +43,14 @@ OPTIONS
|
||||
Pass the `--local` option to `git pack-objects`, see
|
||||
gitlink:git-pack-objects[1].
|
||||
|
||||
-f::
|
||||
Pass the `--no-reuse-delta` option to `git pack-objects`, see
|
||||
gitlink:git-pack-objects[1].
|
||||
|
||||
-q::
|
||||
Pass the `-q` option to `git pack-objects`, see
|
||||
gitlink:git-pack-objects[1].
|
||||
|
||||
-n::
|
||||
Do not update the server information with
|
||||
`git update-server-info`.
|
||||
|
4
cache.h
4
cache.h
@ -246,8 +246,8 @@ void datestamp(char *buf, int bufsize);
|
||||
unsigned long approxidate(const char *);
|
||||
|
||||
extern int setup_ident(void);
|
||||
extern const char *git_author_info(void);
|
||||
extern const char *git_committer_info(void);
|
||||
extern const char *git_author_info(int);
|
||||
extern const char *git_committer_info(int);
|
||||
|
||||
struct checkout {
|
||||
const char *base_dir;
|
||||
|
@ -118,8 +118,8 @@ int main(int argc, char **argv)
|
||||
add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i]));
|
||||
|
||||
/* Person/date information */
|
||||
add_buffer(&buffer, &size, "author %s\n", git_author_info());
|
||||
add_buffer(&buffer, &size, "committer %s\n\n", git_committer_info());
|
||||
add_buffer(&buffer, &size, "author %s\n", git_author_info(1));
|
||||
add_buffer(&buffer, &size, "committer %s\n\n", git_committer_info(1));
|
||||
|
||||
/* And add the comment */
|
||||
while (fgets(comment, sizeof(comment), stdin) != NULL)
|
||||
|
@ -14,6 +14,10 @@ while : ; do
|
||||
-v)
|
||||
verbose=--verbose
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
|
@ -1,11 +1,13 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2005, 2006 Junio C Hamano
|
||||
|
||||
USAGE='[--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] <mbox>
|
||||
or, when resuming [--skip | --resolved]'
|
||||
. git-sh-setup
|
||||
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
|
||||
stop_here () {
|
||||
echo "$1" >"$dotest/next"
|
||||
exit 1
|
||||
|
@ -21,6 +21,8 @@
|
||||
USAGE='[-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]'
|
||||
. git-sh-setup
|
||||
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
|
||||
keep_subject= query_apply= continue= utf8= resume=t
|
||||
while case "$#" in 0) break ;; esac
|
||||
do
|
||||
|
33
git-fetch.sh
33
git-fetch.sh
@ -368,20 +368,25 @@ fetch_main "$reflist"
|
||||
# automated tag following
|
||||
case "$no_tags$tags" in
|
||||
'')
|
||||
taglist=$(IFS=" " &&
|
||||
git-ls-remote $upload_pack --tags "$remote" |
|
||||
sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' |
|
||||
while read sha1 name
|
||||
do
|
||||
test -f "$GIT_DIR/$name" && continue
|
||||
git-check-ref-format "$name" || {
|
||||
echo >&2 "warning: tag ${name} ignored"
|
||||
continue
|
||||
}
|
||||
git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
|
||||
echo >&2 "Auto-following $name"
|
||||
echo ".${name}:${name}"
|
||||
done)
|
||||
case "$reflist" in
|
||||
*:refs/*)
|
||||
# effective only when we are following remote branch
|
||||
# using local tracking branch.
|
||||
taglist=$(IFS=" " &&
|
||||
git-ls-remote $upload_pack --tags "$remote" |
|
||||
sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' |
|
||||
while read sha1 name
|
||||
do
|
||||
test -f "$GIT_DIR/$name" && continue
|
||||
git-check-ref-format "$name" || {
|
||||
echo >&2 "warning: tag ${name} ignored"
|
||||
continue
|
||||
}
|
||||
git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
|
||||
echo >&2 "Auto-following $name"
|
||||
echo ".${name}:${name}"
|
||||
done)
|
||||
esac
|
||||
case "$taglist" in
|
||||
'') ;;
|
||||
?*)
|
||||
|
@ -189,7 +189,7 @@ my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
|
||||
sub show_date {
|
||||
my ($time, $tz) = @_;
|
||||
my $minutes = abs($tz);
|
||||
$minutes = ($minutes / 100) * 60 + ($minutes % 100);
|
||||
$minutes = int($minutes / 100) * 60 + ($minutes % 100);
|
||||
if ($tz < 0) {
|
||||
$minutes = -$minutes;
|
||||
}
|
||||
|
@ -142,6 +142,8 @@ case "$#,$common,$no_commit" in
|
||||
1,*,)
|
||||
# We are not doing octopus, not fast forward, and have only
|
||||
# one common. See if it is really trivial.
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
|
||||
echo "Trying really trivial in-index merge..."
|
||||
git-update-index --refresh 2>/dev/null
|
||||
if git-read-tree --trivial -m -u $common $head "$1" &&
|
||||
@ -179,6 +181,9 @@ case "$#,$common,$no_commit" in
|
||||
;;
|
||||
esac
|
||||
|
||||
# We are going to make a new commit.
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
|
||||
case "$use_strategies" in
|
||||
'')
|
||||
case "$#" in
|
||||
|
@ -3,17 +3,20 @@
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
#
|
||||
|
||||
USAGE='[-a] [-d] [-l] [-n]'
|
||||
USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
|
||||
. git-sh-setup
|
||||
|
||||
no_update_info= all_into_one= remove_redundant= local=
|
||||
no_update_info= all_into_one= remove_redundant=
|
||||
local= quiet= no_reuse_delta=
|
||||
while case "$#" in 0) break ;; esac
|
||||
do
|
||||
case "$1" in
|
||||
-n) no_update_info=t ;;
|
||||
-a) all_into_one=t ;;
|
||||
-d) remove_redundant=t ;;
|
||||
-l) local=t ;;
|
||||
-q) quiet=-q ;;
|
||||
-f) no_reuse_delta=--no-reuse-delta ;;
|
||||
-l) local=--local ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
shift
|
||||
@ -39,9 +42,7 @@ case ",$all_into_one," in
|
||||
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
|
||||
;;
|
||||
esac
|
||||
if [ "$local" ]; then
|
||||
pack_objects="$pack_objects --local"
|
||||
fi
|
||||
pack_objects="$pack_objects $local $quiet $no_reuse_delta"
|
||||
name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) 2>&1 |
|
||||
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
|
||||
exit 1
|
||||
|
@ -50,6 +50,9 @@ case "$common" in
|
||||
;;
|
||||
esac
|
||||
|
||||
# We are going to make a new commit.
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
|
||||
# Find an optimum merge base if there are more than one candidates.
|
||||
LF='
|
||||
'
|
||||
|
33
ident.c
33
ident.c
@ -156,8 +156,18 @@ static int copy(char *buf, int size, int offset, const char *src)
|
||||
return offset;
|
||||
}
|
||||
|
||||
static const char au_env[] = "GIT_AUTHOR_NAME";
|
||||
static const char co_env[] = "GIT_COMMITTER_NAME";
|
||||
static const char *env_hint =
|
||||
"\n*** Environment problem:\n"
|
||||
"*** Your name cannot be determined from your system services (gecos).\n"
|
||||
"*** You would need to set %s and %s\n"
|
||||
"*** environment variables; otherwise you won't be able to perform\n"
|
||||
"*** certain operations because of \"empty ident\" errors.\n"
|
||||
"*** Alternatively, you can use user.name configuration variable.\n\n";
|
||||
|
||||
static const char *get_ident(const char *name, const char *email,
|
||||
const char *date_str)
|
||||
const char *date_str, int error_on_no_name)
|
||||
{
|
||||
static char buffer[1000];
|
||||
char date[50];
|
||||
@ -168,9 +178,14 @@ static const char *get_ident(const char *name, const char *email,
|
||||
if (!email)
|
||||
email = git_default_email;
|
||||
|
||||
if (!*name || !*email)
|
||||
die("empty ident %s <%s> not allowed",
|
||||
name, email);
|
||||
if (!*name) {
|
||||
if (name == git_default_name && env_hint) {
|
||||
fprintf(stderr, env_hint, au_env, co_env);
|
||||
env_hint = NULL; /* warn only once, for "git-var -l" */
|
||||
}
|
||||
if (error_on_no_name)
|
||||
die("empty ident %s <%s> not allowed", name, email);
|
||||
}
|
||||
|
||||
strcpy(date, git_default_date);
|
||||
if (date_str)
|
||||
@ -187,16 +202,18 @@ static const char *get_ident(const char *name, const char *email,
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char *git_author_info(void)
|
||||
const char *git_author_info(int error_on_no_name)
|
||||
{
|
||||
return get_ident(getenv("GIT_AUTHOR_NAME"),
|
||||
getenv("GIT_AUTHOR_EMAIL"),
|
||||
getenv("GIT_AUTHOR_DATE"));
|
||||
getenv("GIT_AUTHOR_DATE"),
|
||||
error_on_no_name);
|
||||
}
|
||||
|
||||
const char *git_committer_info(void)
|
||||
const char *git_committer_info(int error_on_no_name)
|
||||
{
|
||||
return get_ident(getenv("GIT_COMMITTER_NAME"),
|
||||
getenv("GIT_COMMITTER_EMAIL"),
|
||||
getenv("GIT_COMMITTER_DATE"));
|
||||
getenv("GIT_COMMITTER_DATE"),
|
||||
error_on_no_name);
|
||||
}
|
||||
|
538
pack-objects.c
538
pack-objects.c
@ -4,22 +4,47 @@
|
||||
#include "pack.h"
|
||||
#include "csum-file.h"
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
|
||||
static const char pack_usage[] = "git-pack-objects [-q] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
|
||||
static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
|
||||
|
||||
struct object_entry {
|
||||
unsigned char sha1[20];
|
||||
unsigned long size;
|
||||
unsigned long offset;
|
||||
unsigned int depth;
|
||||
unsigned int hash;
|
||||
unsigned long size; /* uncompressed size */
|
||||
unsigned long offset; /* offset into the final pack file;
|
||||
* nonzero if already written.
|
||||
*/
|
||||
unsigned int depth; /* delta depth */
|
||||
unsigned int delta_limit; /* base adjustment for in-pack delta */
|
||||
unsigned int hash; /* name hint hash */
|
||||
enum object_type type;
|
||||
unsigned long delta_size;
|
||||
struct object_entry *delta;
|
||||
enum object_type in_pack_type; /* could be delta */
|
||||
unsigned long delta_size; /* delta data size (uncompressed) */
|
||||
struct object_entry *delta; /* delta base object */
|
||||
struct packed_git *in_pack; /* already in pack */
|
||||
unsigned int in_pack_offset;
|
||||
struct object_entry *delta_child; /* delitified objects who bases me */
|
||||
struct object_entry *delta_sibling; /* other deltified objects who
|
||||
* uses the same base as me
|
||||
*/
|
||||
};
|
||||
|
||||
/*
|
||||
* Objects we are going to pack are colected in objects array (dynamically
|
||||
* expanded). nr_objects & nr_alloc controls this array. They are stored
|
||||
* in the order we see -- typically rev-list --objects order that gives us
|
||||
* nice "minimum seek" order.
|
||||
*
|
||||
* sorted-by-sha ans sorted-by-type are arrays of pointers that point at
|
||||
* elements in the objects array. The former is used to build the pack
|
||||
* index (lists object names in the ascending order to help offset lookup),
|
||||
* and the latter is used to group similar things together by try_delta()
|
||||
* heuristics.
|
||||
*/
|
||||
|
||||
static unsigned char object_list_sha1[20];
|
||||
static int non_empty = 0;
|
||||
static int no_reuse_delta = 0;
|
||||
static int local = 0;
|
||||
static int incremental = 0;
|
||||
static struct object_entry **sorted_by_sha, **sorted_by_type;
|
||||
@ -28,6 +53,138 @@ static int nr_objects = 0, nr_alloc = 0;
|
||||
static const char *base_name;
|
||||
static unsigned char pack_file_sha1[20];
|
||||
static int progress = 1;
|
||||
static volatile int progress_update = 0;
|
||||
|
||||
/*
|
||||
* The object names in objects array are hashed with this hashtable,
|
||||
* to help looking up the entry by object name. Binary search from
|
||||
* sorted_by_sha is also possible but this was easier to code and faster.
|
||||
* This hashtable is built after all the objects are seen.
|
||||
*/
|
||||
static int *object_ix = NULL;
|
||||
static int object_ix_hashsz = 0;
|
||||
|
||||
/*
|
||||
* Pack index for existing packs give us easy access to the offsets into
|
||||
* corresponding pack file where each object's data starts, but the entries
|
||||
* do not store the size of the compressed representation (uncompressed
|
||||
* size is easily available by examining the pack entry header). We build
|
||||
* a hashtable of existing packs (pack_revindex), and keep reverse index
|
||||
* here -- pack index file is sorted by object name mapping to offset; this
|
||||
* pack_revindex[].revindex array is an ordered list of offsets, so if you
|
||||
* know the offset of an object, next offset is where its packed
|
||||
* representation ends.
|
||||
*/
|
||||
struct pack_revindex {
|
||||
struct packed_git *p;
|
||||
unsigned long *revindex;
|
||||
} *pack_revindex = NULL;
|
||||
static int pack_revindex_hashsz = 0;
|
||||
|
||||
/*
|
||||
* stats
|
||||
*/
|
||||
static int written = 0;
|
||||
static int written_delta = 0;
|
||||
static int reused = 0;
|
||||
static int reused_delta = 0;
|
||||
|
||||
static int pack_revindex_ix(struct packed_git *p)
|
||||
{
|
||||
unsigned int ui = (unsigned int) p;
|
||||
int i;
|
||||
|
||||
ui = ui ^ (ui >> 16); /* defeat structure alignment */
|
||||
i = (int)(ui % pack_revindex_hashsz);
|
||||
while (pack_revindex[i].p) {
|
||||
if (pack_revindex[i].p == p)
|
||||
return i;
|
||||
if (++i == pack_revindex_hashsz)
|
||||
i = 0;
|
||||
}
|
||||
return -1 - i;
|
||||
}
|
||||
|
||||
static void prepare_pack_ix(void)
|
||||
{
|
||||
int num;
|
||||
struct packed_git *p;
|
||||
for (num = 0, p = packed_git; p; p = p->next)
|
||||
num++;
|
||||
if (!num)
|
||||
return;
|
||||
pack_revindex_hashsz = num * 11;
|
||||
pack_revindex = xcalloc(sizeof(*pack_revindex), pack_revindex_hashsz);
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
num = pack_revindex_ix(p);
|
||||
num = - 1 - num;
|
||||
pack_revindex[num].p = p;
|
||||
}
|
||||
/* revindex elements are lazily initialized */
|
||||
}
|
||||
|
||||
static int cmp_offset(const void *a_, const void *b_)
|
||||
{
|
||||
unsigned long a = *(unsigned long *) a_;
|
||||
unsigned long b = *(unsigned long *) b_;
|
||||
if (a < b)
|
||||
return -1;
|
||||
else if (a == b)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ordered list of offsets of objects in the pack.
|
||||
*/
|
||||
static void prepare_pack_revindex(struct pack_revindex *rix)
|
||||
{
|
||||
struct packed_git *p = rix->p;
|
||||
int num_ent = num_packed_objects(p);
|
||||
int i;
|
||||
void *index = p->index_base + 256;
|
||||
|
||||
rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));
|
||||
for (i = 0; i < num_ent; i++) {
|
||||
long hl = *((long *)(index + 24 * i));
|
||||
rix->revindex[i] = ntohl(hl);
|
||||
}
|
||||
/* This knows the pack format -- the 20-byte trailer
|
||||
* follows immediately after the last object data.
|
||||
*/
|
||||
rix->revindex[num_ent] = p->pack_size - 20;
|
||||
qsort(rix->revindex, num_ent, sizeof(unsigned long), cmp_offset);
|
||||
}
|
||||
|
||||
static unsigned long find_packed_object_size(struct packed_git *p,
|
||||
unsigned long ofs)
|
||||
{
|
||||
int num;
|
||||
int lo, hi;
|
||||
struct pack_revindex *rix;
|
||||
unsigned long *revindex;
|
||||
num = pack_revindex_ix(p);
|
||||
if (num < 0)
|
||||
die("internal error: pack revindex uninitialized");
|
||||
rix = &pack_revindex[num];
|
||||
if (!rix->revindex)
|
||||
prepare_pack_revindex(rix);
|
||||
revindex = rix->revindex;
|
||||
lo = 0;
|
||||
hi = num_packed_objects(p) + 1;
|
||||
do {
|
||||
int mi = (lo + hi) / 2;
|
||||
if (revindex[mi] == ofs) {
|
||||
return revindex[mi+1] - ofs;
|
||||
}
|
||||
else if (ofs < revindex[mi])
|
||||
hi = mi;
|
||||
else
|
||||
lo = mi + 1;
|
||||
} while (lo < hi);
|
||||
die("internal error: pack revindex corrupt");
|
||||
}
|
||||
|
||||
static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
|
||||
{
|
||||
@ -78,35 +235,69 @@ static unsigned long write_object(struct sha1file *f, struct object_entry *entry
|
||||
{
|
||||
unsigned long size;
|
||||
char type[10];
|
||||
void *buf = read_sha1_file(entry->sha1, type, &size);
|
||||
void *buf;
|
||||
unsigned char header[10];
|
||||
unsigned hdrlen, datalen;
|
||||
enum object_type obj_type;
|
||||
int to_reuse = 0;
|
||||
|
||||
if (!buf)
|
||||
die("unable to read %s", sha1_to_hex(entry->sha1));
|
||||
if (size != entry->size)
|
||||
die("object %s size inconsistency (%lu vs %lu)", sha1_to_hex(entry->sha1), size, entry->size);
|
||||
|
||||
/*
|
||||
* The object header is a byte of 'type' followed by zero or
|
||||
* more bytes of length. For deltas, the 20 bytes of delta sha1
|
||||
* follows that.
|
||||
*/
|
||||
obj_type = entry->type;
|
||||
if (entry->delta) {
|
||||
buf = delta_against(buf, size, entry);
|
||||
size = entry->delta_size;
|
||||
obj_type = OBJ_DELTA;
|
||||
if (! entry->in_pack)
|
||||
to_reuse = 0; /* can't reuse what we don't have */
|
||||
else if (obj_type == OBJ_DELTA)
|
||||
to_reuse = 1; /* check_object() decided it for us */
|
||||
else if (obj_type != entry->in_pack_type)
|
||||
to_reuse = 0; /* pack has delta which is unusable */
|
||||
else if (entry->delta)
|
||||
to_reuse = 0; /* we want to pack afresh */
|
||||
else
|
||||
to_reuse = 1; /* we have it in-pack undeltified,
|
||||
* and we do not need to deltify it.
|
||||
*/
|
||||
|
||||
if (! to_reuse) {
|
||||
buf = read_sha1_file(entry->sha1, type, &size);
|
||||
if (!buf)
|
||||
die("unable to read %s", sha1_to_hex(entry->sha1));
|
||||
if (size != entry->size)
|
||||
die("object %s size inconsistency (%lu vs %lu)",
|
||||
sha1_to_hex(entry->sha1), size, entry->size);
|
||||
if (entry->delta) {
|
||||
buf = delta_against(buf, size, entry);
|
||||
size = entry->delta_size;
|
||||
obj_type = OBJ_DELTA;
|
||||
}
|
||||
/*
|
||||
* The object header is a byte of 'type' followed by zero or
|
||||
* more bytes of length. For deltas, the 20 bytes of delta
|
||||
* sha1 follows that.
|
||||
*/
|
||||
hdrlen = encode_header(obj_type, size, header);
|
||||
sha1write(f, header, hdrlen);
|
||||
|
||||
if (entry->delta) {
|
||||
sha1write(f, entry->delta, 20);
|
||||
hdrlen += 20;
|
||||
}
|
||||
datalen = sha1write_compressed(f, buf, size);
|
||||
free(buf);
|
||||
}
|
||||
hdrlen = encode_header(obj_type, size, header);
|
||||
sha1write(f, header, hdrlen);
|
||||
if (entry->delta) {
|
||||
sha1write(f, entry->delta, 20);
|
||||
hdrlen += 20;
|
||||
else {
|
||||
struct packed_git *p = entry->in_pack;
|
||||
use_packed_git(p);
|
||||
|
||||
datalen = find_packed_object_size(p, entry->in_pack_offset);
|
||||
buf = p->pack_base + entry->in_pack_offset;
|
||||
sha1write(f, buf, datalen);
|
||||
unuse_packed_git(p);
|
||||
hdrlen = 0; /* not really */
|
||||
if (obj_type == OBJ_DELTA)
|
||||
reused_delta++;
|
||||
reused++;
|
||||
}
|
||||
datalen = sha1write_compressed(f, buf, size);
|
||||
free(buf);
|
||||
if (obj_type == OBJ_DELTA)
|
||||
written_delta++;
|
||||
written++;
|
||||
return hdrlen + datalen;
|
||||
}
|
||||
|
||||
@ -132,24 +323,41 @@ static void write_pack_file(void)
|
||||
int i;
|
||||
struct sha1file *f;
|
||||
unsigned long offset;
|
||||
unsigned long mb;
|
||||
struct pack_header hdr;
|
||||
unsigned last_percent = 999;
|
||||
int do_progress = 0;
|
||||
|
||||
if (!base_name)
|
||||
f = sha1fd(1, "<stdout>");
|
||||
else
|
||||
f = sha1create("%s-%s.%s", base_name, sha1_to_hex(object_list_sha1), "pack");
|
||||
else {
|
||||
f = sha1create("%s-%s.%s", base_name,
|
||||
sha1_to_hex(object_list_sha1), "pack");
|
||||
do_progress = progress;
|
||||
}
|
||||
if (do_progress)
|
||||
fprintf(stderr, "Writing %d objects.\n", nr_objects);
|
||||
|
||||
hdr.hdr_signature = htonl(PACK_SIGNATURE);
|
||||
hdr.hdr_version = htonl(PACK_VERSION);
|
||||
hdr.hdr_entries = htonl(nr_objects);
|
||||
sha1write(f, &hdr, sizeof(hdr));
|
||||
offset = sizeof(hdr);
|
||||
for (i = 0; i < nr_objects; i++)
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
offset = write_one(f, objects + i, offset);
|
||||
if (do_progress) {
|
||||
unsigned percent = written * 100 / nr_objects;
|
||||
if (progress_update || percent != last_percent) {
|
||||
fprintf(stderr, "%4u%% (%u/%u) done\r",
|
||||
percent, written, nr_objects);
|
||||
progress_update = 0;
|
||||
last_percent = percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (do_progress)
|
||||
fputc('\n', stderr);
|
||||
|
||||
sha1close(f, pack_file_sha1, 1);
|
||||
mb = offset >> 20;
|
||||
offset &= 0xfffff;
|
||||
}
|
||||
|
||||
static void write_index_file(void)
|
||||
@ -196,18 +404,20 @@ static int add_object_entry(unsigned char *sha1, unsigned int hash)
|
||||
{
|
||||
unsigned int idx = nr_objects;
|
||||
struct object_entry *entry;
|
||||
struct packed_git *p;
|
||||
unsigned int found_offset = 0;
|
||||
struct packed_git *found_pack = NULL;
|
||||
|
||||
if (incremental || local) {
|
||||
struct packed_git *p;
|
||||
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
struct pack_entry e;
|
||||
|
||||
if (find_pack_entry_one(sha1, &e, p)) {
|
||||
if (incremental)
|
||||
return 0;
|
||||
if (local && !p->pack_local)
|
||||
return 0;
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
struct pack_entry e;
|
||||
if (find_pack_entry_one(sha1, &e, p)) {
|
||||
if (incremental)
|
||||
return 0;
|
||||
if (local && !p->pack_local)
|
||||
return 0;
|
||||
if (!found_pack) {
|
||||
found_offset = e.offset;
|
||||
found_pack = e.p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -221,39 +431,143 @@ static int add_object_entry(unsigned char *sha1, unsigned int hash)
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
memcpy(entry->sha1, sha1, 20);
|
||||
entry->hash = hash;
|
||||
if (found_pack) {
|
||||
entry->in_pack = found_pack;
|
||||
entry->in_pack_offset = found_offset;
|
||||
}
|
||||
nr_objects = idx+1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int locate_object_entry_hash(unsigned char *sha1)
|
||||
{
|
||||
int i;
|
||||
unsigned int ui;
|
||||
memcpy(&ui, sha1, sizeof(unsigned int));
|
||||
i = ui % object_ix_hashsz;
|
||||
while (0 < object_ix[i]) {
|
||||
if (!memcmp(sha1, objects[object_ix[i]-1].sha1, 20))
|
||||
return i;
|
||||
if (++i == object_ix_hashsz)
|
||||
i = 0;
|
||||
}
|
||||
return -1 - i;
|
||||
}
|
||||
|
||||
static struct object_entry *locate_object_entry(unsigned char *sha1)
|
||||
{
|
||||
int i = locate_object_entry_hash(sha1);
|
||||
if (0 <= i)
|
||||
return &objects[object_ix[i]-1];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void check_object(struct object_entry *entry)
|
||||
{
|
||||
char type[20];
|
||||
|
||||
if (!sha1_object_info(entry->sha1, type, &entry->size)) {
|
||||
if (!strcmp(type, "commit")) {
|
||||
entry->type = OBJ_COMMIT;
|
||||
} else if (!strcmp(type, "tree")) {
|
||||
entry->type = OBJ_TREE;
|
||||
} else if (!strcmp(type, "blob")) {
|
||||
entry->type = OBJ_BLOB;
|
||||
} else if (!strcmp(type, "tag")) {
|
||||
entry->type = OBJ_TAG;
|
||||
} else
|
||||
die("unable to pack object %s of type %s",
|
||||
sha1_to_hex(entry->sha1), type);
|
||||
if (entry->in_pack) {
|
||||
unsigned char base[20];
|
||||
unsigned long size;
|
||||
struct object_entry *base_entry;
|
||||
|
||||
/* We want in_pack_type even if we do not reuse delta.
|
||||
* There is no point not reusing non-delta representations.
|
||||
*/
|
||||
check_reuse_pack_delta(entry->in_pack,
|
||||
entry->in_pack_offset,
|
||||
base, &size,
|
||||
&entry->in_pack_type);
|
||||
|
||||
/* Check if it is delta, and the base is also an object
|
||||
* we are going to pack. If so we will reuse the existing
|
||||
* delta.
|
||||
*/
|
||||
if (!no_reuse_delta &&
|
||||
entry->in_pack_type == OBJ_DELTA &&
|
||||
(base_entry = locate_object_entry(base))) {
|
||||
|
||||
/* Depth value does not matter - find_deltas()
|
||||
* will never consider reused delta as the
|
||||
* base object to deltify other objects
|
||||
* against, in order to avoid circular deltas.
|
||||
*/
|
||||
|
||||
/* uncompressed size of the delta data */
|
||||
entry->size = entry->delta_size = size;
|
||||
entry->delta = base_entry;
|
||||
entry->type = OBJ_DELTA;
|
||||
|
||||
entry->delta_sibling = base_entry->delta_child;
|
||||
base_entry->delta_child = entry;
|
||||
|
||||
return;
|
||||
}
|
||||
/* Otherwise we would do the usual */
|
||||
}
|
||||
else
|
||||
|
||||
if (sha1_object_info(entry->sha1, type, &entry->size))
|
||||
die("unable to get type of object %s",
|
||||
sha1_to_hex(entry->sha1));
|
||||
|
||||
if (!strcmp(type, "commit")) {
|
||||
entry->type = OBJ_COMMIT;
|
||||
} else if (!strcmp(type, "tree")) {
|
||||
entry->type = OBJ_TREE;
|
||||
} else if (!strcmp(type, "blob")) {
|
||||
entry->type = OBJ_BLOB;
|
||||
} else if (!strcmp(type, "tag")) {
|
||||
entry->type = OBJ_TAG;
|
||||
} else
|
||||
die("unable to pack object %s of type %s",
|
||||
sha1_to_hex(entry->sha1), type);
|
||||
}
|
||||
|
||||
static void hash_objects(void)
|
||||
{
|
||||
int i;
|
||||
struct object_entry *oe;
|
||||
|
||||
object_ix_hashsz = nr_objects * 2;
|
||||
object_ix = xcalloc(sizeof(int), object_ix_hashsz);
|
||||
for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
|
||||
int ix = locate_object_entry_hash(oe->sha1);
|
||||
if (0 <= ix) {
|
||||
error("the same object '%s' added twice",
|
||||
sha1_to_hex(oe->sha1));
|
||||
continue;
|
||||
}
|
||||
ix = -1 - ix;
|
||||
object_ix[ix] = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
|
||||
{
|
||||
struct object_entry *child = me->delta_child;
|
||||
unsigned int m = n;
|
||||
while (child) {
|
||||
unsigned int c = check_delta_limit(child, n + 1);
|
||||
if (m < c)
|
||||
m = c;
|
||||
child = child->delta_sibling;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static void get_object_details(void)
|
||||
{
|
||||
int i;
|
||||
struct object_entry *entry = objects;
|
||||
struct object_entry *entry;
|
||||
|
||||
for (i = 0; i < nr_objects; i++)
|
||||
check_object(entry++);
|
||||
hash_objects();
|
||||
prepare_pack_ix();
|
||||
for (i = 0, entry = objects; i < nr_objects; i++, entry++)
|
||||
check_object(entry);
|
||||
for (i = 0, entry = objects; i < nr_objects; i++, entry++)
|
||||
if (!entry->delta && entry->delta_child)
|
||||
entry->delta_limit =
|
||||
check_delta_limit(entry, 1);
|
||||
}
|
||||
|
||||
typedef int (*entry_sort_t)(const struct object_entry *, const struct object_entry *);
|
||||
@ -326,6 +640,16 @@ static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_de
|
||||
if (cur_entry->type != old_entry->type)
|
||||
return -1;
|
||||
|
||||
/* If the current object is at edge, take the depth the objects
|
||||
* that depend on the current object into account -- otherwise
|
||||
* they would become too deep.
|
||||
*/
|
||||
if (cur_entry->delta_child) {
|
||||
if (max_depth <= cur_entry->delta_limit)
|
||||
return 0;
|
||||
max_depth -= cur_entry->delta_limit;
|
||||
}
|
||||
|
||||
size = cur_entry->size;
|
||||
if (size < 50)
|
||||
return -1;
|
||||
@ -359,17 +683,25 @@ static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_de
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void progress_interval(int signum)
|
||||
{
|
||||
signal(SIGALRM, progress_interval);
|
||||
progress_update = 1;
|
||||
}
|
||||
|
||||
static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
{
|
||||
int i, idx;
|
||||
unsigned int array_size = window * sizeof(struct unpacked);
|
||||
struct unpacked *array = xmalloc(array_size);
|
||||
int eye_candy;
|
||||
unsigned processed = 0;
|
||||
unsigned last_percent = 999;
|
||||
|
||||
memset(array, 0, array_size);
|
||||
i = nr_objects;
|
||||
idx = 0;
|
||||
eye_candy = i - (nr_objects / 20);
|
||||
if (progress)
|
||||
fprintf(stderr, "Deltifying %d objects.\n", nr_objects);
|
||||
|
||||
while (--i >= 0) {
|
||||
struct object_entry *entry = list[i];
|
||||
@ -378,15 +710,29 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
char type[10];
|
||||
int j;
|
||||
|
||||
if (progress && i <= eye_candy) {
|
||||
eye_candy -= nr_objects / 20;
|
||||
fputc('.', stderr);
|
||||
processed++;
|
||||
if (progress) {
|
||||
unsigned percent = processed * 100 / nr_objects;
|
||||
if (percent != last_percent || progress_update) {
|
||||
fprintf(stderr, "%4u%% (%u/%u) done\r",
|
||||
percent, processed, nr_objects);
|
||||
progress_update = 0;
|
||||
last_percent = percent;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->delta)
|
||||
/* This happens if we decided to reuse existing
|
||||
* delta from a pack. "!no_reuse_delta &&" is implied.
|
||||
*/
|
||||
continue;
|
||||
|
||||
free(n->data);
|
||||
n->entry = entry;
|
||||
n->data = read_sha1_file(entry->sha1, type, &size);
|
||||
if (size != entry->size)
|
||||
die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(entry->sha1), size, entry->size);
|
||||
|
||||
j = window;
|
||||
while (--j > 0) {
|
||||
unsigned int other_idx = idx + j;
|
||||
@ -404,6 +750,9 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
idx = 0;
|
||||
}
|
||||
|
||||
if (progress)
|
||||
fputc('\n', stderr);
|
||||
|
||||
for (i = 0; i < window; ++i)
|
||||
free(array[i].data);
|
||||
free(array);
|
||||
@ -412,15 +761,9 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
static void prepare_pack(int window, int depth)
|
||||
{
|
||||
get_object_details();
|
||||
|
||||
if (progress)
|
||||
fprintf(stderr, "Packing %d objects", nr_objects);
|
||||
sorted_by_type = create_sorted_list(type_size_sort);
|
||||
if (window && depth)
|
||||
find_deltas(sorted_by_type, window+1, depth);
|
||||
if (progress)
|
||||
fputc('\n', stderr);
|
||||
write_pack_file();
|
||||
}
|
||||
|
||||
static int reuse_cached_pack(unsigned char *sha1, int pack_to_stdout)
|
||||
@ -443,8 +786,9 @@ static int reuse_cached_pack(unsigned char *sha1, int pack_to_stdout)
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Reusing %d objects pack %s\n", nr_objects,
|
||||
sha1_to_hex(sha1));
|
||||
if (progress)
|
||||
fprintf(stderr, "Reusing %d objects pack %s\n", nr_objects,
|
||||
sha1_to_hex(sha1));
|
||||
|
||||
if (pack_to_stdout) {
|
||||
if (copy_fd(ifd, 1))
|
||||
@ -483,10 +827,6 @@ int main(int argc, char **argv)
|
||||
int window = 10, depth = 10, pack_to_stdout = 0;
|
||||
struct object_entry **list;
|
||||
int i;
|
||||
struct timeval prev_tv;
|
||||
int eye_candy = 0;
|
||||
int eye_candy_incr = 500;
|
||||
|
||||
|
||||
setup_git_directory();
|
||||
|
||||
@ -524,6 +864,10 @@ int main(int argc, char **argv)
|
||||
progress = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--no-reuse-delta", arg)) {
|
||||
no_reuse_delta = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--stdout", arg)) {
|
||||
pack_to_stdout = 1;
|
||||
continue;
|
||||
@ -539,30 +883,25 @@ int main(int argc, char **argv)
|
||||
usage(pack_usage);
|
||||
|
||||
prepare_packed_git();
|
||||
|
||||
if (progress) {
|
||||
struct itimerval v;
|
||||
v.it_interval.tv_sec = 1;
|
||||
v.it_interval.tv_usec = 0;
|
||||
v.it_value = v.it_interval;
|
||||
signal(SIGALRM, progress_interval);
|
||||
setitimer(ITIMER_REAL, &v, NULL);
|
||||
fprintf(stderr, "Generating pack...\n");
|
||||
gettimeofday(&prev_tv, NULL);
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), stdin) != NULL) {
|
||||
unsigned int hash;
|
||||
char *p;
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (progress && (eye_candy <= nr_objects)) {
|
||||
if (progress_update) {
|
||||
fprintf(stderr, "Counting objects...%d\r", nr_objects);
|
||||
if (eye_candy && (50 <= eye_candy_incr)) {
|
||||
struct timeval tv;
|
||||
int time_diff;
|
||||
gettimeofday(&tv, NULL);
|
||||
time_diff = (tv.tv_sec - prev_tv.tv_sec);
|
||||
time_diff <<= 10;
|
||||
time_diff += (tv.tv_usec - prev_tv.tv_usec);
|
||||
if ((1 << 9) < time_diff)
|
||||
eye_candy_incr += 50;
|
||||
else if (50 < eye_candy_incr)
|
||||
eye_candy_incr -= 50;
|
||||
}
|
||||
eye_candy += eye_candy_incr;
|
||||
progress_update = 0;
|
||||
}
|
||||
if (get_sha1_hex(line, sha1))
|
||||
die("expected sha1, got garbage:\n %s", line);
|
||||
@ -594,10 +933,21 @@ int main(int argc, char **argv)
|
||||
;
|
||||
else {
|
||||
prepare_pack(window, depth);
|
||||
if (progress && pack_to_stdout) {
|
||||
/* the other end usually displays progress itself */
|
||||
struct itimerval v = {{0,},};
|
||||
setitimer(ITIMER_REAL, &v, NULL);
|
||||
signal(SIGALRM, SIG_IGN );
|
||||
progress_update = 0;
|
||||
}
|
||||
write_pack_file();
|
||||
if (!pack_to_stdout) {
|
||||
write_index_file();
|
||||
puts(sha1_to_hex(object_list_sha1));
|
||||
}
|
||||
}
|
||||
if (progress)
|
||||
fprintf(stderr, "Total %d, written %d (delta %d), reused %d (delta %d)\n",
|
||||
nr_objects, written, written_delta, reused, reused_delta);
|
||||
return 0;
|
||||
}
|
||||
|
4
pack.h
4
pack.h
@ -29,5 +29,7 @@ struct pack_header {
|
||||
};
|
||||
|
||||
extern int verify_pack(struct packed_git *, int);
|
||||
|
||||
extern int check_reuse_pack_delta(struct packed_git *, unsigned long,
|
||||
unsigned char *, unsigned long *,
|
||||
enum object_type *);
|
||||
#endif
|
||||
|
38
sha1_file.c
38
sha1_file.c
@ -247,6 +247,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
|
||||
for ( ; cp < ep && *cp != sep; cp++)
|
||||
;
|
||||
if (last != cp) {
|
||||
struct stat st;
|
||||
struct alternate_object_database *alt;
|
||||
/* 43 = 40-byte + 2 '/' + terminating NUL */
|
||||
int pfxlen = cp - last;
|
||||
@ -269,9 +270,19 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
|
||||
}
|
||||
else
|
||||
memcpy(ent->base, last, pfxlen);
|
||||
|
||||
ent->name = ent->base + pfxlen + 1;
|
||||
ent->base[pfxlen] = ent->base[pfxlen + 3] = '/';
|
||||
ent->base[entlen-1] = 0;
|
||||
ent->base[pfxlen + 3] = '/';
|
||||
ent->base[pfxlen] = ent->base[entlen-1] = 0;
|
||||
|
||||
/* Detect cases where alternate disappeared */
|
||||
if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
|
||||
error("object directory %s does not exist; "
|
||||
"check .git/objects/info/alternates.",
|
||||
ent->base);
|
||||
goto bad;
|
||||
}
|
||||
ent->base[pfxlen] = '/';
|
||||
|
||||
/* Prevent the common mistake of listing the same
|
||||
* thing twice, or object directory itself.
|
||||
@ -552,7 +563,9 @@ static void prepare_packed_git_one(char *objdir, int local)
|
||||
len = strlen(path);
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
fprintf(stderr, "unable to open object pack directory: %s: %s\n", path, strerror(errno));
|
||||
if (errno != ENOENT)
|
||||
error("unable to open object pack directory: %s: %s\n",
|
||||
path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
path[len++] = '/';
|
||||
@ -828,6 +841,25 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
|
||||
return offset;
|
||||
}
|
||||
|
||||
int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
|
||||
unsigned char *base, unsigned long *sizep,
|
||||
enum object_type *kindp)
|
||||
{
|
||||
unsigned long ptr;
|
||||
int status = -1;
|
||||
|
||||
use_packed_git(p);
|
||||
ptr = offset;
|
||||
ptr = unpack_object_header(p, ptr, kindp, sizep);
|
||||
if (*kindp != OBJ_DELTA)
|
||||
goto done;
|
||||
memcpy(base, p->pack_base + ptr, 20);
|
||||
status = 0;
|
||||
done:
|
||||
unuse_packed_git(p);
|
||||
return status;
|
||||
}
|
||||
|
||||
void packed_object_info_detail(struct pack_entry *e,
|
||||
char *type,
|
||||
unsigned long *size,
|
||||
|
22
t/t3700-add.sh
Executable file
22
t/t3700-add.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006 Carl D. Worth
|
||||
#
|
||||
|
||||
test_description='Test of git-add, including the -- option.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success \
|
||||
'Test of git-add' \
|
||||
'touch foo && git-add foo'
|
||||
|
||||
test_expect_success \
|
||||
'Post-check that foo is in the index' \
|
||||
'git-ls-files foo | grep foo'
|
||||
|
||||
test_expect_success \
|
||||
'Test that "git-add -- -q" works' \
|
||||
'touch -- -q && git-add -- -q'
|
||||
|
||||
test_done
|
6
var.c
6
var.c
@ -12,7 +12,7 @@ static const char var_usage[] = "git-var [-l | <variable>]";
|
||||
|
||||
struct git_var {
|
||||
const char *name;
|
||||
const char *(*read)(void);
|
||||
const char *(*read)(int);
|
||||
};
|
||||
static struct git_var git_vars[] = {
|
||||
{ "GIT_COMMITTER_IDENT", git_committer_info },
|
||||
@ -24,7 +24,7 @@ static void list_vars(void)
|
||||
{
|
||||
struct git_var *ptr;
|
||||
for(ptr = git_vars; ptr->read; ptr++) {
|
||||
printf("%s=%s\n", ptr->name, ptr->read());
|
||||
printf("%s=%s\n", ptr->name, ptr->read(0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ static const char *read_var(const char *var)
|
||||
val = NULL;
|
||||
for(ptr = git_vars; ptr->read; ptr++) {
|
||||
if (strcmp(var, ptr->name) == 0) {
|
||||
val = ptr->read();
|
||||
val = ptr->read(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user