Merge GIT 1.5.1-rc1

This commit is contained in:
Johannes Sixt
2007-03-22 14:09:46 +01:00
58 changed files with 1046 additions and 443 deletions

View File

@@ -0,0 +1,28 @@
GIT v1.5.0.5 Release Notes
==========================
Fixes since v1.5.0.3
--------------------
* Bugfixes
- git-merge (hence git-pull) did not refuse fast-forwarding
when the working tree had local changes that would have
conflicted with it.
- git.el does not add duplicate sign-off lines.
- git-commit shows the full stat of the resulting commit, not
just about the files in the current directory, when run from
a subdirectory.
- "git-checkout -m '@{8 hours ago}'" had a funny failure from
eval; fixed.
- git-gui updates.
* Documentation updates
* User manual updates

View File

@@ -25,6 +25,21 @@ Updates since v1.5.0
- "git diff --pretty=format:<string>" to allow more flexible
custom log output.
- "git diff --no-index" can read from '-' (standard input).
- "git diff" also learned --exit-code to exit with non-zero
status when it found differences. In the future we might
want to make this the default but that would be a rather big
backward incompatible change; it will stay as an option for
now.
- "git branch --track" can be used to set up configuration
variables to help it easier to base your work on branches
you track from a remote site.
- "git format-patch --attach" now emits attachments. Use
--inline to get an inlined multipart/mixed.
- "git name-rev" learned --refs=<pattern>, to limit the tags
used for naming the given revisions only to the ones
matching the given pattern.
@@ -39,6 +54,9 @@ Updates since v1.5.0
- "git bundle" can help sneaker-netting your changes between
repositories.
- "git mergetool" can help 3-way file-level conflict
resolution with your favorite graphical merge tools.
- A new configuration "core.symlinks" can be used to disable
symlinks on filesystems that do not support them; they are
checked out as regular files instead.
@@ -46,6 +64,11 @@ Updates since v1.5.0
* Updated behaviour of existing commands.
- "git fsck" does not barf on corrupt loose objects.
- "git archimport" allows remapping when coming up with git
branch names from arch names.
- git-svn got almost a rewrite.
- core.autocrlf configuration, when set to 'true', makes git
@@ -99,6 +122,25 @@ Updates since v1.5.0
- "git fetch" (hence "git clone" and "git pull") are less
noisy when the output does not go to tty.
- "git fetch" between repositories with many refs were slow
even when there are not many changes that needed
transferring. This has been sped up by partially rewriting
the heaviest parts in C.
- "git mailinfo" which splits an e-mail into a patch and the
metainformation was rewritten, thanks to Don Zickus. It
handles nested multipart better.
- send-email learned configurable bcc and chain-reply-to.
- Using objects from packs is now seriouly optimized by clever
use of a cache. This should be most noticeable in git-log
family of commands that involve reading many tree objects.
In addition, traversing revisions while filtering changes
with pathspecs is made faster by terminating the comparison
between the trees as early as possible.
* Hooks
- The sample update hook to show how to send out notification
@@ -106,9 +148,15 @@ Updates since v1.5.0
the repository. Earlier, it showed new commits that appeared
on the branch.
* Others
- git-revert, git-gc and git-cherry-pick are now built-ins.
--
exec >/var/tmp/1
O=v1.5.0.3-268-g3ddad98
O=v1.5.0.5-446-g5d86501
echo O=`git describe master`
git shortlog --no-merges $O..master ^maint

View File

@@ -240,6 +240,19 @@ the largest projects. You probably do not need to adjust this value.
+
Common unit suffixes of 'k', 'm', or 'g' are supported.
core.deltaBaseCacheLimit::
Maximum number of bytes to reserve for caching base objects
that multiple deltafied objects reference. By storing the
entire decompressed base objects in a cache Git is able
to avoid unpacking and decompressing frequently used base
objects multiple times.
+
Default is 16 MiB on all platforms. This should be reasonable
for all users/operating systems, except on the largest projects.
You probably do not need to adjust this value.
+
Common unit suffixes of 'k', 'm', or 'g' are supported.
alias.*::
Command aliases for the gitlink:git[1] command wrapper - e.g.
after defining "alias.last = cat-file commit HEAD", the invocation
@@ -272,6 +285,10 @@ branch.<name>.merge::
`git fetch`) to lookup the default branch for merging. Without
this option, `git pull` defaults to merge the first refspec fetched.
Specify multiple values to get an octopus merge.
If you wish to setup `git pull` so that it merges into <name> from
another branch in the local repository, you can point
branch.<name>.merge to the desired branch, and use the special setting
`.` (a period) for branch.<name>.remote.
color.branch::
A boolean to enable/disable color in the output of
@@ -456,7 +473,7 @@ merge.summary::
merge.tool::
Controls which merge resolution program is used by
gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff",
"meld", "xxdiff", "emerge"
"meld", "xxdiff", "emerge", "vimdiff"
merge.verbosity::
Controls the amount of output shown by the recursive merge

View File

@@ -159,5 +159,10 @@
-w::
Shorthand for "--ignore-all-space".
--exit-code::
Make the program exit with codes similar to diff(1).
That is, it exits with 1 if there were differences and
0 means no differences.
For more detailed explanation on these common options, see also
link:diffcore.html[diffcore documentation].

View File

@@ -25,7 +25,7 @@ OPTIONS
-t or --tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, and emerge.
kdiff3, tkdiff, meld, xxdiff, emerge, and vimdiff.
If a merge resolution program is not specified, 'git mergetool'
will use the configuration variable merge.tool. If the

View File

@@ -60,7 +60,8 @@ The --cc option must be repeated for each user you want on the cc list.
is not set, this will be prompted for.
--no-signed-off-by-cc::
Do not add emails found in Signed-off-by: lines to the cc list.
Do not add emails found in Signed-off-by: or Cc: lines to the
cc list.
--quiet::
Make git-send-email less verbose. One line per email should be

View File

@@ -35,7 +35,9 @@ ifdef::stalenotes[]
You are reading the documentation for the latest version of git.
Documentation for older releases are available here:
* link:v1.5.0.3/git.html[documentation for release 1.5.0.3]
* link:v1.5.0.5/git.html[documentation for release 1.5.0.5]
* link:v1.5.0.5/RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
* link:v1.5.0.3/RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]

View File

@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
DEF_VER=v1.5.0.GIT
DEF_VER=v1.5.1-rc1.GIT
LF='
'

View File

@@ -177,7 +177,7 @@ BASIC_LDFLAGS =
SCRIPT_SH = \
git-bisect.sh git-checkout.sh \
git-clean.sh git-clone.sh git-commit.sh \
git-fetch.sh git-gc.sh \
git-fetch.sh \
git-ls-remote.sh \
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
git-pull.sh git-rebase.sh \
@@ -297,6 +297,7 @@ BUILTIN_OBJS = \
builtin-fmt-merge-msg.o \
builtin-for-each-ref.o \
builtin-fsck.o \
builtin-gc.o \
builtin-grep.o \
builtin-init-db.o \
builtin-log.o \

View File

