git-describe
git-fast-import
git-fetch
+git-fetch--tool
git-fetch-pack
git-findtags
git-fmt-merge-msg
git-merge-recursive
git-merge-resolve
git-merge-stupid
+git-mergetool
git-mktag
git-mktree
git-name-rev
git-merge mainporcelain
git-merge-one-file purehelpers
git-merge-tree ancillaryinterrogators
+git-mergetool ancillarymanipulators
git-mktag plumbingmanipulators
git-mktree plumbingmanipulators
git-mv mainporcelain
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
[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>...
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
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
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
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
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
--------------------
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
---
--- /dev/null
+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
+
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
-----------------
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
#!/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
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
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>
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
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 \
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 \
$(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
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;
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
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[] =
exit(1);
}
- git_config(git_default_config);
+ git_config(git_add_config);
newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
#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
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 */
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);
}
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);
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);
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)
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];
i++;
break;
}
+ if (!strcmp(arg, "--track")) {
+ track = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--no-track")) {
+ track = 0;
+ continue;
+ }
if (!strcmp(arg, "-d")) {
delete = 1;
continue;
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);
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
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;
}
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);
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++)
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,
{
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";
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");
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);
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");
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;
} else
usage(bundle_usage);
}
-
--- /dev/null
+#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, ¬_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]);
+}
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;
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':
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)
{
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;
}
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);
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"))
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)
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)
switch (*subject) {
case 'r': case 'R':
if (!memcmp("e:", subject+1, 2)) {
- subject +=3;
+ subject += 3;
continue;
}
break;
}
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)
}
}
-static void handle_info(void)
+static int handle_filter(char *line);
+
+static int find_boundary(void)
{
- char *sub;
+ while(fgets(line, sizeof(line), fin) != NULL) {
+ if (is_multipart_boundary(line))
+ return 1;
+ }
+ return 0;
+}
+
+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;
- sub = cleanup_subject(subject);
- cleanup_space(name);
- cleanup_space(date);
- cleanup_space(email);
- cleanup_space(sub);
+ /* slurp in this section's info */
+ while (read_one_header_line(line, sizeof(line), fin))
+ check_header(line, p_hdr_data);
- fprintf(fout, "Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
- name, email, sub, date);
+ /* eat the blank line after section info */
+ return (fgets(line, sizeof(line), fin) != NULL);
}
-/* We are inside message body and have read line[] already.
- * Spit out the commit log.
- */
-static int handle_commit_msg(int *seen)
+static inline int patchbreak(const char *line)
{
- if (!cmitmsg)
- return 0;
- do {
- if (!memcmp("diff -", line, 6) ||
- !memcmp("---", line, 3) ||
- !memcmp("Index: ", line, 7))
- 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.
- */
+ /* 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;
+}
- /* Unwrap transfer encoding and optionally
- * normalize the log message to UTF-8.
- */
- decode_transfer_encoding(line);
- if (metainfo_charset)
- convert_to_utf8(line, charset);
- handle_inbody_header(seen, line);
- if (!(*seen & SEEN_PREFIX))
- continue;
+static int handle_commit_msg(char *line)
+{
+ static int still_looking = 1;
- fputs(line, cmitmsg);
- } while (fgets(line, sizeof(line), fin) != NULL);
- fclose(cmitmsg);
- cmitmsg = NULL;
+ if (!cmitmsg)
+ return 0;
+
+ 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;
}
-/* We have done the commit message and have the first
- * line of the patch in line[].
- */
-static void handle_patch(void)
+static int handle_patch(char *line)
{
- 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);
+ fputs(line, patchfile);
+ patch_lines++;
+ return 0;
}
-/* 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)
+static int handle_filter(char *line)
{
- int n = 0;
+ static int filter = 0;
- while (fgets(line, sizeof(line), fin) != NULL) {
- again:
- n++;
- if (is_multipart_boundary(line))
+ /* 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 (handle_commit_msg(seen))
- goto again;
- handle_patch();
- break;
+ filter++;
+ case 1:
+ if (!handle_patch(line))
+ break;
+ filter++;
+ default:
+ return 1;
}
- if (n == 0)
- return -1;
+
return 0;
}
-static void handle_multipart_body(void)
+static void handle_body(void)
{
- int seen = 0;
- int part_num = 0;
+ int rc = 0;
+ static char newline[2000];
+ static char *np = newline;
/* Skip up to the first boundary */
- while (fgets(line, sizeof(line), fin) != NULL)
- if (is_multipart_boundary(line)) {
- part_num = 1;
+ 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
+ * normalize the log message to UTF-8.
+ */
+ decode_transfer_encoding(line);
+ if (metainfo_charset)
+ convert_to_utf8(line, charset);
+
+ 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;
}
- 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;
+ default:
+ rc = handle_filter(line);
}
- else
- check_subheader_line(line);
- }
- fclose(patchfile);
- if (!patch_lines) {
- fprintf(stderr, "No patch found\n");
- exit(1);
- }
+ if (rc)
+ /* nothing left to filter */
+ break;
+ } while (fgets(line, sizeof(line), fin));
+
+ return;
}
-/* Non multipart message */
-static void handle_body(void)
+static void handle_info(void)
{
- int seen = 0;
+ char *sub;
+ char *hdr;
+ int i;
- handle_commit_msg(&seen);
- handle_patch();
- fclose(patchfile);
- if (!patch_lines) {
- fprintf(stderr, "No patch found\n");
- exit(1);
+ 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;
+
+ 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);
+ }
}
+ fprintf(fout, "\n");
}
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;
}
argv[dest_argc] = NULL;
if (verbose)
fprintf(stderr, "Pushing to %s\n", dest);
- err = run_command_v(argv);
+ err = run_command_v_opt(argv, 0);
if (!err)
continue;
switch (err) {
const char *next_sha1, const char *next_name)
{
char buffer[256];
+ const char *argv[6];
sprintf(buffer, "GITHEAD_%s", head_sha1);
setenv(buffer, head_name, 1);
* and $prev on top of us (when reverting), or the change between
* $prev and $commit on top of us (when cherry-picking or replaying).
*/
-
- return run_command_opt(RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,
- "merge-recursive", base_sha1, "--",
- head_sha1, next_sha1, NULL);
+ argv[0] = "merge-recursive";
+ argv[1] = base_sha1;
+ argv[2] = "--";
+ argv[3] = head_sha1;
+ argv[4] = next_sha1;
+ argv[5] = NULL;
+
+ return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
}
static int revert_or_cherry_pick(int argc, const char **argv)
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);
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);
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, ...);
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;
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);
}
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;
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
*)
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
new_name=
force=
branch=
+track=
newbranch=
newbranch_log=
merge=
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
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"
#
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"
{
$log->info("Merged successfully");
print "M M $filename\n";
- $log->debug("Update-existing $dirpart");
+ $log->debug("Merged $dirpart");
# Don't want to actually _DO_ the update if -n specified
unless ( $state->{globaloptions}{-n} )
{
- print "Update-existing $dirpart\n";
+ print "Merged $dirpart\n";
$log->debug($state->{CVSROOT} . "/$state->{module}/$filename");
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
my $kopts = kopts_from_path($filepart);
# Don't want to actually _DO_ the update if -n specified
unless ( $state->{globaloptions}{-n} )
{
- print "Update-existing $dirpart\n";
+ print "Merged $dirpart\n";
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
my $kopts = kopts_from_path($filepart);
print "/$filepart/1.$meta->{revision}/+/$kopts/\n";
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
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=
rsync_slurped_objects=t
}
;;
- *)
- # We will do git native transport with just one call later.
- continue ;;
esac
append_fetch_head "$head" "$remote" \
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
-CREDITS-FILE
GIT-VERSION-FILE
git-citool
git-gui
+++ /dev/null
-#!/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=/var/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
-
@$(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
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
$(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
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
-}
######################################################################
##
##
## version check
+if {{--version} eq $argv || {version} eq $argv} {
+ puts "git-gui version $appvers"
+ exit
+}
+
set req_maj 1
set req_min 5
}
}
}
- 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.
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
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
enable_option transport
switch -- $subcommand {
---version -
-version -
browser -
blame {
disable_option multicommit
# -- 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"
git-update-index --refresh 2>/dev/null
new_head=$(git-rev-parse --verify "$1^0") &&
git-read-tree -v -m -u --exclude-per-directory=.gitignore $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
;;
--- /dev/null
+#!/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
# 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.
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")
$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:
$_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,
{ '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,
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);
}
{ "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 },
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
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;
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:
}
}
+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;
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";
}
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;
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;
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;
}
}
}
-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)
*/
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;
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))
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->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 (array->nr <= i)
+ return NULL;
- if (revs->reverse) {
- struct commit_list *list;
+ c->object.flags |= SHOWN | BOUNDARY;
+ return c;
+ }
- /*
- * 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) {
+ int limit = -1;
- 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 (0 <= revs->max_count) {
+ limit = revs->max_count;
+ if (0 < revs->skip_count)
+ limit += revs->skip_count;
}
-
- if (!revs->commits)
- return NULL;
- c = revs->commits->item;
- list = revs->commits->next;
- free(revs->commits);
- revs->commits = list;
- return c;
+ 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;
}
- if (0 < revs->skip_count) {
- while ((c = get_revision_1(revs)) != NULL) {
- if (revs->skip_count-- <= 0)
+ /*
+ * 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;
}
#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)
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;
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,
#include "run-command.h"
#include "exec_cmd.h"
-int run_command_v_opt(const char **argv, int flags)
+static inline void close_pair(int fd[2])
{
- pid_t pid = fork();
+ 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];
- if (pid < 0)
+ if (need_in) {
+ if (pipe(fdin) < 0)
+ return -ERR_RUN_COMMAND_PIPE;
+ cmd->in = fdin[1];
+ cmd->close_in = 1;
+ }
+
+ cmd->pid = fork();
+ if (cmd->pid < 0) {
+ if (need_in)
+ close_pair(fdin);
return -ERR_RUN_COMMAND_FORK;
- if (!pid) {
- if (flags & RUN_COMMAND_NO_STDIN) {
+ }
+
+ if (!cmd->pid) {
+ if (cmd->no_stdin) {
int fd = open("/dev/null", O_RDWR);
dup2(fd, 0);
close(fd);
+ } else if (need_in) {
+ dup2(fdin[0], 0);
+ close_pair(fdin);
+ } else if (cmd->in) {
+ dup2(cmd->in, 0);
+ close(cmd->in);
}
- if (flags & RUN_COMMAND_STDOUT_TO_STDERR)
+
+ if (cmd->stdout_to_stderr)
dup2(2, 1);
- if (flags & RUN_GIT_CMD) {
- execv_git_cmd(argv);
+ if (cmd->git_cmd) {
+ execv_git_cmd(cmd->argv);
} else {
- execvp(argv[0], (char *const*) argv);
+ execvp(cmd->argv[0], (char *const*) cmd->argv);
}
- die("exec %s failed.", argv[0]);
+ die("exec %s failed.", cmd->argv[0]);
}
+
+ if (need_in)
+ close(fdin[0]);
+ else if (cmd->in)
+ close(cmd->in);
+
+ 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)
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;
}
}
-int run_command_v(const char **argv)
-{
- return run_command_v_opt(argv, 0);
-}
-
-static int run_command_va_opt(int opt, const char *cmd, va_list param)
-{
- int argc;
- const char *argv[MAX_RUN_COMMAND_ARGS];
- const char *arg;
-
- argv[0] = (char*) cmd;
- argc = 1;
- while (argc < MAX_RUN_COMMAND_ARGS) {
- arg = argv[argc++] = va_arg(param, char *);
- if (!arg)
- break;
- }
- if (MAX_RUN_COMMAND_ARGS <= argc)
- return error("too many args to run %s", cmd);
- return run_command_v_opt(argv, opt);
-}
-
-int run_command_opt(int opt, const char *cmd, ...)
+int run_command(struct child_process *cmd)
{
- va_list params;
- int r;
-
- va_start(params, cmd);
- r = run_command_va_opt(opt, cmd, params);
- va_end(params);
- return r;
+ int code = start_command(cmd);
+ if (code)
+ return code;
+ return finish_command(cmd);
}
-int run_command(const char *cmd, ...)
+int run_command_v_opt(const char **argv, int opt)
{
- va_list params;
- int r;
-
- va_start(params, cmd);
- r = run_command_va_opt(0, cmd, params);
- va_end(params);
- return r;
+ 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);
}
#ifndef RUN_COMMAND_H
#define RUN_COMMAND_H
-#define MAX_RUN_COMMAND_ARGS 256
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
int run_command_v_opt(const char **argv, int opt);
-int run_command_v(const char **argv);
-int run_command_opt(int opt, const char *cmd, ...);
-int run_command(const char *cmd, ...);
#endif
*/
#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;
+ int retval = -1;
if (prefix[0] == '!') {
if (prefix[1] != '!')
for_each_ref(handle_one_ref, &list);
for (l = list; l; l = l->next)
commit_list_insert(l->item, &backup);
- while ((commit = pop_most_recent_commit(&list, ONELINE_SEEN))) {
+ while (list) {
char *p;
+ struct commit *commit;
+
+ commit = pop_most_recent_commit(&list, ONELINE_SEEN);
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;
}
/*
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 &&
ln -s real-u .git/logs/refs/heads/u &&
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
'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
--- /dev/null
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: another patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
--- /dev/null
+Author: Junio C Hamano
+Email: junio@kernel.org
+Subject: another patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
--- /dev/null
+Here is an empty patch from A U Thor.
+
--- /dev/null
+>Here is an empty patch from A U Thor.
+
+Hey you forgot the patch!
+
push(@old,$fn);
--
-David Kå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
--
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!
+
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
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
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' '
) | 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' '
'
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
'
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
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
'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
#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
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. */