SYNOPSIS
--------
-'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ARGS]
+'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
+ [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
DESCRIPTION
-----------
environment variable. If no path is given 'git' will print
the current setting and then exit.
+-p|--paginate::
+ Pipe all output into 'less' (or if set, $PAGER).
+
+--git-dir=<path>::
+ Set the path to the repository. This can also be controlled by
+ setting the GIT_DIR environment variable.
+
+--bare::
+ Same as --git-dir=`pwd`.
FURTHER DOCUMENTATION
---------------------
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIB_FILE) $(SIMPLE_LIB)
+ssh-pull.o: ssh-fetch.c
+ssh-push.o: ssh-upload.c
git-local-fetch$X: fetch.o
git-ssh-fetch$X: rsh.o fetch.o
git-ssh-upload$X: rsh.o
struct patch {
char *new_name, *old_name, *def_name;
unsigned int old_mode, new_mode;
- int is_rename, is_copy, is_new, is_delete, is_binary;
+ int is_rename, is_copy, is_new, is_delete, is_binary, is_reverse;
#define BINARY_DELTA_DEFLATED 1
#define BINARY_LITERAL_DEFLATED 2
unsigned long deflate_origlen;
return offset + hdrsize + patchsize;
}
+#define swap(a,b) myswap((a),(b),sizeof(a))
+
+#define myswap(a, b, size) do { \
+ unsigned char mytmp[size]; \
+ memcpy(mytmp, &a, size); \
+ memcpy(&a, &b, size); \
+ memcpy(&b, mytmp, size); \
+} while (0)
+
+static void reverse_patches(struct patch *p)
+{
+ for (; p; p = p->next) {
+ struct fragment *frag = p->fragments;
+
+ swap(p->new_name, p->old_name);
+ swap(p->new_mode, p->old_mode);
+ swap(p->is_new, p->is_delete);
+ swap(p->lines_added, p->lines_deleted);
+ swap(p->old_sha1_prefix, p->new_sha1_prefix);
+
+ for (; frag; frag = frag->next) {
+ swap(frag->newpos, frag->oldpos);
+ swap(frag->newlines, frag->oldlines);
+ }
+ p->is_reverse = !p->is_reverse;
+ }
+}
+
static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
static const char minuses[]= "----------------------------------------------------------------------";
}
static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
- int inaccurate_eof)
+ int reverse, int inaccurate_eof)
{
int match_beginning, match_end;
char *buf = desc->buffer;
int pos, lines;
while (size > 0) {
+ char first;
int len = linelen(patch, size);
int plen;
plen = len-1;
if (len < size && patch[len] == '\\')
plen--;
- switch (*patch) {
+ first = *patch;
+ if (reverse) {
+ if (first == '-')
+ first = '+';
+ else if (first == '+')
+ first = '-';
+ }
+ switch (first) {
case ' ':
case '-':
memcpy(old + oldsize, patch + 1, plen);
oldsize += plen;
- if (*patch == '-')
+ if (first == '-')
break;
/* Fall-through for ' ' */
case '+':
- if (*patch != '+' || !no_add)
+ if (first != '+' || !no_add)
newsize += apply_line(new + newsize, patch,
plen);
break;
void *data;
void *result;
+ /* Binary patch is irreversible */
+ if (patch->is_reverse)
+ return error("cannot reverse-apply a binary patch to '%s'",
+ patch->new_name
+ ? patch->new_name : patch->old_name);
+
data = inflate_it(fragment->patch, fragment->size,
patch->deflate_origlen);
if (!data)
return apply_binary(desc, patch);
while (frag) {
- if (apply_one_fragment(desc, frag, patch->inaccurate_eof) < 0)
+ if (apply_one_fragment(desc, frag, patch->is_reverse,
+ patch->inaccurate_eof) < 0)
return error("patch failed: %s:%ld",
name, frag->oldpos);
frag = frag->next;
return 1;
}
-static int apply_patch(int fd, const char *filename, int inaccurate_eof)
+static int apply_patch(int fd, const char *filename,
+ int reverse, int inaccurate_eof)
{
unsigned long offset, size;
char *buffer = read_patch_file(fd, &size);
nr = parse_chunk(buffer + offset, size, patch);
if (nr < 0)
break;
+ if (reverse)
+ reverse_patches(patch);
if (use_patch(patch)) {
patch_stats(patch);
*listp = patch;
{
int i;
int read_stdin = 1;
+ int reverse = 0;
int inaccurate_eof = 0;
const char *whitespace_option = NULL;
int fd;
if (!strcmp(arg, "-")) {
- apply_patch(0, "<stdin>", inaccurate_eof);
+ apply_patch(0, "<stdin>", reverse, inaccurate_eof);
read_stdin = 0;
continue;
}
parse_whitespace_option(arg + 13);
continue;
}
+ if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
+ reverse = 1;
+ continue;
+ }
if (!strcmp(arg, "--inaccurate-eof")) {
inaccurate_eof = 1;
continue;
usage(apply_usage);
read_stdin = 0;
set_default_whitespace_mode(whitespace_option);
- apply_patch(fd, arg, inaccurate_eof);
+ apply_patch(fd, arg, reverse, inaccurate_eof);
close(fd);
}
set_default_whitespace_mode(whitespace_option);
if (read_stdin)
- apply_patch(0, "<stdin>", inaccurate_eof);
+ apply_patch(0, "<stdin>", reverse, inaccurate_eof);
if (whitespace_error) {
if (squelch_whitespace_errors &&
squelch_whitespace_errors < whitespace_error) {
struct rev_info rev;
int silent = 0;
- git_config(git_default_config); /* no "diff" UI options */
init_revisions(&rev);
+ git_config(git_default_config); /* no "diff" UI options */
rev.abbrev = 0;
argc = setup_revisions(argc, argv, &rev, NULL);
int cached = 0;
int i;
- git_config(git_default_config); /* no "diff" UI options */
init_revisions(&rev);
+ git_config(git_default_config); /* no "diff" UI options */
rev.abbrev = 0;
argc = setup_revisions(argc, argv, &rev, NULL);
static struct rev_info *opt = &log_tree_opt;
int read_stdin = 0;
+ init_revisions(opt);
git_config(git_default_config); /* no "diff" UI options */
nr_sha1 = 0;
- init_revisions(opt);
opt->abbrev = 0;
opt->diff = 1;
argc = setup_revisions(argc, argv, opt, NULL);
* Other cases are errors.
*/
- git_config(git_diff_ui_config);
init_revisions(&rev);
+ git_config(git_diff_ui_config);
+ diff_setup(&rev.diffopt);
argc = setup_revisions(argc, argv, &rev, NULL);
if (!rev.diffopt.output_format) {
return builtin_diff_index(&rev, argc, argv);
else if (ents == 2)
return builtin_diff_tree(&rev, argc, argv, ent);
+ else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
+ /* diff A...B where there is one sane merge base between
+ * A and B. We have ent[0] == merge-base, ent[1] == A,
+ * and ent[2] == B. Show diff between the base and B.
+ */
+ return builtin_diff_tree(&rev, argc, argv, ent);
+ }
else
- return builtin_diff_combined(&rev, argc, argv, ent, ents);
+ return builtin_diff_combined(&rev, argc, argv,
+ ent, ents);
usage(builtin_diff_usage);
}
int cmd_help(int argc, const char **argv, char **envp)
{
- const char *help_cmd = argv[1];
+ const char *help_cmd = argc > 1 ? argv[1] : NULL;
if (!help_cmd)
cmd_usage(0, git_exec_path(), NULL);
else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a"))
{
struct rev_info rev;
- git_config(git_diff_ui_config);
init_revisions(&rev);
+ git_config(git_diff_ui_config);
+ diff_setup(&rev.diffopt);
rev.diff = 1;
rev.diffopt.recursive = 1;
rev.simplify_history = 0;
{
struct rev_info rev;
- git_config(git_diff_ui_config);
init_revisions(&rev);
+ git_config(git_diff_ui_config);
+ diff_setup(&rev.diffopt);
rev.diff = 1;
rev.diffopt.recursive = 1;
rev.combine_merges = 1;
{
struct rev_info rev;
- git_config(git_diff_ui_config);
init_revisions(&rev);
+ git_config(git_diff_ui_config);
+ diff_setup(&rev.diffopt);
rev.always_show_header = 1;
cmd_log_init(argc, argv, envp, &rev);
return cmd_log_walk(&rev);
exit;
}
- open FILE, ">", "$ENV{GIT_DIR}refs/heads/$state->{module}";
- print FILE $commithash;
- close FILE;
+ print LOCKFILE $commithash;
$updater->update();
}
close LOCKFILE;
- unlink($lockfile);
+ my $reffile = "$ENV{GIT_DIR}refs/heads/$state->{module}";
+ unlink($reffile);
+ rename($lockfile, $reffile);
chdir "/";
print "ok\n";
laf="$GIT_DIR/lost-found"
rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
-git fsck-objects |
+git fsck-objects --full |
while read dangling type sha1
do
case "$dangling" in
else
rm -f "$GIT_DIR/ORIG_HEAD"
fi
-git-update-ref -m "reset $reset_type $@" HEAD "$rev"
+git-update-ref -m "reset $reset_type $*" HEAD "$rev"
+update_ref_status=$?
case "$reset_type" in
--hard )
esac
rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" "$GIT_DIR/SQUASH_MSG"
+
+exit $update_ref_status
setenv("PATH", path, 1);
}
+static int handle_options(const char*** argv, int* argc)
+{
+ int handled = 0;
+
+ while (*argc > 0) {
+ const char *cmd = (*argv)[0];
+ if (cmd[0] != '-')
+ break;
+
+ /*
+ * For legacy reasons, the "version" and "help"
+ * commands can be written with "--" prepended
+ * to make them look like flags.
+ */
+ if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
+ break;
+
+ /*
+ * Check remaining flags.
+ */
+ if (!strncmp(cmd, "--exec-path", 11)) {
+ cmd += 11;
+ if (*cmd == '=')
+ git_set_exec_path(cmd + 1);
+ else {
+ puts(git_exec_path());
+ exit(0);
+ }
+ } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
+ setup_pager();
+ } else if (!strcmp(cmd, "--git-dir")) {
+ if (*argc < 1)
+ return -1;
+ setenv("GIT_DIR", (*argv)[1], 1);
+ (*argv)++;
+ (*argc)--;
+ } else if (!strncmp(cmd, "--git-dir=", 10)) {
+ setenv("GIT_DIR", cmd + 10, 1);
+ } else if (!strcmp(cmd, "--bare")) {
+ static char git_dir[1024];
+ setenv("GIT_DIR", getcwd(git_dir, 1024), 1);
+ } else {
+ fprintf(stderr, "Unknown option: %s\n", cmd);
+ cmd_usage(0, NULL, NULL);
+ }
+
+ (*argv)++;
+ (*argc)--;
+ handled++;
+ }
+ return handled;
+}
+
static const char *alias_command;
static char *alias_string = NULL;
subdir = setup_git_directory_gently(&nongit);
if (!nongit) {
- int count;
+ int count, option_count;
const char** new_argv;
alias_command = (*argv)[0];
if (alias_string) {
count = split_cmdline(alias_string, &new_argv);
+ option_count = handle_options(&new_argv, &count);
+ memmove(new_argv - option_count, new_argv,
+ count * sizeof(char *));
+ new_argv -= option_count;
if (count < 1)
die("empty alias for %s", alias_command);
die("cannot handle %s internally", cmd);
}
- /* Default command: "help" */
- cmd = "help";
-
/* Look for flags.. */
- while (argc > 1) {
- cmd = *++argv;
- argc--;
-
- if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
- setup_pager();
- continue;
- }
-
- if (strncmp(cmd, "--", 2))
- break;
-
- cmd += 2;
-
- /*
- * For legacy reasons, the "version" and "help"
- * commands can be written with "--" prepended
- * to make them look like flags.
- */
- if (!strcmp(cmd, "help"))
- break;
- if (!strcmp(cmd, "version"))
- break;
-
- /*
- * Check remaining flags (which by now must be
- * "--exec-path", but maybe we will accept
- * other arguments some day)
- */
- if (!strncmp(cmd, "exec-path", 9)) {
- cmd += 9;
- if (*cmd == '=') {
- git_set_exec_path(cmd + 1);
- continue;
- }
- puts(git_exec_path());
- exit(0);
- }
- cmd_usage(0, NULL, NULL);
+ argv++;
+ argc--;
+ handle_options(&argv, &argc);
+ if (argc > 0) {
+ if (!strncmp(argv[0], "--", 2))
+ argv[0] += 2;
+ } else {
+ /* Default command: "help" */
+ argv[0] = "help";
+ argc = 1;
}
- argv[0] = cmd;
+ cmd = argv[0];
/*
* We search for git commands in the following order:
git_footer_html();
}
+sub git_print_page_path {
+ my $name = shift;
+ my $type = shift;
+
+ if (!defined $name) {
+ print "<div class=\"page_path\"><b>/</b></div>\n";
+ } elsif ($type =~ "blob") {
+ print "<div class=\"page_path\"><b>" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "</b><br/></div>\n";
+ } else {
+ print "<div class=\"page_path\"><b>" . esc_html($name) . "</b><br/></div>\n";
+ }
+}
+
sub git_tag {
my $head = git_read_head($project);
git_header_html();
"<div>" .
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
"</div>\n";
- print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
+ git_print_page_path($file_name);
print "<div class=\"page_body\">\n";
print <<HTML;
<table class="blame">
}
sub git_blob_plain {
+ if (!defined $hash) {
+ if (defined $file_name) {
+ my $base = $hash_base || git_read_head($project);
+ $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
+ } else {
+ die_error(undef, "No file name defined.");
+ }
+ }
my $type = shift;
open my $fd, "-|", "$GIT cat-file blob $hash" or die_error("Couldn't cat $file_name, $hash");
}
sub git_blob {
- if (!defined $hash && defined $file_name) {
- my $base = $hash_base || git_read_head($project);
- $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
- }
+ if (!defined $hash) {
+ if (defined $file_name) {
+ my $base = $hash_base || git_read_head($project);
+ $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
+ } else {
+ die_error(undef, "No file name defined.");
+ }
+ }
my $have_blame = git_get_project_config_bool ('blame');
open my $fd, "-|", "$GIT cat-file blob $hash" or die_error(undef, "Open failed.");
my $mimetype = git_blob_plain_mimetype($fd, $file_name);
"<br/><br/></div>\n" .
"<div class=\"title\">$hash</div>\n";
}
- if (defined $file_name) {
- print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
- }
+ git_print_page_path($file_name, "blob");
print "<div class=\"page_body\">\n";
my $nr;
while (my $line = <$fd>) {
}
if (defined $file_name) {
$base = esc_html("$file_name/");
- print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b></div>\n";
- } else {
- print "<div class=\"page_path\"><b>/</b></div>\n";
}
+ git_print_page_path($file_name);
print "<div class=\"page_body\">\n";
print "<table cellspacing=\"0\">\n";
my $alternate = 0;
"<td class=\"link\">" .
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob") .
# " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash_base;f=$base$t_name")}, "history") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") .
" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$t_hash;f=$base$t_name")}, "raw") .
"</td>\n";
} elsif ($t_type eq "tree") {
"</td>\n" .
"<td class=\"link\">" .
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, "tree") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash_base;f=$base$t_name")}, "history") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash_base;f=$base$t_name")}, "history") .
"</td>\n";
}
print "</tr>\n";
"<td><span class=\"file_status deleted\">[deleted " . file_type($from_mode). "]</span></td>\n" .
"<td class=\"link\">" .
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") .
"</td>\n"
} elsif ($status eq "M" || $status eq "T") {
my $mode_chnge = "";
if ($to_id ne $from_id) {
print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file")}, "diff");
}
- print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") . "\n";
+ print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . "\n";
print "</td>\n";
} elsif ($status eq "R") {
my ($from_file, $to_file) = split "\t", $file;
"<br/><br/></div>\n" .
"<div class=\"title\">$hash vs $hash_parent</div>\n";
}
- if (defined $file_name) {
- print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b></div>\n";
- }
+ git_print_page_path($file_name, "blob");
print "<div class=\"page_body\">\n" .
"<div class=\"diff_info\">blob:" .
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash_parent;hb=$hash_base;f=$file_name")}, $hash_parent) .
}
sub git_history {
- if (!defined $hash) {
- $hash = git_read_head($project);
+ if (!defined $hash_base) {
+ $hash_base = git_read_head($project);
}
- my %co = git_read_commit($hash);
+ my $ftype;
+ my %co = git_read_commit($hash_base);
if (!%co) {
die_error(undef, "Unknown commit object.");
}
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
- " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") .
"<br/><br/>\n" .
"</div>\n";
print "<div>\n" .
- $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
+ $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" .
"</div>\n";
- print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b><br/></div>\n";
+ if (!defined $hash && defined $file_name) {
+ $hash = git_get_hash_by_path($hash_base, $file_name);
+ }
+ if (defined $hash) {
+ $ftype = git_get_type($hash);
+ }
+ git_print_page_path($file_name, $ftype);
open my $fd, "-|",
- "$GIT rev-list --full-history $hash -- \'$file_name\'";
+ "$GIT rev-list --full-history $hash_base -- \'$file_name\'";
print "<table cellspacing=\"0\">\n";
my $alternate = 0;
while (my $line = <$fd>) {
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=$commit;f=$file_name")}, "blob");
- my $blob = git_get_hash_by_path($hash, $file_name);
+ my $blob = git_get_hash_by_path($hash_base, $file_name);
my $blob_parent = git_get_hash_by_path($commit, $file_name);
if (defined $blob && defined $blob_parent && $blob ne $blob_parent) {
print " | " .
cat >test-patch <<\EOF
diff --git a/foo b/bar
similarity index 47%
-copy from foo
-copy to bar
+rename from foo
+rename to bar
--- a/foo
+++ b/bar
@@ -1 +1 @@
'test -f bar && ls -l bar | grep "^-..x......"'
fi
+test_expect_success 'apply reverse' \
+ 'git-apply -R --index --stat --summary --apply test-patch &&
+ test "$(cat foo)" = "This is foo"'
+
+cat >test-patch <<\EOF
+diff --git a/foo b/bar
+similarity index 47%
+copy from foo
+copy to bar
+--- a/foo
++++ b/bar
+@@ -1 +1 @@
+-This is foo
++This is bar
+EOF
+
+test_expect_success 'apply copy' \
+ 'git-apply --index --stat --summary --apply test-patch &&
+ test "$(cat bar)" = "This is bar" -a "$(cat foo)" = "This is foo"'
+
test_done
git-diff-tree -p master binary >B.diff
git-diff-tree -p -C master binary >C.diff
-git-diff-tree -p --full-index master binary >BF.diff
-git-diff-tree -p --full-index -C master binary >CF.diff
+git-diff-tree -p --binary master binary >BF.diff
+git-diff-tree -p --binary -C master binary >CF.diff
test_expect_success 'stat binary diff -- should not fail.' \
'git-checkout master