@@ -372,9 +372,26 @@ static int get_remote_config(const char *key, const char *value)
return 0;
}
static void set_branch_defaults(const char *name, const char *real_ref)
static void set_branch_merge(const char *name, const char *config_remote,
const char *config_repo)
{
char key[1024];
if (sizeof(key) <=
snprintf(key, sizeof(key), "branch.%s.remote", name))
die("what a long branch name you have!");
git_config_set(key, config_remote);
/*
* We do not have to check if we have enough space for
* the 'merge' key, since it's shorter than the
* previous 'remote' key, which we already checked.
*/
snprintf(key, sizeof(key), "branch.%s.merge", name);
git_config_set(key, config_repo);
}
static void set_branch_defaults(const char *name, const char *real_ref)
{
const char *slash = strrchr(real_ref, '/');
if (!slash)
@@ -384,21 +401,15 @@ static void set_branch_defaults(const char *name, const char *real_ref)
start_len = strlen(real_ref);
base_len = slash - real_ref;
git_config(get_remote_config);
if (!config_repo && !config_remote &&
!prefixcmp(real_ref, "refs/heads/")) {
set_branch_merge(name, ".", real_ref);
printf("Branch %s set up to track local branch %s.\n",
name, real_ref);
}
if (config_repo && config_remote) {
if (sizeof(key) <=
snprintf(key, sizeof(key), "branch.%s.remote", name))
die("what a long branch name you have!");
git_config_set(key, config_remote);
/*
* We do not have to check if we have enough space for
* the 'merge' key, since it's shorter than the
* previous 'remote' key, which we already checked.
*/
snprintf(key, sizeof(key), "branch.%s.merge", name);
git_config_set(key, config_repo);
set_branch_merge(name, config_remote, config_repo);
printf("Branch %s set up to track remote branch %s.\n",
name, real_ref);
}

View File

@@ -4,7 +4,7 @@
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
#include "exec_cmd.h"
#include "run-command.h"
/*
* Basic handler for bundle files to connect repositories via sneakernet.
@@ -99,67 +99,6 @@ static int read_header(const char *path, struct bundle_header *header) {
return fd;
}
/* if in && *in >= 0, take that as input file descriptor instead */
static int fork_with_pipe(const char **argv, int *in, int *out)
{
int needs_in, needs_out;
int fdin[2], fdout[2], pid;
needs_in = in && *in < 0;
if (needs_in) {
if (pipe(fdin) < 0)
return error("could not setup pipe");
*in = fdin[1];
}
needs_out = out && *out < 0;
if (needs_out) {
if (pipe(fdout) < 0)
return error("could not setup pipe");
*out = fdout[0];
}
if ((pid = fork()) < 0) {
if (needs_in) {
close(fdin[0]);
close(fdin[1]);
}
if (needs_out) {
close(fdout[0]);
close(fdout[1]);
}
return error("could not fork");
}
if (!pid) {
if (needs_in) {
dup2(fdin[0], 0);
close(fdin[0]);
close(fdin[1]);
} else if (in) {
dup2(*in, 0);
close(*in);
}
if (needs_out) {
dup2(fdout[1], 1);
close(fdout[0]);
close(fdout[1]);
} else if (out) {
dup2(*out, 1);
close(*out);
}
exit(execv_git_cmd(argv));
}
if (needs_in)
close(fdin[0]);
else if (in)
close(*in);
if (needs_out)
close(fdout[1]);
else if (out)
close(*out);
return pid;
}
static int list_refs(struct ref_list *r, int argc, const char **argv)
{
int i;
@@ -263,9 +202,10 @@ static int create_bundle(struct bundle_header *header, const char *path,
int bundle_fd = -1;
const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
const char **argv_pack = xmalloc(5 * sizeof(const char *));
int pid, in, out, i, status, ref_count = 0;
int i, ref_count = 0;
char buffer[1024];
struct rev_info revs;
struct child_process rls;
bundle_fd = (!strcmp(path, "-") ? 1 :
open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
@@ -285,11 +225,13 @@ static int create_bundle(struct bundle_header *header, const char *path,
argv_boundary[1] = "--boundary";
argv_boundary[2] = "--pretty=oneline";
argv_boundary[argc + 2] = NULL;
out = -1;
pid = fork_with_pipe(argv_boundary, NULL, &out);
if (pid < 0)
memset(&rls, 0, sizeof(rls));
rls.argv = argv_boundary;
rls.out = -1;
rls.git_cmd = 1;
if (start_command(&rls))
return -1;
while ((i = read_string(out, buffer, sizeof(buffer))) > 0) {
while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) {
unsigned char sha1[20];
if (buffer[0] == '-') {
write_or_die(bundle_fd, buffer, i);
@@ -303,11 +245,8 @@ static int create_bundle(struct bundle_header *header, const char *path,
object->flags |= SHOWN;
}
}
while ((i = waitpid(pid, &status, 0)) < 0)
if (errno != EINTR)
return error("rev-list died");
if (!WIFEXITED(status) || WEXITSTATUS(status))
return error("rev-list died %d", WEXITSTATUS(status));
if (finish_command(&rls))
return error("rev-list died");
/* write references */
argc = setup_revisions(argc, argv, &revs, NULL);
@@ -352,26 +291,23 @@ static int create_bundle(struct bundle_header *header, const char *path,
argv_pack[2] = "--stdout";
argv_pack[3] = "--thin";
argv_pack[4] = NULL;
in = -1;
out = bundle_fd;
pid = fork_with_pipe(argv_pack, &in, &out);
if (pid < 0)
memset(&rls, 0, sizeof(rls));
rls.argv = argv_pack;
rls.in = -1;
rls.out = bundle_fd;
rls.git_cmd = 1;
if (start_command(&rls))
return error("Could not spawn pack-objects");
for (i = 0; i < revs.pending.nr; i++) {
struct object *object = revs.pending.objects[i].item;
if (object->flags & UNINTERESTING)
write(in, "^", 1);
write(in, sha1_to_hex(object->sha1), 40);
write(in, "\n", 1);
write(rls.in, "^", 1);
write(rls.in, sha1_to_hex(object->sha1), 40);
write(rls.in, "\n", 1);
}
close(in);
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR)
return -1;
if (!WIFEXITED(status) || WEXITSTATUS(status))
if (finish_command(&rls))
return error ("pack-objects died");
return status;
return 0;
}
static int unbundle(struct bundle_header *header, int bundle_fd,
@@ -379,22 +315,17 @@ static int unbundle(struct bundle_header *header, int bundle_fd,
{
const char *argv_index_pack[] = {"index-pack",
"--fix-thin", "--stdin", NULL};
int pid, status, dev_null;
struct child_process ip;
if (verify_bundle(header, 0))
return -1;
dev_null = open("/dev/null", O_WRONLY);
if (dev_null < 0)
return error("Could not open /dev/null");
pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
if (pid < 0)
return error("Could not spawn index-pack");
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR)
return error("index-pack died");
if (!WIFEXITED(status) || WEXITSTATUS(status))
return error("index-pack exited with status %d",
WEXITSTATUS(status));
memset(&ip, 0, sizeof(ip));
ip.argv = argv_index_pack;
ip.in = bundle_fd;
ip.no_stdout = 1;
ip.git_cmd = 1;
if (run_command(&ip))
return error("index-pack died");
return list_heads(header, argc, argv);
}

View File

@@ -17,6 +17,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
int nongit = 0;
int result;
prefix = setup_git_directory_gently(&nongit);
init_revisions(&rev, prefix);
@@ -29,5 +30,6 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
argc = setup_revisions(argc, argv, &rev, NULL);
if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_RAW;
return run_diff_files_cmd(&rev, argc, argv);
result = run_diff_files_cmd(&rev, argc, argv);
return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
}

View File

@@ -14,6 +14,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
struct rev_info rev;
int cached = 0;
int i;
int result;
init_revisions(&rev, prefix);
git_config(git_default_config); /* no "diff" UI options */
@@ -42,5 +43,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
perror("read_cache");
return -1;
}
return run_diff_index(&rev, cached);
result = run_diff_index(&rev, cached);
return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
}

View File

@@ -118,7 +118,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
}
if (!read_stdin)
return 0;
return opt->diffopt.exit_with_status ?
opt->diffopt.has_changes: 0;
if (opt->diffopt.detect_rename)
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
@@ -133,5 +134,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
else
diff_tree_stdin(line);
}
return 0;
return opt->diffopt.exit_with_status ? opt->diffopt.has_changes: 0;
}

View File

@@ -190,6 +190,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
const char *path = NULL;
struct blobinfo blob[2];
int nongit = 0;
int result = 0;
/*
* We could get N tree-ish in the rev.pending_objects list.
@@ -292,17 +293,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!ents) {
switch (blobs) {
case 0:
return run_diff_files_cmd(&rev, argc, argv);
result = run_diff_files_cmd(&rev, argc, argv);
break;
case 1:
if (paths != 1)
usage(builtin_diff_usage);
return builtin_diff_b_f(&rev, argc, argv, blob, path);
result = builtin_diff_b_f(&rev, argc, argv, blob, path);
break;
case 2:
if (paths)
usage(builtin_diff_usage);
return builtin_diff_blobs(&rev, argc, argv, blob);
result = builtin_diff_blobs(&rev, argc, argv, blob);
break;
default:
usage(builtin_diff_usage);
@@ -311,19 +312,21 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
else if (blobs)
usage(builtin_diff_usage);
else if (ents == 1)
return builtin_diff_index(&rev, argc, argv);
result = builtin_diff_index(&rev, argc, argv);
else if (ents == 2)
return builtin_diff_tree(&rev, argc, argv, ent);
result = builtin_diff_tree(&rev, argc, argv, ent);
else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
/* diff A...B where there is one sane merge base between
* A and B. We have ent[0] == merge-base, ent[1] == A,
* and ent[2] == B. Show diff between the base and B.
*/
ent[1] = ent[2];
return builtin_diff_tree(&rev, argc, argv, ent);
result = builtin_diff_tree(&rev, argc, argv, ent);
}
else
return builtin_diff_combined(&rev, argc, argv,
result = builtin_diff_combined(&rev, argc, argv,
ent, ents);
usage(builtin_diff_usage);
if (rev.diffopt.exit_with_status)
result = rev.diffopt.has_changes;
return result;
}

78
builtin-gc.c Normal file
View File

@@ -0,0 +1,78 @@
/*
* git gc builtin command
*
* Cleanup unreachable files and optimize the repository.
*
* Copyright (c) 2007 James Bowes
*
* Based on git-gc.sh, which is
*
* Copyright (c) 2006 Shawn O. Pearce
*/
#include "cache.h"
#include "run-command.h"
#define FAILED_RUN "failed to run %s"
static const char builtin_gc_usage[] = "git-gc [--prune]";
static int pack_refs = -1;
static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL};
static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
static const char *argv_repack[] = {"repack", "-a", "-d", "-l", NULL};
static const char *argv_prune[] = {"prune", NULL};
static const char *argv_rerere[] = {"rerere", "gc", NULL};
static int gc_config(const char *var, const char *value)
{
if (!strcmp(var, "gc.packrefs")) {
if (!strcmp(value, "notbare"))
pack_refs = -1;
else
pack_refs = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value);
}
int cmd_gc(int argc, const char **argv, const char *prefix)
{
int i;
int prune = 0;
git_config(gc_config);
if (pack_refs < 0)
pack_refs = !is_bare_repository();
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--prune")) {
prune = 1;
continue;
}
/* perhaps other parameters later... */
break;
}
if (i != argc)
usage(builtin_gc_usage);
if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
return error(FAILED_RUN, argv_pack_refs[0]);
if (run_command_v_opt(argv_reflog, RUN_GIT_CMD))
return error(FAILED_RUN, argv_reflog[0]);
if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
return error(FAILED_RUN, argv_repack[0]);
if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
return error(FAILED_RUN, argv_prune[0]);
if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
return error(FAILED_RUN, argv_rerere[0]);
return 0;
}

View File

