Merge git://repo.or.cz/alt-git.git

This commit is contained in:
Johannes Sixt
2007-03-22 09:02:00 +01:00
61 changed files with 2262 additions and 1019 deletions

2
.gitignore vendored
View File

@@ -38,6 +38,7 @@ git-diff-tree
git-describe
git-fast-import
git-fetch
git-fetch--tool
git-fetch-pack
git-findtags
git-fmt-merge-msg
@@ -75,6 +76,7 @@ git-merge-ours
git-merge-recursive
git-merge-resolve
git-merge-stupid
git-mergetool
git-mktag
git-mktree
git-name-rev

View File

@@ -0,0 +1,24 @@
GIT v1.5.0.4 Release Notes
==========================
Fixes since v1.5.0.3
--------------------
* Bugfixes
- 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

@@ -124,6 +124,7 @@ git-merge-index plumbingmanipulators
git-merge mainporcelain
git-merge-one-file purehelpers
git-merge-tree ancillaryinterrogators
git-mergetool ancillarymanipulators
git-mktag plumbingmanipulators
git-mktree plumbingmanipulators
git-mv mainporcelain

View File

@@ -453,6 +453,11 @@ merge.summary::
Whether to include summaries of merged commits in newly created
merge commit messages. False by default.
merge.tool::
Controls which merge resolution program is used by
gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff",
"meld", "xxdiff", "emerge"
merge.verbosity::
Controls the amount of output shown by the recursive merge
strategy. Level 0 outputs nothing except a final error

View File

@@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git-branch' [--color | --no-color] [-r | -a]
[-v [--abbrev=<length> | --no-abbrev]]
'git-branch' [-l] [-f] <branchname> [<start-point>]
'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git-branch' (-m | -M) [<oldbranch>] <newbranch>
'git-branch' (-d | -D) [-r] <branchname>...
@@ -26,6 +26,13 @@ It will start out with a head equal to the one given as <start-point>.
If no <start-point> is given, the branch will be created with a head
equal to that of the currently checked out branch.
When a local branch is started off a remote branch, git can setup the
branch so that gitlink:git-pull[1] will appropriately merge from that
remote branch. If this behavior is desired, it is possible to make it
the default using the global `branch.autosetupmerge` configuration
flag. Otherwise, it can be chosen per-branch using the `--track`
and `--no-track` options.
With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
If <oldbranch> had a corresponding reflog, it is renamed to match
<newbranch>, and a reflog entry is created to remember the branch

View File

@@ -8,7 +8,7 @@ git-checkout - Checkout and switch to a branch
SYNOPSIS
--------
[verse]
'git-checkout' [-q] [-f] [-b <new_branch> [-l]] [-m] [<branch>]
'git-checkout' [-q] [-f] [-b [--track | --no-track] <new_branch> [-l]] [-m] [<branch>]
'git-checkout' [<tree-ish>] <paths>...
DESCRIPTION
@@ -18,7 +18,8 @@ When <paths> are not given, this command switches branches by
updating the index and working tree to reflect the specified
branch, <branch>, and updating HEAD to be <branch> or, if
specified, <new_branch>. Using -b will cause <new_branch> to
be created.
be created; in this case you can use the --track or --no-track
options, which will be passed to `git branch`.
When <paths> are given, this command does *not* switch
branches. It updates the named paths in the working tree from
@@ -45,6 +46,16 @@ OPTIONS
by gitlink:git-check-ref-format[1]. Some of these checks
may restrict the characters allowed in a branch name.
--track::
When -b is given and a branch is created off a remote branch,
setup so that git-pull will automatically retrieve data from
the remote branch.
--no-track::
When -b is given and a branch is created off a remote branch,
force that git-pull will automatically retrieve data from
the remote branch independent of the configuration settings.
-l::
Create the new branch's ref log. This activates recording of
all changes to made the branch ref, enabling use of date

View File

@@ -86,7 +86,7 @@ OPTIONS
Remove the line matching the key from config file.
--unset-all::
Remove all matching lines from config file.
Remove all lines matching the key from config file.
-l, --list::
List all variables set in config file.

View File

@@ -121,10 +121,10 @@ so that calling 'cvs' effectively calls git-cvsserver.
Clients known to work
---------------------
CVS 1.12.9 on Debian
CVS 1.11.17 on MacOSX (from Fink package)
Eclipse 3.0, 3.1.2 on MacOSX (see Eclipse CVS Client Notes)
TortoiseCVS
- CVS 1.12.9 on Debian
- CVS 1.11.17 on MacOSX (from Fink package)
- Eclipse 3.0, 3.1.2 on MacOSX (see Eclipse CVS Client Notes)
- TortoiseCVS
Operations supported
--------------------
@@ -148,13 +148,16 @@ Copyright and Authors
This program is copyright The Open University UK - 2006.
Authors: Martyn Smith <martyn@catalyst.net.nz>
Martin Langhoff <martin@catalyst.net.nz>
with ideas and patches from participants of the git-list <git@vger.kernel.org>.
Authors:
- Martyn Smith <martyn@catalyst.net.nz>
- Martin Langhoff <martin@catalyst.net.nz>
with ideas and patches from participants of the git-list <git@vger.kernel.org>.
Documentation
--------------
Documentation by Martyn Smith <martyn@catalyst.net.nz> and Martin Langhoff <martin@catalyst.net.nz> Matthias Urlichs <smurf@smurf.noris.de>.
Documentation by Martyn Smith <martyn@catalyst.net.nz>, Martin Langhoff <martin@catalyst.net.nz>, and Matthias Urlichs <smurf@smurf.noris.de>.
GIT
---

View File

@@ -0,0 +1,46 @@
git-mergetool(1)
================
NAME
----
git-mergetool - Run merge conflict resolution tools to resolve merge conflicts
SYNOPSIS
--------
'git-mergetool' [--tool=<tool>] [<file>]...
DESCRIPTION
-----------
Use 'git mergetool' to run one of several merge utilities to resolve
merge conflicts. It is typically run after gitlink:git-merge[1].
If one or more <file> parameters are given, the merge tool program will
be run to resolve differences on each file. If no <file> names are
specified, 'git mergetool' will run the merge tool program on every file
with merge conflicts.
OPTIONS
-------
-t or --tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, and emerge.
If a merge resolution program is not specified, 'git mergetool'
will use the configuration variable merge.tool. If the
configuration variable merge.tool is not set, 'git mergetool'
will pick a suitable default.
Author
------
Written by Theodore Y Ts'o <tytso@mit.edu>
Documentation
--------------
Documentation by Theodore Y Ts'o.
GIT
---
Part of the gitlink:git[7] suite

View File

@@ -40,13 +40,13 @@ OPTIONS
pre-receive Hook
----------------
Before any ref is updated, if $GIT_DIR/hooks/pre-receive file exists
and is executable, it will be invoked once, with three parameters
per ref to be updated:
and is executable, it will be invoked once with no parameters. The
standard input of the hook will be one line per ref to be updated:
$GIT_DIR/hooks/pre-receive (refname sha1-old sha1-new)+
sha1-old SP sha1-new SP refname LF
The refname parameter is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 arguments after
The refname value is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 values before
each refname are the object names for the refname before and after
the update. Refs to be created will have sha1-old equal to 0{40},
while refs to be deleted will have sha1-new equal to 0{40}, otherwise
@@ -86,13 +86,14 @@ post-receive Hook
-----------------
After all refs were updated (or attempted to be updated), if any
ref update was successful, and if $GIT_DIR/hooks/post-receive
file exists and is executable, it will be invoke once with three
parameters for each successfully updated ref:
file exists and is executable, it will be invoke once with no
parameters. The standard input of the hook will be one line
for each successfully updated ref:
$GIT_DIR/hooks/post-receive (refname sha1-old sha1-new)+
sha1-old SP sha1-new SP refname LF
The refname parameter is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 arguments after
The refname value is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 values before
each refname are the object names for the refname before and after
the update. Refs that were created will have sha1-old equal to
0{40}, while refs that were deleted will have sha1-new equal to
@@ -105,18 +106,17 @@ ref listing the commits pushed to the repository:
#!/bin/sh
# mail out commit update information.
while test $# -gt 0
while read oval nval ref
do
if expr "$2" : '0*$' >/dev/null
if expr "$oval" : '0*$' >/dev/null
then
echo "Created a new ref, with the following commits:"
git-rev-list --pretty "$2"
git-rev-list --pretty "$nval"
else
echo "New commits:"
git-rev-list --pretty "$3" "^$2"
git-rev-list --pretty "$nval" "^$oval"
fi |
mail -s "Changes to ref $1" commit-list@mydomain
shift; shift; shift; # discard this ref's args
mail -s "Changes to ref $ref" commit-list@mydomain
done
exit 0

View File

@@ -40,7 +40,8 @@ The --cc option must be repeated for each user you want on the cc list.
the first will be sent as replies to the first email sent. When using
this, it is recommended that the first file given be an overview of the
entire patch series.
Default is --chain-reply-to
Default is the value of the 'sendemail.chainreplyto' configuration
value; if that is unspecified, default to --chain-reply-to.
--compose::
Use $EDITOR to edit an introductory message for the
@@ -91,6 +92,26 @@ The --cc option must be repeated for each user you want on the cc list.
The --to option must be repeated for each user you want on the to list.
CONFIGURATION
-------------
sendemail.aliasesfile::
To avoid typing long email addresses, point this to one or more
email aliases files. You must also supply 'sendemail.aliasfiletype'.
sendemail.aliasfiletype::
Format of the file(s) specified in sendemail.aliasesfile. Must be
one of 'mutt', 'mailrc', 'pine', or 'gnus'.
sendemail.bcc::
Email address (or alias) to always bcc.
sendemail.chainreplyto::
Boolean value specifying the default to the '--chain_reply_to'
parameter.
sendemail.smtpserver::
Default smtp server to use.
Author
------
Written by Ryan Anderson <ryan@michonline.com>

View File

@@ -104,6 +104,14 @@ accepts. However '--fetch-all' only fetches from the current
Like 'git-rebase'; this requires that the working tree be clean
and have no uncommitted changes.
+
--
-l;;
--local;;
Do not fetch remotely; only run 'git-rebase' against the
last fetched commit from the upstream SVN.
--
+
'dcommit'::
Commit each diff from a specified head directly to the SVN

View File

@@ -179,7 +179,7 @@ SCRIPT_SH = \
git-clean.sh git-clone.sh git-commit.sh \
git-fetch.sh git-gc.sh \
git-ls-remote.sh \
git-merge-one-file.sh git-parse-remote.sh \
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
git-pull.sh git-rebase.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-sh-setup.sh \
@@ -293,6 +293,7 @@ BUILTIN_OBJS = \
builtin-diff-files.o \
builtin-diff-index.o \
builtin-diff-tree.o \
builtin-fetch--tool.o \
builtin-fmt-merge-msg.o \
builtin-for-each-ref.o \
builtin-fsck.o \
@@ -952,8 +953,7 @@ dist: git.spec git-archive
$(TAR) rf $(GIT_TARNAME).tar \
$(GIT_TARNAME)/git.spec \
$(GIT_TARNAME)/version \
$(GIT_TARNAME)/git-gui/version \
$(GIT_TARNAME)/git-gui/credits
$(GIT_TARNAME)/git-gui/version
@rm -rf $(GIT_TARNAME)
gzip -f -9 $(GIT_TARNAME).tar

View File

@@ -12,6 +12,8 @@
static const char builtin_add_usage[] =
"git-add [-n] [-v] [-f] [--interactive | -i] [--] <filepattern>...";
static const char *excludes_file;
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
{
char *seen;
@@ -67,6 +69,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
path = git_path("info/exclude");
if (!access(path, R_OK))
add_excludes_from_file(dir, path);
if (!access(excludes_file, R_OK))
add_excludes_from_file(dir, excludes_file);
/*
* Calculate common prefix for the pathspec, and
@@ -88,6 +92,18 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
prune_directory(dir, pathspec, baselen);
}
static int git_add_config(const char *var, const char *value)
{
if (!strcmp(var, "core.excludesfile")) {
if (!value)
die("core.excludesfile without value");
excludes_file = xstrdup(value);
return 0;
}
return git_default_config(var, value);
}
static struct lock_file lock_file;
static const char ignore_warning[] =
@@ -115,7 +131,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
exit(1);
}
git_config(git_default_config);
git_config(git_add_config);
newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);

View File

@@ -12,7 +12,7 @@
#include "builtin.h"
static const char builtin_branch_usage[] =
"git-branch [-r] (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
"git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
#define REF_UNKNOWN_TYPE 0x00
#define REF_LOCAL_BRANCH 0x01
@@ -22,6 +22,8 @@ static const char builtin_branch_usage[] =
static const char *head;
static unsigned char head_sha1[20];
static int branch_track_remotes;
static int branch_use_color;
static char branch_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */
@@ -64,6 +66,9 @@ int git_branch_config(const char *var, const char *value)
color_parse(value, var, branch_colors[slot]);
return 0;
}
if (!strcmp(var, "branch.autosetupmerge"))
branch_track_remotes = git_config_bool(var, value);
return git_default_config(var, value);
}
@@ -309,14 +314,108 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
free_ref_list(&ref_list);
}
static char *config_repo;
static char *config_remote;
static const char *start_ref;
static int start_len;
static int base_len;
static int get_remote_branch_name(const char *value)
{
const char *colon;
const char *end;
if (*value == '+')
value++;
colon = strchr(value, ':');
if (!colon)
return 0;
end = value + strlen(value);
/* Try an exact match first. */
if (!strcmp(colon + 1, start_ref)) {
/* Truncate the value before the colon. */
nfasprintf(&config_repo, "%.*s", colon - value, value);
return 1;
}
/* Try with a wildcard match now. */
if (end - value > 2 && end[-2] == '/' && end[-1] == '*' &&
colon - value > 2 && colon[-2] == '/' && colon[-1] == '*' &&
(end - 2) - (colon + 1) == base_len &&
!strncmp(colon + 1, start_ref, base_len)) {
/* Replace the star with the remote branch name. */
nfasprintf(&config_repo, "%.*s%s",
(colon - 2) - value, value,
start_ref + base_len);
return 1;
}
return 0;
}
static int get_remote_config(const char *key, const char *value)
{
const char *var;
if (prefixcmp(key, "remote."))
return 0;
var = strrchr(key, '.');
if (var == key + 6)
return 0;
if (!strcmp(var, ".fetch") && get_remote_branch_name(value))
nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7);
return 0;
}
static void set_branch_defaults(const char *name, const char *real_ref)
{
char key[1024];
const char *slash = strrchr(real_ref, '/');
if (!slash)
return;
start_ref = real_ref;
start_len = strlen(real_ref);
base_len = slash - real_ref;
git_config(get_remote_config);
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);
printf("Branch %s set up to track remote branch %s.\n",
name, real_ref);
}
if (config_repo)
free(config_repo);
if (config_remote)
free(config_remote);
}
static void create_branch(const char *name, const char *start_name,
unsigned char *start_sha1,
int force, int reflog)
int force, int reflog, int track)
{
struct ref_lock *lock;
struct commit *commit;
unsigned char sha1[20];
char ref[PATH_MAX], msg[PATH_MAX + 20];
char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
int forcing = 0;
snprintf(ref, sizeof ref, "refs/heads/%s", name);
@@ -331,12 +430,23 @@ static void create_branch(const char *name, const char *start_name,
forcing = 1;
}
if (start_sha1)
/* detached HEAD */
hashcpy(sha1, start_sha1);
else if (get_sha1(start_name, sha1))
real_ref = NULL;
if (get_sha1(start_name, sha1))
die("Not a valid object name: '%s'.", start_name);
switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
case 0:
/* Not branching from any existing branch */
real_ref = NULL;
break;
case 1:
/* Unique completion -- good */
break;
default:
die("Ambiguous object name: '%s'.", start_name);
break;
}
if ((commit = lookup_commit_reference(sha1)) == NULL)
die("Not a valid branch point: '%s'.", start_name);
hashcpy(sha1, commit->object.sha1);
@@ -355,8 +465,17 @@ static void create_branch(const char *name, const char *start_name,
snprintf(msg, sizeof msg, "branch: Created from %s",
start_name);
/* When branching off a remote branch, set up so that git-pull
automatically merges from there. So far, this is only done for
remotes registered via .git/config. */
if (real_ref && track)
set_branch_defaults(name, real_ref);
if (write_ref_sha1(lock, sha1, msg) < 0)
die("Failed to write ref: %s.", strerror(errno));
if (real_ref)
free(real_ref);
}
static void rename_branch(const char *oldname, const char *newname, int force)
@@ -398,11 +517,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
int delete = 0, force_delete = 0, force_create = 0;
int rename = 0, force_rename = 0;
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
int reflog = 0;
int reflog = 0, track;
int kinds = REF_LOCAL_BRANCH;
int i;
git_config(git_branch_config);
track = branch_track_remotes;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -413,6 +533,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
i++;
break;
}
if (!strcmp(arg, "--track")) {
track = 1;
continue;
}
if (!strcmp(arg, "--no-track")) {
track = 0;
continue;
}
if (!strcmp(arg, "-d")) {
delete = 1;
continue;
@@ -498,10 +626,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
rename_branch(head, argv[i], force_rename);
else if (rename && (i == argc - 2))
rename_branch(argv[i], argv[i + 1], force_rename);
else if (i == argc - 1)
create_branch(argv[i], head, head_sha1, force_create, reflog);
else if (i == argc - 2)
create_branch(argv[i], argv[i+1], NULL, force_create, reflog);
else if (i == argc - 1 || i == argc - 2)
create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head,
force_create, reflog, track);
else
usage(builtin_branch_usage);

