environment variable 'GIT_DIFF_OPTS'. For example, if you
prefer context diff:
- GIT_DIFF_OPTS=-c git-diff-index -p $(cat .git/HEAD)
+ GIT_DIFF_OPTS=-c git-diff-index -p HEAD
2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the
--name-status::
Show only names and status of changed files.
+--full-index::
+ Instead of the first handful characters, show full
+ object name of pre- and post-image blob on the "index"
+ line when generating a patch format output.
+
-B::
Break complete rewrite changes into pairs of delete and create.
SYNOPSIS
--------
-'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] [<patch>...]
+'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [<patch>...]
DESCRIPTION
-----------
the result with this option, which would apply the
deletion part but not addition part.
+--allow-binary-replacement::
+ When applying a patch, which is a git-enhanced patch
+ that was prepared to record the pre- and post-image object
+ name in full, and the path being patched exactly matches
+ the object the patch applies to (i.e. "index" line's
+ pre-image object name is what is in the working tree),
+ and the post-image object is available in the object
+ database, use the post-image object as the patch
+ result. This allows binary files to be patched in a
+ very limited way.
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
Normally a commit would identify a new "HEAD" state, and while git
doesn't care where you save the note about that state, in practice we
-tend to just write the result to the file `.git/HEAD`, so that we can
-always see what the last committed state was.
+tend to just write the result to the file that is pointed at by
+`.git/HEAD`, so that we can always see what the last committed
+state was.
OPTIONS
-------
do not have the 'git-daemon-export-ok' file.
--inetd::
- Have the server run as an inetd service.
+ Have the server run as an inetd service. Implies --syslog.
--port::
Listen on an alternative port.
*what* you are going to commit is without having to write a new tree
object and compare it that way, and to do that, you just do
- git-diff-index --cached $(cat .git/HEAD)
+ git-diff-index --cached HEAD
Example: let's say I had renamed `commit.c` to `git-commit.c`, and I had
done an "git-update-index" to make that effective in the index file.
"git-diff-files" wouldn't show anything at all, since the index file
matches my working directory. But doing a "git-diff-index" does:
- torvalds@ppc970:~/git> git-diff-index --cached $(cat .git/HEAD)
+ torvalds@ppc970:~/git> git-diff-index --cached HEAD
-100644 blob 4161aecc6700a2eb579e842af0b7f22b98443f74 commit.c
+100644 blob 4161aecc6700a2eb579e842af0b7f22b98443f74 git-commit.c
have not actually done a "git-update-index" on it yet - there is no
"object" associated with the new state, and you get:
- torvalds@ppc970:~/v2.6/linux> git-diff-index $(cat .git/HEAD )
+ torvalds@ppc970:~/v2.6/linux> git-diff-index HEAD
*100644->100664 blob 7476bb......->000000...... kernel/sched.c
ie it shows that the tree has changed, and that `kernel/sched.c` has is
So for example
- git-fsck-objects --unreachable $(cat .git/HEAD .git/refs/heads/*)
+ git-fsck-objects --unreachable HEAD $(cat .git/refs/heads/*)
will do quite a _lot_ of verification on the tree. There are a few
extra validity tests to be added (make sure that tree objects are
OPTIONS
-------
---pretty=<format>:
+--pretty=<format>::
Controls the way the commit log is formatted.
--max-count=<n>::
changes. To illustrate, suppose you start from what has been
commited last to your repository:
- $ JC=`cat .git/HEAD`
+ $ JC=`git-rev-parse --verify "HEAD^0"`
$ git-checkout-index -f -u -a $JC
You do random edits, without running git-update-index. And then
we did `ln -sf refs/heads/newbranch .git/HEAD`, and when we want
to find out which branch we are on, we did `readlink .git/HEAD`.
This was fine, and internally that is what still happens by
-default, but on platforms that does not have working symlinks,
-or that does not have the `readlink(1)` command, this was a bit
+default, but on platforms that do not have working symlinks,
+or that do not have the `readlink(1)` command, this was a bit
cumbersome. On some platforms, `ln -sf` does not even work as
advertised (horrors).
SYNOPSIS
--------
-'git-<command>' <args>
+'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ARGS]
DESCRIPTION
-----------
-
-This is reference information for the core git commands.
-
+'git' is both a program and a directory content tracker system.
+The program 'git' is just a wrapper to reach the core git programs
+(or a potty if you like, as it's not exactly porcelain but still
+brings your stuff to the plumbing).
+
+OPTIONS
+-------
+--version::
+ prints the git suite version that the 'git' program came from.
+
+--help::
+ prints the synopsis and a list of available commands.
+ If a git command is named this option will bring up the
+ man-page for that command.
+
+--exec-path::
+ path to wherever your core git programs are installed.
+ This can also be controlled by setting the GIT_EXEC_PATH
+ environment variable. If no path is given 'git' will print
+ the current setting and then exit.
+
+CORE GIT COMMANDS
+-----------------
Before reading this cover to cover, you may want to take a look
at the link:tutorial.html[tutorial] document.
gitlink:git-hash-object[1]::
Computes the object ID from a file.
-gitlink:git-index-pack.html[1]::
+gitlink:git-index-pack[1]::
Build pack index file for an existing packed archive.
gitlink:git-init-db[1]::
------------------------
include::../README[]
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org> and the git-list <git@vger.kernel.org>.
+Authors
+-------
+ git's founding father is Linus Torvalds <torvalds@osdl.org>.
+ The current git nurse is Junio C. Hamano <junkio@cox.net>.
+ The git potty was written by Andres Ericsson <ae@op5.se>.
+ General upbringing is handled by the git-list <git@vger.kernel.org>.
Documentation
--------------
# Define USE_STDEV below if you want git to care about the underlying device
# change being considered an inode change from the update-cache perspective.
-GIT_VERSION = 0.99.9i
+GIT_VERSION = 0.99.9j
# CFLAGS and LDFLAGS are for the users to override from the command line.
git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \
- git-tag.sh git-verify-tag.sh git-whatchanged.sh git.sh \
+ git-tag.sh git-verify-tag.sh git-whatchanged.sh \
git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-merge-ours.sh git-grep.sh \
# The ones that do not have to link with lcrypto nor lz.
SIMPLE_PROGRAMS = \
git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \
- git-stripspace$X git-var$X git-daemon$X
+ git-stripspace$X git-daemon$X
# ... and all the rest
PROGRAMS = \
git-unpack-objects$X git-update-index$X git-update-server-info$X \
git-upload-pack$X git-verify-pack$X git-write-tree$X \
git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
- git-name-rev$X git-pack-redundant$X $(SIMPLE_PROGRAMS)
+ git-name-rev$X git-pack-redundant$X git-var$X $(SIMPLE_PROGRAMS)
# Backward compatibility -- to be removed after 1.0
PROGRAMS += git-ssh-pull$X git-ssh-push$X
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules
-all: $(PROGRAMS) $(SCRIPTS)
+all: $(PROGRAMS) $(SCRIPTS) git
all:
$(MAKE) -C templates
-git: git.sh Makefile
- rm -f $@+ $@
- sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \
- -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
- -e 's/@@X@@/$(X)/g' \
- $(GIT_LIST_TWEAK) <$@.sh >$@+
- chmod +x $@+
- mv $@+ $@
+# Only use $(CFLAGS). We don't need anything else.
+git: git.c Makefile
+ $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
+ $(CFLAGS) $@.c -o $@
$(filter-out git,$(patsubst %.sh,%,$(SCRIPT_SH))) : % : %.sh
rm -f $@
### Installation rules
-install: $(PROGRAMS) $(SCRIPTS)
+install: $(PROGRAMS) $(SCRIPTS) git
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir))
- $(INSTALL) $(PROGRAMS) $(SCRIPTS) $(call shellquote,$(DESTDIR)$(bindir))
+ $(INSTALL) git $(PROGRAMS) $(SCRIPTS) $(call shellquote,$(DESTDIR)$(bindir))
$(MAKE) -C templates install
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
$(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
### Maintainer's dist rules
-git-core.spec: git-core.spec.in Makefile
+git.spec: git.spec.in Makefile
sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@
-GIT_TARNAME=git-core-$(GIT_VERSION)
-dist: git-core.spec git-tar-tree
+GIT_TARNAME=git-$(GIT_VERSION)
+dist: git.spec git-tar-tree
./git-tar-tree HEAD $(GIT_TARNAME) > $(GIT_TARNAME).tar
@mkdir -p $(GIT_TARNAME)
- @cp git-core.spec $(GIT_TARNAME)
- $(TAR) rf $(GIT_TARNAME).tar $(GIT_TARNAME)/git-core.spec
+ @cp git.spec $(GIT_TARNAME)
+ $(TAR) rf $(GIT_TARNAME).tar $(GIT_TARNAME)/git.spec
@rm -rf $(GIT_TARNAME)
gzip -f -9 $(GIT_TARNAME).tar
rpm: dist
- $(RPMBUILD) -ta git-core-$(GIT_VERSION).tar.gz
+ $(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
deb: dist
rm -rf $(GIT_TARNAME)
clean:
rm -f *.o mozilla-sha1/*.o ppc/*.o compat/*.o $(PROGRAMS) $(LIB_FILE)
rm -f $(filter-out gitk,$(SCRIPTS))
- rm -f git-core.spec *.pyc *.pyo
+ rm -f *.spec *.pyc *.pyo
rm -rf $(GIT_TARNAME)
rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
rm -f git-core_$(GIT_VERSION)-*.dsc
that commit, and you should save it away for later use. Normally,
you'd commit a new `HEAD` state, and while git doesn't care where you
save the note about that state, in practice we tend to just write the
-result to the file `.git/HEAD`, so that we can always see what the
-last committed state was.
+result to the file pointed at by `.git/HEAD`, so that we can always see
+what the last committed state was.
Here is an ASCII art by Jon Loeliger that illustrates how
various pieces fit together.
follow the convention of having the top commit name in `.git/HEAD`,
you can do
- git-cat-file commit $(cat .git/HEAD)
+ git-cat-file commit HEAD
to see what the top commit was.
// --numstat does numeric diffstat, and doesn't actually apply
// --index-info shows the old and new index info for paths if available.
//
+static int allow_binary_replacement = 0;
static int check_index = 0;
static int write_index = 0;
static int diffstat = 0;
static int show_index_info = 0;
static int line_termination = '\n';
static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] <patch>...";
/*
* For "diff-stat" like behaviour, we keep track of the biggest change
patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
- if (!patchsize && !metadata_changes(patch)) {
+ if (!patchsize) {
static const char binhdr[] = "Binary files ";
if (sizeof(binhdr) - 1 < size - offset - hdrsize &&
sizeof(binhdr)-1))
patch->is_binary = 1;
- if (patch->is_binary && !apply && !check)
- ;
- else
+ /* Empty patch cannot be applied if:
+ * - it is a binary patch and we do not do binary_replace, or
+ * - text patch without metadata change
+ */
+ if ((apply || check) &&
+ (patch->is_binary
+ ? !allow_binary_replacement
+ : !metadata_changes(patch)))
die("patch with only garbage at line %d", linenr);
}
static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
{
struct fragment *frag = patch->fragments;
+ const char *name = patch->old_name ? patch->old_name : patch->new_name;
+
+ if (patch->is_binary) {
+ unsigned char sha1[20];
+
+ if (!allow_binary_replacement)
+ return error("cannot apply binary patch to '%s' "
+ "without --allow-binary-replacement",
+ name);
+
+ /* For safety, we require patch index line to contain
+ * full 40-byte textual SHA1 for old and new, at least for now.
+ */
+ if (strlen(patch->old_sha1_prefix) != 40 ||
+ strlen(patch->new_sha1_prefix) != 40 ||
+ get_sha1_hex(patch->old_sha1_prefix, sha1) ||
+ get_sha1_hex(patch->new_sha1_prefix, sha1))
+ return error("cannot apply binary patch to '%s' "
+ "without full index line", name);
+
+ if (patch->old_name) {
+ unsigned char hdr[50];
+ int hdrlen;
+
+ /* See if the old one matches what the patch
+ * applies to.
+ */
+ write_sha1_file_prepare(desc->buffer, desc->size,
+ "blob", sha1, hdr, &hdrlen);
+ if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
+ return error("the patch applies to '%s' (%s), "
+ "which does not match the "
+ "current contents.",
+ name, sha1_to_hex(sha1));
+ }
+ else {
+ /* Otherwise, the old one must be empty. */
+ if (desc->size)
+ return error("the patch applies to an empty "
+ "'%s' but it is not empty", name);
+ }
+
+ /* For now, we do not record post-image data in the patch,
+ * and require the object already present in the recipient's
+ * object database.
+ */
+ if (desc->buffer) {
+ free(desc->buffer);
+ desc->alloc = desc->size = 0;
+ }
+ get_sha1_hex(patch->new_sha1_prefix, sha1);
+
+ if (memcmp(sha1, null_sha1, 20)) {
+ char type[10];
+ unsigned long size;
+
+ desc->buffer = read_sha1_file(sha1, type, &size);
+ if (!desc->buffer)
+ return error("the necessary postimage %s for "
+ "'%s' does not exist",
+ patch->new_sha1_prefix, name);
+ desc->alloc = desc->size = size;
+ }
+
+ return 0;
+ }
while (frag) {
if (apply_one_fragment(desc, frag) < 0)
- return error("patch failed: %s:%ld", patch->old_name, frag->oldpos);
+ return error("patch failed: %s:%ld",
+ name, frag->oldpos);
frag = frag->next;
}
return 0;
struct stat st;
const char *old_name = patch->old_name;
const char *new_name = patch->new_name;
+ const char *name = old_name ? old_name : new_name;
if (old_name) {
int changed;
}
if (apply_data(patch, &st) < 0)
- return error("%s: patch does not apply", old_name);
+ return error("%s: patch does not apply", name);
return 0;
}
diffstat = 1;
continue;
}
+ if (!strcmp(arg, "--allow-binary-replacement")) {
+ allow_binary_replacement = 1;
+ continue;
+ }
if (!strcmp(arg, "--numstat")) {
apply = 0;
numstat = 1;
extern void rollback_index_file(struct cache_file *);
extern int trust_executable_bit;
+extern int only_use_symrefs;
+extern int diff_rename_limit_default;
#define MTIME_CHANGED 0x0001
#define CTIME_CHANGED 0x0002
unsigned char parent[20];
struct commit_list **pptr;
struct commit_graft *graft;
+ unsigned n_refs = 0;
if (item->object.parsed)
return 0;
return error("bad tree pointer in commit %s\n", sha1_to_hex(item->object.sha1));
item->tree = lookup_tree(parent);
if (item->tree)
- add_ref(&item->object, &item->tree->object);
+ n_refs++;
bufptr += 46; /* "tree " + "hex sha1" + "\n" */
pptr = &item->parents;
new_parent = lookup_commit(parent);
if (new_parent) {
pptr = &commit_list_insert(new_parent, pptr)->next;
- add_ref(&item->object, &new_parent->object);
+ n_refs++;
}
}
if (graft) {
if (!new_parent)
continue;
pptr = &commit_list_insert(new_parent, pptr)->next;
- add_ref(&item->object, &new_parent->object);
+ n_refs++;
}
}
item->date = parse_commit_date(bufptr);
+
+ if (track_object_refs) {
+ unsigned i = 0;
+ struct commit_list *p;
+ struct object_refs *refs = alloc_object_refs(n_refs);
+ if (item->tree)
+ refs->ref[i++] = &item->tree->object;
+ for (p = item->parents; p; p = p->next)
+ refs->ref[i++] = &p->item->object;
+ set_object_refs(&item->object, refs);
+ }
+
return 0;
}
return 0;
}
+ if (!strcmp(var, "core.symrefsonly")) {
+ only_use_symrefs = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "user.name")) {
strncpy(git_default_name, value, sizeof(git_default_name));
return 0;
return 0;
}
+ if (!strcmp(var, "diff.renamelimit")) {
+ diff_rename_limit_default = git_config_int(var, value);
+ return 0;
+ }
+
/* Add other config variables here.. */
return 0;
}
}
if (!strcmp(arg, "--inetd")) {
inetd_mode = 1;
+ log_syslog = 1;
continue;
}
if (!strcmp(arg, "--verbose")) {
}
if (!strcmp(arg, "--syslog")) {
log_syslog = 1;
- openlog("git-daemon", 0, LOG_DAEMON);
continue;
}
if (!strcmp(arg, "--export-all")) {
}
if (!strncmp(arg, "--timeout=", 10)) {
timeout = atoi(arg+10);
+ continue;
}
if (!strncmp(arg, "--init-timeout=", 15)) {
init_timeout = atoi(arg+15);
+ continue;
}
if (!strcmp(arg, "--")) {
ok_paths = &argv[i+1];
usage(daemon_usage);
}
+ if (log_syslog)
+ openlog("git-daemon", 0, LOG_DAEMON);
+
if (inetd_mode) {
fclose(stderr); //FIXME: workaround
return execute();
- } else {
- return serve(port);
}
+
+ return serve(port);
}
+git-core (0.99.9j-0) unstable; urgency=low
+
+ * GIT 0.99.9j aka 1.0rc3
+
+ -- Junio C Hamano <junkio@cox.net> Wed, 16 Nov 2005 20:39:55 -0800
+
git-core (0.99.9i-0) unstable; urgency=low
* GIT 0.99.9i aka 1.0rc2
static int use_size_cache;
+int diff_rename_limit_default = -1;
+
static char *quote_one(const char *str)
{
int needlen;
memset(one->sha1, 0, 20);
}
-static void run_diff(struct diff_filepair *p)
+static void run_diff(struct diff_filepair *p, struct diff_options *o)
{
const char *pgm = external_diff();
char msg[PATH_MAX*2+300], *xfrm_msg;
if (memcmp(one->sha1, two->sha1, 20)) {
char one_sha1[41];
+ const char *index_fmt = o->full_index ? "index %s..%s" : "index %.7s..%.7s";
memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
len += snprintf(msg + len, sizeof(msg) - len,
- "index %.7s..%.7s", one_sha1,
- sha1_to_hex(two->sha1));
+ index_fmt, one_sha1, sha1_to_hex(two->sha1));
if (one->mode == two->mode)
len += snprintf(msg + len, sizeof(msg) - len,
" %06o", one->mode);
int diff_setup_done(struct diff_options *options)
{
- if ((options->find_copies_harder || 0 <= options->rename_limit) &&
- options->detect_rename != DIFF_DETECT_COPY)
+ if ((options->find_copies_harder &&
+ options->detect_rename != DIFF_DETECT_COPY) ||
+ (0 <= options->rename_limit && !options->detect_rename))
return -1;
+ if (options->detect_rename && options->rename_limit < 0)
+ options->rename_limit = diff_rename_limit_default;
if (options->setup & DIFF_SETUP_USE_CACHE) {
if (!active_cache)
/* read-cache does not die even when it fails
options->line_termination = 0;
else if (!strncmp(arg, "-l", 2))
options->rename_limit = strtoul(arg+2, NULL, 10);
+ else if (!strcmp(arg, "--full-index"))
+ options->full_index = 1;
else if (!strcmp(arg, "--name-only"))
options->output_format = DIFF_FORMAT_NAME;
else if (!strcmp(arg, "--name-status"))
return 0;
}
-static void diff_flush_patch(struct diff_filepair *p)
+static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
{
if (diff_unmodified_pair(p))
return;
(DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
return; /* no tree diffs in patch format */
- run_diff(p);
+ run_diff(p, o);
}
int diff_queue_is_empty(void)
die("internal error in diff-resolve-rename-copy");
switch (diff_output_format) {
case DIFF_FORMAT_PATCH:
- diff_flush_patch(p);
+ diff_flush_patch(p, options);
break;
case DIFF_FORMAT_RAW:
case DIFF_FORMAT_NAME_STATUS:
const char *orderfile;
const char *pickaxe;
unsigned recursive:1,
- tree_in_recursive:1;
+ tree_in_recursive:1,
+ full_index:1;
int break_opt;
int detect_rename;
int find_copies_harder;
" -u synonym for -p.\n" \
" --name-only show only names of changed files.\n" \
" --name-status show names and status of changed files.\n" \
+" --full-index show full object name on index ines.\n" \
" -R swap input file pairs.\n" \
" -B detect complete rewrites.\n" \
" -M detect renames.\n" \
register_rename_src(p->one, 1);
}
if (rename_dst_nr == 0 ||
- (0 <= rename_limit && rename_limit < rename_dst_nr))
+ (0 < rename_limit && rename_limit < rename_dst_nr))
goto cleanup; /* nothing to do */
/* We really want to cull the candidates list early
char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
int trust_executable_bit = 1;
+int only_use_symrefs = 0;
static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
*git_graft_file;
/* Look up all the requirements, warn about missing objects.. */
for (i = 0; i < nr_objs; i++) {
struct object *obj = objs[i];
- struct object_list *refs;
if (!obj->parsed) {
if (!standalone && has_sha1_file(obj->sha1))
continue;
}
- for (refs = obj->refs; refs; refs = refs->next) {
- if (refs->item->parsed ||
- (!standalone && has_sha1_file(refs->item->sha1)))
- continue;
- printf("broken link from %7s %s\n",
- obj->type, sha1_to_hex(obj->sha1));
- printf(" to %7s %s\n",
- refs->item->type, sha1_to_hex(refs->item->sha1));
+ if (obj->refs) {
+ const struct object_refs *refs = obj->refs;
+ unsigned j;
+ for (j = 0; j < refs->count; j++) {
+ struct object *ref = refs->ref[j];
+ if (ref->parsed ||
+ (!standalone && has_sha1_file(ref->sha1)))
+ continue;
+ printf("broken link from %7s %s\n",
+ obj->type, sha1_to_hex(obj->sha1));
+ printf(" to %7s %s\n",
+ ref->type, sha1_to_hex(ref->sha1));
+ }
}
if (show_unreachable && !(obj->flags & REACHABLE)) {
default:
break;
}
+ free(last->name);
+ free(last);
}
last = entry;
}
+ if (last) {
+ free(last->name);
+ free(last);
+ }
+ item->entries = NULL;
retval = 0;
if (has_full_path) {
#
. git-sh-setup || die "Not a git archive"
-files=$(git-diff-index --cached --name-only HEAD) || exit
-if [ "$files" ]; then
- echo "Dirty index: cannot apply patches (dirty: $files)" >&2
- exit 1
-fi
-
usage () {
echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--3way] <mbox>"
echo >&2 " or, when resuming"
- echo >&2 " $0 [--skip]"
+ echo >&2 " $0 [--skip | --resolved]"
exit 1;
}
}
prec=4
-dotest=.dotest sign= utf8= keep= skip= interactive=
+dotest=.dotest sign= utf8= keep= skip= interactive= resolved=
while case "$#" in 0) break;; esac
do
-k|--k|--ke|--kee|--keep)
keep=t; shift ;;
+ -r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved)
+ resolved=t; shift ;;
+
--sk|--ski|--skip)
skip=t; shift ;;
esac
done
+# If the dotest directory exists, but we have finished applying all the
+# patches in them, clear it out.
if test -d "$dotest" &&
last=$(cat "$dotest/last") &&
next=$(cat "$dotest/next") &&
die "previous dotest directory $dotest still exists but mbox given."
resume=yes
else
- # Make sure we are not given --skip
- test ",$skip," = ,, ||
- die "we are not resuming."
+ # Make sure we are not given --skip nor --resolved
+ test ",$skip,$resolved," = ,,, ||
+ die "we are not resuming."
# Start afresh.
mkdir -p "$dotest" || exit
exit 1
}
+ # -s, -u and -k flags are kept for the resuming session after
+ # a patch failure.
+ # -3 and -i can and must be given when resuming.
echo "$sign" >"$dotest/sign"
echo "$utf8" >"$dotest/utf8"
echo "$keep" >"$dotest/keep"
echo 1 >"$dotest/next"
fi
+case "$resolved" in
+'')
+ files=$(git-diff-index --cached --name-only HEAD) || exit
+ if [ "$files" ]; then
+ echo "Dirty index: cannot apply patches (dirty: $files)" >&2
+ exit 1
+ fi
+esac
+
if test "$(cat "$dotest/utf8")" = t
then
utf8=-u
go_next
continue
}
+
+ # If we are not resuming, parse and extract the patch information
+ # into separate files:
+ # - info records the authorship and title
+ # - msg is the rest of commit log message
+ # - patch is the patch body.
+ #
+ # When we are resuming, these files are either already prepared
+ # by the user, or the user can tell us to do so by --resolved flag.
case "$resume" in
'')
git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
fi
} >"$dotest/final-commit"
;;
+ *)
+ case "$resolved,$interactive" in
+ tt)
+ # This is used only for interactive view option.
+ git-diff-index -p --cached HEAD >"$dotest/patch"
+ ;;
+ esac
esac
resume=
echo "Applying '$SUBJECT'"
echo
- git-apply --index "$dotest/patch"; apply_status=$?
+ case "$resolved" in
+ '')
+ git-apply --index "$dotest/patch"
+ apply_status=$?
+ ;;
+ t)
+ # Resolved means the user did all the hard work, and
+ # we do not have to do any patch application. Just
+ # trust what the user has in the index file and the
+ # working tree.
+ resolved=
+ apply_status=0
+ ;;
+ esac
+
if test $apply_status = 1 && test "$threeway" = t
then
if (fall_back_3way)
open HEAD, ">$git_dir/refs/heads/$ps->{branch}";
print HEAD $commitid;
close HEAD;
- unlink ("$git_dir/HEAD");
- symlink("refs/heads/$ps->{branch}","$git_dir/HEAD");
+ system('git-update-ref', 'HEAD', "$ps->{branch}");
# tag accordingly
ptag($ps->{id}, $commitid); # private tag
use Getopt::Std;
use File::Temp qw(tempdir);
use Data::Dumper;
+use File::Basename qw(basename);
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
die "GIT_DIR is not defined or is unreadable";
our ($opt_h, $opt_p, $opt_v, $opt_c );
-getopt('hpvc');
+getopts('hpvc');
$opt_h && usage();
# grab the commit message
`git-cat-file commit $commit | sed -e '1,/^\$/d' > .msg`;
-$? && die "Error extraction the commit message";
+$? && die "Error extracting the commit message";
my (@afiles, @dfiles, @mfiles);
my @files = `git-diff-tree -r $parent $commit`;
if ($dirtypatch) {
print "NOTE: One or more hunks failed to apply cleanly.\n";
- print "Resolve the conflicts and then commit using:n";
+ print "Resolve the conflicts and then commit using:\n";
print "\n $cmd\n\n";
- exit;
+ exit(1);
}
}
sub usage {
print STDERR <<END;
-Usage: GIT_DIR=/path/to/.gi ${\basename $0} # fetch/update GIT from CVS
- [-h] [-p] [ parent ] commit
+Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [ parent ] commit
END
exit(1);
}
"Either use the correct '-o branch' option,\n".
"or import to a new repository.\n";
- $last_branch = basename(readlink("$git_dir/HEAD"));
+ open(F, "git-symbolic-ref HEAD |") or
+ die "Cannot run git-symbolic-ref: $!\n";
+ chomp ($last_branch = <F>);
+ $last_branch = basename($last_branch);
+ close(F);
unless($last_branch) {
warn "Cannot read the last branch name: $! -- assuming 'master'\n";
$last_branch = "master";
print "DONE; creating $orig_branch branch\n" if $opt_v;
system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master")
unless -f "$git_dir/refs/heads/master";
- unlink("$git_dir/HEAD");
- symlink("refs/heads/$orig_branch","$git_dir/HEAD");
+ system('git-update-ref', 'HEAD', "$orig_branch");
unless ($opt_i) {
system('git checkout');
die "checkout failed: $?\n" if $?;
esac
shift
done
+[ "$pattern" ] || {
+ echo >&2 "usage: 'git grep <pattern> [pathspec*]'"
+ exit 1
+}
git-ls-files -z "${git_flags[@]}" "$@" |
xargs -0 grep "${flags[@]}" -e "$pattern"
# Copyright (C) 2005 Fredrik Kuivinen
#
-import sys, math, random, os, re, signal, tempfile, stat, errno, traceback
+import sys
+sys.path.append('''@@GIT_PYTHON_PATH@@''')
+
+import math, random, os, re, signal, tempfile, stat, errno, traceback
from heapq import heappush, heappop
from sets import Set
-sys.path.append('''@@GIT_PYTHON_PATH@@''')
from gitMergeCommon import *
outputIndent = 0
-f "$git_dir/svn2git"
or die "'$git_dir/svn2git' does not exist.\n".
"You need that file for incremental imports.\n";
- $last_branch = basename(readlink("$git_dir/HEAD"));
+ open(F, "git-symbolic-ref HEAD |") or
+ die "Cannot run git-symbolic-ref: $!\n";
+ chomp ($last_branch = <F>);
+ $last_branch = basename($last_branch);
+ close(F);
unless($last_branch) {
warn "Cannot read the last branch name: $! -- assuming 'master'\n";
$last_branch = "master";
print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0);
system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master")
unless -f "$git_dir/refs/heads/master";
- unlink("$git_dir/HEAD");
- symlink("refs/heads/$orig_branch","$git_dir/HEAD");
+ system('git-update-ref', 'HEAD', "$orig_branch");
unless ($opt_i) {
system('git checkout');
die "checkout failed: $?\n" if $?;
--- /dev/null
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+static const char git_usage[] =
+ "Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
+
+/* most gui terms set COLUMNS (although some don't export it) */
+static int term_columns(void)
+{
+ char *col_string = getenv("COLUMNS");
+ int n_cols = 0;
+
+ if (col_string && (n_cols = atoi(col_string)) > 0)
+ return n_cols;
+
+ return 80;
+}
+
+static void oom(void)
+{
+ fprintf(stderr, "git: out of memory\n");
+ exit(1);
+}
+
+static inline void mput_char(char c, unsigned int num)
+{
+ while(num--)
+ putchar(c);
+}
+
+static struct cmdname {
+ size_t len;
+ char name[1];
+} **cmdname;
+static int cmdname_alloc, cmdname_cnt;
+
+static void add_cmdname(const char *name, int len)
+{
+ struct cmdname *ent;
+ if (cmdname_alloc <= cmdname_cnt) {
+ cmdname_alloc = cmdname_alloc + 200;
+ cmdname = realloc(cmdname, cmdname_alloc * sizeof(*cmdname));
+ if (!cmdname)
+ oom();
+ }
+ ent = malloc(sizeof(*ent) + len);
+ if (!ent)
+ oom();
+ ent->len = len;
+ memcpy(ent->name, name, len+1);
+ cmdname[cmdname_cnt++] = ent;
+}
+
+static int cmdname_compare(const void *a_, const void *b_)
+{
+ struct cmdname *a = *(struct cmdname **)a_;
+ struct cmdname *b = *(struct cmdname **)b_;
+ return strcmp(a->name, b->name);
+}
+
+static void pretty_print_string_list(struct cmdname **cmdname, int longest)
+{
+ int cols = 1;
+ int space = longest + 1; /* min 1 SP between words */
+ int max_cols = term_columns() - 1; /* don't print *on* the edge */
+ int i;
+
+ if (space < max_cols)
+ cols = max_cols / space;
+
+ qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);
+
+ for (i = 0; i < cmdname_cnt; ) {
+ int c;
+ printf(" ");
+
+ for (c = cols; c && i < cmdname_cnt; i++) {
+ printf("%s", cmdname[i]->name);
+
+ if (--c)
+ mput_char(' ', space - cmdname[i]->len);
+ }
+ putchar('\n');
+ }
+}
+
+static void list_commands(const char *exec_path, const char *pattern)
+{
+ unsigned int longest = 0;
+ char path[PATH_MAX];
+ int dirlen;
+ DIR *dir = opendir(exec_path);
+ struct dirent *de;
+
+ if (!dir) {
+ fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno));
+ exit(1);
+ }
+
+ dirlen = strlen(exec_path);
+ if (PATH_MAX - 20 < dirlen) {
+ fprintf(stderr, "git: insanely long exec-path '%s'\n",
+ exec_path);
+ exit(1);
+ }
+
+ memcpy(path, exec_path, dirlen);
+ path[dirlen++] = '/';
+
+ while ((de = readdir(dir)) != NULL) {
+ struct stat st;
+ int entlen;
+
+ if (strncmp(de->d_name, "git-", 4))
+ continue;
+ strcpy(path+dirlen, de->d_name);
+ if (stat(path, &st) || /* stat, not lstat */
+ !S_ISREG(st.st_mode) ||
+ !(st.st_mode & S_IXUSR))
+ continue;
+
+ entlen = strlen(de->d_name);
+
+ if (longest < entlen)
+ longest = entlen;
+
+ add_cmdname(de->d_name + 4, entlen-4);
+ }
+ closedir(dir);
+
+ printf("git commands available in '%s'\n", exec_path);
+ printf("----------------------------");
+ mput_char('-', strlen(exec_path));
+ putchar('\n');
+ pretty_print_string_list(cmdname, longest - 4);
+ putchar('\n');
+}
+
+#ifdef __GNUC__
+static void usage(const char *exec_path, const char *fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3), __noreturn__));
+#endif
+static void usage(const char *exec_path, const char *fmt, ...)
+{
+ if (fmt) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ printf("git: ");
+ vprintf(fmt, ap);
+ va_end(ap);
+ putchar('\n');
+ }
+ else
+ puts(git_usage);
+
+ putchar('\n');
+
+ if(exec_path)
+ list_commands(exec_path, "git-*");
+
+ exit(1);
+}
+
+static void prepend_to_path(const char *dir, int len)
+{
+ char *path, *old_path = getenv("PATH");
+ int path_len = len;
+
+ if (!old_path)
+ old_path = "/usr/local/bin:/usr/bin:/bin";
+
+ path_len = len + strlen(old_path) + 1;
+
+ path = malloc(path_len + 1);
+ path[path_len + 1] = '\0';
+
+ memcpy(path, dir, len);
+ path[len] = ':';
+ memcpy(path + len + 1, old_path, path_len - len);
+
+ setenv("PATH", path, 1);
+}
+
+static void show_man_page(char *git_cmd)
+{
+ char *page;
+
+ if (!strncmp(git_cmd, "git", 3))
+ page = git_cmd;
+ else {
+ int page_len = strlen(git_cmd) + 4;
+
+ page = malloc(page_len + 1);
+ strcpy(page, "git-");
+ strcpy(page + 4, git_cmd);
+ page[page_len] = 0;
+ }
+
+ execlp("man", "man", page, NULL);
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ char git_command[PATH_MAX + 1];
+ char wd[PATH_MAX + 1];
+ int i, len, show_help = 0;
+ char *exec_path = getenv("GIT_EXEC_PATH");
+
+ getcwd(wd, PATH_MAX);
+
+ if (!exec_path)
+ exec_path = GIT_EXEC_PATH;
+
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+
+ if (strncmp(arg, "--", 2))
+ break;
+
+ arg += 2;
+
+ if (!strncmp(arg, "exec-path", 9)) {
+ arg += 9;
+ if (*arg == '=')
+ exec_path = arg + 1;
+ else {
+ puts(exec_path);
+ exit(0);
+ }
+ }
+ else if (!strcmp(arg, "version")) {
+ printf("git version %s\n", GIT_VERSION);
+ exit(0);
+ }
+ else if (!strcmp(arg, "help"))
+ show_help = 1;
+ else if (!show_help)
+ usage(NULL, NULL);
+ }
+
+ if (i >= argc || show_help) {
+ if (i >= argc)
+ usage(exec_path, NULL);
+
+ show_man_page(argv[i]);
+ }
+
+ if (*exec_path != '/') {
+ if (!getcwd(git_command, sizeof(git_command))) {
+ fprintf(stderr,
+ "git: cannot determine current directory");
+ exit(1);
+ }
+ len = strlen(git_command);
+
+ /* Trivial cleanup */
+ while (!strncmp(exec_path, "./", 2)) {
+ exec_path += 2;
+ while (*exec_path == '/')
+ *exec_path++;
+ }
+ snprintf(git_command + len, sizeof(git_command) - len,
+ "/%s", exec_path);
+ }
+ else
+ strcpy(git_command, exec_path);
+ len = strlen(git_command);
+ prepend_to_path(git_command, len);
+
+ strncat(&git_command[len], "/git-", sizeof(git_command) - len);
+ len += 5;
+ strncat(&git_command[len], argv[i], sizeof(git_command) - len);
+
+ if (access(git_command, X_OK))
+ usage(exec_path, "'%s' is not a git-command", argv[i]);
+
+ /* execve() can only ever return if it fails */
+ execve(git_command, &argv[i], envp);
+ printf("Failed to run command '%s': %s\n", git_command, strerror(errno));
+
+ return 1;
+}
+++ /dev/null
-#!/bin/sh
-
-cmd=
-path=$(dirname "$0")
-case "$#" in
-0) ;;
-*) cmd="$1"
- shift
- case "$cmd" in
- -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
- echo "git version @@GIT_VERSION@@"
- exit 0 ;;
- esac
-
- test -x "$path/git-$cmd" && exec "$path/git-$cmd" "$@"
-
- case '@@X@@' in
- '')
- ;;
- *)
- test -x "$path/git-$cmd@@X@@" &&
- exec "$path/git-$cmd@@X@@" "$@"
- ;;
- esac
- ;;
-esac
-
-echo "Usage: git COMMAND [OPTIONS] [TARGET]"
-if [ -n "$cmd" ]; then
- echo "git command '$cmd' not found."
-fi
-echo "git commands are:"
-
-fmt <<\EOF | sed -e 's/^/ /'
-add
-apply
-archimport
-bisect
-branch
-checkout
-cherry
-clone
-commit
-count-objects
-cvsimport
-diff
-fetch
-format-patch
-fsck-objects
-get-tar-commit-id
-init-db
-log
-ls-remote
-octopus
-pack-objects
-parse-remote
-patch-id
-prune
-pull
-push
-rebase
-relink
-rename
-repack
-request-pull
-reset
-resolve
-revert
-send-email
-shortlog
-show-branch
-status
-tag
-verify-tag
-whatchanged
-EOF
# Pass --without docs to rpmbuild if you don't want the documentation
-Name: git-core
+Name: git
Version: @@VERSION@@
Release: 1%{?dist}
Summary: Git core and tools
Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Requires: zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, python >= 2.3, expat
+Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk
%description
This is a stupid (but extremely fast) directory content manager. It
rudimentary tools that can be used as a SCM, but you should look
elsewhere for tools for ordinary humans layered on top of this.
+This is a dummy package which brings in all subpackages.
+
+%package core
+Summary: Core git tools
+Group: Development/Tools
+Requires: zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, python >= 2.3, expat
+%description core
+This is a stupid (but extremely fast) directory content manager. It
+doesn't do a whole lot, but what it _does_ do is track directory
+contents efficiently. It is intended to be the base of an efficient,
+distributed source code management system. This package includes
+rudimentary tools that can be used as a SCM, but you should look
+elsewhere for tools for ordinary humans layered on top of this.
+
+These are the core tools with minimal dependencies.
+
%package svn
Summary: Git tools for importing Subversion repositories
Group: Development/Tools
%description email
Git tools for sending email.
-%package tk
+%package -n gitk
Summary: Git revision tree visualiser ('gitk')
Group: Development/Tools
Requires: git-core = %{version}-%{release}, tk >= 8.4
-%description tk
+%description -n gitk
Git revision tree visualiser ('gitk')
%prep
%clean
rm -rf $RPM_BUILD_ROOT
+%files
+# These are no files in the root package
+
%files svn
%defattr(-,root,root)
%{_bindir}/*svn*
%{!?_without_docs: %{_mandir}/man1/*email*.1*}
%{!?_without_docs: %doc Documentation/*email*.html }
-%files tk
+%files -n gitk
%defattr(-,root,root)
%doc Documentation/*gitk*.txt
%{_bindir}/*gitk*
%{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
%{!?_without_docs: %doc Documentation/*gitk*.html }
-%files -f bin-man-doc-files
+%files core -f bin-man-doc-files
%defattr(-,root,root)
%{_datadir}/git-core/
%doc README COPYING Documentation/*.txt
%{!?_without_docs: %doc Documentation/*.html }
%changelog
+* Mon Nov 14 2005 H. Peter Anvin <hpa@zytor.com> 0.99.9j-1
+- Change subpackage names to git-<name> instead of git-core-<name>
+- Create empty root package which brings in all subpackages
+- Rename git-tk -> gitk
+
* Thu Nov 10 2005 Chris Wright <chrisw@osdl.org> 0.99.9g-1
- zlib dependency fix
- Minor cleanups from split
rename(request->tmpfile, prevfile);
unlink(request->tmpfile);
+ if (request->local != -1)
+ error("fd leakage in start: %d", request->local);
request->local = open(request->tmpfile,
O_WRONLY | O_CREAT | O_EXCL, 0666);
/* This could have failed due to the "lazy directory creation";
/* Try to get the request started, abort the request on error */
if (!start_active_slot(slot)) {
request->state = ABORTED;
- close(request->local);
+ close(request->local); request->local = -1;
free(request->url);
return;
}
struct stat st;
fchmod(request->local, 0444);
- close(request->local);
+ close(request->local); request->local = -1;
if (request->http_code == 416) {
fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
{
struct transfer_request *entry = request_queue_head;
+ if (request->local != -1)
+ error("fd leakage in release: %d", request->local);
if (request == request_queue_head) {
request_queue_head = request->next;
} else {
if (request->repo->next != NULL) {
request->repo =
request->repo->next;
+ close(request->local);
+ request->local = -1;
start_request(request);
} else {
finish_request(request);
curl_errorstr);
}
} else {
+ fclose(indexfile);
return error("Unable to start request");
}
curl_errorstr);
}
} else {
+ fclose(packfile);
return error("Unable to start request");
}
fetch_alternates(alt->base);
if (request->repo->next != NULL) {
request->repo = request->repo->next;
+ close(request->local); request->local = -1;
start_request(request);
}
} else {
}
#endif
}
+ if (request->local != -1) {
+ close(request->local); request->local = -1;
+ }
if (request->state == ABORTED) {
release_request(request);
nr_objs++;
}
-void add_ref(struct object *refer, struct object *target)
+struct object_refs *alloc_object_refs(unsigned count)
{
- struct object_list **pp, *p;
+ struct object_refs *refs;
+ size_t size = sizeof(*refs) + count*sizeof(struct object *);
- if (!track_object_refs)
+ refs = xmalloc(size);
+ memset(refs, 0, size);
+ refs->count = count;
+ return refs;
+}
+
+static int compare_object_pointers(const void *a, const void *b)
+{
+ const struct object * const *pa = a;
+ const struct object * const *pb = b;
+ return *pa - *pb;
+}
+
+void set_object_refs(struct object *obj, struct object_refs *refs)
+{
+ unsigned int i, j;
+
+ /* Do not install empty list of references */
+ if (refs->count < 1) {
+ free(refs);
return;
+ }
- pp = &refer->refs;
- while ((p = *pp) != NULL) {
- if (p->item == target)
- return;
- pp = &p->next;
+ /* Sort the list and filter out duplicates */
+ qsort(refs->ref, refs->count, sizeof(refs->ref[0]),
+ compare_object_pointers);
+ for (i = j = 1; i < refs->count; i++) {
+ if (refs->ref[i] != refs->ref[i - 1])
+ refs->ref[j++] = refs->ref[i];
+ }
+ if (j < refs->count) {
+ /* Duplicates were found - reallocate list */
+ size_t size = sizeof(*refs) + j*sizeof(struct object *);
+ refs->count = j;
+ refs = xrealloc(refs, size);
}
- target->used = 1;
- p = xmalloc(sizeof(*p));
- p->item = target;
- p->next = NULL;
- *pp = p;
+ for (i = 0; i < refs->count; i++)
+ refs->ref[i]->used = 1;
+ obj->refs = refs;
}
void mark_reachable(struct object *obj, unsigned int mask)
{
- struct object_list *p = obj->refs;
-
if (!track_object_refs)
die("cannot do reachability with object refs turned off");
/* If we've been here already, don't bother */
if (obj->flags & mask)
return;
obj->flags |= mask;
- while (p) {
- mark_reachable(p->item, mask);
- p = p->next;
+ if (obj->refs) {
+ const struct object_refs *refs = obj->refs;
+ unsigned i;
+ for (i = 0; i < refs->count; i++)
+ mark_reachable(refs->ref[i], mask);
}
}
const char *name;
};
+struct object_refs {
+ unsigned count;
+ struct object *ref[0];
+};
+
struct object {
unsigned parsed : 1;
unsigned used : 1;
unsigned int flags;
unsigned char sha1[20];
const char *type;
- struct object_list *refs;
+ struct object_refs *refs;
void *util;
};
/** Returns the object, with potentially excess memory allocated. **/
struct object *lookup_unknown_object(const unsigned char *sha1);
-void add_ref(struct object *refer, struct object *target);
+struct object_refs *alloc_object_refs(unsigned count);
+void set_object_refs(struct object *obj, struct object_refs *refs);
void mark_reachable(struct object *obj, unsigned int mask);
return llist_insert_back(list, sha1);
}
-/* computes A\B */
-void llist_sorted_difference_inplace(struct llist *A,
- struct llist *B)
-{
- struct llist_item *prev, *a, *b, *x;
-
- prev = a = A->front;
- b = B->front;
-
- while (a != NULL && b != NULL) {
- int cmp = memcmp(a->sha1, b->sha1, 20);
- if (!cmp) {
- x = a;
- if (a == A->front)
- A->front = a->next;
- a = prev->next = a->next;
-
- if (a == NULL) /* end of list */
- A->back = prev;
- A->size--;
- free(x);
- b = b->next;
- } else
- if (cmp > 0)
- b = b->next;
- else {
- prev = a;
- a = a->next;
- }
- }
-}
-
/* returns a pointer to an item in front of sha1 */
inline struct llist_item * llist_sorted_remove(struct llist *list, char *sha1,
struct llist_item *hint)
return prev;
}
+/* computes A\B */
+void llist_sorted_difference_inplace(struct llist *A,
+ struct llist *B)
+{
+ struct llist_item *hint, *b;
+
+ hint = NULL;
+ b = B->front;
+
+ while (b) {
+ hint = llist_sorted_remove(A, b->sha1, hint);
+ b = b->next;
+ }
+}
+
inline struct pack_list * pack_list_insert(struct pack_list **pl,
struct pack_list *entry)
{
int fd, len, written;
#if USE_SYMLINK_HEAD
- unlink(git_HEAD);
- if (!symlink(refs_heads_master, git_HEAD))
- return 0;
- fprintf(stderr, "no symlink - falling back to symbolic ref\n");
+ if (!only_use_symrefs) {
+ unlink(git_HEAD);
+ if (!symlink(refs_heads_master, git_HEAD))
+ return 0;
+ fprintf(stderr, "no symlink - falling back to symbolic ref\n");
+ }
#endif
len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
{
unsigned char sha1[20];
struct object *o;
- struct object_list *ref;
int i;
struct packed_git *p = info[pack_ix]->p;
int num = num_packed_objects(p);
die("corrupt pack file %s?", p->pack_name);
if ((o = lookup_object(sha1)) == NULL)
die("cannot parse %s", sha1_to_hex(sha1));
- for (ref = o->refs; ref; ref = ref->next)
- ref->item->flags = 0;
+ if (o->refs) {
+ struct object_refs *refs = o->refs;
+ int j;
+ for (j = 0; j < refs->count; j++)
+ refs->ref[j]->flags = 0;
+ }
o->flags = 0;
}
die("corrupt pack file %s?", p->pack_name);
if ((o = lookup_object(sha1)) == NULL)
die("cannot find %s", sha1_to_hex(sha1));
- for (ref = o->refs; ref; ref = ref->next)
- ref->item->flags |= REFERENCED;
+ if (o->refs) {
+ struct object_refs *refs = o->refs;
+ int j;
+ for (j = 0; j < refs->count; j++)
+ refs->ref[j]->flags |= REFERENCED;
+ }
o->flags |= INTERNAL;
}
die("cannot find %s", sha1_to_hex(sha1));
show(o, pack_ix);
- for (ref = o->refs; ref; ref = ref->next)
- show(ref->item, pack_ix);
+ if (o->refs) {
+ struct object_refs *refs = o->refs;
+ int j;
+ for (j = 0; j < refs->count; j++)
+ show(refs->ref[j], pack_ix);
+ }
}
}
struct packed_git *p;
unsigned long idx_size;
void *idx_map;
- char sha1[20];
+ unsigned char sha1[20];
if (check_packed_git_idx(path, &idx_size, &idx_map))
return NULL;
int main(int argc, const char **argv)
{
setup_git_directory();
+ git_config(git_default_config);
switch (argc) {
case 2:
check_symref(argv[1]);
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply handling binary patches
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat >file1 <<EOF
+A quick brown fox jumps over the lazy dog.
+A tiny little penguin runs around in circles.
+There is a flag with Linux written on it.
+A slow black-and-white panda just sits there,
+munching on his bamboo.
+EOF
+cat file1 >file2
+cat file1 >file4
+
+git-update-index --add --remove file1 file2 file4
+git-commit -m 'Initial Version' 2>/dev/null
+
+git-checkout -b binary
+tr 'x' '\0' <file1 >file3
+cat file3 >file4
+git-add file2
+tr '\0' 'v' <file3 >file1
+rm -f file2
+git-update-index --add --remove file1 file2 file3 file4
+git-commit -m 'Second Version'
+
+git-diff-tree -p master binary >B.diff
+git-diff-tree -p -C master binary >C.diff
+
+git-diff-tree -p --full-index master binary >BF.diff
+git-diff-tree -p --full-index -C master binary >CF.diff
+
+test_expect_success 'stat binary diff -- should not fail.' \
+ 'git-checkout master
+ git-apply --stat --summary B.diff'
+
+test_expect_success 'stat binary diff (copy) -- should not fail.' \
+ 'git-checkout master
+ git-apply --stat --summary C.diff'
+
+test_expect_failure 'check binary diff -- should fail.' \
+ 'git-checkout master
+ git-apply --check B.diff'
+
+test_expect_failure 'check binary diff (copy) -- should fail.' \
+ 'git-checkout master
+ git-apply --check C.diff'
+
+test_expect_failure 'check incomplete binary diff with replacement -- should fail.' \
+ 'git-checkout master
+ git-apply --check --allow-binary-replacement B.diff'
+
+test_expect_failure 'check incomplete binary diff with replacement (copy) -- should fail.' \
+ 'git-checkout master
+ git-apply --check --allow-binary-replacement C.diff'
+
+test_expect_success 'check binary diff with replacement.' \
+ 'git-checkout master
+ git-apply --check --allow-binary-replacement BF.diff'
+
+test_expect_success 'check binary diff with replacement (copy).' \
+ 'git-checkout master
+ git-apply --check --allow-binary-replacement CF.diff'
+
+# Now we start applying them.
+
+do_reset () {
+ rm -f file?
+ git-reset --hard
+ git-checkout -f master
+}
+
+test_expect_failure 'apply binary diff -- should fail.' \
+ 'do_reset
+ git-apply B.diff'
+
+test_expect_failure 'apply binary diff -- should fail.' \
+ 'do_reset
+ git-apply --index B.diff'
+
+test_expect_failure 'apply binary diff (copy) -- should fail.' \
+ 'do_reset
+ git-apply C.diff'
+
+test_expect_failure 'apply binary diff (copy) -- should fail.' \
+ 'do_reset
+ git-apply --index C.diff'
+
+test_expect_failure 'apply binary diff without replacement -- should fail.' \
+ 'do_reset
+ git-apply BF.diff'
+
+test_expect_failure 'apply binary diff without replacement (copy) -- should fail.' \
+ 'do_reset
+ git-apply CF.diff'
+
+test_expect_success 'apply binary diff.' \
+ 'do_reset
+ git-apply --allow-binary-replacement --index BF.diff &&
+ test -z "$(git-diff --name-status binary)"'
+
+test_expect_success 'apply binary diff (copy).' \
+ 'do_reset
+ git-apply --allow-binary-replacement --index CF.diff &&
+ test -z "$(git-diff --name-status binary)"'
+
+test_done
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in trash subdirectory.
PATH=$(pwd)/..:$PATH
+GIT_EXEC_PATH=$(pwd)/..
+export GIT_EXEC_PATH
# Test repository
test=trash
item->tag[taglen] = '\0';
item->tagged = lookup_object_type(object, type);
- if (item->tagged)
- add_ref(&item->object, item->tagged);
+ if (item->tagged && track_object_refs) {
+ struct object_refs *refs = alloc_object_refs(1);
+ refs->ref[0] = item->tagged;
+ set_object_refs(&item->object, refs);
+ }
return 0;
}
{
void *bufptr = buffer;
struct tree_entry_list **list_p;
+ int n_refs = 0;
if (item->object.parsed)
return 0;
obj = &entry->item.blob->object;
}
if (obj)
- add_ref(&item->object, obj);
+ n_refs++;
entry->parent = NULL; /* needs to be filled by the user */
*list_p = entry;
list_p = &entry->next;
}
+
+ if (track_object_refs) {
+ struct tree_entry_list *entry;
+ unsigned i = 0;
+ struct object_refs *refs = alloc_object_refs(n_refs);
+ for (entry = item->entries; entry; entry = entry->next)
+ refs->ref[i++] = entry->item.any;
+ set_object_refs(&item->object, refs);
+ }
+
return 0;
}