@@ -166,11 +166,12 @@ 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;
const char *index = p->index_data;
index += 4 * 256;
rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
for (i = 0; i < num_ent; i++) {
unsigned int hl = *((unsigned int *)((char *) index + 24*i));
uint32_t hl = *((uint32_t *)(index + 24 * i));
rix->revindex[i].offset = ntohl(hl);
rix->revindex[i].nr = i;
}
@@ -217,11 +218,11 @@ static off_t find_packed_object_size(struct packed_git *p, off_t ofs)
return entry[1].offset - ofs;
}
static unsigned char *find_packed_object_name(struct packed_git *p,
off_t ofs)
static const unsigned char *find_packed_object_name(struct packed_git *p,
off_t ofs)
{
struct revindex_entry *entry = find_packed_object(p, ofs);
return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4;
return ((unsigned char *)p->index_data) + 4 * 256 + 24 * entry->nr + 4;
}
static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
@@ -996,7 +997,8 @@ static void check_object(struct object_entry *entry)
* delta.
*/
if (!no_reuse_delta) {
unsigned char c, *base_name;
unsigned char c;
const unsigned char *base_name;
off_t ofs;
unsigned long used_0;
/* there is at least 20 bytes left in the pack */

View File

@@ -323,10 +323,10 @@ static int do_push(const char *repo)
int dest_refspec_nr = refspec_nr;
const char **dest_refspec = refspec;
const char *dest = uri[i];
const char *sender = "git-send-pack";
const char *sender = "send-pack";
if (!prefixcmp(dest, "http://") ||
!prefixcmp(dest, "https://"))
sender = "git-http-push";
sender = "http-push";
else if (thin)
argv[dest_argc++] = "--thin";
argv[0] = sender;
@@ -336,7 +336,7 @@ static int do_push(const char *repo)
argv[dest_argc] = NULL;
if (verbose)
fprintf(stderr, "Pushing to %s\n", dest);
err = run_command_v_opt(argv, 0);
err = run_command_v_opt(argv, RUN_GIT_CMD);
if (!err)
continue;
switch (err) {

View File

@@ -235,8 +235,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
unsigned char head[20];
struct commit *base, *next;
int i;
char *oneline, *encoding, *reencoded_message = NULL;
const char *message;
char *oneline, *reencoded_message = NULL;
const char *message, *encoding;
git_config(git_default_config);
me = action == REVERT ? "revert" : "cherry-pick";

View File

@@ -37,6 +37,7 @@ extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
extern int cmd_fsck(int argc, const char **argv, const char *prefix);
extern int cmd_gc(int argc, const char **argv, const char *prefix);
extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
extern int cmd_grep(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix);

12
cache.h
View File

@@ -229,6 +229,7 @@ extern const char *apply_default_whitespace;
extern int zlib_compression_level;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
extern size_t delta_base_cache_limit;
extern int auto_crlf;
#define GIT_REPO_VERSION 0
@@ -372,10 +373,11 @@ struct pack_window {
extern struct packed_git {
struct packed_git *next;
struct pack_window *windows;
uint32_t *index_base;
time_t mtime;
const void *index_data;
off_t index_size;
off_t pack_size;
time_t mtime;
int index_version;
int pack_fd;
int pack_local;
unsigned char sha1[20];
@@ -413,7 +415,7 @@ extern int server_supports(const char *feature);
extern struct packed_git *parse_pack_index(unsigned char *sha1);
extern struct packed_git *parse_pack_index_file(const unsigned char *sha1,
char *idx_path);
const char *idx_path);
extern void prepare_packed_git(void);
extern void reprepare_packed_git(void);
@@ -425,7 +427,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
extern void pack_report(void);
extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
extern void unuse_pack(struct pack_window **);
extern struct packed_git *add_packed_git(char *, int, int);
extern struct packed_git *add_packed_git(const char *, int, int);
extern uint32_t num_packed_objects(const struct packed_git *p);
extern int nth_packed_object_sha1(const struct packed_git *, uint32_t, unsigned char*);
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
@@ -451,7 +453,7 @@ extern int check_repository_format_version(const char *var, const char *value);
extern char git_default_email[MAX_GITNAME];
extern char git_default_name[MAX_GITNAME];
extern char *git_commit_encoding;
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;
extern int copy_fd(int ifd, int ofd);

View File

@@ -331,6 +331,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.deltabasecachelimit")) {
delta_base_cache_limit = git_config_int(var, value);
return 0;
}
if (!strcmp(var, "core.autocrlf")) {
if (value && !strcasecmp(value, "input")) {
auto_crlf = -1;
@@ -351,12 +356,12 @@ int git_default_config(const char *var, const char *value)
}
if (!strcmp(var, "i18n.commitencoding")) {
git_commit_encoding = strdup(value);
git_commit_encoding = xstrdup(value);
return 0;
}
if (!strcmp(var, "i18n.logoutputencoding")) {
git_log_output_encoding = strdup(value);
git_log_output_encoding = xstrdup(value);
return 0;
}

View File

@@ -3,6 +3,7 @@
#include "pkt-line.h"
#include "quote.h"
#include "refs.h"
#include "run-command.h"
#include "spawn-pipe.h"
static char *server_capabilities;
@@ -615,8 +616,8 @@ static void git_proxy_connect(int fd[2], char *host)
{
const char *port = STR(DEFAULT_GIT_PORT);
char *colon, *end;
int pipefd[2][2];
pid_t pid;
const char *argv[4];
struct child_process proxy;
if (host[0] == '[') {
end = strchr(host + 1, ']');
@@ -635,18 +636,18 @@ static void git_proxy_connect(int fd[2], char *host)
port = colon + 1;
}
if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
die("unable to create pipe pair for communication");
{
const char *argv[] = { NULL, host, port, NULL };
pid = spawnvpe_pipe(git_proxy_command, argv, environ,
pipefd[1], pipefd[0]);
}
if (pid < 0)
die("fork failed");
fd[0] = pipefd[0][0];
fd[1] = pipefd[1][1];
argv[0] = git_proxy_command;
argv[1] = host;
argv[2] = port;
argv[3] = NULL;
memset(&proxy, 0, sizeof(proxy));
proxy.argv = argv;
proxy.in = -1;
proxy.out = -1;
if (start_command(&proxy))
die("cannot start proxy %s", argv[0]);
fd[0] = proxy.out; /* read from proxy stdout */
fd[1] = proxy.in; /* write to proxy stdin */
}
#define MAX_CMD_LEN 1024

View File

@@ -1,6 +1,6 @@
;;; git.el --- A user interface for git
;; Copyright (C) 2005, 2006 Alexandre Julliard <julliard@winehq.org>
;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard <julliard@winehq.org>
;; Version: 1.0
@@ -213,6 +213,23 @@ and returns the process output as a string."
(error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))
(message "Running git %s...done" (car args)))
(defun git-run-hook (hook env &rest args)
"Run a git hook and display its output if any."
(let ((dir default-directory)
(hook-name (expand-file-name (concat ".git/hooks/" hook))))
(or (not (file-executable-p hook-name))
(let (status (buffer (get-buffer-create "*Git Hook Output*")))
(with-current-buffer buffer
(erase-buffer)
(cd dir)
(setq status
(if env
(apply #'call-process "env" nil (list buffer t) nil
(append (git-get-env-strings env) (list hook-name) args))
(apply #'call-process hook-name nil (list buffer t) nil args))))
(display-message-or-buffer buffer)
(eq 0 status)))))
(defun git-get-string-sha1 (string)
"Read a SHA1 from the specified string."
(and string
@@ -590,6 +607,20 @@ and returns the process output as a string."
(when modified
(apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified)))))
(defun git-run-pre-commit-hook ()
"Run the pre-commit hook if any."
(unless git-status (error "Not in git-status buffer."))
(let ((files (git-marked-files-state 'added 'deleted 'modified)))
(or (not files)
(not (file-executable-p ".git/hooks/pre-commit"))
(let ((index-file (make-temp-file "gitidx")))
(unwind-protect
(let ((head-tree (unless (git-empty-db-p) (git-rev-parse "HEAD^{tree}"))))
(git-read-tree head-tree index-file)
(git-update-index index-file files)
(git-run-hook "pre-commit" `(("GIT_INDEX_FILE" . ,index-file))))
(delete-file index-file))))))
(defun git-do-commit ()
"Perform the actual commit using the current buffer as log message."
(interactive)
@@ -622,7 +653,8 @@ and returns the process output as a string."
(git-run-command nil nil "rerere"))
(git-refresh-files)
(git-refresh-ewoc-hf git-status)
(message "Committed %s." commit))
(message "Committed %s." commit)
(git-run-hook "post-commit" nil))
(message "Commit aborted."))))
(message "No files to commit.")))
(delete-file index-file))))))
@@ -944,28 +976,29 @@ and returns the process output as a string."
"Commit the marked file(s), asking for a commit message."
(interactive)
(unless git-status (error "Not in git-status buffer."))
(let ((buffer (get-buffer-create "*git-commit*"))
(coding-system (git-get-commits-coding-system))
author-name author-email subject date)
(when (eq 0 (buffer-size buffer))
(when (file-readable-p ".dotest/info")
(with-temp-buffer
(insert-file-contents ".dotest/info")
(goto-char (point-min))
(when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
(setq author-name (match-string 1))
(setq author-email (match-string 2)))
(goto-char (point-min))
(when (re-search-forward "^Subject: \\(.*\\)$" nil t)
(setq subject (match-string 1)))
(goto-char (point-min))
(when (re-search-forward "^Date: \\(.*\\)$" nil t)
(setq date (match-string 1)))))
(git-setup-log-buffer buffer author-name author-email subject date))
(log-edit #'git-do-commit nil #'git-log-edit-files buffer)
(setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
(setq buffer-file-coding-system coding-system)
(re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))
(when (git-run-pre-commit-hook)
(let ((buffer (get-buffer-create "*git-commit*"))
(coding-system (git-get-commits-coding-system))
author-name author-email subject date)
(when (eq 0 (buffer-size buffer))
(when (file-readable-p ".dotest/info")
(with-temp-buffer
(insert-file-contents ".dotest/info")
(goto-char (point-min))
(when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
(setq author-name (match-string 1))
(setq author-email (match-string 2)))
(goto-char (point-min))
(when (re-search-forward "^Subject: \\(.*\\)$" nil t)
(setq subject (match-string 1)))
(goto-char (point-min))
(when (re-search-forward "^Date: \\(.*\\)$" nil t)
(setq date (match-string 1)))))
(git-setup-log-buffer buffer author-name author-email subject date))
(log-edit #'git-do-commit nil #'git-log-edit-files buffer)
(setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
(setq buffer-file-coding-system coding-system)
(re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
(defun git-find-file ()
"Visit the current file in its own buffer."

View File

@@ -170,8 +170,10 @@ static int handle_diff_files_args(struct rev_info *revs,
else if (!strcmp(argv[1], "--theirs"))
revs->max_count = 3;
else if (!strcmp(argv[1], "-n") ||
!strcmp(argv[1], "--no-index"))
!strcmp(argv[1], "--no-index")) {
revs->max_count = -2;
revs->diffopt.exit_with_status = 1;
}
else if (!strcmp(argv[1], "-q"))
*silent = 1;
else
@@ -237,6 +239,7 @@ int setup_diff_no_index(struct rev_info *revs,
break;
} else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) {
i = argc - 3;
revs->diffopt.exit_with_status = 1;
break;
}
if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
@@ -321,6 +324,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
struct cache_entry *ce = active_cache[i];
int changed;
if (revs->diffopt.quiet && revs->diffopt.has_changes)
break;
if (!ce_path_match(ce, revs->prune_data))
continue;
@@ -562,6 +568,9 @@ static int diff_cache(struct rev_info *revs,
struct cache_entry *ce = *ac;
int same = (entries > 1) && ce_same_name(ce, ac[1]);
if (revs->diffopt.quiet && revs->diffopt.has_changes)
break;
if (!ce_path_match(ce, pathspec))
goto skip_entry;

36
diff.c
View File

@@ -1953,6 +1953,23 @@ int diff_setup_done(struct diff_options *options)
if (options->abbrev <= 0 || 40 < options->abbrev)
options->abbrev = 40; /* full */
/*
* It does not make sense to show the first hit we happened
* to have found. It does not make sense not to return with
* exit code in such a case either.
*/
if (options->quiet) {
options->output_format = DIFF_FORMAT_NO_OUTPUT;
options->exit_with_status = 1;
}
/*
* If we postprocess in diffcore, we cannot simply return
* upon the first hit. We need to run diff as usual.
*/
if (options->pickaxe || options->filter)
options->quiet = 0;
return 0;
}
@@ -2129,6 +2146,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->color_diff = options->color_diff_words = 1;
else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0;
else if (!strcmp(arg, "--exit-code"))
options->exit_with_status = 1;
else if (!strcmp(arg, "--quiet"))
options->quiet = 1;
else
return 0;
return 1;
@@ -2893,6 +2914,8 @@ static void diffcore_apply_filter(const char *filter)
void diffcore_std(struct diff_options *options)
{
if (options->quiet)
return;
if (options->break_opt != -1)
diffcore_break(options->break_opt);
if (options->detect_rename)
@@ -2905,18 +2928,11 @@ void diffcore_std(struct diff_options *options)
diffcore_order(options->orderfile);
diff_resolve_rename_copy();
diffcore_apply_filter(options->filter);
options->has_changes = !!diff_queued_diff.nr;
}
void diffcore_std_no_resolve(struct diff_options *options)
{
if (options->pickaxe)
diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
if (options->orderfile)
diffcore_order(options->orderfile);
diffcore_apply_filter(options->filter);
}
void diff_addremove(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
@@ -2952,6 +2968,7 @@ void diff_addremove(struct diff_options *options,
fill_filespec(two, sha1, mode);
diff_queue(&diff_queued_diff, one, two);
options->has_changes = 1;
}
void diff_change(struct diff_options *options,
@@ -2977,6 +2994,7 @@ void diff_change(struct diff_options *options,
fill_filespec(two, new_sha1, new_mode);
diff_queue(&diff_queued_diff, one, two);
options->has_changes = 1;
}
void diff_unmerge(struct diff_options *options,

7
diff.h
View File

@@ -56,7 +56,10 @@ struct diff_options {
silent_on_remove:1,
find_copies_harder:1,
color_diff:1,
color_diff_words:1;
color_diff_words:1,
has_changes:1,
quiet:1,
exit_with_status:1;
int context;
int break_opt;
int detect_rename;
@@ -170,8 +173,6 @@ extern int diff_setup_done(struct diff_options *);
extern void diffcore_std(struct diff_options *);
extern void diffcore_std_no_resolve(struct diff_options *);
#define COMMON_DIFF_OPTIONS_HELP \
"\ncommon diff options:\n" \
" -z output diff-raw with lines terminated with NUL.\n" \

View File

@@ -20,13 +20,14 @@ int is_bare_repository_cfg = -1; /* unspecified */
int log_all_ref_updates = -1; /* unspecified */
int warn_ambiguous_refs = 1;
int repository_format_version;
char *git_commit_encoding;
const char *git_commit_encoding;
const char *git_log_output_encoding;
int shared_repository = PERM_UMASK;
const char *apply_default_whitespace;
int zlib_compression_level = Z_DEFAULT_COMPRESSION;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
size_t delta_base_cache_limit = 16 * 1024 * 1024;
int pager_in_use;
int pager_use_color = 1;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */

View File

@@ -77,9 +77,9 @@ do
*)
git-mailinfo $keep_subject $utf8 \
.dotest/msg .dotest/patch <$i >.dotest/info || exit 1
test -s $dotest/patch || {
test -s .dotest/patch || {
echo "Patch is empty. Was is split wrong?"
stop_here $this
exit 1
}
git-stripspace < .dotest/msg > .dotest/msg-clean
;;

View File

@@ -89,7 +89,7 @@ while [ "$#" != "0" ]; do
esac
done
case "$new_branch,$track" in
case "$newbranch,$track" in
,--*)
die "git checkout: --track and --no-track require -b"
esac

View File

@@ -947,6 +947,7 @@ sub req_update
# we need to merge with the local changes ( M=successful merge, C=conflict merge )
$log->info("Merging $file_local, $file_old, $file_new");
print "M Merging differences between 1.$oldmeta->{revision} and 1.$meta->{revision} into $filename\n";
$log->debug("Temporary directory for merge is $dir");
@@ -973,6 +974,7 @@ sub req_update
elsif ( $return == 1 )
{
$log->info("Merged with conflicts");
print "E cvs update: conflicts found in $filename\n";
print "M C $filename\n";
# Don't want to actually _DO_ the update if -n specified
@@ -1067,6 +1069,7 @@ sub req_ci
$log->info("Created index '$file_index' with for head $state->{module} - exit status $?");
my @committedfiles = ();
my %oldmeta;
# foreach file specified on the command line ...
foreach my $filename ( @{$state->{args}} )
@@ -1077,6 +1080,7 @@ sub req_ci
next unless ( exists $state->{entries}{$filename}{modified_filename} or not $state->{entries}{$filename}{unchanged} );
my $meta = $updater->getmeta($filename);
$oldmeta{$filename} = $meta;
my $wrev = revparse($filename);
@@ -1205,11 +1209,18 @@ sub req_ci
$log->debug("Checked-in $dirpart : $filename");
print "M $state->{CVSROOT}/$state->{module}/$filename,v <-- $dirpart$filepart\n";
if ( defined $meta->{filehash} && $meta->{filehash} eq "deleted" )
{
print "M new revision: delete; previous revision: 1.$oldmeta{$filename}{revision}\n";
print "Remove-entry $dirpart\n";
print "$filename\n";
} else {
if ($meta->{revision} == 1) {
print "M initial revision: 1.1\n";
} else {
print "M new revision: 1.$meta->{revision}; previous revision: 1.$oldmeta{$filename}{revision}\n";
}
print "Checked-in $dirpart\n";
print "$filename\n";
my $kopts = kopts_from_path($filepart);
@@ -1295,7 +1306,7 @@ sub req_status
}
if ( defined($meta->{revision}) )
{
print "M Repository revision:\t1." . $meta->{revision} . "\t$state->{repository}/$filename,v\n";
print "M Repository revision:\t1." . $meta->{revision} . "\t$state->{CVSROOT}/$state->{module}/$filename,v\n";
print "M Sticky Tag:\t\t(none)\n";
print "M Sticky Date:\t\t(none)\n";
print "M Sticky Options:\t\t(none)\n";

View File

@@ -157,7 +157,7 @@ then
fi
fi
fetch_native () {
fetch_all_at_once () {
eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
eval "$eval"
@@ -165,7 +165,9 @@ fetch_native () {
( : subshell because we muck with IFS
IFS=" $LF"
(
if test -f "$remote" ; then
if test "$remote" = . ; then
git-show-ref $rref || echo failed "$remote"
elif test -f "$remote" ; then
test -n "$shallow_depth" &&
die "shallow clone with bundle is not supported"
git-bundle unbundle "$remote" $rref ||
@@ -188,7 +190,7 @@ fetch_native () {
}
fetch_dumb () {
fetch_per_ref () {
reflist="$1"
refs=
rref=
@@ -292,10 +294,10 @@ fetch_dumb () {
fetch_main () {
case "$remote" in
http://* | https://* | ftp://* | rsync://* )
fetch_dumb "$@"
fetch_per_ref "$@"
;;
*)
fetch_native "$@"
fetch_all_at_once "$@"
;;
esac
}

4
git-merge.sh Normal file → Executable file
View File

@@ -337,13 +337,13 @@ f,*)
# Again the most common case of merging one remote.
echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
git-update-index --refresh 2>/dev/null
new_head=$(git-rev-parse --verify "$1^0") &&
merge_local_changes $head $new_head &&
msg="Fast forward"
if test -n "$have_message"
then
msg="$msg (no commit created; -m option ignored)"
fi
new_head=$(git-rev-parse --verify "$1^0") &&
merge_local_changes $head $new_head &&
finish "$new_head" "$msg" || exit
dropsave
exit 0

View File

@@ -185,9 +185,9 @@ merge_file () {
mv -- "$BACKUP" "$path.orig"
fi
;;
meld)
meld|vimdiff)
touch "$BACKUP"
meld -- "$LOCAL" "$path" "$REMOTE"
$merge_tool -- "$LOCAL" "$path" "$REMOTE"
if test "$path" -nt "$BACKUP" ; then
status=0;
else
@@ -288,10 +288,15 @@ done
if test -z "$merge_tool"; then
merge_tool=`git-config merge.tool`
if test $merge_tool = kdiff3 -o $merge_tool = tkdiff -o \
$merge_tool = xxdiff -o $merge_tool = meld ; then
unset merge_tool
fi
case "$merge_tool" in
kdiff3 | tkdiff | xxdiff | meld | emerge | vimdiff)
;; # happy
*)
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
echo >&2 "Resetting to default..."
unset merge_tool
;;
esac
fi
if test -z "$merge_tool" ; then
@@ -305,6 +310,8 @@ if test -z "$merge_tool" ; then
merge_tool=meld
elif type emacs >/dev/null 2>&1; then
merge_tool=emerge
elif type vimdiff >/dev/null 2>&1; then
merge_tool=vimdiff
else
echo "No available merge resolution programs available."
exit 1
@@ -312,7 +319,7 @@ if test -z "$merge_tool" ; then
fi
case "$merge_tool" in
kdiff3|tkdiff|meld|xxdiff)
kdiff3|tkdiff|meld|xxdiff|vimdiff)
if ! type "$merge_tool" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available"
exit 1

View File

@@ -9,6 +9,9 @@ get_data_source () {
*/*)
echo ''
;;
.)
echo self
;;
*)
if test "$(git-config --get "remote.$1.url")"
then
@@ -31,6 +34,9 @@ get_remote_url () {
'')
echo "$1"
;;
self)
echo "$1"
;;
config)
git-config --get "remote.$1.url"
;;
@@ -57,7 +63,7 @@ get_default_remote () {
get_remote_default_refs_for_push () {
data_source=$(get_data_source "$1")
case "$data_source" in
'' | branches)
'' | branches | self)
;; # no default push mapping, just send matching refs.
config)
git-config --get-all "remote.$1.push" ;;
@@ -163,6 +169,10 @@ get_remote_default_refs_for_fetch () {
case "$data_source" in
'')
echo "HEAD:" ;;
self)
canon_refs_list_for_fetch -d "$1" \
$(git-for-each-ref --format='%(refname):')
;;
config)
canon_refs_list_for_fetch -d "$1" \
$(git-config --get-all "remote.$1.fetch") ;;
@@ -177,7 +187,7 @@ get_remote_default_refs_for_fetch () {
}' "$GIT_DIR/remotes/$1")
;;
*)
die "internal error: get-remote-default-ref-for-push $1" ;;
die "internal error: get-remote-default-ref-for-fetch $1" ;;
esac
}

View File

@@ -65,8 +65,8 @@ Options:
Defaults to on.
--no-signed-off-cc Suppress the automatic addition of email addresses
that appear in a Signed-off-by: line, to the cc: list.
Note: Using this option is not recommended.
that appear in Signed-off-by: or Cc: lines to the cc:
list. Note: Using this option is not recommended.
--smtp-server If set, specifies the outgoing SMTP server to use.
Defaults to localhost.
@@ -572,8 +572,8 @@ foreach my $t (@files) {
}
} else {
$message .= $_;
if (/^Signed-off-by: (.*)$/i && !$no_signed_off_cc) {
my $c = $1;
if (/^(Signed-off-by|Cc): (.*)$/i && !$no_signed_off_cc) {
my $c = $2;
chomp $c;
push @cc, $c;
printf("(sob) Adding cc: %s from line '%s'\n",

1
git.c
View File

@@ -253,6 +253,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "format-patch", cmd_format_patch, RUN_SETUP },
{ "fsck", cmd_fsck, RUN_SETUP },
{ "fsck-objects", cmd_fsck, RUN_SETUP },
{ "gc", cmd_gc, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id },
{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
{ "help", cmd_help },

View File

@@ -64,9 +64,9 @@ static int copy_file(const char *source, char *dest, const char *hex,
}
/* If we got ENOENT there is no point continuing. */
if (errno == ENOENT) {
if (warn_if_not_exists)
fprintf(stderr, "does not exist %s\n", source);
return -1;
if (!warn_if_not_exists)
return -1;
return error("does not exist %s", source);
}
}
if (use_symlink) {
@@ -74,9 +74,8 @@ static int copy_file(const char *source, char *dest, const char *hex,
if (stat(source, &st)) {
if (!warn_if_not_exists && errno == ENOENT)
return -1;
fprintf(stderr, "cannot stat %s: %s\n", source,
strerror(errno));
return -1;
return error("cannot stat %s: %s", source,
strerror(errno));
}
if (!symlink(source, dest)) {
pull_say("symlink %s\n", hex);
@@ -90,25 +89,21 @@ static int copy_file(const char *source, char *dest, const char *hex,
if (ifd < 0) {
if (!warn_if_not_exists && errno == ENOENT)
return -1;
fprintf(stderr, "cannot open %s\n", source);
return -1;
return error("cannot open %s", source);
}
ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (ofd < 0) {
fprintf(stderr, "cannot open %s\n", dest);
close(ifd);
return -1;
return error("cannot open %s", dest);
}
status = copy_fd(ifd, ofd);
close(ofd);
if (status)
fprintf(stderr, "cannot write %s\n", dest);
else
pull_say("copy %s\n", hex);
return status;
return error("cannot write %s", dest);
pull_say("copy %s\n", hex);
return 0;
}
fprintf(stderr, "failed to copy %s with given copy methods.\n", hex);
return -1;
return error("failed to copy %s with given copy methods.", hex);
}
static int fetch_pack(const unsigned char *sha1)
@@ -181,13 +176,11 @@ int fetch_ref(char *ref, unsigned char *sha1)
ifd = open(filename, O_RDONLY);
if (ifd < 0) {
close(ifd);
fprintf(stderr, "cannot open %s\n", filename);
return -1;
return error("cannot open %s", filename);
}
if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
close(ifd);
fprintf(stderr, "cannot read from %s\n", filename);
return -1;
return error("cannot read from %s", filename);
}
close(ifd);
pull_say("ref %s\n", sha1_to_hex(sha1));

View File

@@ -1,20 +1,17 @@
#include "cache.h"
#include "spawn-pipe.h"
#include "run-command.h"
static const char *pgm;
static const char *arguments[9]; /* last one is always NULL */
static const char *arguments[9];
static int one_shot, quiet;
static int err;
static void run_program(void)
{
pid_t pid = spawnvpe_pipe(pgm, arguments, environ, NULL, NULL);
int status;
if (pid < 0)
die("unable to fork");
if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status)) {
struct child_process child;
memset(&child, 0, sizeof(child));
child.argv = arguments;
if (run_command(&child)) {
if (one_shot) {
err++;
} else {
@@ -31,6 +28,7 @@ static int merge_entry(int pos, const char *path)
if (pos >= active_nr)
die("git-merge-index: %s not in the cache", path);
arguments[0] = pgm;
arguments[1] = "";
arguments[2] = "";
arguments[3] = "";
@@ -38,6 +36,7 @@ static int merge_entry(int pos, const char *path)
arguments[5] = "";
arguments[6] = "";
arguments[7] = "";
arguments[8] = NULL;
found = 0;
do {
static char hexbuf[4][60];

View File

@@ -5,7 +5,7 @@ static int verify_packfile(struct packed_git *p,
struct pack_window **w_curs)
{
off_t index_size = p->index_size;
void *index_base = p->index_base;
const unsigned char *index_base = p->index_data;
SHA_CTX ctx;
unsigned char sha1[20];
off_t offset = 0, pack_sig = p->pack_size - 20;
@@ -31,7 +31,7 @@ static int verify_packfile(struct packed_git *p,
if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL)))
return error("Packfile %s SHA1 mismatch with itself",
p->pack_name);
if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40))
if (hashcmp(sha1, index_base + index_size - 40))
return error("Packfile %s SHA1 mismatch with idx",
p->pack_name);
unuse_pack(w_curs);
@@ -127,7 +127,7 @@ static void show_pack_info(struct packed_git *p)
int verify_pack(struct packed_git *p, int verbose)
{
off_t index_size = p->index_size;
void *index_base = p->index_base;
const unsigned char *index_base = p->index_data;
SHA_CTX ctx;
unsigned char sha1[20];
int ret;
@@ -137,7 +137,7 @@ int verify_pack(struct packed_git *p, int verbose)
SHA1_Init(&ctx);
SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20));
SHA1_Final(sha1, &ctx);
if (hashcmp(sha1, (unsigned char *)index_base + index_size - 20))
if (hashcmp(sha1, index_base + index_size - 20))
ret = error("Packfile index for %s SHA1 mismatch",
p->pack_name);

View File

@@ -17,7 +17,7 @@ static int load_all_packs, verbose, alt_odb;
struct llist_item {
struct llist_item *next;
unsigned char *sha1;
const unsigned char *sha1;
};
static struct llist {
struct llist_item *front;
@@ -104,9 +104,9 @@ static struct llist * llist_copy(struct llist *list)
return ret;
}
static inline struct llist_item * llist_insert(struct llist *list,
struct llist_item *after,
unsigned char *sha1)
static inline struct llist_item *llist_insert(struct llist *list,
struct llist_item *after,
const unsigned char *sha1)
{
struct llist_item *new = llist_item_get();
new->sha1 = sha1;
@@ -128,12 +128,14 @@ static inline struct llist_item * llist_insert(struct llist *list,
return new;
}
static inline struct llist_item *llist_insert_back(struct llist *list, unsigned char *sha1)
static inline struct llist_item *llist_insert_back(struct llist *list,
const unsigned char *sha1)
{
return llist_insert(list, list->back, sha1);
}
static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint)
static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
const unsigned char *sha1, struct llist_item *hint)
{
struct llist_item *prev = NULL, *l;
@@ -246,12 +248,12 @@ static struct pack_list * pack_list_difference(const struct pack_list *A,
static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
{
int p1_off, p2_off;
unsigned char *p1_base, *p2_base;
const unsigned char *p1_base, *p2_base;
struct llist_item *p1_hint = NULL, *p2_hint = NULL;
p1_off = p2_off = 256 * 4 + 4;
p1_base = (unsigned char *) p1->pack->index_base;
p2_base = (unsigned char *) p2->pack->index_base;
p1_base = p1->pack->index_data;
p2_base = p2->pack->index_data;
while (p1_off <= p1->pack->index_size - 3 * 20 &&
p2_off <= p2->pack->index_size - 3 * 20)
@@ -351,11 +353,11 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
{
size_t ret = 0;
int p1_off, p2_off;
unsigned char *p1_base, *p2_base;
const unsigned char *p1_base, *p2_base;
p1_off = p2_off = 256 * 4 + 4;
p1_base = (unsigned char *)p1->index_base;
p2_base = (unsigned char *)p2->index_base;
p1_base = p1->index_data;
p2_base = p2->index_data;
while (p1_off <= p1->index_size - 3 * 20 &&
p2_off <= p2->index_size - 3 * 20)
@@ -534,7 +536,7 @@ static struct pack_list * add_pack(struct packed_git *p)
{
struct pack_list l;
size_t off;
unsigned char *base;
const unsigned char *base;
if (!p->pack_local && !(alt_odb || verbose))
return NULL;
@@ -543,7 +545,7 @@ static struct pack_list * add_pack(struct packed_git *p)
llist_init(&l.all_objects);
off = 256 * 4 + 4;
base = (unsigned char *)p->index_base;
base = p->index_data;
while (off <= p->index_size - 3 * 20) {
llist_insert_back(l.all_objects, base + off);
off += 24;

30
pack.h
View File

@@ -16,24 +16,15 @@ struct pack_header {
};
/*
* Packed object index header
*
* struct pack_idx_header {
* uint32_t idx_signature;
* uint32_t idx_version;
* };
*
* Note: this header isn't active yet. In future versions of git
* we may change the index file format. At that time we would start
* the first four bytes of the new index format with this signature,
* as all older git binaries would find this value illegal and abort
* reading the file.
* The first four bytes of index formats later than version 1 should
* start with this signature, as all older git binaries would find this
* value illegal and abort reading the file.
*
* This is the case because the number of objects in a packfile
* cannot exceed 1,431,660,000 as every object would need at least
* 3 bytes of data and the overall packfile cannot exceed 4 GiB due
* to the 32 bit offsets used by the index. Clearly the signature
* exceeds this maximum.
* 3 bytes of data and the overall packfile cannot exceed 4 GiB with
* version 1 of the index file due to the offsets limited to 32 bits.
* Clearly the signature exceeds this maximum.
*
* Very old git binaries will also compare the first 4 bytes to the
* next 4 bytes in the index and abort with a "non-monotonic index"
@@ -43,6 +34,15 @@ struct pack_header {
*/
#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */
/*
* Packed object index header
*/
struct pack_idx_header {
uint32_t idx_signature;
uint32_t idx_version;
};
extern int verify_pack(struct packed_git *, int);
#define PH_ERROR_EOF (-1)

View File

@@ -382,10 +382,10 @@ static const char *unpack(void)
}
} else {
const char *keeper[6];
int fd[2], s, len, status;
pid_t pid;
int s, len, status;
char keep_arg[256];
char packname[46];
struct child_process ip;
s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid());
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
@@ -397,11 +397,11 @@ static const char *unpack(void)
keeper[3] = hdr_arg;
keeper[4] = keep_arg;
keeper[5] = NULL;
if (pipe(fd) < 0)
return "index-pack pipe failed";
pid = spawnv_git_cmd(keeper, NULL, fd);
if (pid < 0)
memset(&ip, 0, sizeof(ip));
ip.argv = keeper;
ip.out = -1;
ip.git_cmd = 1;
if (start_command(&ip))
return "index-pack fork failed";
/*
@@ -412,9 +412,8 @@ static const char *unpack(void)
* later on. If we don't get that then tough luck with it.
*/
for (len = 0;
len < 46 && (s = xread(fd[0], packname+len, 46-len)) > 0;
len < 46 && (s = xread(ip.out, packname+len, 46-len)) > 0;
len += s);
close(fd[0]);
if (len == 46 && packname[45] == '\n' &&
memcmp(packname, "keep\t", 5) == 0) {
char path[PATH_MAX];
@@ -424,14 +423,8 @@ static const char *unpack(void)
pack_lockfile = xstrdup(path);
}
/* Then wrap our index-pack process. */
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR)
return "waitpid failed";
if (WIFEXITED(status)) {
int code = WEXITSTATUS(status);
if (code)
return "index-pack exited with error code";
status = finish_command(&ip);
if (!status) {
reprepare_packed_git();
return NULL;
}
@@ -485,7 +478,7 @@ int main(int argc, char **argv)
if (!dir)
usage(receive_pack_usage);
if(!enter_repo(dir, 0))
if (!enter_repo(dir, 0))
die("'%s': unable to chdir or not a git archive", dir);
if (is_repository_shallow())

View File

@@ -213,6 +213,13 @@ static int everybody_uninteresting(struct commit_list *orig)
return 1;
}
/*
* The goal is to get REV_TREE_NEW as the result only if the
* diff consists of all '+' (and no other changes), and
* REV_TREE_DIFFERENT otherwise (of course if the trees are
* the same we want REV_TREE_SAME). That means that once we
* get to REV_TREE_DIFFERENT, we do not have to look any further.
*/
static int tree_difference = REV_TREE_SAME;
static void file_add_remove(struct diff_options *options,
@@ -236,6 +243,8 @@ static void file_add_remove(struct diff_options *options,
diff = REV_TREE_NEW;
}
tree_difference = diff;
if (tree_difference == REV_TREE_DIFFERENT)
options->has_changes = 1;
}
static void file_change(struct diff_options *options,
@@ -245,6 +254,7 @@ static void file_change(struct diff_options *options,
const char *base, const char *path)
{
tree_difference = REV_TREE_DIFFERENT;
options->has_changes = 1;
}
int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
@@ -254,6 +264,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
if (!t2)
return REV_TREE_DIFFERENT;
tree_difference = REV_TREE_SAME;
revs->pruning.has_changes = 0;
if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
&revs->pruning) < 0)
return REV_TREE_DIFFERENT;
@@ -277,11 +288,12 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
empty.buf = "";
empty.size = 0;
tree_difference = 0;
tree_difference = REV_TREE_SAME;
revs->pruning.has_changes = 0;
retval = diff_tree(&empty, &real, "", &revs->pruning);
free(tree);
return retval >= 0 && !tree_difference;
return retval >= 0 && (tree_difference == REV_TREE_SAME);
}
static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
@@ -545,6 +557,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->ignore_merges = 1;
revs->simplify_history = 1;
revs->pruning.recursive = 1;
revs->pruning.quiet = 1;
revs->pruning.add_remove = file_add_remove;
revs->pruning.change = file_change;
revs->lifo = 1;

View File

@@ -11,10 +11,11 @@ static inline void close_pair(int fd[2])
int start_command(struct child_process *cmd)
{
int need_in = !cmd->no_stdin && cmd->in < 0;
int need_in, need_out;
int fdin[2] = { -1, -1 };
int fd_o[2] = { -1, -1 };
int fdout[2] = { -1, -1 };
need_in = !cmd->no_stdin && cmd->in < 0;
if (need_in) {
if (pipe(fdin) < 0)
return -ERR_RUN_COMMAND_PIPE;
@@ -22,27 +23,49 @@ int start_command(struct child_process *cmd)
cmd->close_in = 1;
}
need_out = !cmd->no_stdout
&& !cmd->stdout_to_stderr
&& cmd->out < 0;
if (need_out) {
if (pipe(fdout) < 0) {
if (need_in)
close_pair(fdin);
return -ERR_RUN_COMMAND_PIPE;
}
cmd->out = fdout[0];
cmd->close_out = 1;
}
{
if (cmd->no_stdin) {
if (cmd->no_stdin)
fdin[0] = open("/dev/null", O_RDWR);
} else if (need_in) {
else if (need_in) {
/* nothing */
} else if (cmd->in) {
fdin[0] = cmd->in;
}
if (cmd->stdout_to_stderr)
fd_o[1] = dup(2);
if (cmd->no_stdout)
fdout[1] = open("/dev/null", O_RDWR);
else if (cmd->stdout_to_stderr)
fdout[1] = dup(2);
else if (need_out) {
/* nothing */
} else if (cmd->out > 1) {
fdout[1] = cmd->out;
}
if (cmd->git_cmd) {
cmd->pid = spawnv_git_cmd(cmd->argv, fdin, fd_o);
cmd->pid = spawnv_git_cmd(cmd->argv, fdin, fdout);
} else {
cmd->pid = spawnvpe_pipe(cmd->argv[0], cmd->argv, environ, fdin, fd_o);
cmd->pid = spawnvpe_pipe(cmd->argv[0], cmd->argv, environ, fdin, fdout);
}
}
if (cmd->pid < 0) {
if (need_in) {
if (need_in)
close_pair(fdin);
}
if (need_out)
close_pair(fdout);
return -ERR_RUN_COMMAND_FORK;
}
@@ -53,6 +76,8 @@ int finish_command(struct child_process *cmd)
{
if (cmd->close_in)
close(cmd->in);
if (cmd->close_out)
close(cmd->out);
for (;;) {
int status, code;

View File

@@ -15,8 +15,11 @@ struct child_process {
const char **argv;
pid_t pid;
int in;
int out;
unsigned close_in:1;
unsigned close_out:1;
unsigned no_stdin:1;
unsigned no_stdout:1;
unsigned git_cmd:1; /* if this is to be git sub-command */
unsigned stdout_to_stderr:1;
};

View File

@@ -3,7 +3,7 @@
#include "tag.h"
#include "refs.h"
#include "pkt-line.h"
#include "exec_cmd.h"
#include "run-command.h"
static const char send_pack_usage[] =
"git-send-pack [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@ -19,16 +19,12 @@ static int use_thin_pack;
*/
static int pack_objects(int fd, struct ref *refs)
{
int pipe_fd[2];
int pack_fd[2] = { -1, fd };
pid_t pid;
/*
* The child becomes pack-objects --revs; we feed
* the revision parameters to it via its stdin and
* let its stdout go back to the other end.
*/
static const char *args[] = {
const char *args[] = {
"pack-objects",
"--all-progress",
"--revs",
@@ -36,20 +32,22 @@ static int pack_objects(int fd, struct ref *refs)
NULL,
NULL,
};
struct child_process po;
if (use_thin_pack)
args[4] = "--thin";
if (pipe(pipe_fd) < 0)
return error("send-pack: pipe failed");
pid = spawnv_git_cmd(args, pipe_fd, pack_fd);
if (pid < 0)
return error("send-pack: unable to fork git-pack-objects");
memset(&po, 0, sizeof(po));
po.argv = args;
po.in = -1;
po.out = fd;
po.git_cmd = 1;
if (start_command(&po))
die("git-pack-objects failed (%s)", strerror(errno));
/*
* We feed the pack-objects we just spawned with revision
* parameters by writing to the pipe.
*/
while (refs) {
char buf[42];
@@ -58,38 +56,23 @@ static int pack_objects(int fd, struct ref *refs)
memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
buf[0] = '^';
buf[41] = '\n';
if (!write_or_whine(pipe_fd[1], buf, 42,
if (!write_or_whine(po.in, buf, 42,
"send-pack: send refs"))
break;
}
if (!is_null_sha1(refs->new_sha1)) {
memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
buf[40] = '\n';
if (!write_or_whine(pipe_fd[1], buf, 41,
if (!write_or_whine(po.in, buf, 41,
"send-pack: send refs"))
break;
}
refs = refs->next;
}
close(pipe_fd[1]);
for (;;) {
int status, code;
pid_t waiting = waitpid(pid, &status, 0);
if (waiting < 0) {
if (errno == EINTR)
continue;
return error("waitpid failed (%s)", strerror(errno));
}
if ((waiting != pid) || WIFSIGNALED(status) ||
!WIFEXITED(status))
return error("pack-objects died with strange error");
code = WEXITSTATUS(status);
if (code)
return -code;
return 0;
}
if (finish_command(&po))
return error("pack-objects died with strange error");
return 0;
}
static void unmark_and_free(struct commit_list *list, unsigned int mark)

View File

@@ -440,16 +440,15 @@ void pack_report()
pack_mapped, peak_pack_mapped);
}
static int check_packed_git_idx(const char *path,
unsigned long *idx_size_,
void **idx_map_)
static int check_packed_git_idx(const char *path, struct packed_git *p)
{
void *idx_map;
uint32_t *index;
struct pack_idx_header *hdr;
size_t idx_size;
uint32_t nr, i;
uint32_t nr, i, *index;
int fd = open(path, O_RDONLY);
struct stat st;
if (fd < 0)
return -1;
if (fstat(fd, &st)) {
@@ -464,15 +463,12 @@ static int check_packed_git_idx(const char *path,
idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
index = idx_map;
*idx_map_ = idx_map;
*idx_size_ = idx_size;
/* a future index format would start with this, as older git
* binaries would fail the non-monotonic index check below.
* give a nicer warning to the user if we can.
*/
if (index[0] == htonl(PACK_IDX_SIGNATURE)) {
hdr = idx_map;
if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
munmap(idx_map, idx_size);
return error("index file %s is a newer version"
" and is not supported by this binary"
@@ -481,6 +477,7 @@ static int check_packed_git_idx(const char *path,
}
nr = 0;
index = idx_map;
for (i = 0; i < 256; i++) {
uint32_t n = ntohl(index[i]);
if (n < nr) {
@@ -502,6 +499,9 @@ static int check_packed_git_idx(const char *path,
return error("wrong index file size in %s", path);
}
p->index_version = 1;
p->index_data = idx_map;
p->index_size = idx_size;
return 0;
}
@@ -624,7 +624,7 @@ static int open_packed_git_1(struct packed_git *p)
return error("end of packfile %s is unavailable", p->pack_name);
if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
return error("packfile %s signature is unavailable", p->pack_name);
idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40;
idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
if (hashcmp(sha1, idx_sha1))
return error("packfile %s does not match index", p->pack_name);
return 0;
@@ -720,38 +720,37 @@ unsigned char* use_pack(struct packed_git *p,
return win->base + offset;
}
struct packed_git *add_packed_git(char *path, int path_len, int local)
struct packed_git *add_packed_git(const char *path, int path_len, int local)
{
struct stat st;
struct packed_git *p;
unsigned long idx_size;
void *idx_map;
unsigned char sha1[20];
struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2);
if (check_packed_git_idx(path, &idx_size, &idx_map))
/*
* Make sure a corresponding .pack file exists and that
* the index looks sane.
*/
path_len -= strlen(".idx");
if (path_len < 1)
return NULL;
/* do we have a corresponding .pack file? */
strcpy(path + path_len - 4, ".pack");
if (stat(path, &st) || !S_ISREG(st.st_mode)) {
munmap(idx_map, idx_size);
memcpy(p->pack_name, path, path_len);
strcpy(p->pack_name + path_len, ".pack");
if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) ||
check_packed_git_idx(path, p)) {
free(p);
return NULL;
}
/* ok, it looks sane as far as we can check without
* actually mapping the pack file.
*/
p = xmalloc(sizeof(*p) + path_len + 2);
strcpy(p->pack_name, path);
p->index_size = idx_size;
p->pack_size = st.st_size;
p->index_base = idx_map;
p->next = NULL;
p->windows = NULL;
p->pack_fd = -1;
p->pack_local = local;
p->mtime = st.st_mtime;
if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
hashcpy(p->sha1, sha1);
if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
hashclr(p->sha1);
return p;
}
@@ -761,23 +760,19 @@ struct packed_git *parse_pack_index(unsigned char *sha1)
return parse_pack_index_file(sha1, path);
}
struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_path)
struct packed_git *parse_pack_index_file(const unsigned char *sha1,
const char *idx_path)
{
struct packed_git *p;
unsigned long idx_size;
void *idx_map;
char *path;
const char *path = sha1_pack_name(sha1);
struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2);
if (check_packed_git_idx(idx_path, &idx_size, &idx_map))
if (check_packed_git_idx(idx_path, p)) {
free(p);
return NULL;
}
path = sha1_pack_name(sha1);
p = xmalloc(sizeof(*p) + strlen(path) + 2);
strcpy(p->pack_name, path);
p->index_size = idx_size;
p->pack_size = 0;
p->index_base = idx_map;
p->next = NULL;
p->windows = NULL;
p->pack_fd = -1;
@@ -1367,6 +1362,87 @@ static void *unpack_compressed_entry(struct packed_git *p,
return buffer;
}
#define MAX_DELTA_CACHE (256)
static size_t delta_base_cached;
static struct delta_base_cache_entry {
struct packed_git *p;
off_t base_offset;
unsigned long size;
void *data;
enum object_type type;
} delta_base_cache[MAX_DELTA_CACHE];
static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
{
unsigned long hash;
hash = (unsigned long)p + (unsigned long)base_offset;
hash += (hash >> 8) + (hash >> 16);
return hash & 0xff;
}
static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
unsigned long *base_size, enum object_type *type, int keep_cache)
{
void *ret;
unsigned long hash = pack_entry_hash(p, base_offset);
struct delta_base_cache_entry *ent = delta_base_cache + hash;
ret = ent->data;
if (ret && ent->p == p && ent->base_offset == base_offset)
goto found_cache_entry;
return unpack_entry(p, base_offset, type, base_size);
found_cache_entry:
if (!keep_cache) {
ent->data = NULL;
delta_base_cached -= ent->size;
}
else {
ret = xmalloc(ent->size + 1);
memcpy(ret, ent->data, ent->size);
((char *)ret)[ent->size] = 0;
}
*type = ent->type;
*base_size = ent->size;
return ret;
}
static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
{
if (ent->data) {
free(ent->data);
ent->data = NULL;
delta_base_cached -= ent->size;
}
}
static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
void *base, unsigned long base_size, enum object_type type)
{
unsigned long i, hash = pack_entry_hash(p, base_offset);
struct delta_base_cache_entry *ent = delta_base_cache + hash;
release_delta_base_cache(ent);
delta_base_cached += base_size;
for (i = 0; delta_base_cached > delta_base_cache_limit
&& i < ARRAY_SIZE(delta_base_cache); i++) {
struct delta_base_cache_entry *f = delta_base_cache + i;
if (f->type == OBJ_BLOB)
release_delta_base_cache(f);
}
for (i = 0; delta_base_cached > delta_base_cache_limit
&& i < ARRAY_SIZE(delta_base_cache); i++)
release_delta_base_cache(delta_base_cache + i);
ent->p = p;
ent->base_offset = base_offset;
ent->type = type;
ent->data = base;
ent->size = base_size;
}
static void *unpack_delta_entry(struct packed_git *p,
struct pack_window **w_curs,
off_t curpos,
@@ -1380,7 +1456,7 @@ static void *unpack_delta_entry(struct packed_git *p,
off_t base_offset;
base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
base = unpack_entry(p, base_offset, type, &base_size);
base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0);
if (!base)
die("failed to read delta base object"
" at %"PRIuMAX" from %s",
@@ -1393,7 +1469,7 @@ static void *unpack_delta_entry(struct packed_git *p,
if (!result)
die("failed to apply delta");
free(delta_data);
free(base);
add_delta_base_cache(p, base_offset, base, base_size, *type);
return result;
}
@@ -1433,24 +1509,27 @@ uint32_t num_packed_objects(const struct packed_git *p)
int nth_packed_object_sha1(const struct packed_git *p, uint32_t n,
unsigned char* sha1)
{
void *index = p->index_base + 256;
const unsigned char *index = p->index_data;
index += 4 * 256;
if (num_packed_objects(p) <= n)
return -1;
hashcpy(sha1, (unsigned char *) index + (24 * n) + 4);
hashcpy(sha1, index + 24 * n + 4);
return 0;
}
off_t find_pack_entry_one(const unsigned char *sha1,
struct packed_git *p)
{
uint32_t *level1_ofs = p->index_base;
const uint32_t *level1_ofs = p->index_data;
int hi = ntohl(level1_ofs[*sha1]);
int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
void *index = p->index_base + 256;
const unsigned char *index = p->index_data;
index += 4 * 256;
do {
int mi = (lo + hi) / 2;
int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
int cmp = hashcmp(index + 24 * mi + 4, sha1);
if (!cmp)
return ntohl(*((uint32_t *)((char *)index + (24 * mi))));
if (cmp > 0)
@@ -1574,7 +1653,7 @@ static void *read_packed_sha1(const unsigned char *sha1,
if (!find_pack_entry(sha1, &e, NULL))
return NULL;
else
return unpack_entry(e.p, e.offset, type, size);
return cache_or_unpack_entry(e.p, e.offset, size, type, 1);
}
/*

View File

@@ -147,9 +147,15 @@ test_expect_success 'test overriding tracking setup via --no-track' \
git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
(git-show-ref -q refs/remotes/local/master || git-fetch local) &&
git-branch --no-track my2 local/master &&
git-config branch.autosetupmerge false &&
! test $(git-config branch.my2.remote) = local &&
! test $(git-config branch.my2.merge) = refs/heads/master'
test_expect_success 'test local tracking setup' \
'git branch --track my6 s &&
test $(git-config branch.my6.remote) = . &&
test $(git-config branch.my6.merge) = refs/heads/s'
# Keep this test last, as it changes the current branch
cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master

79
t/t4017-diff-retval.sh Executable file
View File

@@ -0,0 +1,79 @@
#!/bin/sh
test_description='Return value of diffs'
. ./test-lib.sh
test_expect_success 'setup' '
echo 1 >a &&
git add . &&
git commit -m first &&
echo 2 >b &&
git add . &&
git commit -a -m second
'
test_expect_success 'git diff-tree HEAD^ HEAD' '
git diff-tree --exit-code HEAD^ HEAD
test $? = 1
'
test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
git diff-tree --exit-code HEAD^ HEAD -- a
test $? = 0
'
test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
git diff-tree --exit-code HEAD^ HEAD -- b
test $? = 1
'
test_expect_success 'echo HEAD | git diff-tree --stdin' '
echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
test $? = 1
'
test_expect_success 'git diff-tree HEAD HEAD' '
git diff-tree --exit-code HEAD HEAD
test $? = 0
'
test_expect_success 'git diff-files' '
git diff-files --exit-code
test $? = 0
'
test_expect_success 'git diff-index --cached HEAD' '
git diff-index --exit-code --cached HEAD
test $? = 0
'
test_expect_success 'git diff-index --cached HEAD^' '
git diff-index --exit-code --cached HEAD^
test $? = 1
'
test_expect_success 'git diff-index --cached HEAD^' '
echo text >>b &&
echo 3 >c &&
git add . && {
git diff-index --exit-code --cached HEAD^
test $? = 1
}
'
test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
git commit -m "text in b" && {
git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
test $? = 1
}
'
test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b
test $? = 0
'
test_expect_success 'git diff-files' '
echo 3 >>c && {
git diff-files --exit-code
test $? = 1
}
'
test_expect_success 'git diff-index --cached HEAD' '
git update-index c && {
git diff-index --exit-code --cached HEAD
test $? = 1
}
'
test_done

80
t/t4017-quiet.sh Executable file
View File

@@ -0,0 +1,80 @@
#!/bin/sh
test_description='Return value of diffs'
. ./test-lib.sh
test_expect_success 'setup' '
echo 1 >a &&
git add . &&
git commit -m first &&
echo 2 >b &&
git add . &&
git commit -a -m second
'
test_expect_success 'git diff-tree HEAD^ HEAD' '
git diff-tree --quiet HEAD^ HEAD >cnt
test $? = 1 && test $(wc -l <cnt) = 0
'
test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
git diff-tree --quiet HEAD^ HEAD -- a >cnt
test $? = 0 && test $(wc -l <cnt) = 0
'
test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
git diff-tree --quiet HEAD^ HEAD -- b >cnt
test $? = 1 && test $(wc -l <cnt) = 0
'
# this diff outputs one line: sha1 of the given head
test_expect_success 'echo HEAD | git diff-tree --stdin' '
echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
test $? = 1 && test $(wc -l <cnt) = 1
'
test_expect_success 'git diff-tree HEAD HEAD' '
git diff-tree --quiet HEAD HEAD >cnt
test $? = 0 && test $(wc -l <cnt) = 0
'
test_expect_success 'git diff-files' '
git diff-files --quiet >cnt
test $? = 0 && test $(wc -l <cnt) = 0
'
test_expect_success 'git diff-index --cached HEAD' '
git diff-index --quiet --cached HEAD >cnt
test $? = 0 && test $(wc -l <cnt) = 0
'
test_expect_success 'git diff-index --cached HEAD^' '
git diff-index --quiet --cached HEAD^ >cnt
test $? = 1 && test $(wc -l <cnt) = 0
'
test_expect_success 'git diff-index --cached HEAD^' '
echo text >>b &&
echo 3 >c &&
git add . && {
git diff-index --quiet --cached HEAD^ >cnt
test $? = 1 && test $(wc -l <cnt) = 0
}
'
test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
git commit -m "text in b" && {
git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
test $? = 1 && test $(wc -l <cnt) = 0
}
'
test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
test $? = 0 && test $(wc -l <cnt) = 0
'
test_expect_success 'git diff-files' '
echo 3 >>c && {
git diff-files --quiet >cnt
test $? = 1 && test $(wc -l <cnt) = 0
}
'
test_expect_success 'git diff-index --cached HEAD' '
git update-index c && {
git diff-index --quiet --cached HEAD >cnt
test $? = 1 && test $(wc -l <cnt) = 0
}
'
test_done

View File

@@ -80,7 +80,7 @@ test_expect_success \
cd "$TRASH"
test_expect_success \
'pack with delta' \
'pack with REF_DELTA' \
'pwd &&
packname_2=$(git-pack-objects test-2 <obj-list)'
@@ -88,7 +88,7 @@ rm -fr .git2
mkdir .git2
test_expect_success \
'unpack with delta' \
'unpack with REF_DELTA' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
git-init &&
@@ -98,7 +98,7 @@ test_expect_success \
unset GIT_OBJECT_DIRECTORY
cd "$TRASH/.git2"
test_expect_success \
'check unpack with delta' \
'check unpack with REF_DELTA' \
'(cd ../.git && find objects -type f -print) |
while read path
do
@@ -109,6 +109,42 @@ test_expect_success \
done'
cd "$TRASH"
test_expect_success \
'pack with OFS_DELTA' \
'pwd &&
packname_3=$(git-pack-objects --delta-base-offset test-3 <obj-list)'
rm -fr .git2
mkdir .git2
test_expect_success \
'unpack with OFS_DELTA' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
git-init &&
git-unpack-objects -n <test-3-${packname_3}.pack &&
git-unpack-objects <test-3-${packname_3}.pack'
unset GIT_OBJECT_DIRECTORY
cd "$TRASH/.git2"
test_expect_success \
'check unpack with OFS_DELTA' \
'(cd ../.git && find objects -type f -print) |
while read path
do
cmp $path ../.git/$path || {
echo $path differs.
return 1
}
done'
cd "$TRASH"
test_expect_success \
'compare delta flavors' \
'size_2=`stat -c "%s" test-2-${packname_2}.pack` &&
size_3=`stat -c "%s" test-3-${packname_3}.pack` &&
test $size_2 -gt $size_3'
rm -fr .git2
mkdir .git2
@@ -127,12 +163,11 @@ test_expect_success \
} >current &&
diff expect current'
test_expect_success \
'use packed deltified objects' \
'use packed deltified (REF_DELTA) objects' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
rm -f .git2/objects/pack/test-?.idx &&
rm .git2/objects/pack/test-* &&
cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
git-diff-tree --root -p $commit &&
while read object
@@ -143,11 +178,28 @@ test_expect_success \
} >current &&
diff expect current'
test_expect_success \
'use packed deltified (OFS_DELTA) objects' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
rm .git2/objects/pack/test-* &&
cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && {
git-diff-tree --root -p $commit &&
while read object
do
t=`git-cat-file -t $object` &&
git-cat-file $t $object || return 1
done <obj-list
} >current &&
diff expect current'
unset GIT_OBJECT_DIRECTORY
test_expect_success \
'verify pack' \
'git-verify-pack test-1-${packname_1}.idx test-2-${packname_2}.idx'
'git-verify-pack test-1-${packname_1}.idx \
test-2-${packname_2}.idx \
test-3-${packname_3}.idx'
test_expect_success \
'verify-pack catches mismatched .idx and .pack files' \
@@ -208,6 +260,13 @@ test_expect_success \
git-index-pack test-3.pack &&
cmp test-3.idx test-2-${packname_2}.idx &&
cp test-3-${packname_3}.pack test-3.pack &&
git-index-pack -o tmp.idx test-3-${packname_3}.pack &&
cmp tmp.idx test-3-${packname_3}.idx &&
git-index-pack test-3.pack &&
cmp test-3.idx test-3-${packname_3}.idx &&
:'
test_done

View File

@@ -29,5 +29,29 @@ test_expect_success 'checking the results' '
diff file cloned/file
'
test_expect_success 'test . as a remote' '
git branch copy master &&
git config branch.copy.remote . &&
git config branch.copy.merge refs/heads/master &&
echo updated >file &&
git commit -a -m updated &&
git checkout copy &&
test `cat file` = file &&
git pull &&
test `cat file` = updated
'
test_expect_success 'the default remote . should not break explicit pull' '
git checkout -b second master^ &&
echo modified >file &&
git commit -a -m modified &&
git checkout copy &&
git reset --hard HEAD^ &&
test `cat file` = file &&
git pull . second &&
test `cat file` = modified
'
test_done

View File

@@ -5,9 +5,8 @@
#include "diff.h"
#include "tree.h"
static char *malloc_base(const char *base, const char *path, int pathlen)
static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
{
int baselen = strlen(base);
char *newbase = xmalloc(baselen + pathlen + 2);
memcpy(newbase, base, baselen);
memcpy(newbase + baselen, path, pathlen);
@@ -16,9 +15,9 @@ static char *malloc_base(const char *base, const char *path, int pathlen)
}
static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
const char *base);
const char *base, int baselen);
static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt)
{
unsigned mode1, mode2;
const char *path1, *path2;
@@ -28,15 +27,15 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
sha1 = tree_entry_extract(t1, &path1, &mode1);
sha2 = tree_entry_extract(t2, &path2, &mode2);
pathlen1 = strlen(path1);
pathlen2 = strlen(path2);
pathlen1 = tree_entry_len(path1, sha1);
pathlen2 = tree_entry_len(path2, sha2);
cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
if (cmp < 0) {
show_entry(opt, "-", t1, base);
show_entry(opt, "-", t1, base, baselen);
return -1;
}
if (cmp > 0) {
show_entry(opt, "+", t2, base);
show_entry(opt, "+", t2, base, baselen);
return 1;
}
if (!opt->find_copies_harder && !hashcmp(sha1, sha2) && mode1 == mode2)
@@ -47,14 +46,14 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
* file, we need to consider it a remove and an add.
*/
if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
show_entry(opt, "-", t1, base);
show_entry(opt, "+", t2, base);
show_entry(opt, "-", t1, base, baselen);
show_entry(opt, "+", t2, base, baselen);
return 0;
}
if (opt->recursive && S_ISDIR(mode1)) {
int retval;
char *newbase = malloc_base(base, path1, pathlen1);
char *newbase = malloc_base(base, baselen, path1, pathlen1);
if (opt->tree_in_recursive)
opt->change(opt, mode1, mode2,
sha1, sha2, base, path1);
@@ -67,20 +66,28 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
return 0;
}
static int interesting(struct tree_desc *desc, const char *base, struct diff_options *opt)
/*
* Is a tree entry interesting given the pathspec we have?
*
* Return:
* - positive for yes
* - zero for no
* - negative for "no, and no subsequent entries will be either"
*/
static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt)
{
const char *path;
const unsigned char *sha1;
unsigned mode;
int i;
int baselen, pathlen;
int pathlen;
if (!opt->nr_paths)
return 1;
(void)tree_entry_extract(desc, &path, &mode);
sha1 = tree_entry_extract(desc, &path, &mode);
pathlen = strlen(path);
baselen = strlen(base);
pathlen = tree_entry_len(path, sha1);
for (i=0; i < opt->nr_paths; i++) {
const char *match = opt->paths[i];
@@ -121,18 +128,21 @@ static int interesting(struct tree_desc *desc, const char *base, struct diff_opt
}
/* A whole sub-tree went away or appeared */
static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)
static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
{
while (desc->size) {
if (interesting(desc, base, opt))
show_entry(opt, prefix, desc, base);
int show = tree_entry_interesting(desc, base, baselen, opt);
if (show < 0)
break;
if (show)
show_entry(opt, prefix, desc, base, baselen);
update_tree_entry(desc);
}
}
/* A file entry went away or appeared */
static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
const char *base)
const char *base, int baselen)
{
unsigned mode;
const char *path;
@@ -140,7 +150,8 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
if (opt->recursive && S_ISDIR(mode)) {
enum object_type type;
char *newbase = malloc_base(base, path, strlen(path));
int pathlen = tree_entry_len(path, sha1);
char *newbase = malloc_base(base, baselen, path, pathlen);
struct tree_desc inner;
void *tree;
@@ -149,7 +160,7 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
die("corrupt tree sha %s", sha1_to_hex(sha1));
inner.buf = tree;
show_tree(opt, prefix, &inner, newbase);
show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
free(tree);
free(newbase);
@@ -158,28 +169,45 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
}
}
static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
{
while (t->size) {
int show = tree_entry_interesting(t, base, baselen, opt);
if (!show) {
update_tree_entry(t);
continue;
}
/* Skip it all? */
if (show < 0)
t->size = 0;
return;
}
}
int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
{
while (t1->size | t2->size) {
if (opt->nr_paths && t1->size && !interesting(t1, base, opt)) {
update_tree_entry(t1);
continue;
}
if (opt->nr_paths && t2->size && !interesting(t2, base, opt)) {
update_tree_entry(t2);
continue;
int baselen = strlen(base);
for (;;) {
if (opt->quiet && opt->has_changes)
break;
if (opt->nr_paths) {
skip_uninteresting(t1, base, baselen, opt);
skip_uninteresting(t2, base, baselen, opt);
}
if (!t1->size) {
show_entry(opt, "+", t2, base);
if (!t2->size)
break;
show_entry(opt, "+", t2, base, baselen);
update_tree_entry(t2);
continue;
}
if (!t2->size) {
show_entry(opt, "-", t1, base);
show_entry(opt, "-", t1, base, baselen);
update_tree_entry(t1);
continue;
}
switch (compare_tree_entry(t1, t2, base, opt)) {
switch (compare_tree_entry(t1, t2, base, baselen, opt)) {
case -1:
update_tree_entry(t1);
continue;

View File

@@ -32,7 +32,7 @@ static void entry_clear(struct name_entry *a)
static void entry_extract(struct tree_desc *t, struct name_entry *a)
{
a->sha1 = tree_entry_extract(t, &a->path, &a->mode);
a->pathlen = strlen(a->path);
a->pathlen = tree_entry_len(a->path, a->sha1);
}
void update_tree_entry(struct tree_desc *desc)
@@ -169,7 +169,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
sha1 = tree_entry_extract(t, &entry, mode);
update_tree_entry(t);
entrylen = strlen(entry);
entrylen = tree_entry_len(entry, sha1);
if (entrylen > namelen)
continue;
cmp = memcmp(name, entry, entrylen);

View File

@@ -13,6 +13,11 @@ struct name_entry {
int pathlen;
};
static inline int tree_entry_len(const char *name, const unsigned char *sha1)
{
return (char *)sha1 - (char *)name - 1;
}
void update_tree_entry(struct tree_desc *);
const unsigned char *tree_entry_extract(struct tree_desc *, const char **, unsigned int *);

4
tree.c
View File

@@ -153,10 +153,8 @@ static void track_tree_refs(struct tree *item)
/* Count how many entries there are.. */
desc.buf = item->buffer;
desc.size = item->size;
while (desc.size) {
while (tree_entry(&desc, &entry))
n_refs++;
update_tree_entry(&desc);
}
/* Allocate object refs and walk it again.. */
i = 0;