View File

@@ -160,7 +160,28 @@ static int fork_with_pipe(const char **argv, int *in, int *out)
return pid;
}
static int verify_bundle(struct bundle_header *header)
static int list_refs(struct ref_list *r, int argc, const char **argv)
{
int i;
for (i = 0; i < r->nr; i++) {
if (argc > 1) {
int j;
for (j = 1; j < argc; j++)
if (!strcmp(r->list[i].name, argv[j]))
break;
if (j == argc)
continue;
}
printf("%s %s\n", sha1_to_hex(r->list[i].sha1),
r->list[i].name);
}
return 0;
}
#define PREREQ_MARK (1u<<16)
static int verify_bundle(struct bundle_header *header, int verbose)
{
/*
* Do fast check, then if any prereqs are missing then go line by line
@@ -179,7 +200,7 @@ static int verify_bundle(struct bundle_header *header)
struct ref_list_entry *e = p->list + i;
struct object *o = parse_object(e->sha1);
if (o) {
o->flags |= BOUNDARY_SHOW;
o->flags |= PREREQ_MARK;
add_pending_object(&revs, o, e->name);
continue;
}
@@ -187,7 +208,7 @@ static int verify_bundle(struct bundle_header *header)
error(message);
error("%s %s", sha1_to_hex(e->sha1), e->name);
}
if (revs.pending.nr == 0)
if (revs.pending.nr != p->nr)
return ret;
req_nr = revs.pending.nr;
setup_revisions(2, argv, &revs, NULL);
@@ -202,7 +223,7 @@ static int verify_bundle(struct bundle_header *header)
i = req_nr;
while (i && (commit = get_revision(&revs)))
if (commit->object.flags & BOUNDARY_SHOW)
if (commit->object.flags & PREREQ_MARK)
i--;
for (i = 0; i < req_nr; i++)
@@ -216,56 +237,24 @@ static int verify_bundle(struct bundle_header *header)
for (i = 0; i < refs.nr; i++)
clear_commit_marks((struct commit *)refs.objects[i].item, -1);
if (verbose) {
struct ref_list *r;
r = &header->references;
printf("The bundle contains %d ref%s\n",
r->nr, (1 < r->nr) ? "s" : "");
list_refs(r, 0, NULL);
r = &header->prerequisites;
printf("The bundle requires these %d ref%s\n",
r->nr, (1 < r->nr) ? "s" : "");
list_refs(r, 0, NULL);
}
return ret;
}
static int list_heads(struct bundle_header *header, int argc, const char **argv)
{
int i;
struct ref_list *r = &header->references;
for (i = 0; i < r->nr; i++) {
if (argc > 1) {
int j;
for (j = 1; j < argc; j++)
if (!strcmp(r->list[i].name, argv[j]))
break;
if (j == argc)
continue;
}
printf("%s %s\n", sha1_to_hex(r->list[i].sha1),
r->list[i].name);
}
return 0;
}
static void show_commit(struct commit *commit)
{
write_or_die(1, sha1_to_hex(commit->object.sha1), 40);
write_or_die(1, "\n", 1);
if (commit->parents) {
free_commit_list(commit->parents);
commit->parents = NULL;
}
}
static void show_object(struct object_array_entry *p)
{
/* An object with name "foo\n0000000..." can be used to
* confuse downstream git-pack-objects very badly.
*/
const char *ep = strchr(p->name, '\n');
int len = ep ? ep - p->name : strlen(p->name);
write_or_die(1, sha1_to_hex(p->item->sha1), 40);
write_or_die(1, " ", 1);
if (len)
write_or_die(1, p->name, len);
write_or_die(1, "\n", 1);
}
static void show_edge(struct commit *commit)
{
; /* nothing to do */
return list_refs(&header->references, argc, argv);
}
static int create_bundle(struct bundle_header *header, const char *path,
@@ -273,19 +262,23 @@ 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(4 * sizeof(const char *));
int pid, in, out, i, status;
const char **argv_pack = xmalloc(5 * sizeof(const char *));
int pid, in, out, i, status, ref_count = 0;
char buffer[1024];
struct rev_info revs;
bundle_fd = (!strcmp(path, "-") ? 1 :
open(path, O_CREAT | O_WRONLY, 0666));
open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
if (bundle_fd < 0)
return error("Could not write to '%s'", path);
return error("Could not create '%s': %s", path, strerror(errno));
/* write signature */
write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
/* init revs to list objects for pack-objects later */
save_commit_buffer = 0;
init_revisions(&revs, NULL);
/* write prerequisites */
memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *));
argv_boundary[0] = "rev-list";
@@ -296,9 +289,20 @@ static int create_bundle(struct bundle_header *header, const char *path,
pid = fork_with_pipe(argv_boundary, NULL, &out);
if (pid < 0)
return -1;
while ((i = read_string(out, buffer, sizeof(buffer))) > 0)
if (buffer[0] == '-')
while ((i = read_string(out, buffer, sizeof(buffer))) > 0) {
unsigned char sha1[20];
if (buffer[0] == '-') {
write_or_die(bundle_fd, buffer, i);
if (!get_sha1_hex(buffer + 1, sha1)) {
struct object *object = parse_object(sha1);
object->flags |= UNINTERESTING;
add_pending_object(&revs, object, buffer);
}
} else if (!get_sha1_hex(buffer, sha1)) {
struct object *object = parse_object(sha1);
object->flags |= SHOWN;
}
}
while ((i = waitpid(pid, &status, 0)) < 0)
if (errno != EINTR)
return error("rev-list died");
@@ -306,28 +310,38 @@ static int create_bundle(struct bundle_header *header, const char *path,
return error("rev-list died %d", WEXITSTATUS(status));
/* write references */
save_commit_buffer = 0;
init_revisions(&revs, NULL);
revs.tag_objects = 1;
revs.tree_objects = 1;
revs.blob_objects = 1;
argc = setup_revisions(argc, argv, &revs, NULL);
if (argc > 1)
return error("unrecognized argument: %s'", argv[1]);
for (i = 0; i < revs.pending.nr; i++) {
struct object_array_entry *e = revs.pending.objects + i;
if (!(e->item->flags & UNINTERESTING)) {
unsigned char sha1[20];
char *ref;
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
continue;
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
write_or_die(bundle_fd, " ", 1);
write_or_die(bundle_fd, ref, strlen(ref));
write_or_die(bundle_fd, "\n", 1);
free(ref);
unsigned char sha1[20];
char *ref;
if (e->item->flags & UNINTERESTING)
continue;
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
continue;
/*
* Make sure the refs we wrote out is correct; --max-count and
* other limiting options could have prevented all the tips
* from getting output.
*/
if (!(e->item->flags & SHOWN)) {
warn("ref '%s' is excluded by the rev-list options",
e->name);
continue;
}
ref_count++;
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
write_or_die(bundle_fd, " ", 1);
write_or_die(bundle_fd, ref, strlen(ref));
write_or_die(bundle_fd, "\n", 1);
free(ref);
}
if (!ref_count)
die ("Refusing to create empty bundle.");
/* end header */
write_or_die(bundle_fd, "\n", 1);
@@ -336,36 +350,42 @@ static int create_bundle(struct bundle_header *header, const char *path,
argv_pack[0] = "pack-objects";
argv_pack[1] = "--all-progress";
argv_pack[2] = "--stdout";
argv_pack[3] = NULL;
argv_pack[3] = "--thin";
argv_pack[4] = NULL;
in = -1;
out = bundle_fd;
pid = fork_with_pipe(argv_pack, &in, &out);
if (pid < 0)
return error("Could not spawn pack-objects");
close(1);
dup2(in, 1);
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);
}
close(in);
prepare_revision_walk(&revs);
mark_edges_uninteresting(revs.commits, &revs, show_edge);
traverse_commit_list(&revs, show_commit, show_object);
close(1);
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR)
return -1;
if (!WIFEXITED(status) || WEXITSTATUS(status))
return error ("pack-objects died");
return 0;
return status;
}
static int unbundle(struct bundle_header *header, int bundle_fd,
int argc, const char **argv)
{
const char *argv_index_pack[] = {"index-pack", "--stdin", NULL};
const char *argv_index_pack[] = {"index-pack",
"--fix-thin", "--stdin", NULL};
int pid, status, dev_null;
if (verify_bundle(header))
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");
@@ -402,12 +422,12 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
memset(&header, 0, sizeof(header));
if (strcmp(cmd, "create") &&
!(bundle_fd = read_header(bundle_file, &header)))
(bundle_fd = read_header(bundle_file, &header)) < 0)
return 1;
if (!strcmp(cmd, "verify")) {
close(bundle_fd);
if (verify_bundle(&header))
if (verify_bundle(&header, 1))
return 1;
fprintf(stderr, "%s is okay\n", bundle_file);
return 0;
@@ -427,4 +447,3 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
} else
usage(bundle_usage);
}

505
builtin-fetch--tool.c Normal file
View File

@@ -0,0 +1,505 @@
#include "cache.h"
#include "refs.h"
#include "commit.h"
#define CHUNK_SIZE 1024
static char *get_stdin(void)
{
int offset = 0;
char *data = xmalloc(CHUNK_SIZE);
while (1) {
int cnt = xread(0, data + offset, CHUNK_SIZE);
if (cnt < 0)
die("error reading standard input: %s",
strerror(errno));
if (cnt == 0) {
data[offset] = 0;
break;
}
offset += cnt;
data = xrealloc(data, offset + CHUNK_SIZE);
}
return data;
}
static void show_new(enum object_type type, unsigned char *sha1_new)
{
fprintf(stderr, " %s: %s\n", typename(type),
find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
}
static int update_ref(const char *action,
const char *refname,
unsigned char *sha1,
unsigned char *oldval)
{
int len;
char msg[1024];
char *rla = getenv("GIT_REFLOG_ACTION");
static struct ref_lock *lock;
if (!rla)
rla = "(reflog update)";
len = snprintf(msg, sizeof(msg), "%s: %s", rla, action);
if (sizeof(msg) <= len)
die("insanely long action");
lock = lock_any_ref_for_update(refname, oldval);
if (!lock)
return 1;
if (write_ref_sha1(lock, sha1, msg) < 0)
return 1;
return 0;
}
static int update_local_ref(const char *name,
const char *new_head,
const char *note,
int verbose, int force)
{
unsigned char sha1_old[20], sha1_new[20];
char oldh[41], newh[41];
struct commit *current, *updated;
enum object_type type;
if (get_sha1_hex(new_head, sha1_new))
die("malformed object name %s", new_head);
type = sha1_object_info(sha1_new, NULL);
if (type < 0)
die("object %s not found", new_head);
if (!*name) {
/* Not storing */
if (verbose) {
fprintf(stderr, "* fetched %s\n", note);
show_new(type, sha1_new);
}
return 0;
}
if (get_sha1(name, sha1_old)) {
char *msg;
just_store:
/* new ref */
if (!strncmp(name, "refs/tags/", 10))
msg = "storing tag";
else
msg = "storing head";
fprintf(stderr, "* %s: storing %s\n",
name, note);
show_new(type, sha1_new);
return update_ref(msg, name, sha1_new, NULL);
}
if (!hashcmp(sha1_old, sha1_new)) {
if (verbose) {
fprintf(stderr, "* %s: same as %s\n", name, note);
show_new(type, sha1_new);
}
return 0;
}
if (!strncmp(name, "refs/tags/", 10)) {
fprintf(stderr, "* %s: updating with %s\n", name, note);
show_new(type, sha1_new);
return update_ref("updating tag", name, sha1_new, NULL);
}
current = lookup_commit_reference(sha1_old);
updated = lookup_commit_reference(sha1_new);
if (!current || !updated)
goto just_store;
strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
if (in_merge_bases(current, &updated, 1)) {
fprintf(stderr, "* %s: fast forward to %s\n",
name, note);
fprintf(stderr, " old..new: %s..%s\n", oldh, newh);
return update_ref("fast forward", name, sha1_new, sha1_old);
}
if (!force) {
fprintf(stderr,
"* %s: not updating to non-fast forward %s\n",
name, note);
fprintf(stderr,
" old...new: %s...%s\n", oldh, newh);
return 1;
}
fprintf(stderr,
"* %s: forcing update to non-fast forward %s\n",
name, note);
fprintf(stderr, " old...new: %s...%s\n", oldh, newh);
return update_ref("forced-update", name, sha1_new, sha1_old);
}
static int append_fetch_head(FILE *fp,
const char *head, const char *remote,
const char *remote_name, const char *remote_nick,
const char *local_name, int not_for_merge,
int verbose, int force)
{
struct commit *commit;
int remote_len, i, note_len;
unsigned char sha1[20];
char note[1024];
const char *what, *kind;
if (get_sha1(head, sha1))
return error("Not a valid object name: %s", head);
commit = lookup_commit_reference(sha1);
if (!commit)
not_for_merge = 1;
if (!strcmp(remote_name, "HEAD")) {
kind = "";
what = "";
}
else if (!strncmp(remote_name, "refs/heads/", 11)) {
kind = "branch";
what = remote_name + 11;
}
else if (!strncmp(remote_name, "refs/tags/", 10)) {
kind = "tag";
what = remote_name + 10;
}
else if (!strncmp(remote_name, "refs/remotes/", 13)) {
kind = "remote branch";
what = remote_name + 13;
}
else {
kind = "";
what = remote_name;
}
remote_len = strlen(remote);
for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)
;
remote_len = i + 1;
if (4 < i && !strncmp(".git", remote + i - 3, 4))
remote_len = i - 3;
note_len = 0;
if (*what) {
if (*kind)
note_len += sprintf(note + note_len, "%s ", kind);
note_len += sprintf(note + note_len, "'%s' of ", what);
}
note_len += sprintf(note + note_len, "%.*s", remote_len, remote);
fprintf(fp, "%s\t%s\t%s\n",
sha1_to_hex(commit ? commit->object.sha1 : sha1),
not_for_merge ? "not-for-merge" : "",
note);
return update_local_ref(local_name, head, note, verbose, force);
}
static char *keep;
static void remove_keep(void)
{
if (keep && *keep)
unlink(keep);
}
static void remove_keep_on_signal(int signo)
{
remove_keep();
signal(SIGINT, SIG_DFL);
raise(signo);
}
static char *find_local_name(const char *remote_name, const char *refs,
int *force_p, int *not_for_merge_p)
{
const char *ref = refs;
int len = strlen(remote_name);
while (ref) {
const char *next;
int single_force, not_for_merge;
while (*ref == '\n')
ref++;
if (!*ref)
break;
next = strchr(ref, '\n');
single_force = not_for_merge = 0;
if (*ref == '+') {
single_force = 1;
ref++;
}
if (*ref == '.') {
not_for_merge = 1;
ref++;
if (*ref == '+') {
single_force = 1;
ref++;
}
}
if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
const char *local_part = ref + len + 1;
char *ret;
int retlen;
if (!next)
retlen = strlen(local_part);
else
retlen = next - local_part;
ret = xmalloc(retlen + 1);
memcpy(ret, local_part, retlen);
ret[retlen] = 0;
*force_p = single_force;
*not_for_merge_p = not_for_merge;
return ret;
}
ref = next;
}
return NULL;
}
static int fetch_native_store(FILE *fp,
const char *remote,
const char *remote_nick,
const char *refs,
int verbose, int force)
{
char buffer[1024];
int err = 0;
signal(SIGINT, remove_keep_on_signal);
atexit(remove_keep);
while (fgets(buffer, sizeof(buffer), stdin)) {
int len;
char *cp;
char *local_name;
int single_force, not_for_merge;
for (cp = buffer; *cp && !isspace(*cp); cp++)
;
if (*cp)
*cp++ = 0;
len = strlen(cp);
if (len && cp[len-1] == '\n')
cp[--len] = 0;
if (!strcmp(buffer, "failed"))
die("Fetch failure: %s", remote);
if (!strcmp(buffer, "pack"))
continue;
if (!strcmp(buffer, "keep")) {
char *od = get_object_directory();
int len = strlen(od) + strlen(cp) + 50;
keep = xmalloc(len);
sprintf(keep, "%s/pack/pack-%s.keep", od, cp);
continue;
}
local_name = find_local_name(cp, refs,
&single_force, &not_for_merge);
if (!local_name)
continue;
err |= append_fetch_head(fp,
buffer, remote, cp, remote_nick,
local_name, not_for_merge,
verbose, force || single_force);
}
return err;
}
static int parse_reflist(const char *reflist)
{
const char *ref;
printf("refs='");
for (ref = reflist; ref; ) {
const char *next;
while (*ref && isspace(*ref))
ref++;
if (!*ref)
break;
for (next = ref; *next && !isspace(*next); next++)
;
printf("\n%.*s", (int)(next - ref), ref);
ref = next;
}
printf("'\n");
printf("rref='");
for (ref = reflist; ref; ) {
const char *next, *colon;
while (*ref && isspace(*ref))
ref++;
if (!*ref)
break;
for (next = ref; *next && !isspace(*next); next++)
;
if (*ref == '.')
ref++;
if (*ref == '+')
ref++;
colon = strchr(ref, ':');
putchar('\n');
printf("%.*s", (int)((colon ? colon : next) - ref), ref);
ref = next;
}
printf("'\n");
return 0;
}
static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
const char **refs)
{
int i, matchlen, replacelen;
int found_one = 0;
const char *remote = *refs++;
numrefs--;
if (numrefs == 0) {
fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",
remote);
printf("empty\n");
}
for (i = 0; i < numrefs; i++) {
const char *ref = refs[i];
const char *lref = ref;
const char *colon;
const char *tail;
const char *ls;
const char *next;
if (*lref == '+')
lref++;
colon = strchr(lref, ':');
tail = lref + strlen(lref);
if (!(colon &&
2 < colon - lref &&
colon[-1] == '*' &&
colon[-2] == '/' &&
2 < tail - (colon + 1) &&
tail[-1] == '*' &&
tail[-2] == '/')) {
/* not a glob */
if (!found_one++)
printf("explicit\n");
printf("%s\n", ref);
continue;
}
/* glob */
if (!found_one++)
printf("glob\n");
/* lref to colon-2 is remote hierarchy name;
* colon+1 to tail-2 is local.
*/
matchlen = (colon-1) - lref;
replacelen = (tail-1) - (colon+1);
for (ls = ls_remote_result; ls; ls = next) {
const char *eol;
unsigned char sha1[20];
int namelen;
while (*ls && isspace(*ls))
ls++;
next = strchr(ls, '\n');
eol = !next ? (ls + strlen(ls)) : next;
if (!memcmp("^{}", eol-3, 3))
continue;
if (eol - ls < 40)
continue;
if (get_sha1_hex(ls, sha1))
continue;
ls += 40;
while (ls < eol && isspace(*ls))
ls++;
/* ls to next (or eol) is the name.
* is it identical to lref to colon-2?
*/
if ((eol - ls) <= matchlen ||
strncmp(ls, lref, matchlen))
continue;
/* Yes, it is a match */
namelen = eol - ls;
if (lref != ref)
putchar('+');
printf("%.*s:%.*s%.*s\n",
namelen, ls,
replacelen, colon + 1,
namelen - matchlen, ls + matchlen);
}
}
return 0;
}
int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
{
int verbose = 0;
int force = 0;
while (1 < argc) {
const char *arg = argv[1];
if (!strcmp("-v", arg))
verbose = 1;
else if (!strcmp("-f", arg))
force = 1;
else
break;
argc--;
argv++;
}
if (argc <= 1)
return error("Missing subcommand");
if (!strcmp("append-fetch-head", argv[1])) {
int result;
FILE *fp;
if (argc != 8)
return error("append-fetch-head takes 6 args");
fp = fopen(git_path("FETCH_HEAD"), "a");
result = append_fetch_head(fp, argv[2], argv[3],
argv[4], argv[5],
argv[6], !!argv[7][0],
verbose, force);
fclose(fp);
return result;
}
if (!strcmp("native-store", argv[1])) {
int result;
FILE *fp;
if (argc != 5)
return error("fetch-native-store takes 3 args");
fp = fopen(git_path("FETCH_HEAD"), "a");
result = fetch_native_store(fp, argv[2], argv[3], argv[4],
verbose, force);
fclose(fp);
return result;
}
if (!strcmp("parse-reflist", argv[1])) {
const char *reflist;
if (argc != 3)
return error("parse-reflist takes 1 arg");
reflist = argv[2];
if (!strcmp(reflist, "-"))
reflist = get_stdin();
return parse_reflist(reflist);
}
if (!strcmp("expand-refs-wildcard", argv[1])) {
const char *reflist;
if (argc < 4)
return error("expand-refs-wildcard takes at least 2 args");
reflist = argv[2];
if (!strcmp(reflist, "-"))
reflist = get_stdin();
return expand_refs_wildcard(reflist, argc - 3, argv + 3);
}
return error("Unknown subcommand: %s", argv[1]);
}

View File

@@ -431,6 +431,19 @@ static const char emsg_missing_context_len[] =
static const char emsg_missing_argument[] =
"option requires an argument -%s";
static int strtoul_ui(char const *s, unsigned int *result)
{
unsigned long ul;
char *p;
errno = 0;
ul = strtoul(s, &p, 10);
if (errno || *p || p == s || (unsigned int) ul != ul)
return -1;
*result = ul;
return 0;
}
int cmd_grep(int argc, const char **argv, const char *prefix)
{
int hit = 0;
@@ -553,7 +566,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
scan = arg + 1;
break;
}
if (sscanf(scan, "%u", &num) != 1)
if (strtoul_ui(scan, &num))
die(emsg_invalid_context_len, scan);
switch (arg[1]) {
case 'A':

View File

@@ -11,19 +11,22 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
static int keep_subject;
static const char *metainfo_charset;
static char line[1000];
static char date[1000];
static char name[1000];
static char email[1000];
static char subject[1000];
static enum {
TE_DONTCARE, TE_QP, TE_BASE64,
} transfer_encoding;
static char charset[256];
static enum {
TYPE_TEXT, TYPE_OTHER,
} message_type;
static char multipart_boundary[1000];
static int multipart_boundary_len;
static char charset[256];
static int patch_lines;
static char **p_hdr_data, **s_hdr_data;
#define MAX_HDR_PARSED 10
#define MAX_BOUNDARIES 5
static char *sanity_check(char *name, char *email)
{
@@ -137,15 +140,13 @@ static int handle_from(char *in_line)
return 1;
}
static int handle_date(char *line)
static int handle_header(char *line, char *data, int ofs)
{
strcpy(date, line);
return 0;
}
if (!line || !data)
return 1;
strcpy(data, line+ofs);
static int handle_subject(char *line)
{
strcpy(subject, line);
return 0;
}
@@ -177,17 +178,32 @@ static int slurp_attr(const char *line, const char *name, char *attr)
return 1;
}
static int handle_subcontent_type(char *line)
struct content_type {
char *boundary;
int boundary_len;
};
static struct content_type content[MAX_BOUNDARIES];
static struct content_type *content_top = content;
static int handle_content_type(char *line)
{
/* We do not want to mess with boundary. Note that we do not
* handle nested multipart.
*/
if (strcasestr(line, "boundary=")) {
fprintf(stderr, "Not handling nested multipart message.\n");
exit(1);
char boundary[256];
if (strcasestr(line, "text/") == NULL)
message_type = TYPE_OTHER;
if (slurp_attr(line, "boundary=", boundary + 2)) {
memcpy(boundary, "--", 2);
if (content_top++ >= &content[MAX_BOUNDARIES]) {
fprintf(stderr, "Too many boundaries to handle\n");
exit(1);
}
content_top->boundary_len = strlen(boundary);
content_top->boundary = xmalloc(content_top->boundary_len+1);
strcpy(content_top->boundary, boundary);
}
slurp_attr(line, "charset=", charset);
if (*charset) {
if (slurp_attr(line, "charset=", charset)) {
int i, c;
for (i = 0; (c = charset[i]) != 0; i++)
charset[i] = tolower(c);
@@ -195,17 +211,6 @@ static int handle_subcontent_type(char *line)
return 0;
}
static int handle_content_type(char *line)
{
*multipart_boundary = 0;
if (slurp_attr(line, "boundary=", multipart_boundary + 2)) {
memcpy(multipart_boundary, "--", 2);
multipart_boundary_len = strlen(multipart_boundary);
}
slurp_attr(line, "charset=", charset);
return 0;
}
static int handle_content_transfer_encoding(char *line)
{
if (strcasestr(line, "base64"))
@@ -219,7 +224,7 @@ static int handle_content_transfer_encoding(char *line)
static int is_multipart_boundary(const char *line)
{
return (!memcmp(line, multipart_boundary, multipart_boundary_len));
return (!memcmp(line, content_top->boundary, content_top->boundary_len));
}
static int eatspace(char *line)
@@ -230,62 +235,6 @@ static int eatspace(char *line)
return len;
}
#define SEEN_FROM 01
#define SEEN_DATE 02
#define SEEN_SUBJECT 04
#define SEEN_BOGUS_UNIX_FROM 010
#define SEEN_PREFIX 020
/* First lines of body can have From:, Date:, and Subject: or empty */
static void handle_inbody_header(int *seen, char *line)
{
if (*seen & SEEN_PREFIX)
return;
if (isspace(*line)) {
char *cp;
for (cp = line + 1; *cp; cp++) {
if (!isspace(*cp))
break;
}
if (!*cp)
return;
}
if (!memcmp(">From", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_BOGUS_UNIX_FROM)) {
*seen |= SEEN_BOGUS_UNIX_FROM;
return;
}
}
if (!memcmp("From:", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
*seen |= SEEN_FROM;
return;
}
}
if (!memcmp("Date:", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_DATE)) {
handle_date(line+6);
*seen |= SEEN_DATE;
return;
}
}
if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
if (!(*seen & SEEN_SUBJECT)) {
handle_subject(line+9);
*seen |= SEEN_SUBJECT;
return;
}
}
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
if (!(*seen & SEEN_SUBJECT)) {
handle_subject(line);
*seen |= SEEN_SUBJECT;
return;
}
}
*seen |= SEEN_PREFIX;
}
static char *cleanup_subject(char *subject)
{
if (keep_subject)
@@ -296,7 +245,7 @@ static char *cleanup_subject(char *subject)
switch (*subject) {
case 'r': case 'R':
if (!memcmp("e:", subject+1, 2)) {
subject +=3;
subject += 3;
continue;
}
break;
@@ -341,57 +290,62 @@ static void cleanup_space(char *buf)
}
static void decode_header(char *it);
typedef int (*header_fn_t)(char *);
struct header_def {
const char *name;
header_fn_t func;
int namelen;
static char *header[MAX_HDR_PARSED] = {
"From","Subject","Date",
};
static void check_header(char *line, struct header_def *header)
static int check_header(char *line, char **hdr_data)
{
int i;
if (header[0].namelen <= 0) {
for (i = 0; header[i].name; i++)
header[i].namelen = strlen(header[i].name);
}
for (i = 0; header[i].name; i++) {
int len = header[i].namelen;
if (!strncasecmp(line, header[i].name, len) &&
/* search for the interesting parts */
for (i = 0; header[i]; i++) {
int len = strlen(header[i]);
if (!hdr_data[i] &&
!strncasecmp(line, header[i], len) &&
line[len] == ':' && isspace(line[len + 1])) {
/* Unwrap inline B and Q encoding, and optionally
* normalize the meta information to utf8.
*/
decode_header(line + len + 2);
header[i].func(line + len + 2);
break;
hdr_data[i] = xmalloc(1000 * sizeof(char));
if (! handle_header(line, hdr_data[i], len + 2)) {
return 1;
}
}
}
}
static void check_subheader_line(char *line)
{
static struct header_def header[] = {
{ "Content-Type", handle_subcontent_type },
{ "Content-Transfer-Encoding",
handle_content_transfer_encoding },
{ NULL },
};
check_header(line, header);
}
static void check_header_line(char *line)
{
static struct header_def header[] = {
{ "From", handle_from },
{ "Date", handle_date },
{ "Subject", handle_subject },
{ "Content-Type", handle_content_type },
{ "Content-Transfer-Encoding",
handle_content_transfer_encoding },
{ NULL },
};
check_header(line, header);
/* Content stuff */
if (!strncasecmp(line, "Content-Type", 12) &&
line[12] == ':' && isspace(line[12 + 1])) {
decode_header(line + 12 + 2);
if (! handle_content_type(line)) {
return 1;
}
}
if (!strncasecmp(line, "Content-Transfer-Encoding", 25) &&
line[25] == ':' && isspace(line[25 + 1])) {
decode_header(line + 25 + 2);
if (! handle_content_transfer_encoding(line)) {
return 1;
}
}
/* for inbody stuff */
if (!memcmp(">From", line, 5) && isspace(line[5]))
return 1;
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
for (i = 0; header[i]; i++) {
if (!memcmp("Subject: ", header[i], 9)) {
if (! handle_header(line, hdr_data[i], 0)) {
return 1;
}
}
}
}
/* no match */
return 0;
}
static int is_rfc2822_header(char *line)
@@ -647,39 +601,170 @@ static void decode_transfer_encoding(char *line)
}
}
static void handle_info(void)
static int handle_filter(char *line);
static int find_boundary(void)
{
char *sub;
sub = cleanup_subject(subject);
cleanup_space(name);
cleanup_space(date);
cleanup_space(email);
cleanup_space(sub);
fprintf(fout, "Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
name, email, sub, date);
while(fgets(line, sizeof(line), fin) != NULL) {
if (is_multipart_boundary(line))
return 1;
}
return 0;
}
/* We are inside message body and have read line[] already.
* Spit out the commit log.
*/
static int handle_commit_msg(int *seen)
static int handle_boundary(void)
{
again:
if (!memcmp(line+content_top->boundary_len, "--", 2)) {
/* we hit an end boundary */
/* pop the current boundary off the stack */
free(content_top->boundary);
/* technically won't happen as is_multipart_boundary()
will fail first. But just in case..
*/
if (content_top-- < content) {
fprintf(stderr, "Detected mismatched boundaries, "
"can't recover\n");
exit(1);
}
handle_filter("\n");
/* skip to the next boundary */
if (!find_boundary())
return 0;
goto again;
}
/* set some defaults */
transfer_encoding = TE_DONTCARE;
charset[0] = 0;
message_type = TYPE_TEXT;
/* slurp in this section's info */
while (read_one_header_line(line, sizeof(line), fin))
check_header(line, p_hdr_data);
/* eat the blank line after section info */
return (fgets(line, sizeof(line), fin) != NULL);
}
static inline int patchbreak(const char *line)
{
/* Beginning of a "diff -" header? */
if (!memcmp("diff -", line, 6))
return 1;
/* CVS "Index: " line? */
if (!memcmp("Index: ", line, 7))
return 1;
/*
* "--- <filename>" starts patches without headers
* "---<sp>*" is a manual separator
*/
if (!memcmp("---", line, 3)) {
line += 3;
/* space followed by a filename? */
if (line[0] == ' ' && !isspace(line[1]))
return 1;
/* Just whitespace? */
for (;;) {
unsigned char c = *line++;
if (c == '\n')
return 1;
if (!isspace(c))
break;
}
return 0;
}
return 0;
}
static int handle_commit_msg(char *line)
{
static int still_looking = 1;
if (!cmitmsg)
return 0;
do {
if (!memcmp("diff -", line, 6) ||
!memcmp("---", line, 3) ||
!memcmp("Index: ", line, 7))
if (still_looking) {
char *cp = line;
if (isspace(*line)) {
for (cp = line + 1; *cp; cp++) {
if (!isspace(*cp))
break;
}
if (!*cp)
return 0;
}
if ((still_looking = check_header(cp, s_hdr_data)) != 0)
return 0;
}
if (patchbreak(line)) {
fclose(cmitmsg);
cmitmsg = NULL;
return 1;
}
fputs(line, cmitmsg);
return 0;
}
static int handle_patch(char *line)
{
fputs(line, patchfile);
patch_lines++;
return 0;
}
static int handle_filter(char *line)
{
static int filter = 0;
/* filter tells us which part we left off on
* a non-zero return indicates we hit a filter point
*/
switch (filter) {
case 0:
if (!handle_commit_msg(line))
break;
if ((multipart_boundary[0] && is_multipart_boundary(line))) {
/* We come here when the first part had only
* the commit message without any patch. We
* pretend we have not seen this line yet, and
* go back to the loop.
*/
return 1;
filter++;
case 1:
if (!handle_patch(line))
break;
filter++;
default:
return 1;
}
return 0;
}
static void handle_body(void)
{
int rc = 0;
static char newline[2000];
static char *np = newline;
/* Skip up to the first boundary */
if (content_top->boundary) {
if (!find_boundary())
return;
}
do {
/* process any boundary lines */
if (content_top->boundary && is_multipart_boundary(line)) {
/* flush any leftover */
if ((transfer_encoding == TE_BASE64) &&
(np != newline)) {
handle_filter(newline);
}
if (!handle_boundary())
return;
}
/* Unwrap transfer encoding and optionally
@@ -689,105 +774,80 @@ static int handle_commit_msg(int *seen)
if (metainfo_charset)
convert_to_utf8(line, charset);
handle_inbody_header(seen, line);
if (!(*seen & SEEN_PREFIX))
switch (transfer_encoding) {
case TE_BASE64:
{
char *op = line;
/* binary data most likely doesn't have newlines */
if (message_type != TYPE_TEXT) {
rc = handle_filter(line);
break;
}
/* this is a decoded line that may contain
* multiple new lines. Pass only one chunk
* at a time to handle_filter()
*/
do {
while (*op != '\n' && *op != 0)
*np++ = *op++;
*np = *op;
if (*np != 0) {
/* should be sitting on a new line */
*(++np) = 0;
op++;
rc = handle_filter(newline);
np = newline;
}
} while (*op != 0);
/* the partial chunk is saved in newline and
* will be appended by the next iteration of fgets
*/
break;
}
default:
rc = handle_filter(line);
}
if (rc)
/* nothing left to filter */
break;
} while (fgets(line, sizeof(line), fin));
return;
}
static void handle_info(void)
{
char *sub;
char *hdr;
int i;
for (i = 0; header[i]; i++) {
/* only print inbody headers if we output a patch file */
if (patch_lines && s_hdr_data[i])
hdr = s_hdr_data[i];
else if (p_hdr_data[i])
hdr = p_hdr_data[i];
else
continue;
fputs(line, cmitmsg);
} while (fgets(line, sizeof(line), fin) != NULL);
fclose(cmitmsg);
cmitmsg = NULL;
return 0;
}
/* We have done the commit message and have the first
* line of the patch in line[].
*/
static void handle_patch(void)
{
do {
if (multipart_boundary[0] && is_multipart_boundary(line))
break;
/* Only unwrap transfer encoding but otherwise do not
* do anything. We do *NOT* want UTF-8 conversion
* here; we are dealing with the user payload.
*/
decode_transfer_encoding(line);
fputs(line, patchfile);
patch_lines++;
} while (fgets(line, sizeof(line), fin) != NULL);
}
/* multipart boundary and transfer encoding are set up for us, and we
* are at the end of the sub header. do equivalent of handle_body up
* to the next boundary without closing patchfile --- we will expect
* that the first part to contain commit message and a patch, and
* handle other parts as pure patches.
*/
static int handle_multipart_one_part(int *seen)
{
int n = 0;
while (fgets(line, sizeof(line), fin) != NULL) {
again:
n++;
if (is_multipart_boundary(line))
break;
if (handle_commit_msg(seen))
goto again;
handle_patch();
break;
}
if (n == 0)
return -1;
return 0;
}
static void handle_multipart_body(void)
{
int seen = 0;
int part_num = 0;
/* Skip up to the first boundary */
while (fgets(line, sizeof(line), fin) != NULL)
if (is_multipart_boundary(line)) {
part_num = 1;
break;
if (!memcmp(header[i], "Subject", 7)) {
sub = cleanup_subject(hdr);
cleanup_space(sub);
fprintf(fout, "Subject: %s\n", sub);
} else if (!memcmp(header[i], "From", 4)) {
handle_from(hdr);
fprintf(fout, "Author: %s\n", name);
fprintf(fout, "Email: %s\n", email);
} else {
cleanup_space(hdr);
fprintf(fout, "%s: %s\n", header[i], hdr);
}
if (!part_num)
return;
/* We are on boundary line. Start slurping the subhead. */
while (1) {
int hdr = read_one_header_line(line, sizeof(line), fin);
if (!hdr) {
if (handle_multipart_one_part(&seen) < 0)
return;
/* Reset per part headers */
transfer_encoding = TE_DONTCARE;
charset[0] = 0;
}
else
check_subheader_line(line);
}
fclose(patchfile);
if (!patch_lines) {
fprintf(stderr, "No patch found\n");
exit(1);
}
}
/* Non multipart message */
static void handle_body(void)
{
int seen = 0;
handle_commit_msg(&seen);
handle_patch();
fclose(patchfile);
if (!patch_lines) {
fprintf(stderr, "No patch found\n");
exit(1);
}
fprintf(fout, "\n");
}
int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
@@ -809,18 +869,16 @@ int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
fclose(cmitmsg);
return -1;
}
while (1) {
int hdr = read_one_header_line(line, sizeof(line), fin);
if (!hdr) {
if (multipart_boundary[0])
handle_multipart_body();
else
handle_body();
handle_info();
break;
}
check_header_line(line);
}
p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));
s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));
/* process the email header */
while (read_one_header_line(line, sizeof(line), fin))
check_header(line, p_hdr_data);
handle_body();
handle_info();
return 0;
}

View File

@@ -304,8 +304,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
if (!access(".mailmap", R_OK))
read_mailmap(".mailmap");
if (rev.pending.nr == 0)
if (rev.pending.nr == 0) {
if (isatty(0))
fprintf(stderr, "(reading log to summarize from standard input)\n");
read_from_stdin(&list);
}
else
get_from_rev(&rev, &list);

View File

@@ -32,6 +32,7 @@ extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
extern int cmd_diff(int argc, const char **argv, const char *prefix);
extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
extern int cmd_fetch__tool(int argc, const char **argv, const char *prefix);
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);

View File

@@ -373,6 +373,7 @@ extern struct packed_git {
struct packed_git *next;
struct pack_window *windows;
uint32_t *index_base;
time_t mtime;
off_t index_size;
off_t pack_size;
int pack_fd;
@@ -481,6 +482,7 @@ extern struct tag *alloc_tag_node(void);
extern void alloc_report(void);
/* trace.c */
extern int nfasprintf(char **str, const char *fmt, ...);
extern int nfvasprintf(char **str, const char *fmt, va_list va);
extern void trace_printf(const char *format, ...);
extern void trace_argv_printf(const char **argv, int count, const char *format, ...);

View File

@@ -1058,7 +1058,7 @@ static void load_tree(struct tree_entry *root)
struct tree_entry *e = new_tree_entry();
if (t->entry_count == t->entry_capacity)
root->tree = t = grow_tree_content(t, 8);
root->tree = t = grow_tree_content(t, t->entry_count);
t->entries[t->entry_count++] = e;
e->tree = NULL;
@@ -1066,7 +1066,7 @@ static void load_tree(struct tree_entry *root)
if (!c)
die("Corrupt mode in %s", sha1_to_hex(sha1));
e->versions[0].mode = e->versions[1].mode;
e->name = to_atom(c, (unsigned short)strlen(c));
e->name = to_atom(c, strlen(c));
c += e->name->str_len + 1;
hashcpy(e->versions[0].sha1, (unsigned char*)c);
hashcpy(e->versions[1].sha1, (unsigned char*)c);
@@ -1225,9 +1225,9 @@ static int tree_content_set(
}
if (t->entry_count == t->entry_capacity)
root->tree = t = grow_tree_content(t, 8);
root->tree = t = grow_tree_content(t, t->entry_count);
e = new_tree_entry();
e->name = to_atom(p, (unsigned short)n);
e->name = to_atom(p, n);
e->versions[0].mode = 0;
hashclr(e->versions[0].sha1);
t->entries[t->entry_count++] = e;

View File

@@ -290,6 +290,10 @@ do
git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
<"$dotest/$msgnum" >"$dotest/info" ||
stop_here $this
test -s $dotest/patch || {
echo "Patch is empty. Was is split wrong?"
stop_here $this
}
git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
;;
esac

View File

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

View File

@@ -12,6 +12,7 @@ new=
new_name=
force=
branch=
track=
newbranch=
newbranch_log=
merge=
@@ -33,7 +34,10 @@ while [ "$#" != "0" ]; do
die "git checkout: we do not like '$newbranch' as a branch name."
;;
"-l")
newbranch_log=1
newbranch_log=-l
;;
"--track"|"--no-track")
track="$arg"
;;
"-f")
force=1
@@ -85,6 +89,11 @@ while [ "$#" != "0" ]; do
esac
done
case "$new_branch,$track" in
,--*)
die "git checkout: --track and --no-track require -b"
esac
case "$force$merge" in
11)
die "git checkout: -f and -m are incompatible"
@@ -199,7 +208,7 @@ else
work=`git write-tree` &&
git read-tree --reset -u $new || exit
eval GITHEAD_$new=${new_name:-${branch:-$new}} &&
eval GITHEAD_$new='${new_name:-${branch:-$new}}' &&
eval GITHEAD_$work=local &&
export GITHEAD_$new GITHEAD_$work &&
git merge-recursive $old -- $new $work
@@ -242,11 +251,7 @@ fi
#
if [ "$?" -eq 0 ]; then
if [ "$newbranch" ]; then
if [ "$newbranch_log" ]; then
mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$newbranch")
touch "$GIT_DIR/logs/refs/heads/$newbranch"
fi
git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
git-branch $track $newbranch_log "$newbranch" "$new_name" || exit
branch="$newbranch"
fi
if test -n "$branch"

View File

@@ -109,133 +109,11 @@ ls_remote_result=$(git ls-remote $exec "$remote") ||
die "Cannot get the repository state from $remote"
append_fetch_head () {
head_="$1"
remote_="$2"
remote_name_="$3"
remote_nick_="$4"
local_name_="$5"
case "$6" in
t) not_for_merge_='not-for-merge' ;;
'') not_for_merge_= ;;
esac
# remote-nick is the URL given on the command line (or a shorthand)
# remote-name is the $GIT_DIR relative refs/ path we computed
# for this refspec.
# the $note_ variable will be fed to git-fmt-merge-msg for further
# processing.
case "$remote_name_" in
HEAD)
note_= ;;
refs/heads/*)
note_="$(expr "$remote_name_" : 'refs/heads/\(.*\)')"
note_="branch '$note_' of " ;;
refs/tags/*)
note_="$(expr "$remote_name_" : 'refs/tags/\(.*\)')"
note_="tag '$note_' of " ;;
refs/remotes/*)
note_="$(expr "$remote_name_" : 'refs/remotes/\(.*\)')"
note_="remote branch '$note_' of " ;;
*)
note_="$remote_name of " ;;
esac
remote_1_=$(expr "z$remote_" : 'z\(.*\)\.git/*$') &&
remote_="$remote_1_"
note_="$note_$remote_"
# 2.6.11-tree tag would not be happy to be fed to resolve.
if git-cat-file commit "$head_" >/dev/null 2>&1
then
headc_=$(git-rev-parse --verify "$head_^0") || exit
echo "$headc_ $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
else
echo "$head_ not-for-merge $note_" >>"$GIT_DIR/FETCH_HEAD"
fi
update_local_ref "$local_name_" "$head_" "$note_"
}
update_local_ref () {
# If we are storing the head locally make sure that it is
# a fast forward (aka "reverse push").
label_=$(git-cat-file -t $2)
newshort_=$(git-rev-parse --short $2)
if test -z "$1" ; then
[ "$verbose" ] && echo >&2 "* fetched $3"
[ "$verbose" ] && echo >&2 " $label_: $newshort_"
return 0
fi
oldshort_=$(git show-ref --hash --abbrev "$1" 2>/dev/null)
case "$1" in
refs/tags/*)
# Tags need not be pointing at commits so there
# is no way to guarantee "fast-forward" anyway.
if test -n "$oldshort_"
then
if now_=$(git show-ref --hash "$1") && test "$now_" = "$2"
then
[ "$verbose" ] && echo >&2 "* $1: same as $3"
[ "$verbose" ] && echo >&2 " $label_: $newshort_" ||:
else
echo >&2 "* $1: updating with $3"
echo >&2 " $label_: $newshort_"
git-update-ref -m "$GIT_REFLOG_ACTION: updating tag" "$1" "$2"
fi
else
echo >&2 "* $1: storing $3"
echo >&2 " $label_: $newshort_"
git-update-ref -m "$GIT_REFLOG_ACTION: storing tag" "$1" "$2"
fi
;;
refs/heads/* | refs/remotes/*)
# $1 is the ref being updated.
# $2 is the new value for the ref.
local=$(git-rev-parse --verify "$1^0" 2>/dev/null)
if test "$local"
then
# Require fast-forward.
mb=$(git-merge-base "$local" "$2") &&
case "$2,$mb" in
$local,*)
if test -n "$verbose"
then
echo >&2 "* $1: same as $3"
echo >&2 " $label_: $newshort_"
fi
;;
*,$local)
echo >&2 "* $1: fast forward to $3"
echo >&2 " old..new: $oldshort_..$newshort_"
git-update-ref -m "$GIT_REFLOG_ACTION: fast-forward" "$1" "$2" "$local"
;;
*)
false
;;
esac || {
case ",$force,$single_force," in
*,t,*)
echo >&2 "* $1: forcing update to non-fast forward $3"
echo >&2 " old...new: $oldshort_...$newshort_"
git-update-ref -m "$GIT_REFLOG_ACTION: forced-update" "$1" "$2" "$local"
;;
*)
echo >&2 "* $1: not updating to non-fast forward $3"
echo >&2 " old...new: $oldshort_...$newshort_"
exit 1
;;
esac
}
else
echo >&2 "* $1: storing $3"
echo >&2 " $label_: $newshort_"
git-update-ref -m "$GIT_REFLOG_ACTION: storing head" "$1" "$2"
fi
;;
esac
flags=
test -n "$verbose" && flags="$flags -v"
test -n "$force" && flags="$flags -f"
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
git-fetch--tool $flags append-fetch-head "$@"
}
# updating the current HEAD with git-fetch in a bare
@@ -279,7 +157,38 @@ then
fi
fi
fetch_main () {
fetch_native () {
eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
eval "$eval"
( : subshell because we muck with IFS
IFS=" $LF"
(
if test -f "$remote" ; then
test -n "$shallow_depth" &&
die "shallow clone with bundle is not supported"
git-bundle unbundle "$remote" $rref ||
echo failed "$remote"
else
git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \
"$remote" $rref ||
echo failed "$remote"
fi
) |
(
flags=
test -n "$verbose" && flags="$flags -v"
test -n "$force" && flags="$flags -f"
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
git-fetch--tool $flags native-store \
"$remote" "$remote_nick" "$refs"
)
) || exit
}
fetch_dumb () {
reflist="$1"
refs=
rref=
@@ -371,9 +280,6 @@ fetch_main () {
rsync_slurped_objects=t
}
;;
*)
# We will do git native transport with just one call later.
continue ;;
esac
append_fetch_head "$head" "$remote" \
@@ -381,80 +287,17 @@ fetch_main () {
done
case "$remote" in
http://* | https://* | ftp://* | rsync://* )
;; # we are already done.
*)
( : subshell because we muck with IFS
IFS=" $LF"
(
if test -f "$remote" ; then
test -n "$shallow_depth" &&
die "shallow clone with bundle is not supported"
git-bundle unbundle "$remote" $rref ||
echo failed "$remote"
else
git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \
"$remote" $rref ||
echo failed "$remote"
fi
) |
(
trap '
if test -n "$keepfile" && test -f "$keepfile"
then
rm -f "$keepfile"
fi
' 0
keepfile=
while read sha1 remote_name
do
case "$sha1" in
failed)
echo >&2 "Fetch failure: $remote"
exit 1 ;;
# special line coming from index-pack with the pack name
pack)
continue ;;
keep)
keepfile="$GIT_OBJECT_DIRECTORY/pack/pack-$remote_name.keep"
continue ;;
esac
found=
single_force=
for ref in $refs
do
case "$ref" in
+$remote_name:*)
single_force=t
not_for_merge=
found="$ref"
break ;;
.+$remote_name:*)
single_force=t
not_for_merge=t
found="$ref"
break ;;
.$remote_name:*)
not_for_merge=t
found="$ref"
break ;;
$remote_name:*)
not_for_merge=
found="$ref"
break ;;
esac
done
local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
append_fetch_head "$sha1" "$remote" \
"$remote_name" "$remote_nick" "$local_name" \
"$not_for_merge" || exit
done
)
) || exit ;;
esac
}
fetch_main () {
case "$remote" in
http://* | https://* | ftp://* | rsync://* )
fetch_dumb "$@"
;;
*)
fetch_native "$@"
;;
esac
}
fetch_main "$reflist" || exit

1
git-gui/.gitignore vendored
View File

@@ -1,4 +1,3 @@
CREDITS-FILE
GIT-VERSION-FILE
git-citool
git-gui

View File

@@ -1,71 +0,0 @@
#!/bin/sh
CF=CREDITS-FILE
tip=
tree_search ()
{
head=$1
tree=$2
for p in $(git rev-list --parents --max-count=1 $head 2>/dev/null)
do
test $tree = $(git rev-parse $p^{tree} 2>/dev/null) &&
vn=$(git describe --abbrev=4 $p 2>/dev/null) &&
case "$vn" in
gitgui-[0-9]*) echo $p; break;;
esac
done
}
generate_credits ()
{
tip=$1 &&
rm -f "$2" &&
git shortlog -n -s $tip | sed 's/: .*$//' >"$2" || exit
}
# Always use the tarball credits file if found, just
# in case we are somehow contained in a larger git
# repository that doesn't actually track our state.
# (At least one package manager is doing this.)
#
# We may be a subproject, so try looking for the merge
# commit that supplied this directory content if we are
# not at the toplevel. We probably will always be the
# second parent in the commit, but we shouldn't rely on
# that fact.
#
credits_tmp=/tmp/gitgui-credits-$$
trap 'rm -f "$credits_tmp"' 0
orig="$credits_tmp"
if test -f credits
then
orig=credits
elif prefix="$(git rev-parse --show-prefix 2>/dev/null)" &&
test -n "$prefix" &&
head=$(git rev-list --max-count=1 HEAD -- . 2>/dev/null) &&
tree=$(git rev-parse --verify "HEAD:$prefix" 2>/dev/null) &&
tip=$(tree_search $head $tree) &&
test -n "$tip"
then
generate_credits $tip "$orig" || exit
elif tip="$(git rev-parse --verify HEAD 2>/dev/null)" &&
test -n "$tip"
then
generate_credits $tip "$orig" || exit
else
echo "error: Cannot locate authorship information." >&2
exit 1
fi
if test -f "$orig" && cmp -s "$orig" "$CF"
then
: noop
else
rm -f "$CF" &&
cat "$orig" >"$CF"
fi

View File

@@ -7,8 +7,9 @@ GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
@$(SHELL_PATH) ./GIT-VERSION-GEN
-include GIT-VERSION-FILE
SCRIPT_SH = git-gui.sh
GITGUI_BUILT_INS = git-citool
ALL_PROGRAMS = git-gui $(GITGUI_BUILT_INS)
ALL_PROGRAMS = $(GITGUI_BUILT_INS) $(patsubst %.sh,%,$(SCRIPT_SH))
ifndef SHELL_PATH
SHELL_PATH = /bin/sh
@@ -27,28 +28,29 @@ ifndef V
QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
endif
ifeq ($(findstring $(MAKEFLAGS),s),s)
QUIET_GEN =
QUIET_BUILT_IN =
endif
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
git-gui: git-gui.sh GIT-VERSION-FILE CREDITS-FILE
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
$(QUIET_GEN)rm -f $@ $@+ && \
sed -n \
-e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \
-e '1,/^set gitgui_credits /p' \
$@.sh >$@+ && \
cat CREDITS-FILE >>$@+ && \
sed -e '1,/^set gitgui_credits /d' $@.sh >>$@+ && \
chmod +x $@+ && \
mv $@+ $@
CREDITS-FILE: CREDITS-GEN .FORCE-CREDITS-FILE
$(QUIET_GEN)$(SHELL_PATH) ./CREDITS-GEN
$(GITGUI_BUILT_INS): git-gui
$(QUIET_BUILT_IN)rm -f $@ && ln git-gui $@
# These can record GITGUI_VERSION
$(patsubst %.sh,%,$(SCRIPT_SH)): GIT-VERSION-FILE
all:: $(ALL_PROGRAMS)
install: all
@@ -56,14 +58,12 @@ install: all
$(INSTALL) git-gui '$(DESTDIR_SQ)$(gitexecdir_SQ)'
$(foreach p,$(GITGUI_BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
dist-version: CREDITS-FILE
dist-version:
@mkdir -p $(TARDIR)
@echo $(GITGUI_VERSION) > $(TARDIR)/version
@cat CREDITS-FILE > $(TARDIR)/credits
clean::
rm -f $(ALL_PROGRAMS) GIT-VERSION-FILE CREDITS-FILE
rm -f $(ALL_PROGRAMS) GIT-VERSION-FILE
.PHONY: all install dist-version clean
.PHONY: .FORCE-GIT-VERSION-FILE
.PHONY: .FORCE-CREDITS-FILE

View File

@@ -19,9 +19,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA}
set gitgui_credits {
Paul Mackerras
}
######################################################################
##
@@ -302,6 +299,11 @@ proc ask_popup {msg} {
##
## version check
if {{--version} eq $argv || {version} eq $argv} {
puts "git-gui version $appvers"
exit
}
set req_maj 1
set req_min 5
@@ -1171,7 +1173,7 @@ File [short_path $path] cannot be committed by this program.
}
}
}
if {!$files_ready} {
if {!$files_ready && ![string match *merge $curType]} {
info_popup {No changes to commit.
You must add at least 1 file before you can commit.
@@ -4492,61 +4494,6 @@ proc do_commit {} {
commit_tree
}
proc do_credits {} {
global gitgui_credits
set w .credits_dialog
toplevel $w
wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
label $w.header -text {git-gui Contributors} -font font_uibold
pack $w.header -side top -fill x
frame $w.buttons
button $w.buttons.close -text {Close} \
-font font_ui \
-command [list destroy $w]
pack $w.buttons.close -side right
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
frame $w.credits
text $w.credits.t \
-background [$w.header cget -background] \
-yscrollcommand [list $w.credits.sby set] \
-width 20 \
-height 10 \
-wrap none \
-borderwidth 1 \
-relief solid \
-padx 5 -pady 5 \
-font font_ui
scrollbar $w.credits.sby -command [list $w.credits.t yview]
pack $w.credits.sby -side right -fill y
pack $w.credits.t -fill both -expand 1
pack $w.credits -side top -fill both -expand 1 -padx 5 -pady 5
label $w.desc \
-text "All portions are copyrighted by their respective authors
and are distributed under the GNU General Public License." \
-padx 5 -pady 5 \
-justify left \
-anchor w \
-borderwidth 1 \
-relief solid \
-font font_ui
pack $w.desc -side top -fill x -padx 5 -pady 5
$w.credits.t insert end "[string trim $gitgui_credits]\n"
$w.credits.t conf -state disabled
$w.credits.t see 1.0
bind $w <Visibility> "grab $w; focus $w"
bind $w <Key-Escape> [list destroy $w]
wm title $w [$w.header cget -text]
tkwait window $w
}
proc do_about {} {
global appvers copyright
global tcl_patchLevel tk_patchLevel
@@ -4563,10 +4510,6 @@ proc do_about {} {
button $w.buttons.close -text {Close} \
-font font_ui \
-command [list destroy $w]
button $w.buttons.credits -text {Contributors} \
-font font_ui \
-command do_credits
pack $w.buttons.credits -side left
pack $w.buttons.close -side right
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
@@ -5116,8 +5059,6 @@ enable_option branch
enable_option transport
switch -- $subcommand {
--version -
version -
browser -
blame {
disable_option multicommit
@@ -5488,11 +5429,6 @@ bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
# -- Not a normal commit type invocation? Do that instead!
#
switch -- $subcommand {
--version -
version {
puts "git-gui version $appvers"
exit
}
browser {
if {[llength $argv] != 1} {
puts stderr "usage: $argv0 browser commit"

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

@@ -339,7 +339,12 @@ f,*)
git-update-index --refresh 2>/dev/null
new_head=$(git-rev-parse --verify "$1^0") &&
merge_local_changes $head $new_head &&
finish "$new_head" "Fast forward" || exit
msg="Fast forward"
if test -n "$have_message"
then
msg="$msg (no commit created; -m option ignored)"
fi
finish "$new_head" "$msg" || exit
dropsave
exit 0
;;

352
git-mergetool.sh Executable file
View File

@@ -0,0 +1,352 @@
#!/bin/sh
#
# This program resolves merge conflicts in git
#
# Copyright (c) 2006 Theodore Y. Ts'o
#
# This file is licensed under the GPL v2, or a later version
# at the discretion of Junio C Hammano.
#
USAGE='[--tool=tool] [file to merge] ...'
SUBDIRECTORY_OK=Yes
. git-sh-setup
require_work_tree
# Returns true if the mode reflects a symlink
function is_symlink () {
test "$1" = 120000
}
function local_present () {
test -n "$local_mode"
}
function remote_present () {
test -n "$remote_mode"
}
function base_present () {
test -n "$base_mode"
}
cleanup_temp_files () {
if test "$1" = --save-backup ; then
mv -- "$BACKUP" "$path.orig"
rm -f -- "$LOCAL" "$REMOTE" "$BASE"
else
rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
fi
}
function describe_file () {
mode="$1"
branch="$2"
file="$3"
echo -n " "
if test -z "$mode"; then
echo -n "'$path' was deleted"
elif is_symlink "$mode" ; then
echo -n "'$path' is a symlink containing '"
cat "$file"
echo -n "'"
else
if base_present; then
echo -n "'$path' was created"
else
echo -n "'$path' was modified"
fi
fi
echo " in the $branch branch"
}
resolve_symlink_merge () {
while /bin/true; do
echo -n "Use (r)emote or (l)ocal, or (a)bort? "
read ans
case "$ans" in
[lL]*)
git-checkout-index -f --stage=2 -- "$path"
git-add -- "$path"
cleanup_temp_files --save-backup
return
;;
[rR]*)
git-checkout-index -f --stage=3 -- "$path"
git-add -- "$path"
cleanup_temp_files --save-backup
return
;;
[qQ]*)
exit 1
;;
esac
done
}
resolve_deleted_merge () {
while /bin/true; do
echo -n "Use (m)odified or (d)eleted file, or (a)bort? "
read ans
case "$ans" in
[mM]*)
git-add -- "$path"
cleanup_temp_files --save-backup
return
;;
[dD]*)
git-rm -- "$path"
cleanup_temp_files
return
;;
[qQ]*)
exit 1
;;
esac
done
}
merge_file () {
path="$1"
if test ! -f "$path" ; then
echo "$path: file not found"
exit 1
fi
f=`git-ls-files -u -- "$path"`
if test -z "$f" ; then
echo "$path: file does not need merging"
exit 1
fi
BACKUP="$path.BACKUP.$$"
LOCAL="$path.LOCAL.$$"
REMOTE="$path.REMOTE.$$"
BASE="$path.BASE.$$"
mv -- "$path" "$BACKUP"
cp -- "$BACKUP" "$path"
base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
base_present && git cat-file blob ":1:$path" > "$BASE" 2>/dev/null
local_present && git cat-file blob ":2:$path" > "$LOCAL" 2>/dev/null
remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null
if test -z "$local_mode" -o -z "$remote_mode"; then
echo "Deleted merge conflict for $path:"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_deleted_merge
return
fi
if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
echo "Symlink merge conflict for $path:"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_symlink_merge
return
fi
echo "Normal merge conflict for $path:"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
echo -n "Hit return to start merge resolution tool ($merge_tool): "
read ans
case "$merge_tool" in
kdiff3)
if base_present ; then
(kdiff3 --auto --L1 "$path (Base)" -L2 "$path (Local)" --L3 "$path (Remote)" \
-o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
else
(kdiff3 --auto -L1 "$path (Local)" --L2 "$path (Remote)" \
-o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
fi
status=$?
if test "$status" -eq 0; then
rm "$BACKUP"
fi
;;
tkdiff)
if base_present ; then
tkdiff -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
else
tkdiff -o "$path" -- "$LOCAL" "$REMOTE"
fi
status=$?
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
fi
;;
meld)
touch "$BACKUP"
meld -- "$LOCAL" "$path" "$REMOTE"
if test "$path" -nt "$BACKUP" ; then
status=0;
else
while true; do
echo "$path seems unchanged."
echo -n "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
y*|Y*) status=0; break ;;
n*|N*) status=1; break ;;
esac
done
fi
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
fi
;;
xxdiff)
touch "$BACKUP"
if base_present ; then
xxdiff -X --show-merged-pane \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
else
xxdiff -X --show-merged-pane \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$path" -- "$LOCAL" "$REMOTE"
fi
if test "$path" -nt "$BACKUP" ; then
status=0;
else
while true; do
echo "$path seems unchanged."
echo -n "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
y*|Y*) status=0; break ;;
n*|N*) status=1; break ;;
esac
done
fi
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
fi
;;
emerge)
if base_present ; then
emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$path"
else
emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$path"
fi
status=$?
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
fi
;;
esac
if test "$status" -ne 0; then
echo "merge of $path failed" 1>&2
mv -- "$BACKUP" "$path"
exit 1
fi
git add -- "$path"
cleanup_temp_files
}
while case $# in 0) break ;; esac
do
case "$1" in
-t|--tool*)
case "$#,$1" in
*,*=*)
merge_tool=`expr "z$1" : 'z-[^=]*=\(.*\)'`
;;
1,*)
usage ;;
*)
merge_tool="$2"
shift ;;
esac
;;
--)
break
;;
-*)
usage
;;
*)
break
;;
esac
shift
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
fi
if test -z "$merge_tool" ; then
if type kdiff3 >/dev/null 2>&1 && test -n "$DISPLAY"; then
merge_tool="kdiff3";
elif type tkdiff >/dev/null 2>&1 && test -n "$DISPLAY"; then
merge_tool=tkdiff
elif type xxdiff >/dev/null 2>&1 && test -n "$DISPLAY"; then
merge_tool=xxdiff
elif type meld >/dev/null 2>&1 && test -n "$DISPLAY"; then
merge_tool=meld
elif type emacs >/dev/null 2>&1; then
merge_tool=emerge
else
echo "No available merge resolution programs available."
exit 1
fi
fi
case "$merge_tool" in
kdiff3|tkdiff|meld|xxdiff)
if ! type "$merge_tool" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available"
exit 1
fi
;;
emerge)
if ! type "emacs" > /dev/null 2>&1; then
echo "Emacs is not available"
exit 1
fi
;;
*)
echo "Unknown merge tool: $merge_tool"
exit 1
;;
esac
if test $# -eq 0 ; then
files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u`
if test -z "$files" ; then
echo "No files need merging"
exit 0
fi
echo Merging the files: $files
git ls-files -u | sed -e 's/^[^ ]* //' | sort -u | while read i
do
echo ""
merge_file "$i" < /dev/tty > /dev/tty
done
else
while test $# -gt 0; do
echo ""
merge_file "$1"
shift
done
fi
exit 0

View File

@@ -81,51 +81,8 @@ get_remote_default_refs_for_push () {
# is to help prevent randomly "globbed" ref from being chosen as
# a merge candidate
expand_refs_wildcard () {
remote="$1"
shift
first_one=yes
if test "$#" = 0
then
echo empty
echo >&2 "Nothing specified for fetching with remote.$remote.fetch"
fi
for ref
do
lref=${ref#'+'}
# a non glob pattern is given back as-is.
expr "z$lref" : 'zrefs/.*/\*:refs/.*/\*$' >/dev/null || {
if test -n "$first_one"
then
echo "explicit"
first_one=
fi
echo "$ref"
continue
}
# glob
if test -n "$first_one"
then
echo "glob"
first_one=
fi
from=`expr "z$lref" : 'z\(refs/.*/\)\*:refs/.*/\*$'`
to=`expr "z$lref" : 'zrefs/.*/\*:\(refs/.*/\)\*$'`
local_force=
test "z$lref" = "z$ref" || local_force='+'
echo "$ls_remote_result" |
sed -e '/\^{}$/d' |
(
IFS=' '
while read sha1 name
do
# ignore the ones that do not start with $from
mapped=${name#"$from"}
test "z$name" = "z$mapped" && continue
echo "${local_force}${name}:${to}${mapped}"
done
)
done
echo "$ls_remote_result" |
git fetch--tool expand-refs-wildcard "-" "$@"
}
# Subroutine to canonicalize remote:local notation.

View File

@@ -73,6 +73,10 @@ mkdir $tmp_dir || exit 2
for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
echo $patch_name
(cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3
test -s $dotest/patch || {
echo "Patch is empty. Was is split wrong?"
stop_here $this
}
# Parse the author information
export GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' "$tmp_info")

View File

@@ -149,6 +149,16 @@ if ($@) {
$term = new FakeTerm "$@: going non-interactive";
}
my $def_chain = $repo->config_boolean('sendemail.chainreplyto');
if ($def_chain and $def_chain eq 'false') {
$chain_reply_to = 0;
}
@bcclist = $repo->config('sendemail.bcc');
if (!@bcclist or !$bcclist[0]) {
@bcclist = ();
}
# Begin by accumulating all the variables (defined above), that we will end up
# needing, first, from the command line:

View File

@@ -56,7 +56,7 @@ my ($_stdin, $_help, $_edit,
$_message, $_file,
$_template, $_shared,
$_version, $_fetch_all,
$_merge, $_strategy, $_dry_run,
$_merge, $_strategy, $_dry_run, $_local,
$_prefix, $_no_checkout, $_verbose);
$Git::SVN::_follow_parent = 1;
my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
@@ -145,6 +145,7 @@ my %cmd = (
{ 'merge|m|M' => \$_merge,
'verbose|v' => \$_verbose,
'strategy|s=s' => \$_strategy,
'local|l' => \$_local,
'fetch-all|all' => \$_fetch_all,
%fc_opts } ],
'commit-diff' => [ \&cmd_commit_diff,
@@ -439,7 +440,9 @@ sub cmd_rebase {
command_noisy('status');
exit 1;
}
$_fetch_all ? $gs->fetch_all : $gs->fetch;
unless ($_local) {
$_fetch_all ? $gs->fetch_all : $gs->fetch;
}
command_noisy(rebase_cmd(), $gs->refname);
}

1
git.c
View File

@@ -247,6 +247,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "diff-files", cmd_diff_files },
{ "diff-index", cmd_diff_index, RUN_SETUP },
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
{ "fetch--tool", cmd_fetch__tool, RUN_SETUP },
{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
{ "format-patch", cmd_format_patch, RUN_SETUP },

View File

@@ -37,7 +37,7 @@ $(makfile): ../GIT-CFLAGS Makefile
echo ' echo $(instdir_SQ)' >> $@
else
$(makfile): Makefile.PL ../GIT-CFLAGS
$(QUIET_GEN)'$(PERL_PATH_SQ)' $< PREFIX='$(prefix_SQ)'
'$(PERL_PATH_SQ)' $< PREFIX='$(prefix_SQ)'
endif
# this is just added comfort for calling make directly in perl dir

View File

@@ -67,47 +67,11 @@ struct command {
static struct command *commands;
static const char update_hook[] = "hooks/update";
static const char pre_receive_hook[] = "hooks/pre-receive";
static const char post_receive_hook[] = "hooks/post-receive";
static int run_hook(const char *hook_name,
struct command *first_cmd,
int single)
static int hook_status(int code, const char *hook_name)
{
struct command *cmd;
int argc, code;
const char **argv;
for (argc = 0, cmd = first_cmd; cmd; cmd = cmd->next) {
if (!cmd->error_string)
argc += 3;
if (single)
break;
}
if (!argc || access(hook_name, X_OK) < 0)
return 0;
argv = xmalloc(sizeof(*argv) * (2 + argc));
argv[0] = hook_name;
for (argc = 1, cmd = first_cmd; cmd; cmd = cmd->next) {
if (!cmd->error_string) {
argv[argc++] = xstrdup(cmd->ref_name);
argv[argc++] = xstrdup(sha1_to_hex(cmd->old_sha1));
argv[argc++] = xstrdup(sha1_to_hex(cmd->new_sha1));
}
if (single)
break;
}
argv[argc] = NULL;
code = run_command_v_opt(argv,
RUN_COMMAND_NO_STDIN | RUN_COMMAND_STDOUT_TO_STDERR);
while (--argc > 0)
free((char*)argv[argc]);
free(argv);
switch (code) {
case 0:
return 0;
@@ -115,6 +79,8 @@ static int run_hook(const char *hook_name,
return error("hook fork failed");
case -ERR_RUN_COMMAND_EXEC:
return error("hook execute failed");
case -ERR_RUN_COMMAND_PIPE:
return error("hook pipe failed");
case -ERR_RUN_COMMAND_WAITPID:
return error("waitpid failed");
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
@@ -129,6 +95,69 @@ static int run_hook(const char *hook_name,
}
}
static int run_hook(const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
struct command *cmd;
struct child_process proc;
const char *argv[2];
int have_input = 0, code;
for (cmd = commands; !have_input && cmd; cmd = cmd->next) {
if (!cmd->error_string)
have_input = 1;
}
if (!have_input || access(hook_name, X_OK) < 0)
return 0;
argv[0] = hook_name;
argv[1] = NULL;
memset(&proc, 0, sizeof(proc));
proc.argv = argv;
proc.in = -1;
proc.stdout_to_stderr = 1;
code = start_command(&proc);
if (code)
return hook_status(code, hook_name);
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string) {
size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
sha1_to_hex(cmd->old_sha1),
sha1_to_hex(cmd->new_sha1),
cmd->ref_name);
if (write_in_full(proc.in, buf, n) != n)
break;
}
}
return hook_status(finish_command(&proc), hook_name);
}
static int run_update_hook(struct command *cmd)
{
static const char update_hook[] = "hooks/update";
struct child_process proc;
const char *argv[5];
if (access(update_hook, X_OK) < 0)
return 0;
argv[0] = update_hook;
argv[1] = cmd->ref_name;
argv[2] = sha1_to_hex(cmd->old_sha1);
argv[3] = sha1_to_hex(cmd->new_sha1);
argv[4] = NULL;
memset(&proc, 0, sizeof(proc));
proc.argv = argv;
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
return hook_status(run_command(&proc), update_hook);
}
static const char *update(struct command *cmd)
{
const char *name = cmd->ref_name;
@@ -165,7 +194,7 @@ static const char *update(struct command *cmd)
return "non-fast forward";
}
}
if (run_hook(update_hook, cmd, 1)) {
if (run_update_hook(cmd)) {
error("hook declined to update %s", name);
return "hook declined";
}
@@ -238,7 +267,7 @@ static void execute_commands(const char *unpacker_error)
return;
}
if (run_hook(pre_receive_hook, commands, 0)) {
if (run_hook(pre_receive_hook)) {
while (cmd) {
cmd->error_string = "pre-receive hook declined";
cmd = cmd->next;
@@ -485,7 +514,7 @@ int main(int argc, char **argv)
unlink(pack_lockfile);
if (report_status)
report(unpack_status);
run_hook(post_receive_hook, commands, 0);
run_hook(post_receive_hook);
run_update_post_hook(commands);
}
return 0;

View File

@@ -437,36 +437,6 @@ static void limit_list(struct rev_info *revs)
continue;
p = &commit_list_insert(commit, p)->next;
}
if (revs->boundary) {
/* mark the ones that are on the result list first */
for (list = newlist; list; list = list->next) {
struct commit *commit = list->item;
commit->object.flags |= TMP_MARK;
}
for (list = newlist; list; list = list->next) {
struct commit *commit = list->item;
struct object *obj = &commit->object;
struct commit_list *parent;
if (obj->flags & UNINTERESTING)
continue;
for (parent = commit->parents;
parent;
parent = parent->next) {
struct commit *pcommit = parent->item;
if (!(pcommit->object.flags & UNINTERESTING))
continue;
pcommit->object.flags |= BOUNDARY;
if (pcommit->object.flags & TMP_MARK)
continue;
pcommit->object.flags |= TMP_MARK;
p = &commit_list_insert(pcommit, p)->next;
}
}
for (list = newlist; list; list = list->next) {
struct commit *commit = list->item;
commit->object.flags &= ~TMP_MARK;
}
}
revs->commits = newlist;
}
@@ -1193,17 +1163,6 @@ static void rewrite_parents(struct rev_info *revs, struct commit *commit)
}
}
static void mark_boundary_to_show(struct commit *commit)
{
struct commit_list *p = commit->parents;
while (p) {
commit = p->item;
p = p->next;
if (commit->object.flags & BOUNDARY)
commit->object.flags |= BOUNDARY_SHOW;
}
}
static int commit_match(struct commit *commit, struct rev_info *opt)
{
if (!opt->grep_filter)
@@ -1235,15 +1194,9 @@ static struct commit *get_revision_1(struct rev_info *revs)
*/
if (!revs->limited) {
if (revs->max_age != -1 &&
(commit->date < revs->max_age)) {
if (revs->boundary)
commit->object.flags |=
BOUNDARY_SHOW | BOUNDARY;
else
continue;
} else
add_parents_to_list(revs, commit,
&revs->commits);
(commit->date < revs->max_age))
continue;
add_parents_to_list(revs, commit, &revs->commits);
}
if (commit->object.flags & SHOWN)
continue;
@@ -1252,18 +1205,6 @@ static struct commit *get_revision_1(struct rev_info *revs)
revs->ignore_packed))
continue;
/* We want to show boundary commits only when their
* children are shown. When path-limiter is in effect,
* rewrite_parents() drops some commits from getting shown,
* and there is no point showing boundary parents that
* are not shown. After rewrite_parents() rewrites the
* parents of a commit that is shown, we mark the boundary
* parents with BOUNDARY_SHOW.
*/
if (commit->object.flags & BOUNDARY_SHOW) {
commit->object.flags |= SHOWN;
return commit;
}
if (commit->object.flags & UNINTERESTING)
continue;
if (revs->min_age != -1 && (commit->date > revs->min_age))
@@ -1286,80 +1227,136 @@ static struct commit *get_revision_1(struct rev_info *revs)
if (revs->parents)
rewrite_parents(revs, commit);
}
if (revs->boundary)
mark_boundary_to_show(commit);
commit->object.flags |= SHOWN;
return commit;
} while (revs->commits);
return NULL;
}
static void gc_boundary(struct object_array *array)
{
unsigned nr = array->nr;
unsigned alloc = array->alloc;
struct object_array_entry *objects = array->objects;
if (alloc <= nr) {
unsigned i, j;
for (i = j = 0; i < nr; i++) {
if (objects[i].item->flags & SHOWN)
continue;
if (i != j)
objects[j] = objects[i];
j++;
}
for (i = j; i < nr; i++)
objects[i].item = NULL;
array->nr = j;
}
}
struct commit *get_revision(struct rev_info *revs)
{
struct commit *c = NULL;
struct commit_list *l;
if (revs->reverse) {
struct commit_list *list;
/*
* rev_info.reverse is used to note the fact that we
* want to output the list of revisions in reverse
* order. To accomplish this goal, reverse can have
* different values:
*
* 0 do nothing
* 1 reverse the list
* 2 internal use: we have already obtained and
* reversed the list, now we only need to yield
* its items.
*/
if (revs->reverse == 1) {
revs->reverse = 0;
list = NULL;
while ((c = get_revision(revs)))
commit_list_insert(c, &list);
revs->commits = list;
revs->reverse = 2;
if (revs->boundary == 2) {
unsigned i;
struct object_array *array = &revs->boundary_commits;
struct object_array_entry *objects = array->objects;
for (i = 0; i < array->nr; i++) {
c = (struct commit *)(objects[i].item);
if (!c)
continue;
if (!(c->object.flags & CHILD_SHOWN))
continue;
if (!(c->object.flags & SHOWN))
break;
}
if (!revs->commits)
if (array->nr <= i)
return NULL;
c = revs->commits->item;
list = revs->commits->next;
free(revs->commits);
revs->commits = list;
c->object.flags |= SHOWN | BOUNDARY;
return c;
}
if (0 < revs->skip_count) {
while ((c = get_revision_1(revs)) != NULL) {
if (revs->skip_count-- <= 0)
if (revs->reverse) {
int limit = -1;
if (0 <= revs->max_count) {
limit = revs->max_count;
if (0 < revs->skip_count)
limit += revs->skip_count;
}
l = NULL;
while ((c = get_revision_1(revs))) {
commit_list_insert(c, &l);
if ((0 < limit) && !--limit)
break;
}
revs->commits = l;
revs->reverse = 0;
revs->max_count = -1;
c = NULL;
}
/*
* Now pick up what they want to give us
*/
c = get_revision_1(revs);
if (c) {
while (0 < revs->skip_count) {
revs->skip_count--;
c = get_revision_1(revs);
if (!c)
break;
}
}
/* Check the max_count ... */
/*
* Check the max_count.
*/
switch (revs->max_count) {
case -1:
break;
case 0:
if (revs->boundary) {
struct commit_list *list = revs->commits;
while (list) {
list->item->object.flags |=
BOUNDARY_SHOW | BOUNDARY;
list = list->next;
}
/* all remaining commits are boundary commits */
revs->max_count = -1;
revs->limited = 1;
} else
return NULL;
c = NULL;
break;
default:
revs->max_count--;
}
if (c)
c->object.flags |= SHOWN;
if (!revs->boundary) {
return c;
return get_revision_1(revs);
}
if (!c) {
/*
* get_revision_1() runs out the commits, and
* we are done computing the boundaries.
* switch to boundary commits output mode.
*/
revs->boundary = 2;
return get_revision(revs);
}
/*
* boundary commits are the commits that are parents of the
* ones we got from get_revision_1() but they themselves are
* not returned from get_revision_1(). Before returning
* 'c', we need to mark its parents that they could be boundaries.
*/
for (l = c->parents; l; l = l->next) {
struct object *p;
p = &(l->item->object);
if (p->flags & (CHILD_SHOWN | SHOWN))
continue;
p->flags |= CHILD_SHOWN;
gc_boundary(&revs->boundary_commits);
add_object_array(p, NULL, &revs->boundary_commits);
}
return c;
}

View File

@@ -7,7 +7,7 @@
#define SHOWN (1u<<3)
#define TMP_MARK (1u<<4) /* for isolated cases; clean after use */
#define BOUNDARY (1u<<5)
#define BOUNDARY_SHOW (1u<<6)
#define CHILD_SHOWN (1u<<6)
#define ADDED (1u<<7) /* Parents already parsed and added? */
#define SYMMETRIC_LEFT (1u<<8)
@@ -21,6 +21,9 @@ struct rev_info {
struct commit_list *commits;
struct object_array pending;
/* Parents of shown commits */
struct object_array boundary_commits;
/* Basic information */
const char *prefix;
void *prune_data;
@@ -40,10 +43,10 @@ struct rev_info {
edge_hint:1,
limited:1,
unpacked:1, /* see also ignore_packed below */
boundary:1,
boundary:2,
left_right:1,
parents:1,
reverse:2;
reverse:1;
/* Diff flags */
unsigned int diff:1,

View File

@@ -3,29 +3,60 @@
#include "exec_cmd.h"
#include "spawn-pipe.h"
int run_command_v_opt(const char **argv, int flags)
static inline void close_pair(int fd[2])
{
pid_t pid;
int fd_i[2] = { -1, -1 };
close(fd[0]);
close(fd[1]);
}
int start_command(struct child_process *cmd)
{
int need_in = !cmd->no_stdin && cmd->in < 0;
int fdin[2] = { -1, -1 };
int fd_o[2] = { -1, -1 };
if (need_in) {
if (pipe(fdin) < 0)
return -ERR_RUN_COMMAND_PIPE;
cmd->in = fdin[1];
cmd->close_in = 1;
}
{
if (flags & RUN_COMMAND_NO_STDIN) {
fd_i[0] = open("/dev/null", O_RDWR);
if (cmd->no_stdin) {
fdin[0] = open("/dev/null", O_RDWR);
} else if (need_in) {
/* nothing */
} else if (cmd->in) {
fdin[0] = cmd->in;
}
if (flags & RUN_COMMAND_STDOUT_TO_STDERR)
if (cmd->stdout_to_stderr)
fd_o[1] = dup(2);
if (flags & RUN_GIT_CMD) {
pid = spawnv_git_cmd(argv, fd_i, fd_o);
if (cmd->git_cmd) {
cmd->pid = spawnv_git_cmd(cmd->argv, fdin, fd_o);
} else {
pid = spawnvpe_pipe(argv[0], argv, environ, fd_i, fd_o);
cmd->pid = spawnvpe_pipe(cmd->argv[0], cmd->argv, environ, fdin, fd_o);
}
}
if (pid < 0)
if (cmd->pid < 0) {
if (need_in) {
close_pair(fdin);
}
return -ERR_RUN_COMMAND_FORK;
}
return 0;
}
int finish_command(struct child_process *cmd)
{
if (cmd->close_in)
close(cmd->in);
for (;;) {
int status, code;
pid_t waiting = waitpid(pid, &status, 0);
pid_t waiting = waitpid(cmd->pid, &status, 0);
if (waiting < 0) {
if (errno == EINTR)
@@ -33,7 +64,7 @@ int run_command_v_opt(const char **argv, int flags)
error("waitpid failed (%s)", strerror(errno));
return -ERR_RUN_COMMAND_WAITPID;
}
if (waiting != pid)
if (waiting != cmd->pid)
return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
if (WIFSIGNALED(status))
return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
@@ -46,3 +77,22 @@ int run_command_v_opt(const char **argv, int flags)
return 0;
}
}
int run_command(struct child_process *cmd)
{
int code = start_command(cmd);
if (code)
return code;
return finish_command(cmd);
}
int run_command_v_opt(const char **argv, int opt)
{
struct child_process cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.argv = argv;
cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
return run_command(&cmd);
}

View File

@@ -4,12 +4,27 @@
enum {
ERR_RUN_COMMAND_FORK = 10000,
ERR_RUN_COMMAND_EXEC,
ERR_RUN_COMMAND_PIPE,
ERR_RUN_COMMAND_WAITPID,
ERR_RUN_COMMAND_WAITPID_WRONG_PID,
ERR_RUN_COMMAND_WAITPID_SIGNAL,
ERR_RUN_COMMAND_WAITPID_NOEXIT,
};
struct child_process {
const char **argv;
pid_t pid;
int in;
unsigned close_in:1;
unsigned no_stdin:1;
unsigned git_cmd:1; /* if this is to be git sub-command */
unsigned stdout_to_stderr:1;
};
int start_command(struct child_process *);
int finish_command(struct child_process *);
int run_command(struct child_process *);
#define RUN_COMMAND_NO_STDIN 1
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
#define RUN_COMMAND_STDOUT_TO_STDERR 4

View File

@@ -749,6 +749,7 @@ struct packed_git *add_packed_git(char *path, int path_len, int local)
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);
return p;
@@ -833,6 +834,60 @@ static void prepare_packed_git_one(char *objdir, int local)
closedir(dir);
}
static int sort_pack(const void *a_, const void *b_)
{
struct packed_git *a = *((struct packed_git **)a_);
struct packed_git *b = *((struct packed_git **)b_);
int st;
/*
* Local packs tend to contain objects specific to our
* variant of the project than remote ones. In addition,
* remote ones could be on a network mounted filesystem.
* Favor local ones for these reasons.
*/
st = a->pack_local - b->pack_local;
if (st)
return -st;
/*
* Younger packs tend to contain more recent objects,
* and more recent objects tend to get accessed more
* often.
*/
if (a->mtime < b->mtime)
return 1;
else if (a->mtime == b->mtime)
return 0;
return -1;
}
static void rearrange_packed_git(void)
{
struct packed_git **ary, *p;
int i, n;
for (n = 0, p = packed_git; p; p = p->next)
n++;
if (n < 2)
return;
/* prepare an array of packed_git for easier sorting */
ary = xcalloc(n, sizeof(struct packed_git *));
for (n = 0, p = packed_git; p; p = p->next)
ary[n++] = p;
qsort(ary, n, sizeof(struct packed_git *), sort_pack);
/* link them back again */
for (i = 0; i < n - 1; i++)
ary[i]->next = ary[i + 1];
ary[n - 1]->next = NULL;
packed_git = ary[0];
free(ary);
}
static int prepare_packed_git_run_once = 0;
void prepare_packed_git(void)
{
@@ -847,6 +902,7 @@ void prepare_packed_git(void)
prepare_packed_git_one(alt->base, 0);
alt->name[-1] = '/';
}
rearrange_packed_git();
prepare_packed_git_run_once = 1;
}

View File

@@ -602,10 +602,10 @@ static int handle_one_ref(const char *path,
*/
#define ONELINE_SEEN (1u<<20)
int get_sha1_oneline(const char *prefix, unsigned char *sha1)
static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
{
struct commit_list *list = NULL, *backup = NULL, *l;
struct commit *commit = NULL;
int retval = -1;
if (prefix[0] == '!') {
if (prefix[1] != '!')
@@ -619,22 +619,22 @@ int get_sha1_oneline(const char *prefix, unsigned char *sha1)
commit_list_insert(l->item, &backup);
while (list) {
char *p;
struct commit *commit;
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
if (!commit)
break;
parse_object(commit->object.sha1);
if (!commit->buffer || !(p = strstr(commit->buffer, "\n\n")))
continue;
if (!prefixcmp(p + 2, prefix)) {
hashcpy(sha1, commit->object.sha1);
retval = 0;
break;
}
}
free_commit_list(list);
for (l = backup; l; l = l->next)
clear_commit_marks(l->item, ONELINE_SEEN);
return commit == NULL;
return retval;
}
/*

View File

@@ -47,17 +47,6 @@ test_expect_success \
test ! -f .git/refs/heads/d/e/f &&
test ! -f .git/logs/refs/heads/d/e/f'
cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 checkout: Created from master
EOF
test_expect_success \
'git checkout -b g/h/i -l should create a branch and a log' \
'GIT_COMMITTER_DATE="2005-05-26 23:30" \
git-checkout -b g/h/i -l master &&
test -f .git/refs/heads/g/h/i &&
test -f .git/logs/refs/heads/g/h/i &&
diff expect .git/logs/refs/heads/g/h/i'
test_expect_success \
'git branch j/k should work after branch j has been deleted' \
'git-branch j &&
@@ -119,4 +108,58 @@ test_expect_failure \
git-branch -m u v'
}
test_expect_success 'test tracking setup via --track' \
'git-config remote.local.url . &&
git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
(git-show-ref -q refs/remotes/local/master || git-fetch local) &&
git-branch --track my1 local/master &&
test $(git-config branch.my1.remote) = local &&
test $(git-config branch.my1.merge) = refs/heads/master'
test_expect_success 'test tracking setup (non-wildcard, matching)' \
'git-config remote.local.url . &&
git-config remote.local.fetch refs/heads/master:refs/remotes/local/master &&
(git-show-ref -q refs/remotes/local/master || git-fetch local) &&
git-branch --track my4 local/master &&
test $(git-config branch.my4.remote) = local &&
test $(git-config branch.my4.merge) = refs/heads/master'
test_expect_success 'test tracking setup (non-wildcard, not matching)' \
'git-config remote.local.url . &&
git-config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
(git-show-ref -q refs/remotes/local/master || git-fetch local) &&
git-branch --track my5 local/master &&
! test $(git-config branch.my5.remote) = local &&
! test $(git-config branch.my5.merge) = refs/heads/master'
test_expect_success 'test tracking setup via config' \
'git-config branch.autosetupmerge true &&
git-config remote.local.url . &&
git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
(git-show-ref -q refs/remotes/local/master || git-fetch local) &&
git-branch my3 local/master &&
test $(git-config branch.my3.remote) = local &&
test $(git-config branch.my3.merge) = refs/heads/master'
test_expect_success 'test overriding tracking setup via --no-track' \
'git-config branch.autosetupmerge true &&
git-config remote.local.url . &&
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 &&
! test $(git-config branch.my2.remote) = local &&
! test $(git-config branch.my2.merge) = refs/heads/master'
# 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
EOF
test_expect_success \
'git checkout -b g/h/i -l should create a branch and a log' \
'GIT_COMMITTER_DATE="2005-05-26 23:30" \
git-checkout -b g/h/i -l master &&
test -f .git/refs/heads/g/h/i &&
test -f .git/logs/refs/heads/g/h/i &&
diff expect .git/logs/refs/heads/g/h/i'
test_done

View File

@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \
'git-mailsplit -o. ../t5100/sample.mbox >last &&
last=`cat last` &&
echo total is $last &&
test `cat last` = 6'
test `cat last` = 8'
for mail in `echo 00*`
do

5
t/t5100/info0007 Normal file
View File

@@ -0,0 +1,5 @@
Author: A U Thor
Email: a.u.thor@example.com
Subject: another patch
Date: Fri, 9 Jun 2006 00:44:16 -0700

5
t/t5100/info0008 Normal file
View File

@@ -0,0 +1,5 @@
Author: Junio C Hamano
Email: junio@kernel.org
Subject: another patch
Date: Fri, 9 Jun 2006 00:44:16 -0700

2
t/t5100/msg0007 Normal file
View File

@@ -0,0 +1,2 @@
Here is an empty patch from A U Thor.

4
t/t5100/msg0008 Normal file
View File

@@ -0,0 +1,4 @@
>Here is an empty patch from A U Thor.
Hey you forgot the patch!

View File

@@ -61,7 +61,7 @@ diff --git a/git-cvsimport-script b/git-cvsimport-script
push(@old,$fn);
--
David K<EFBFBD>gedal
David Kågedal
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org

0
t/t5100/patch0007 Normal file
View File

0
t/t5100/patch0008 Normal file
View File

View File

@@ -386,3 +386,21 @@ index 9123cdc..918dcf8 100644
--
1.4.0.g6f2b
From nobody Mon Sep 17 00:00:00 2001
From: A U Thor <a.u.thor@example.com>
Date: Fri, 9 Jun 2006 00:44:16 -0700
Subject: [PATCH] another patch
Here is an empty patch from A U Thor.
From nobody Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junio@kernel.org>
Date: Fri, 9 Jun 2006 00:44:16 -0700
Subject: re: [PATCH] another patch
From: A U Thor <a.u.thor@example.com>
Subject: [PATCH] another patch
>Here is an empty patch from A U Thor.
Hey you forgot the patch!

View File

@@ -25,8 +25,8 @@ test_expect_success setup '
cat >victim/.git/hooks/pre-receive <<'EOF'
#!/bin/sh
echo "$@" >>$GIT_DIR/pre-receive.args
read x; printf "$x" >$GIT_DIR/pre-receive.stdin
printf "$@" >>$GIT_DIR/pre-receive.args
cat - >$GIT_DIR/pre-receive.stdin
echo STDOUT pre-receive
echo STDERR pre-receive >&2
EOF
@@ -44,8 +44,8 @@ chmod u+x victim/.git/hooks/update
cat >victim/.git/hooks/post-receive <<'EOF'
#!/bin/sh
echo "$@" >>$GIT_DIR/post-receive.args
read x; printf "$x" >$GIT_DIR/post-receive.stdin
printf "$@" >>$GIT_DIR/post-receive.args
cat - >$GIT_DIR/post-receive.stdin
echo STDOUT post-receive
echo STDERR post-receive >&2
EOF
@@ -80,11 +80,10 @@ test_expect_success 'hooks ran' '
test -f victim/.git/post-update.stdin
'
test_expect_success 'pre-receive hook arguments' '
echo \
refs/heads/master $commit0 $commit1 \
refs/heads/tofail $commit1 $commit0 \
| git diff - victim/.git/pre-receive.args
test_expect_success 'pre-receive hook input' '
(echo $commit0 $commit1 refs/heads/master;
echo $commit1 $commit0 refs/heads/tofail
) | git diff - victim/.git/pre-receive.stdin
'
test_expect_success 'update hook arguments' '
@@ -93,9 +92,9 @@ test_expect_success 'update hook arguments' '
) | git diff - victim/.git/update.args
'
test_expect_success 'post-receive hook arguments' '
echo refs/heads/master $commit0 $commit1 |
git diff - victim/.git/post-receive.args
test_expect_success 'post-receive hook input' '
echo $commit0 $commit1 refs/heads/master |
git diff - victim/.git/post-receive.stdin
'
test_expect_success 'post-update hook arguments' '
@@ -104,12 +103,15 @@ test_expect_success 'post-update hook arguments' '
'
test_expect_success 'all hook stdin is /dev/null' '
! test -s victim/.git/pre-receive.stdin &&
! test -s victim/.git/update.stdin &&
! test -s victim/.git/post-receive.stdin &&
! test -s victim/.git/post-update.stdin
'
test_expect_success 'all *-receive hook args are empty' '
! test -s victim/.git/pre-receive.args &&
! test -s victim/.git/post-receive.args
'
test_expect_failure 'send-pack produced no output' '
test -s send.out
'

View File

@@ -90,6 +90,13 @@ test_expect_success 'create bundle 1' '
git bundle create bundle1 master^..master
'
test_expect_success 'header of bundle looks right' '
head -n 1 "$D"/bundle1 | grep "^#" &&
head -n 2 "$D"/bundle1 | grep "^-[0-9a-f]\{40\} " &&
head -n 3 "$D"/bundle1 | grep "^[0-9a-f]\{40\} " &&
head -n 4 "$D"/bundle1 | grep "^$"
'
test_expect_success 'create bundle 2' '
cd "$D" &&
git bundle create bundle2 master~2..master
@@ -101,10 +108,41 @@ test_expect_failure 'unbundle 1' '
git fetch "$D/bundle1" master:master
'
test_expect_success 'bundle 1 has only 3 files ' '
cd "$D" &&
(
while read x && test -n "$x"
do
:;
done
cat
) <bundle1 >bundle.pack &&
git index-pack bundle.pack &&
verify=$(git verify-pack -v bundle.pack) &&
test 4 = $(echo "$verify" | wc -l)
'
test_expect_success 'unbundle 2' '
cd "$D/bundle" &&
git fetch ../bundle2 master:master &&
test "tip" = "$(git log -1 --pretty=oneline master | cut -b42-)"
'
test_expect_success 'bundle does not prerequisite objects' '
cd "$D" &&
touch file2 &&
git add file2 &&
git commit -m add.file2 file2 &&
git bundle create bundle3 -1 HEAD &&
(
while read x && test -n "$x"
do
:;
done
cat
) <bundle3 >bundle.pack &&
git index-pack bundle.pack &&
test 4 = $(git verify-pack -v bundle.pack | wc -l)
'
test_done

View File

@@ -501,4 +501,54 @@ test_expect_success \
'test `git-rev-parse --verify branch^1` \
= `git-rev-parse --verify K^1`'
###
### series L
###
cat >input <<INPUT_END
blob
mark :1
data <<EOF
some data
EOF
blob
mark :2
data <<EOF
other data
EOF
commit refs/heads/L
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
create L
COMMIT
M 644 :1 b.
M 644 :1 b/other
M 644 :1 ba
commit refs/heads/L
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
update L
COMMIT
M 644 :2 b.
M 644 :2 b/other
M 644 :2 ba
INPUT_END
cat >expect <<EXPECT_END
:100644 100644 4268632... 55d3a52... M b.
:040000 040000 0ae5cac... 443c768... M b
:100644 100644 4268632... 55d3a52... M ba
EXPECT_END
test_expect_success \
'L: verify internal tree sorting' \
'git-fast-import <input &&
git-diff --raw L^ L >output &&
git diff expect output'
test_done

View File

@@ -210,7 +210,7 @@ case "$refname_type" in
fi
# If this tag succeeds another, then show which tag it replaces
prevtag=$(git describe $newrev^ 2>/dev/null | sed 's/-g.*//')
prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
if [ -n "$prevtag" ]; then
echo " replaces $prevtag"
fi

18
trace.c
View File

@@ -26,14 +26,14 @@
#include "quote.h"
/* Stolen from "imap-send.c". */
static int git_vasprintf(char **strp, const char *fmt, va_list ap)
int nfvasprintf(char **strp, const char *fmt, va_list ap)
{
int len;
char tmp[1024];
if ((len = vsnprintf(tmp, sizeof(tmp), fmt, ap)) < 0 ||
!(*strp = xmalloc(len + 1)))
return -1;
die("Fatal: Out of memory\n");
if (len >= (int)sizeof(tmp))
vsprintf(*strp, fmt, ap);
else
@@ -41,13 +41,15 @@ static int git_vasprintf(char **strp, const char *fmt, va_list ap)
return len;
}
/* Stolen from "imap-send.c". */
int nfvasprintf(char **str, const char *fmt, va_list va)
int nfasprintf(char **str, const char *fmt, ...)
{
int ret = git_vasprintf(str, fmt, va);
if (ret < 0)
die("Fatal: Out of memory\n");
return ret;
int rc;
va_list args;
va_start(args, fmt);
rc = nfvasprintf(str, fmt, args);
va_end(args);
return rc;
}
/* Get a trace file descriptor from GIT_TRACE env variable. */