From 73136b2e8a8ee024320c5ac6a0f14f912432bf03 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 3 May 2006 15:20:21 +0200 Subject: [PATCH 01/90] fetch, pull: ask config for remote information Now you can say [remote.junio] url = git://git.kernel.org/pub/scm/git/git.git fetch = next:next in your .git/config. [jc: fixed up the log message that still said "pull" ] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-parse-remote.sh | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/git-parse-remote.sh b/git-parse-remote.sh index c9b899e3d7..187f0883c9 100755 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -10,7 +10,10 @@ get_data_source () { # Not so fast. This could be the partial URL shorthand... token=$(expr "z$1" : 'z\([^/]*\)/') remainder=$(expr "z$1" : 'z[^/]*/\(.*\)') - if test -f "$GIT_DIR/branches/$token" + if test "$(git-repo-config --get "remote.$token.url")" + then + echo config-partial + elif test -f "$GIT_DIR/branches/$token" then echo branches-partial else @@ -18,7 +21,10 @@ get_data_source () { fi ;; *) - if test -f "$GIT_DIR/remotes/$1" + if test "$(git-repo-config --get "remote.$1.url")" + then + echo config + elif test -f "$GIT_DIR/remotes/$1" then echo remotes elif test -f "$GIT_DIR/branches/$1" @@ -35,6 +41,15 @@ get_remote_url () { case "$data_source" in '') echo "$1" ;; + config-partial) + token=$(expr "z$1" : 'z\([^/]*\)/') + remainder=$(expr "z$1" : 'z[^/]*/\(.*\)') + url=$(git-repo-config --get "remote.$token.url") + echo "$url/$remainder" + ;; + config) + git-repo-config --get "remote.$1.url" + ;; remotes) sed -ne '/^URL: */{ s///p @@ -56,8 +71,10 @@ get_remote_url () { get_remote_default_refs_for_push () { data_source=$(get_data_source "$1") case "$data_source" in - '' | branches | branches-partial) + '' | config-partial | branches | branches-partial) ;; # no default push mapping, just send matching refs. + config) + git-repo-config --get-all "remote.$1.push" ;; remotes) sed -ne '/^Push: */{ s///p @@ -111,8 +128,11 @@ canon_refs_list_for_fetch () { get_remote_default_refs_for_fetch () { data_source=$(get_data_source "$1") case "$data_source" in - '' | branches-partial) + '' | config-partial | branches-partial) echo "HEAD:" ;; + config) + canon_refs_list_for_fetch \ + $(git-repo-config --get-all "remote.$1.fetch") ;; branches) remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1") case "$remote_branch" in '') remote_branch=master ;; esac From a4a6e4ab32648631204398691f4066719dea1029 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 3 May 2006 15:27:26 +0200 Subject: [PATCH 02/90] Add a conversion tool to migrate remote information into the config Use this tool to rewrite the .git/remotes/* files into the config. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/remotes2config.sh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 contrib/remotes2config.sh diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh new file mode 100644 index 0000000000..25901e2b3b --- /dev/null +++ b/contrib/remotes2config.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# Use this tool to rewrite your .git/remotes/ files into the config. + +. git-sh-setup + +if [ -d "$GIT_DIR"/remotes ]; then + echo "Rewriting $GIT_DIR/remotes" >&2 + error=0 + # rewrite into config + { + cd "$GIT_DIR"/remotes + ls | while read f; do + name=$(echo -n "$f" | tr -c "A-Za-z0-9" ".") + sed -n \ + -e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \ + -e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \ + -e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \ + < "$f" + done + echo done + } | while read key value regex; do + case $key in + done) + if [ $error = 0 ]; then + mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old + fi ;; + *) + echo "git-repo-config $key "$value" $regex" + git-repo-config $key "$value" $regex || error=1 ;; + esac + done +fi + + From 0377db77da3f66b8a2bd2f8a9391d22f7a576e34 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 01:16:40 +0200 Subject: [PATCH 03/90] Teach fmt-patch to write individual files. When called with "--stdout", it still writes to standard output. Notable differences to git-format-patch: - since fmt-patch uses the standardized logging machinery, it is no longer "From nobody", but "From ", - the empty lines before and after the "---" just before the diffstat are no longer there, - git-format-patch outputs the commit_sha1 just before the first diff, which fmt-patch does not, - the file names are no longer output to stdout, but to stderr (since stdout is freopen()ed all the time), and - "git fmt-patch HEAD^" does not work as expected: it outputs *all* commits reachable from HEAD^! The last one is possibly a showstopper. At least I used to call that command quite often... Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/builtin-log.c b/builtin-log.c index a39aed6d86..576703c47e 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -69,12 +69,65 @@ int cmd_log(int argc, const char **argv, char **envp) return cmd_log_wc(argc, argv, envp, &rev); } +static int istitlechar(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '.' || c == '_'; +} + +static void reopen_stdout(struct commit *commit, int nr) +{ + char filename[1024]; + char *sol; + int len; + + + sprintf(filename, "%04d", nr); + len = strlen(filename); + + sol = strstr(commit->buffer, "\n\n"); + if (sol) { + int j, space = 1; + + sol += 2; + /* strip [PATCH] or [PATCH blabla] */ + if (!strncmp(sol, "[PATCH", 6)) { + char *eos = strchr(sol + 6, ']'); + if (eos) { + while (isspace(*eos)) + eos++; + sol = eos; + } + } + + for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) { + if (istitlechar(sol[j])) { + if (space) { + filename[len++] = '-'; + space = 0; + } + filename[len++] = sol[j]; + if (sol[j] == '.') + while (sol[j + 1] == '.') + j++; + } else + space = 1; + } + while (filename[len - 1] == '.' || filename[len - 1] == '-') + len--; + } + strcpy(filename + len, ".txt"); + fprintf(stderr, "%s\n", filename); + freopen(filename, "w", stdout); +} + int cmd_format_patch(int argc, const char **argv, char **envp) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; - int nr = 0; + int nr = 0, total; + int use_stdout = 0; init_revisions(&rev); rev.commit_format = CMIT_FMT_EMAIL; @@ -87,20 +140,37 @@ int cmd_format_patch(int argc, const char **argv, char **envp) rev.diffopt.output_format = DIFF_FORMAT_PATCH; argc = setup_revisions(argc, argv, &rev, "HEAD"); + while (argc > 1) { + if (!strcmp(argv[1], "--stdout")) + use_stdout = 1; + else + die ("unrecognized argument: %s", argv[1]); + argc--; + argv++; + } + prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { + /* ignore merges */ + if (commit->parents && commit->parents->next) + continue; nr++; list = realloc(list, nr * sizeof(list[0])); list[nr - 1] = commit; } + total = nr; while (0 <= --nr) { int shown; commit = list[nr]; + if (!use_stdout) + reopen_stdout(commit, total - nr); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; if (shown) printf("-- \n%s\n\n", git_version_string); + if (!use_stdout) + fclose(stdout); } free(list); return 0; From 81f3a188a3781fa4a818ed27ee83430682a98ec4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 03:33:05 +0200 Subject: [PATCH 04/90] fmt-patch: output file names to stdout Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/builtin-log.c b/builtin-log.c index 576703c47e..1649f4943e 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -75,6 +75,8 @@ static int istitlechar(char c) (c >= '0' && c <= '9') || c == '.' || c == '_'; } +static FILE *realstdout = NULL; + static void reopen_stdout(struct commit *commit, int nr) { char filename[1024]; @@ -117,7 +119,7 @@ static void reopen_stdout(struct commit *commit, int nr) len--; } strcpy(filename + len, ".txt"); - fprintf(stderr, "%s\n", filename); + fprintf(realstdout, "%s\n", filename); freopen(filename, "w", stdout); } @@ -149,6 +151,9 @@ int cmd_format_patch(int argc, const char **argv, char **envp) argv++; } + if (!use_stdout) + realstdout = fdopen(dup(1), "w"); + prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { /* ignore merges */ From 2448482b3d5e265dd29fa38c3827565f6f2f31ec Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 03:33:32 +0200 Subject: [PATCH 05/90] fmt-patch: implement -o I had to move the command line parsing around a little; setup_revisions() could mistaken for a valid ref. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 1649f4943e..53a47c9aa4 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -76,15 +76,22 @@ static int istitlechar(char c) } static FILE *realstdout = NULL; +static char *output_directory = NULL; static void reopen_stdout(struct commit *commit, int nr) { char filename[1024]; char *sol; - int len; + int len = 0; + if (output_directory) { + strncpy(filename, output_directory, 1010); + len = strlen(filename); + if (filename[len - 1] != '/') + filename[len++] = '/'; + } - sprintf(filename, "%04d", nr); + sprintf(filename + len, "%04d", nr); len = strlen(filename); sol = strstr(commit->buffer, "\n\n"); @@ -128,7 +135,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp) struct commit *commit; struct commit **list = NULL; struct rev_info rev; - int nr = 0, total; + int nr = 0, total, i, j; int use_stdout = 0; init_revisions(&rev); @@ -140,16 +147,31 @@ int cmd_format_patch(int argc, const char **argv, char **envp) rev.combine_merges = 0; rev.ignore_merges = 1; rev.diffopt.output_format = DIFF_FORMAT_PATCH; - argc = setup_revisions(argc, argv, &rev, "HEAD"); - while (argc > 1) { - if (!strcmp(argv[1], "--stdout")) + /* + * Parse the arguments before setup_revisions(), or something + * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is + * possibly a valid SHA1. + */ + for (i = 1, j = 1; i < argc; i++) { + if (!strcmp(argv[i], "--stdout")) use_stdout = 1; - else - die ("unrecognized argument: %s", argv[1]); - argc--; - argv++; + else if (!strcmp(argv[i], "-o")) { + if (argc < 3) + die ("Which directory?"); + if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST) + die("Could not create directory %s", + argv[i + 1]); + output_directory = strdup(argv[i + 1]); + i++; + } else + argv[j++] = argv[i]; } + argc = j; + + argc = setup_revisions(argc, argv, &rev, "HEAD"); + if (argc > 1) + die ("unrecognized argument: %s", argv[1]); if (!use_stdout) realstdout = fdopen(dup(1), "w"); @@ -177,6 +199,8 @@ int cmd_format_patch(int argc, const char **argv, char **envp) if (!use_stdout) fclose(stdout); } + if (output_directory) + free(output_directory); free(list); return 0; } From 596524b33d50e47e2375cec9e00aff59f0e8278b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 04:30:52 +0200 Subject: [PATCH 06/90] Teach fmt-patch about --numbered Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 9 ++++++++- commit.c | 5 +---- commit.h | 2 +- log-tree.c | 15 ++++++++++++--- rev-list.c | 2 +- revision.h | 1 + show-branch.c | 2 +- 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 53a47c9aa4..43c7ecd5e9 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -137,6 +137,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp) struct rev_info rev; int nr = 0, total, i, j; int use_stdout = 0; + int numbered = 0; init_revisions(&rev); rev.commit_format = CMIT_FMT_EMAIL; @@ -156,6 +157,9 @@ int cmd_format_patch(int argc, const char **argv, char **envp) for (i = 1, j = 1; i < argc; i++) { if (!strcmp(argv[i], "--stdout")) use_stdout = 1; + else if (!strcmp(argv[i], "-n") || + !strcmp(argv[i], "--numbered")) + numbered = 1; else if (!strcmp(argv[i], "-o")) { if (argc < 3) die ("Which directory?"); @@ -186,11 +190,14 @@ int cmd_format_patch(int argc, const char **argv, char **envp) list[nr - 1] = commit; } total = nr; + if (numbered) + rev.total = total; while (0 <= --nr) { int shown; commit = list[nr]; + rev.nr = total - nr; if (!use_stdout) - reopen_stdout(commit, total - nr); + reopen_stdout(commit, rev.nr); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; diff --git a/commit.c b/commit.c index 42b44bba52..93b3903ea7 100644 --- a/commit.c +++ b/commit.c @@ -489,17 +489,14 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject) { int hdr = 1, body = 0; unsigned long offset = 0; int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; - const char *subject = NULL; - if (fmt == CMIT_FMT_EMAIL) - subject = "Subject: [PATCH] "; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; diff --git a/commit.h b/commit.h index 01eec60a1e..8d7514cd00 100644 --- a/commit.h +++ b/commit.h @@ -51,7 +51,7 @@ enum cmit_fmt { }; extern enum cmit_fmt get_commit_format(const char *arg); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev); +extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/log-tree.c b/log-tree.c index aaf2b9423f..1ca529d9fe 100644 --- a/log-tree.c +++ b/log-tree.c @@ -11,6 +11,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra; int len; + char* subject = NULL; opt->loginfo = NULL; if (!opt->verbose_header) { @@ -38,10 +39,18 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) * Print header line of header.. */ - if (opt->commit_format == CMIT_FMT_EMAIL) + if (opt->commit_format == CMIT_FMT_EMAIL) { + if (opt->total > 0) { + static char buffer[64]; + snprintf(buffer, sizeof(buffer), + "Subject: [PATCH %d/%d] ", + opt->nr, opt->total); + subject = buffer; + } else + subject = "Subject: [PATCH] "; printf("From %s Thu Apr 7 15:13:13 2005\n", sha1_to_hex(commit->object.sha1)); - else { + } else { printf("%s%s", opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", diff_unique_abbrev(commit->object.sha1, abbrev_commit)); @@ -55,7 +64,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) /* * And then the pretty-printed message itself */ - len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev); + len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject); printf("%s%s%s", this_header, extra, sep); } diff --git a/rev-list.c b/rev-list.c index 8b0ec388fa..235ae4c7e1 100644 --- a/rev-list.c +++ b/rev-list.c @@ -84,7 +84,7 @@ static void show_commit(struct commit *commit) static char pretty_header[16384]; pretty_print_commit(revs.commit_format, commit, ~0, pretty_header, sizeof(pretty_header), - revs.abbrev); + revs.abbrev, NULL); printf("%s%c", pretty_header, hdr_termination); } fflush(stdout); diff --git a/revision.h b/revision.h index 48d7b4ca94..62759f7bc0 100644 --- a/revision.h +++ b/revision.h @@ -58,6 +58,7 @@ struct rev_info { unsigned int abbrev; enum cmit_fmt commit_format; struct log_info *loginfo; + int nr, total; /* special limits */ int max_count; diff --git a/show-branch.c b/show-branch.c index 24efb65e62..5da3a1a90b 100644 --- a/show-branch.c +++ b/show-branch.c @@ -259,7 +259,7 @@ static void show_one_commit(struct commit *commit, int no_name) struct commit_name *name = commit->object.util; if (commit->object.parsed) pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - pretty, sizeof(pretty), 0); + pretty, sizeof(pretty), 0, NULL); else strcpy(pretty, "(unavailable)"); if (!strncmp(pretty, "[PATCH] ", 8)) From 8ac80a5701780547404523a84f4b1ae67bfa6823 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 04:31:29 +0200 Subject: [PATCH 07/90] Teach fmt-patch about --keep-subject Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 16 ++++++++++++---- log-tree.c | 5 ++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 43c7ecd5e9..0027998f10 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -78,7 +78,7 @@ static int istitlechar(char c) static FILE *realstdout = NULL; static char *output_directory = NULL; -static void reopen_stdout(struct commit *commit, int nr) +static void reopen_stdout(struct commit *commit, int nr, int keep_subject) { char filename[1024]; char *sol; @@ -100,7 +100,7 @@ static void reopen_stdout(struct commit *commit, int nr) sol += 2; /* strip [PATCH] or [PATCH blabla] */ - if (!strncmp(sol, "[PATCH", 6)) { + if (!keep_subject && !strncmp(sol, "[PATCH", 6)) { char *eos = strchr(sol + 6, ']'); if (eos) { while (isspace(*eos)) @@ -138,6 +138,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp) int nr = 0, total, i, j; int use_stdout = 0; int numbered = 0; + int keep_subject = 0; init_revisions(&rev); rev.commit_format = CMIT_FMT_EMAIL; @@ -160,7 +161,11 @@ int cmd_format_patch(int argc, const char **argv, char **envp) else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--numbered")) numbered = 1; - else if (!strcmp(argv[i], "-o")) { + else if (!strcmp(argv[i], "-k") || + !strcmp(argv[i], "--keep-subject")) { + keep_subject = 1; + rev.total = -1; + } else if (!strcmp(argv[i], "-o")) { if (argc < 3) die ("Which directory?"); if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST) @@ -173,6 +178,9 @@ int cmd_format_patch(int argc, const char **argv, char **envp) } argc = j; + if (numbered && keep_subject < 0) + die ("-n and -k are mutually exclusive."); + argc = setup_revisions(argc, argv, &rev, "HEAD"); if (argc > 1) die ("unrecognized argument: %s", argv[1]); @@ -197,7 +205,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp) commit = list[nr]; rev.nr = total - nr; if (!use_stdout) - reopen_stdout(commit, rev.nr); + reopen_stdout(commit, rev.nr, keep_subject); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; diff --git a/log-tree.c b/log-tree.c index 1ca529d9fe..4a3cd6791b 100644 --- a/log-tree.c +++ b/log-tree.c @@ -46,8 +46,11 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) "Subject: [PATCH %d/%d] ", opt->nr, opt->total); subject = buffer; - } else + } else if (opt->total == 0) subject = "Subject: [PATCH] "; + else + subject = "Subject: "; + printf("From %s Thu Apr 7 15:13:13 2005\n", sha1_to_hex(commit->object.sha1)); } else { From e686eb9870d6b382f0760e3e859e93c8c2dfb31b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 6 May 2006 22:56:38 +0200 Subject: [PATCH 08/90] fmt-patch: understand old notation When calling "git fmt-patch HEAD~5", you now get the same as if you would have said "git fmt-patch HEAD~5..". This makes it easier for my fingers which are so used to the old syntax. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-diff.c | 2 +- builtin-log.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/builtin-diff.c b/builtin-diff.c index 636edbf2a7..20873162f9 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -232,7 +232,7 @@ static int builtin_diff_combined(struct rev_info *revs, return 0; } -static void add_head(struct rev_info *revs) +void add_head(struct rev_info *revs) { unsigned char sha1[20]; struct object *obj; diff --git a/builtin-log.c b/builtin-log.c index 0027998f10..d5bbc1cc06 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -11,6 +11,9 @@ #include "log-tree.h" #include "builtin.h" +/* this is in builtin-diff.c */ +void add_head(struct rev_info *revs); + static int cmd_log_wc(int argc, const char **argv, char **envp, struct rev_info *rev) { @@ -185,6 +188,11 @@ int cmd_format_patch(int argc, const char **argv, char **envp) if (argc > 1) die ("unrecognized argument: %s", argv[1]); + if (rev.pending_objects && rev.pending_objects->next == NULL) { + rev.pending_objects->item->flags |= UNINTERESTING; + add_head(&rev); + } + if (!use_stdout) realstdout = fdopen(dup(1), "w"); From 49e3343c9fe0e134e0a8c1ec0ddeb64ae18ee9fd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 14 May 2006 21:59:04 -0700 Subject: [PATCH 09/90] apply --numstat: show new name, not old name. Somehow --stat showed the new name but --numstat showed the old name for renamed/copied paths. Signed-off-by: Junio C Hamano --- apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apply.c b/apply.c index 7c8146a7f3..2151c96c2a 100644 --- a/apply.c +++ b/apply.c @@ -1778,7 +1778,7 @@ static void numstat_patch_list(struct patch *patch) { for ( ; patch; patch = patch->next) { const char *name; - name = patch->old_name ? patch->old_name : patch->new_name; + name = patch->new_name ? patch->new_name : patch->old_name; printf("%d\t%d\t", patch->lines_added, patch->lines_deleted); if (line_termination && quote_c_style(name, NULL, NULL, 0)) quote_c_style(name, NULL, stdout, 0); From 04e4888e5a6db847368a95eb986202fa460b2153 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 15 May 2006 15:15:47 -0700 Subject: [PATCH 10/90] apply --cached: apply a patch without using working tree. A new flag "--cached" takes the cached data, applies the patch and stores the result in the index, without using the working tree. Signed-off-by: Junio C Hamano --- apply.c | 72 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/apply.c b/apply.c index 2151c96c2a..b3b9b40596 100644 --- a/apply.c +++ b/apply.c @@ -17,6 +17,8 @@ // --stat does just a diffstat, and doesn't actually apply // --numstat does numeric diffstat, and doesn't actually apply // --index-info shows the old and new index info for paths if available. +// --index updates the cache as well. +// --cached updates only the cache without ever touching the working tree. // static const char *prefix; static int prefix_length = -1; @@ -26,6 +28,7 @@ static int p_value = 1; static int allow_binary_replacement = 0; static int check_index = 0; static int write_index = 0; +static int cached = 0; static int diffstat = 0; static int numstat = 0; static int summary = 0; @@ -36,7 +39,7 @@ static int show_index_info = 0; static int line_termination = '\n'; static unsigned long p_context = -1; static const char apply_usage[] = -"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=] ..."; +"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=] ..."; static enum whitespace_eol { nowarn_whitespace, @@ -1600,7 +1603,7 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch) return 0; } -static int apply_data(struct patch *patch, struct stat *st) +static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { char *buf; unsigned long size, alloc; @@ -1609,7 +1612,17 @@ static int apply_data(struct patch *patch, struct stat *st) size = 0; alloc = 0; buf = NULL; - if (patch->old_name) { + if (cached) { + if (ce) { + char type[20]; + buf = read_sha1_file(ce->sha1, type, &size); + if (!buf) + return error("read of %s failed", + patch->old_name); + alloc = size; + } + } + else if (patch->old_name) { size = st->st_size; alloc = size + 8192; buf = xmalloc(alloc); @@ -1637,16 +1650,21 @@ static int check_patch(struct patch *patch) const char *old_name = patch->old_name; const char *new_name = patch->new_name; const char *name = old_name ? old_name : new_name; + struct cache_entry *ce = NULL; if (old_name) { - int changed; - int stat_ret = lstat(old_name, &st); + int changed = 0; + int stat_ret = 0; + unsigned st_mode = 0; + if (!cached) + stat_ret = lstat(old_name, &st); if (check_index) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) return error("%s: does not exist in index", old_name); + ce = active_cache[pos]; if (stat_ret < 0) { struct checkout costate; if (errno != ENOENT) @@ -1659,37 +1677,41 @@ static int check_patch(struct patch *patch) costate.quiet = 0; costate.not_new = 0; costate.refresh_cache = 1; - if (checkout_entry(active_cache[pos], + if (checkout_entry(ce, &costate, NULL) || lstat(old_name, &st)) return -1; } - - changed = ce_match_stat(active_cache[pos], &st, 1); + if (!cached) + changed = ce_match_stat(ce, &st, 1); if (changed) return error("%s: does not match index", old_name); + if (cached) + st_mode = ntohl(ce->ce_mode); } else if (stat_ret < 0) return error("%s: %s", old_name, strerror(errno)); + if (!cached) + st_mode = ntohl(create_ce_mode(st.st_mode)); + if (patch->is_new < 0) patch->is_new = 0; - st.st_mode = ntohl(create_ce_mode(st.st_mode)); if (!patch->old_mode) - patch->old_mode = st.st_mode; - if ((st.st_mode ^ patch->old_mode) & S_IFMT) + patch->old_mode = st_mode; + if ((st_mode ^ patch->old_mode) & S_IFMT) return error("%s: wrong type", old_name); - if (st.st_mode != patch->old_mode) + if (st_mode != patch->old_mode) fprintf(stderr, "warning: %s has type %o, expected %o\n", - old_name, st.st_mode, patch->old_mode); + old_name, st_mode, patch->old_mode); } if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) { if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0) return error("%s: already exists in index", new_name); - if (!lstat(new_name, &st)) + if (!cached && !lstat(new_name, &st)) return error("%s: already exists in working directory", new_name); if (errno != ENOENT) return error("%s: %s", new_name, strerror(errno)); @@ -1709,9 +1731,9 @@ static int check_patch(struct patch *patch) return error("new mode (%o) of %s does not match old mode (%o)%s%s", patch->new_mode, new_name, patch->old_mode, same ? "" : " of ", same ? "" : old_name); - } + } - if (apply_data(patch, &st) < 0) + if (apply_data(patch, &st, ce) < 0) return error("%s: patch does not apply", name); return 0; } @@ -1894,7 +1916,8 @@ static void remove_file(struct patch *patch) if (remove_file_from_cache(patch->old_name) < 0) die("unable to remove %s from index", patch->old_name); } - unlink(patch->old_name); + if (!cached) + unlink(patch->old_name); } static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size) @@ -1911,9 +1934,11 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned memcpy(ce->name, path, namelen); ce->ce_mode = create_ce_mode(mode); ce->ce_flags = htons(namelen); - if (lstat(path, &st) < 0) - die("unable to stat newly created file %s", path); - fill_stat_cache_info(ce, &st); + if (!cached) { + if (lstat(path, &st) < 0) + die("unable to stat newly created file %s", path); + fill_stat_cache_info(ce, &st); + } if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0) die("unable to create backing store for newly created file %s", path); if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) @@ -1950,6 +1975,8 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, */ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size) { + if (cached) + return; if (!try_create_file(path, mode, buf, size)) return; @@ -2182,6 +2209,11 @@ int main(int argc, char **argv) check_index = 1; continue; } + if (!strcmp(arg, "--cached")) { + check_index = 1; + cached = 1; + continue; + } if (!strcmp(arg, "--apply")) { apply = 1; continue; From b7627278e200d8a80df6ee437b1a01d88f2b4883 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 15 May 2006 17:25:43 -0700 Subject: [PATCH 11/90] git-am: use apply --cached Now 'git apply' can apply patch without working tree, preparation of pristine preimage and postimage trees that are done when falling back on 3-way merge by "git am" can do so without temporary files. Signed-off-by: Junio C Hamano --- git-am.sh | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/git-am.sh b/git-am.sh index 507ae4dcb9..f50dff2222 100755 --- a/git-am.sh +++ b/git-am.sh @@ -55,46 +55,12 @@ fall_back_3way () { GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ git-write-tree >"$dotest/patch-merge-base+" && # index has the base tree now. - ( - cd "$dotest/patch-merge-tmp-dir" && - GIT_INDEX_FILE="../patch-merge-tmp-index" \ - GIT_OBJECT_DIRECTORY="$O_OBJECT" \ - git-apply $binary --index <../patch - ) + GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ + git-apply $binary --cached <"$dotest/patch" then echo Using index info to reconstruct a base tree... mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base" mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index" - else - # Otherwise, try nearby trees that can be used to apply the - # patch. - ( - N=10 - - # Hoping the patch is against our recent commits... - git-rev-list --max-count=$N HEAD - - # or hoping the patch is against known tags... - git-ls-remote --tags . - ) | - while read base junk - do - # See if we have it as a tree... - git-cat-file tree "$base" >/dev/null 2>&1 || continue - - rm -fr "$dotest"/patch-merge-* && - mkdir "$dotest/patch-merge-tmp-dir" || break - ( - cd "$dotest/patch-merge-tmp-dir" && - GIT_INDEX_FILE=../patch-merge-tmp-index && - GIT_OBJECT_DIRECTORY="$O_OBJECT" && - export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY && - git-read-tree "$base" && - git-apply $binary --index && - mv ../patch-merge-tmp-index ../patch-merge-index && - echo "$base" >../patch-merge-base - ) <"$dotest/patch" 2>/dev/null && break - done fi test -f "$dotest/patch-merge-index" && From fcc387db9bc453dc7e07a262873481af2ee9e5c8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 17 May 2006 01:17:46 -0700 Subject: [PATCH 12/90] read-tree -m -u: do not overwrite or remove untracked working tree files. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a merge results in a creation of a path that did not exist in HEAD, and if you already have that path on the working tree, because the index has not been told about the working tree file, read-tree happily removes it. The issue was brought up by Santi Béjar on the list. Signed-off-by: Junio C Hamano --- read-tree.c | 45 +++++++++++++++++++++--- t/t1002-read-tree-m-u-2way.sh | 66 +++++++++++++++++++++++------------ t/t3500-cherry.sh | 1 + t/t4002-diff-basic.sh | 6 ++-- t/t6022-merge-rename.sh | 1 + 5 files changed, 88 insertions(+), 31 deletions(-) diff --git a/read-tree.c b/read-tree.c index e16e91b173..82e2a9a4d3 100644 --- a/read-tree.c +++ b/read-tree.c @@ -408,7 +408,7 @@ static void verify_uptodate(struct cache_entry *ce) { struct stat st; - if (index_only) + if (index_only || reset) return; if (!lstat(ce->name, &st)) { @@ -426,6 +426,21 @@ static void verify_uptodate(struct cache_entry *ce) die("Entry '%s' not uptodate. Cannot merge.", ce->name); } +/* + * We do not want to remove or overwrite a working tree file that + * is not tracked. + */ +static void verify_absent(const char *path, const char *action) +{ + struct stat st; + + if (index_only || reset || !update) + return; + if (!lstat(path, &st)) + die("Untracked working tree file '%s' " + "would be %s by merge.", path, action); +} + static int merged_entry(struct cache_entry *merge, struct cache_entry *old) { merge->ce_flags |= htons(CE_UPDATE); @@ -443,6 +458,9 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old) verify_uptodate(old); } } + else + verify_absent(merge->name, "overwritten"); + merge->ce_flags &= ~htons(CE_STAGEMASK); add_cache_entry(merge, ADD_CACHE_OK_TO_ADD); return 1; @@ -452,6 +470,8 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old) { if (old) verify_uptodate(old); + else + verify_absent(ce->name, "removed"); ce->ce_mode = 0; add_cache_entry(ce, ADD_CACHE_OK_TO_ADD); return 1; @@ -487,6 +507,7 @@ static int threeway_merge(struct cache_entry **stages) int count; int head_match = 0; int remote_match = 0; + const char *path = NULL; int df_conflict_head = 0; int df_conflict_remote = 0; @@ -498,8 +519,11 @@ static int threeway_merge(struct cache_entry **stages) for (i = 1; i < head_idx; i++) { if (!stages[i]) any_anc_missing = 1; - else + else { + if (!path) + path = stages[i]->name; no_anc_exists = 0; + } } index = stages[0]; @@ -515,8 +539,15 @@ static int threeway_merge(struct cache_entry **stages) remote = NULL; } + if (!path && index) + path = index->name; + if (!path && head) + path = head->name; + if (!path && remote) + path = remote->name; + /* First, if there's a #16 situation, note that to prevent #13 - * and #14. + * and #14. */ if (!same(remote, head)) { for (i = 1; i < head_idx; i++) { @@ -575,6 +606,8 @@ static int threeway_merge(struct cache_entry **stages) (remote_deleted && head && head_match)) { if (index) return deleted_entry(index, index); + else if (path) + verify_absent(path, "removed"); return 0; } /* @@ -592,6 +625,8 @@ static int threeway_merge(struct cache_entry **stages) if (index) { verify_uptodate(index); } + else if (path) + verify_absent(path, "overwritten"); nontrivial_merge = 1; @@ -689,7 +724,7 @@ static int oneway_merge(struct cache_entry **src) merge_size); if (!a) - return deleted_entry(old, NULL); + return deleted_entry(old, old); if (old && same(old, a)) { if (reset) { struct stat st; @@ -699,7 +734,7 @@ static int oneway_merge(struct cache_entry **src) } return keep_entry(old); } - return merged_entry(a, NULL); + return merged_entry(a, old); } static int read_cache_unmerged(void) diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh index 4d175d8ea1..8335a63e2e 100755 --- a/t/t1002-read-tree-m-u-2way.sh +++ b/t/t1002-read-tree-m-u-2way.sh @@ -39,7 +39,6 @@ test_expect_success \ echo nitfol >nitfol && echo bozbar >bozbar && echo rezrov >rezrov && - echo yomin >yomin && git-update-index --add nitfol bozbar rezrov && treeH=`git-write-tree` && echo treeH $treeH && @@ -56,7 +55,8 @@ test_expect_success \ test_expect_success \ '1, 2, 3 - no carry forward' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && git-read-tree -m -u $treeH $treeM && git-ls-files --stage >1-3.out && cmp M.out 1-3.out && @@ -66,11 +66,12 @@ test_expect_success \ check_cache_at frotz clean && check_cache_at nitfol clean' -echo '+100644 X 0 yomin' >expected - test_expect_success \ '4 - carry forward local addition.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && + echo "+100644 X 0 yomin" >expected && + echo yomin >yomin && git-update-index --add yomin && git-read-tree -m -u $treeH $treeM && git-ls-files --stage >4.out || return 1 @@ -85,7 +86,9 @@ test_expect_success \ test_expect_success \ '5 - carry forward local addition.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && + git-read-tree -m -u $treeH && echo yomin >yomin && git-update-index --add yomin && echo yomin yomin >yomin && @@ -103,7 +106,9 @@ test_expect_success \ test_expect_success \ '6 - local addition already has the same.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && + echo frotz >frotz && git-update-index --add frotz && git-read-tree -m -u $treeH $treeM && git-ls-files --stage >6.out && @@ -117,7 +122,8 @@ test_expect_success \ test_expect_success \ '7 - local addition already has the same.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo frotz >frotz && git-update-index --add frotz && echo frotz frotz >frotz && @@ -134,14 +140,16 @@ test_expect_success \ test_expect_success \ '8 - conflicting addition.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo frotz frotz >frotz && git-update-index --add frotz && if git-read-tree -m -u $treeH $treeM; then false; else :; fi' test_expect_success \ '9 - conflicting addition.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo frotz frotz >frotz && git-update-index --add frotz && echo frotz >frotz && @@ -149,7 +157,8 @@ test_expect_success \ test_expect_success \ '10 - path removed.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo rezrov >rezrov && git-update-index --add rezrov && git-read-tree -m -u $treeH $treeM && @@ -160,7 +169,8 @@ test_expect_success \ test_expect_success \ '11 - dirty path removed.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo rezrov >rezrov && git-update-index --add rezrov && echo rezrov rezrov >rezrov && @@ -168,14 +178,16 @@ test_expect_success \ test_expect_success \ '12 - unmatching local changes being removed.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo rezrov rezrov >rezrov && git-update-index --add rezrov && if git-read-tree -m -u $treeH $treeM; then false; else :; fi' test_expect_success \ '13 - unmatching local changes being removed.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo rezrov rezrov >rezrov && git-update-index --add rezrov && echo rezrov >rezrov && @@ -188,7 +200,8 @@ EOF test_expect_success \ '14 - unchanged in two heads.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo nitfol nitfol >nitfol && git-update-index --add nitfol && git-read-tree -m -u $treeH $treeM && @@ -207,7 +220,8 @@ test_expect_success \ test_expect_success \ '15 - unchanged in two heads.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo nitfol nitfol >nitfol && git-update-index --add nitfol && echo nitfol nitfol nitfol >nitfol && @@ -227,14 +241,16 @@ test_expect_success \ test_expect_success \ '16 - conflicting local change.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo bozbar bozbar >bozbar && git-update-index --add bozbar && if git-read-tree -m -u $treeH $treeM; then false; else :; fi' test_expect_success \ '17 - conflicting local change.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo bozbar bozbar >bozbar && git-update-index --add bozbar && echo bozbar bozbar bozbar >bozbar && @@ -242,7 +258,8 @@ test_expect_success \ test_expect_success \ '18 - local change already having a good result.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo gnusto >bozbar && git-update-index --add bozbar && git-read-tree -m -u $treeH $treeM && @@ -254,7 +271,8 @@ test_expect_success \ test_expect_success \ '19 - local change already having a good result, further modified.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo gnusto >bozbar && git-update-index --add bozbar && echo gnusto gnusto >bozbar && @@ -273,7 +291,8 @@ test_expect_success \ test_expect_success \ '20 - no local change, use new tree.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo bozbar >bozbar && git-update-index --add bozbar && git-read-tree -m -u $treeH $treeM && @@ -285,7 +304,8 @@ test_expect_success \ test_expect_success \ '21 - no local change, dirty cache.' \ - 'rm -f .git/index && + 'rm -f .git/index nitfol bozbar rezrov frotz && + git-read-tree --reset -u $treeH && echo bozbar >bozbar && git-update-index --add bozbar && echo gnusto gnusto >bozbar && @@ -294,7 +314,7 @@ test_expect_success \ # Also make sure we did not break DF vs DF/DF case. test_expect_success \ 'DF vs DF/DF case setup.' \ - 'rm -f .git/index && + 'rm -f .git/index echo DF >DF && git-update-index --add DF && treeDF=`git-write-tree` && diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh index b141f89de2..e83bbee074 100755 --- a/t/t3500-cherry.sh +++ b/t/t3500-cherry.sh @@ -30,6 +30,7 @@ test_expect_success \ git-commit -m "Add C." && git-checkout -f master && + rm -f B C && echo Third >> A && git-update-index A && diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh index 769274aa51..56eda63fc2 100755 --- a/t/t4002-diff-basic.sh +++ b/t/t4002-diff-basic.sh @@ -191,7 +191,7 @@ test_expect_success \ 'rm -fr Z [A-Z][A-Z] && git-read-tree $tree_A && git-checkout-index -f -a && - git-read-tree -m $tree_O || return 1 + git-read-tree --reset $tree_O || return 1 git-update-index --refresh >/dev/null ;# this can exit non-zero git-diff-files >.test-a && cmp_diff_files_output .test-a .test-recursive-OA' @@ -201,7 +201,7 @@ test_expect_success \ 'rm -fr Z [A-Z][A-Z] && git-read-tree $tree_B && git-checkout-index -f -a && - git-read-tree -m $tree_O || return 1 + git-read-tree --reset $tree_O || return 1 git-update-index --refresh >/dev/null ;# this can exit non-zero git-diff-files >.test-a && cmp_diff_files_output .test-a .test-recursive-OB' @@ -211,7 +211,7 @@ test_expect_success \ 'rm -fr Z [A-Z][A-Z] && git-read-tree $tree_B && git-checkout-index -f -a && - git-read-tree -m $tree_A || return 1 + git-read-tree --reset $tree_A || return 1 git-update-index --refresh >/dev/null ;# this can exit non-zero git-diff-files >.test-a && cmp_diff_files_output .test-a .test-recursive-AB' diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh index a2d24b5ca9..5ac25647ab 100755 --- a/t/t6022-merge-rename.sh +++ b/t/t6022-merge-rename.sh @@ -111,6 +111,7 @@ test_expect_success 'pull renaming branch into unrenaming one' \ test_expect_success 'pull renaming branch into another renaming one' \ ' + rm -f B git reset --hard git checkout red git pull . white && { From 453ec4bdf403c2e89892266a0a660c21680d3f9d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 16 May 2006 19:02:14 -0700 Subject: [PATCH 13/90] libify git-ls-files directory traversal This moves the core directory traversal and filename exclusion logic into the general git library, making it available for other users directly. If we ever want to do "git commit" or "git add" as a built-in (and we do), we want to be able to handle most of git-ls-files as a library. NOTE! Not all of git-ls-files is libified by this. The index matching and pathspec prefix calculation is still in ls-files.c, but this is a big part of it. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 4 +- dir.c | 295 +++++++++++++++++++++++++++++++++++++++++++ dir.h | 50 ++++++++ ls-files.c | 363 +++++------------------------------------------------ 4 files changed, 376 insertions(+), 336 deletions(-) create mode 100644 dir.c create mode 100644 dir.h diff --git a/Makefile b/Makefile index 9ba608c805..f43ac63c93 100644 --- a/Makefile +++ b/Makefile @@ -199,7 +199,7 @@ LIB_H = \ blob.h cache.h commit.h csum-file.h delta.h \ diff.h object.h pack.h pkt-line.h quote.h refs.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ - tree-walk.h log-tree.h + tree-walk.h log-tree.h dir.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -210,7 +210,7 @@ LIB_OBJS = \ blob.o commit.o connect.o csum-file.o base85.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o \ - quote.o read-cache.o refs.o run-command.o \ + quote.o read-cache.o refs.o run-command.o dir.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ diff --git a/dir.c b/dir.c new file mode 100644 index 0000000000..3f41a5dfea --- /dev/null +++ b/dir.c @@ -0,0 +1,295 @@ +/* + * This handles recursive filename detection with exclude + * files, index knowledge etc.. + * + * Copyright (C) Linus Torvalds, 2005-2006 + * Junio Hamano, 2005-2006 + */ +#include +#include + +#include "cache.h" +#include "dir.h" + +void add_exclude(const char *string, const char *base, + int baselen, struct exclude_list *which) +{ + struct exclude *x = xmalloc(sizeof (*x)); + + x->pattern = string; + x->base = base; + x->baselen = baselen; + if (which->nr == which->alloc) { + which->alloc = alloc_nr(which->alloc); + which->excludes = realloc(which->excludes, + which->alloc * sizeof(x)); + } + which->excludes[which->nr++] = x; +} + +static int add_excludes_from_file_1(const char *fname, + const char *base, + int baselen, + struct exclude_list *which) +{ + int fd, i; + long size; + char *buf, *entry; + + fd = open(fname, O_RDONLY); + if (fd < 0) + goto err; + size = lseek(fd, 0, SEEK_END); + if (size < 0) + goto err; + lseek(fd, 0, SEEK_SET); + if (size == 0) { + close(fd); + return 0; + } + buf = xmalloc(size+1); + if (read(fd, buf, size) != size) + goto err; + close(fd); + + buf[size++] = '\n'; + entry = buf; + for (i = 0; i < size; i++) { + if (buf[i] == '\n') { + if (entry != buf + i && entry[0] != '#') { + buf[i - (i && buf[i-1] == '\r')] = 0; + add_exclude(entry, base, baselen, which); + } + entry = buf + i + 1; + } + } + return 0; + + err: + if (0 <= fd) + close(fd); + return -1; +} + +void add_excludes_from_file(struct dir_struct *dir, const char *fname) +{ + if (add_excludes_from_file_1(fname, "", 0, + &dir->exclude_list[EXC_FILE]) < 0) + die("cannot use %s as an exclude file", fname); +} + +int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) +{ + char exclude_file[PATH_MAX]; + struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; + int current_nr = el->nr; + + if (dir->exclude_per_dir) { + memcpy(exclude_file, base, baselen); + strcpy(exclude_file + baselen, dir->exclude_per_dir); + add_excludes_from_file_1(exclude_file, base, baselen, el); + } + return current_nr; +} + +static void pop_exclude_per_directory(struct dir_struct *dir, int stk) +{ + struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; + + while (stk < el->nr) + free(el->excludes[--el->nr]); +} + +/* Scan the list and let the last match determines the fate. + * Return 1 for exclude, 0 for include and -1 for undecided. + */ +static int excluded_1(const char *pathname, + int pathlen, + struct exclude_list *el) +{ + int i; + + if (el->nr) { + for (i = el->nr - 1; 0 <= i; i--) { + struct exclude *x = el->excludes[i]; + const char *exclude = x->pattern; + int to_exclude = 1; + + if (*exclude == '!') { + to_exclude = 0; + exclude++; + } + + if (!strchr(exclude, '/')) { + /* match basename */ + const char *basename = strrchr(pathname, '/'); + basename = (basename) ? basename+1 : pathname; + if (fnmatch(exclude, basename, 0) == 0) + return to_exclude; + } + else { + /* match with FNM_PATHNAME: + * exclude has base (baselen long) implicitly + * in front of it. + */ + int baselen = x->baselen; + if (*exclude == '/') + exclude++; + + if (pathlen < baselen || + (baselen && pathname[baselen-1] != '/') || + strncmp(pathname, x->base, baselen)) + continue; + + if (fnmatch(exclude, pathname+baselen, + FNM_PATHNAME) == 0) + return to_exclude; + } + } + } + return -1; /* undecided */ +} + +int excluded(struct dir_struct *dir, const char *pathname) +{ + int pathlen = strlen(pathname); + int st; + + for (st = EXC_CMDL; st <= EXC_FILE; st++) { + switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) { + case 0: + return 0; + case 1: + return 1; + } + } + return 0; +} + +static void add_name(struct dir_struct *dir, const char *pathname, int len) +{ + struct dir_entry *ent; + + if (cache_name_pos(pathname, len) >= 0) + return; + + if (dir->nr == dir->alloc) { + int alloc = alloc_nr(dir->alloc); + dir->alloc = alloc; + dir->entries = xrealloc(dir->entries, alloc*sizeof(ent)); + } + ent = xmalloc(sizeof(*ent) + len + 1); + ent->len = len; + memcpy(ent->name, pathname, len); + ent->name[len] = 0; + dir->entries[dir->nr++] = ent; +} + +static int dir_exists(const char *dirname, int len) +{ + int pos = cache_name_pos(dirname, len); + if (pos >= 0) + return 1; + pos = -pos-1; + if (pos >= active_nr) /* can't */ + return 0; + return !strncmp(active_cache[pos]->name, dirname, len); +} + +/* + * Read a directory tree. We currently ignore anything but + * directories, regular files and symlinks. That's because git + * doesn't handle them at all yet. Maybe that will change some + * day. + * + * Also, we ignore the name ".git" (even if it is not a directory). + * That likely will not change. + */ +static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen) +{ + DIR *fdir = opendir(path); + int contents = 0; + + if (fdir) { + int exclude_stk; + struct dirent *de; + char fullname[MAXPATHLEN + 1]; + memcpy(fullname, base, baselen); + + exclude_stk = push_exclude_per_directory(dir, base, baselen); + + while ((de = readdir(fdir)) != NULL) { + int len; + + if ((de->d_name[0] == '.') && + (de->d_name[1] == 0 || + !strcmp(de->d_name + 1, ".") || + !strcmp(de->d_name + 1, "git"))) + continue; + len = strlen(de->d_name); + memcpy(fullname + baselen, de->d_name, len+1); + if (excluded(dir, fullname) != dir->show_ignored) { + if (!dir->show_ignored || DTYPE(de) != DT_DIR) { + continue; + } + } + + switch (DTYPE(de)) { + struct stat st; + int subdir, rewind_base; + default: + continue; + case DT_UNKNOWN: + if (lstat(fullname, &st)) + continue; + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) + break; + if (!S_ISDIR(st.st_mode)) + continue; + /* fallthrough */ + case DT_DIR: + memcpy(fullname + baselen + len, "/", 2); + len++; + rewind_base = dir->nr; + subdir = read_directory_recursive(dir, fullname, fullname, + baselen + len); + if (dir->show_other_directories && + (subdir || !dir->hide_empty_directories) && + !dir_exists(fullname, baselen + len)) { + // Rewind the read subdirectory + while (dir->nr > rewind_base) + free(dir->entries[--dir->nr]); + break; + } + contents += subdir; + continue; + case DT_REG: + case DT_LNK: + break; + } + add_name(dir, fullname, baselen + len); + contents++; + } + closedir(fdir); + + pop_exclude_per_directory(dir, exclude_stk); + } + + return contents; +} + +static int cmp_name(const void *p1, const void *p2) +{ + const struct dir_entry *e1 = *(const struct dir_entry **)p1; + const struct dir_entry *e2 = *(const struct dir_entry **)p2; + + return cache_name_compare(e1->name, e1->len, + e2->name, e2->len); +} + +int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen) +{ + read_directory_recursive(dir, path, base, baselen); + qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); + return dir->nr; +} diff --git a/dir.h b/dir.h new file mode 100644 index 0000000000..e8fc441e7f --- /dev/null +++ b/dir.h @@ -0,0 +1,50 @@ +#ifndef DIR_H +#define DIR_H + +/* + * We maintain three exclude pattern lists: + * EXC_CMDL lists patterns explicitly given on the command line. + * EXC_DIRS lists patterns obtained from per-directory ignore files. + * EXC_FILE lists patterns from fallback ignore files. + */ +#define EXC_CMDL 0 +#define EXC_DIRS 1 +#define EXC_FILE 2 + + +struct dir_entry { + int len; + char name[FLEX_ARRAY]; /* more */ +}; + +struct exclude_list { + int nr; + int alloc; + struct exclude { + const char *pattern; + const char *base; + int baselen; + } **excludes; +}; + +struct dir_struct { + int nr, alloc; + unsigned int show_ignored:1, + show_other_directories:1, + hide_empty_directories:1; + struct dir_entry **entries; + + /* Exclude info */ + const char *exclude_per_dir; + struct exclude_list exclude_list[3]; +}; + +extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen); +extern int excluded(struct dir_struct *, const char *); +extern void add_excludes_from_file(struct dir_struct *, const char *fname); +extern void add_exclude(const char *string, const char *base, + int baselen, struct exclude_list *which); +extern int push_exclude_per_directory(struct dir_struct *, + const char *base, int baselen); + +#endif diff --git a/ls-files.c b/ls-files.c index 4a4af1ca3b..89941a3ff8 100644 --- a/ls-files.c +++ b/ls-files.c @@ -5,23 +5,20 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#include #include #include "cache.h" #include "quote.h" +#include "dir.h" static int abbrev = 0; static int show_deleted = 0; static int show_cached = 0; static int show_others = 0; -static int show_ignored = 0; static int show_stage = 0; static int show_unmerged = 0; static int show_modified = 0; static int show_killed = 0; -static int show_other_directories = 0; -static int hide_empty_directories = 0; static int show_valid_bit = 0; static int line_terminator = '\n'; @@ -38,309 +35,6 @@ static const char *tag_other = ""; static const char *tag_killed = ""; static const char *tag_modified = ""; -static const char *exclude_per_dir = NULL; - -/* We maintain three exclude pattern lists: - * EXC_CMDL lists patterns explicitly given on the command line. - * EXC_DIRS lists patterns obtained from per-directory ignore files. - * EXC_FILE lists patterns from fallback ignore files. - */ -#define EXC_CMDL 0 -#define EXC_DIRS 1 -#define EXC_FILE 2 -static struct exclude_list { - int nr; - int alloc; - struct exclude { - const char *pattern; - const char *base; - int baselen; - } **excludes; -} exclude_list[3]; - -static void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *which) -{ - struct exclude *x = xmalloc(sizeof (*x)); - - x->pattern = string; - x->base = base; - x->baselen = baselen; - if (which->nr == which->alloc) { - which->alloc = alloc_nr(which->alloc); - which->excludes = realloc(which->excludes, - which->alloc * sizeof(x)); - } - which->excludes[which->nr++] = x; -} - -static int add_excludes_from_file_1(const char *fname, - const char *base, - int baselen, - struct exclude_list *which) -{ - int fd, i; - long size; - char *buf, *entry; - - fd = open(fname, O_RDONLY); - if (fd < 0) - goto err; - size = lseek(fd, 0, SEEK_END); - if (size < 0) - goto err; - lseek(fd, 0, SEEK_SET); - if (size == 0) { - close(fd); - return 0; - } - buf = xmalloc(size+1); - if (read(fd, buf, size) != size) - goto err; - close(fd); - - buf[size++] = '\n'; - entry = buf; - for (i = 0; i < size; i++) { - if (buf[i] == '\n') { - if (entry != buf + i && entry[0] != '#') { - buf[i - (i && buf[i-1] == '\r')] = 0; - add_exclude(entry, base, baselen, which); - } - entry = buf + i + 1; - } - } - return 0; - - err: - if (0 <= fd) - close(fd); - return -1; -} - -static void add_excludes_from_file(const char *fname) -{ - if (add_excludes_from_file_1(fname, "", 0, - &exclude_list[EXC_FILE]) < 0) - die("cannot use %s as an exclude file", fname); -} - -static int push_exclude_per_directory(const char *base, int baselen) -{ - char exclude_file[PATH_MAX]; - struct exclude_list *el = &exclude_list[EXC_DIRS]; - int current_nr = el->nr; - - if (exclude_per_dir) { - memcpy(exclude_file, base, baselen); - strcpy(exclude_file + baselen, exclude_per_dir); - add_excludes_from_file_1(exclude_file, base, baselen, el); - } - return current_nr; -} - -static void pop_exclude_per_directory(int stk) -{ - struct exclude_list *el = &exclude_list[EXC_DIRS]; - - while (stk < el->nr) - free(el->excludes[--el->nr]); -} - -/* Scan the list and let the last match determines the fate. - * Return 1 for exclude, 0 for include and -1 for undecided. - */ -static int excluded_1(const char *pathname, - int pathlen, - struct exclude_list *el) -{ - int i; - - if (el->nr) { - for (i = el->nr - 1; 0 <= i; i--) { - struct exclude *x = el->excludes[i]; - const char *exclude = x->pattern; - int to_exclude = 1; - - if (*exclude == '!') { - to_exclude = 0; - exclude++; - } - - if (!strchr(exclude, '/')) { - /* match basename */ - const char *basename = strrchr(pathname, '/'); - basename = (basename) ? basename+1 : pathname; - if (fnmatch(exclude, basename, 0) == 0) - return to_exclude; - } - else { - /* match with FNM_PATHNAME: - * exclude has base (baselen long) implicitly - * in front of it. - */ - int baselen = x->baselen; - if (*exclude == '/') - exclude++; - - if (pathlen < baselen || - (baselen && pathname[baselen-1] != '/') || - strncmp(pathname, x->base, baselen)) - continue; - - if (fnmatch(exclude, pathname+baselen, - FNM_PATHNAME) == 0) - return to_exclude; - } - } - } - return -1; /* undecided */ -} - -static int excluded(const char *pathname) -{ - int pathlen = strlen(pathname); - int st; - - for (st = EXC_CMDL; st <= EXC_FILE; st++) { - switch (excluded_1(pathname, pathlen, &exclude_list[st])) { - case 0: - return 0; - case 1: - return 1; - } - } - return 0; -} - -struct nond_on_fs { - int len; - char name[FLEX_ARRAY]; /* more */ -}; - -static struct nond_on_fs **dir; -static int nr_dir; -static int dir_alloc; - -static void add_name(const char *pathname, int len) -{ - struct nond_on_fs *ent; - - if (cache_name_pos(pathname, len) >= 0) - return; - - if (nr_dir == dir_alloc) { - dir_alloc = alloc_nr(dir_alloc); - dir = xrealloc(dir, dir_alloc*sizeof(ent)); - } - ent = xmalloc(sizeof(*ent) + len + 1); - ent->len = len; - memcpy(ent->name, pathname, len); - ent->name[len] = 0; - dir[nr_dir++] = ent; -} - -static int dir_exists(const char *dirname, int len) -{ - int pos = cache_name_pos(dirname, len); - if (pos >= 0) - return 1; - pos = -pos-1; - if (pos >= active_nr) /* can't */ - return 0; - return !strncmp(active_cache[pos]->name, dirname, len); -} - -/* - * Read a directory tree. We currently ignore anything but - * directories, regular files and symlinks. That's because git - * doesn't handle them at all yet. Maybe that will change some - * day. - * - * Also, we ignore the name ".git" (even if it is not a directory). - * That likely will not change. - */ -static int read_directory(const char *path, const char *base, int baselen) -{ - DIR *fdir = opendir(path); - int contents = 0; - - if (fdir) { - int exclude_stk; - struct dirent *de; - char fullname[MAXPATHLEN + 1]; - memcpy(fullname, base, baselen); - - exclude_stk = push_exclude_per_directory(base, baselen); - - while ((de = readdir(fdir)) != NULL) { - int len; - - if ((de->d_name[0] == '.') && - (de->d_name[1] == 0 || - !strcmp(de->d_name + 1, ".") || - !strcmp(de->d_name + 1, "git"))) - continue; - len = strlen(de->d_name); - memcpy(fullname + baselen, de->d_name, len+1); - if (excluded(fullname) != show_ignored) { - if (!show_ignored || DTYPE(de) != DT_DIR) { - continue; - } - } - - switch (DTYPE(de)) { - struct stat st; - int subdir, rewind_base; - default: - continue; - case DT_UNKNOWN: - if (lstat(fullname, &st)) - continue; - if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) - break; - if (!S_ISDIR(st.st_mode)) - continue; - /* fallthrough */ - case DT_DIR: - memcpy(fullname + baselen + len, "/", 2); - len++; - rewind_base = nr_dir; - subdir = read_directory(fullname, fullname, - baselen + len); - if (show_other_directories && - (subdir || !hide_empty_directories) && - !dir_exists(fullname, baselen + len)) { - // Rewind the read subdirectory - while (nr_dir > rewind_base) - free(dir[--nr_dir]); - break; - } - contents += subdir; - continue; - case DT_REG: - case DT_LNK: - break; - } - add_name(fullname, baselen + len); - contents++; - } - closedir(fdir); - - pop_exclude_per_directory(exclude_stk); - } - - return contents; -} - -static int cmp_name(const void *p1, const void *p2) -{ - const struct nond_on_fs *e1 = *(const struct nond_on_fs **)p1; - const struct nond_on_fs *e2 = *(const struct nond_on_fs **)p2; - - return cache_name_compare(e1->name, e1->len, - e2->name, e2->len); -} /* * Match a pathspec against a filename. The first "len" characters @@ -377,7 +71,7 @@ static int match(const char **spec, char *ps_matched, return 0; } -static void show_dir_entry(const char *tag, struct nond_on_fs *ent) +static void show_dir_entry(const char *tag, struct dir_entry *ent) { int len = prefix_len; int offset = prefix_offset; @@ -393,14 +87,14 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent) putchar(line_terminator); } -static void show_other_files(void) +static void show_other_files(struct dir_struct *dir) { int i; - for (i = 0; i < nr_dir; i++) { + for (i = 0; i < dir->nr; i++) { /* We should not have a matching entry, but we * may have an unmerged entry for this path. */ - struct nond_on_fs *ent = dir[i]; + struct dir_entry *ent = dir->entries[i]; int pos = cache_name_pos(ent->name, ent->len); struct cache_entry *ce; if (0 <= pos) @@ -416,11 +110,11 @@ static void show_other_files(void) } } -static void show_killed_files(void) +static void show_killed_files(struct dir_struct *dir) { int i; - for (i = 0; i < nr_dir; i++) { - struct nond_on_fs *ent = dir[i]; + for (i = 0; i < dir->nr; i++) { + struct dir_entry *ent = dir->entries[i]; char *cp, *sp; int pos, len, killed = 0; @@ -461,7 +155,7 @@ static void show_killed_files(void) } } if (killed) - show_dir_entry(tag_killed, dir[i]); + show_dir_entry(tag_killed, dir->entries[i]); } } @@ -512,7 +206,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) } } -static void show_files(void) +static void show_files(struct dir_struct *dir) { int i; @@ -523,14 +217,14 @@ static void show_files(void) if (baselen) { path = base = prefix; - if (exclude_per_dir) { + if (dir->exclude_per_dir) { char *p, *pp = xmalloc(baselen+1); memcpy(pp, prefix, baselen+1); p = pp; while (1) { char save = *p; *p = 0; - push_exclude_per_directory(pp, p-pp); + push_exclude_per_directory(dir, pp, p-pp); *p++ = save; if (!save) break; @@ -543,17 +237,16 @@ static void show_files(void) free(pp); } } - read_directory(path, base, baselen); - qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name); + read_directory(dir, path, base, baselen); if (show_others) - show_other_files(); + show_other_files(dir); if (show_killed) - show_killed_files(); + show_killed_files(dir); } if (show_cached | show_stage) { for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; - if (excluded(ce->name) != show_ignored) + if (excluded(dir, ce->name) != dir->show_ignored) continue; if (show_unmerged && !ce_stage(ce)) continue; @@ -565,7 +258,7 @@ static void show_files(void) struct cache_entry *ce = active_cache[i]; struct stat st; int err; - if (excluded(ce->name) != show_ignored) + if (excluded(dir, ce->name) != dir->show_ignored) continue; err = lstat(ce->name, &st); if (show_deleted && err) @@ -652,7 +345,9 @@ int main(int argc, const char **argv) { int i; int exc_given = 0; + struct dir_struct dir; + memset(&dir, 0, sizeof(dir)); prefix = setup_git_directory(); if (prefix) prefix_offset = strlen(prefix); @@ -697,7 +392,7 @@ int main(int argc, const char **argv) continue; } if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) { - show_ignored = 1; + dir.show_ignored = 1; continue; } if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) { @@ -709,11 +404,11 @@ int main(int argc, const char **argv) continue; } if (!strcmp(arg, "--directory")) { - show_other_directories = 1; + dir.show_other_directories = 1; continue; } if (!strcmp(arg, "--no-empty-directory")) { - hide_empty_directories = 1; + dir.hide_empty_directories = 1; continue; } if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { @@ -726,27 +421,27 @@ int main(int argc, const char **argv) } if (!strcmp(arg, "-x") && i+1 < argc) { exc_given = 1; - add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]); + add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]); continue; } if (!strncmp(arg, "--exclude=", 10)) { exc_given = 1; - add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]); + add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]); continue; } if (!strcmp(arg, "-X") && i+1 < argc) { exc_given = 1; - add_excludes_from_file(argv[++i]); + add_excludes_from_file(&dir, argv[++i]); continue; } if (!strncmp(arg, "--exclude-from=", 15)) { exc_given = 1; - add_excludes_from_file(arg+15); + add_excludes_from_file(&dir, arg+15); continue; } if (!strncmp(arg, "--exclude-per-directory=", 24)) { exc_given = 1; - exclude_per_dir = arg + 24; + dir.exclude_per_dir = arg + 24; continue; } if (!strcmp(arg, "--full-name")) { @@ -788,7 +483,7 @@ int main(int argc, const char **argv) ps_matched = xcalloc(1, num); } - if (show_ignored && !exc_given) { + if (dir.show_ignored && !exc_given) { fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]); exit(1); @@ -802,7 +497,7 @@ int main(int argc, const char **argv) read_cache(); if (prefix) prune_cache(); - show_files(); + show_files(&dir); if (ps_matched) { /* We need to make sure all pathspec matched otherwise From b4189aa84873718f80c62846cd53e803b5f72362 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 16 May 2006 19:46:16 -0700 Subject: [PATCH 14/90] Clean up git-ls-file directory walking library interface This moves the code to add the per-directory ignore files for the base directory into the library routine. That not only allows us to turn the function push_exclude_per_directory() static again, it also simplifies the library interface a lot (the caller no longer needs to worry about any of the per-directory exclude files at all). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- dir.c | 28 +++++++++++++++++++++++++++- dir.h | 2 -- ls-files.c | 22 +--------------------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/dir.c b/dir.c index 3f41a5dfea..d40b62e1c1 100644 --- a/dir.c +++ b/dir.c @@ -78,7 +78,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname) die("cannot use %s as an exclude file", fname); } -int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) +static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) { char exclude_file[PATH_MAX]; struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; @@ -289,6 +289,32 @@ static int cmp_name(const void *p1, const void *p2) int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen) { + /* + * Make sure to do the per-directory exclude for all the + * directories leading up to our base. + */ + if (baselen) { + if (dir->exclude_per_dir) { + char *p, *pp = xmalloc(baselen+1); + memcpy(pp, base, baselen+1); + p = pp; + while (1) { + char save = *p; + *p = 0; + push_exclude_per_directory(dir, pp, p-pp); + *p++ = save; + if (!save) + break; + p = strchr(p, '/'); + if (p) + p++; + else + p = pp + baselen; + } + free(pp); + } + } + read_directory_recursive(dir, path, base, baselen); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); return dir->nr; diff --git a/dir.h b/dir.h index e8fc441e7f..4f65f57085 100644 --- a/dir.h +++ b/dir.h @@ -44,7 +44,5 @@ extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which); -extern int push_exclude_per_directory(struct dir_struct *, - const char *base, int baselen); #endif diff --git a/ls-files.c b/ls-files.c index 89941a3ff8..dfe1481a35 100644 --- a/ls-files.c +++ b/ls-files.c @@ -215,28 +215,8 @@ static void show_files(struct dir_struct *dir) const char *path = ".", *base = ""; int baselen = prefix_len; - if (baselen) { + if (baselen) path = base = prefix; - if (dir->exclude_per_dir) { - char *p, *pp = xmalloc(baselen+1); - memcpy(pp, prefix, baselen+1); - p = pp; - while (1) { - char save = *p; - *p = 0; - push_exclude_per_directory(dir, pp, p-pp); - *p++ = save; - if (!save) - break; - p = strchr(p, '/'); - if (p) - p++; - else - p = pp + baselen; - } - free(pp); - } - } read_directory(dir, path, base, baselen); if (show_others) show_other_files(dir); From 518920b764ee9150781e68217181b24d0712748e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 17 May 2006 02:48:13 -0700 Subject: [PATCH 15/90] builtin-grep: workaround for non GNU grep. Some implementations do not know what to do with -H; define NO_H_OPTION_IN_GREP when you build git if your grep lacks -H. Most of the time, it can be worked around by prepending /dev/null to the argument list, but that causes -L and -c to slightly misbehave (they both expose /dev/null is given), so when these options are given, do not run external grep that does not understand -H. Signed-off-by: Junio C Hamano --- Makefile | 11 +++++++++++ builtin-grep.c | 22 +++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 9ba608c805..c67108d253 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,8 @@ all: # # Define NO_MMAP if you want to avoid mmap. # +# Define NO_H_OPTION_IN_GREP if your grep does not understand -H. +# # Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3. # # Define NO_IPV6 if you lack IPv6 support and getaddrinfo(). @@ -444,6 +446,12 @@ ifdef NO_ACCURATE_DIFF ALL_CFLAGS += -DNO_ACCURATE_DIFF endif +ifdef NO_H_OPTION_IN_GREP + NO_H_OPTION_IN_GREP=1 +else + NO_H_OPTION_IN_GREP=0 +endif + # Shell quote (do not use $(call) to accomodate ancient setups); SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER)) @@ -526,6 +534,9 @@ git$X git.spec \ %.o: %.S $(CC) -o $*.o -c $(ALL_CFLAGS) $< +builtin-grep.o: builtin-grep.c + $(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_H_OPTION_IN_GREP=$(NO_H_OPTION_IN_GREP) $< + exec_cmd.o: exec_cmd.c $(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $< diff --git a/builtin-grep.c b/builtin-grep.c index 66111de514..36512d8a17 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -453,7 +453,6 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) len = nr = 0; push_arg("grep"); - push_arg("-H"); if (opt->fixed) push_arg("-F"); if (opt->linenum) @@ -503,7 +502,13 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) push_arg("-e"); push_arg(p->pattern); } - push_arg("--"); + + if (NO_H_OPTION_IN_GREP) + push_arg("/dev/null"); + else { + push_arg("-H"); + push_arg("--"); + } hit = 0; argc = nr; @@ -535,8 +540,19 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) * Use the external "grep" command for the case where * we grep through the checked-out files. It tends to * be a lot more optimized + * + * Some grep implementations do not understand -H nor -- + * but /dev/null can be used as a substitution in most + * cases. + * + * However -L and -c would slightly misbehave (-L would + * list /dev/null as a hit, and -c would report 0 hits + * from /dev/null); so do not use the external one on + * such platforms. */ - if (!cached) { + if (!cached && + (!NO_H_OPTION_IN_GREP || + (!opt->count && !opt->unmatch_name_only))) { hit = external_grep(opt, paths, cached); if (hit >= 0) return hit; From e78503db1658ab6650608359cfd8beaf1645239f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 17 May 2006 11:42:14 -0700 Subject: [PATCH 16/90] Revert "builtin-grep: workaround for non GNU grep." This reverts 518920b764ee9150781e68217181b24d0712748e commit. Linus has a more portable alternative. --- Makefile | 11 ----------- builtin-grep.c | 22 +++------------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index c67108d253..9ba608c805 100644 --- a/Makefile +++ b/Makefile @@ -46,8 +46,6 @@ all: # # Define NO_MMAP if you want to avoid mmap. # -# Define NO_H_OPTION_IN_GREP if your grep does not understand -H. -# # Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3. # # Define NO_IPV6 if you lack IPv6 support and getaddrinfo(). @@ -446,12 +444,6 @@ ifdef NO_ACCURATE_DIFF ALL_CFLAGS += -DNO_ACCURATE_DIFF endif -ifdef NO_H_OPTION_IN_GREP - NO_H_OPTION_IN_GREP=1 -else - NO_H_OPTION_IN_GREP=0 -endif - # Shell quote (do not use $(call) to accomodate ancient setups); SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER)) @@ -534,9 +526,6 @@ git$X git.spec \ %.o: %.S $(CC) -o $*.o -c $(ALL_CFLAGS) $< -builtin-grep.o: builtin-grep.c - $(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_H_OPTION_IN_GREP=$(NO_H_OPTION_IN_GREP) $< - exec_cmd.o: exec_cmd.c $(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $< diff --git a/builtin-grep.c b/builtin-grep.c index 36512d8a17..66111de514 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -453,6 +453,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) len = nr = 0; push_arg("grep"); + push_arg("-H"); if (opt->fixed) push_arg("-F"); if (opt->linenum) @@ -502,13 +503,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) push_arg("-e"); push_arg(p->pattern); } - - if (NO_H_OPTION_IN_GREP) - push_arg("/dev/null"); - else { - push_arg("-H"); - push_arg("--"); - } + push_arg("--"); hit = 0; argc = nr; @@ -540,19 +535,8 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) * Use the external "grep" command for the case where * we grep through the checked-out files. It tends to * be a lot more optimized - * - * Some grep implementations do not understand -H nor -- - * but /dev/null can be used as a substitution in most - * cases. - * - * However -L and -c would slightly misbehave (-L would - * list /dev/null as a hit, and -c would report 0 hits - * from /dev/null); so do not use the external one on - * such platforms. */ - if (!cached && - (!NO_H_OPTION_IN_GREP || - (!opt->count && !opt->unmatch_name_only))) { + if (!cached) { hit = external_grep(opt, paths, cached); if (hit >= 0) return hit; From 0d78153952e70c21e94dc6b7eefcb2ac5337a902 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 May 2006 09:33:32 -0700 Subject: [PATCH 17/90] Do "git add" as a builtin First try. Let's see how well this works. In many ways, the hard parts of "git commit" are not so different from this, and a builtin commit would share a lot of the code, I think. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 2 +- builtin-add.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git.c | 1 + 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 builtin-add.c diff --git a/Makefile b/Makefile index f43ac63c93..e6f7794ad0 100644 --- a/Makefile +++ b/Makefile @@ -218,7 +218,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ - builtin-grep.o + builtin-grep.o builtin-add.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-add.c b/builtin-add.c new file mode 100644 index 0000000000..d225988e53 --- /dev/null +++ b/builtin-add.c @@ -0,0 +1,228 @@ +/* + * "git add" builtin command + * + * Copyright (C) 2006 Linus Torvalds + */ +#include + +#include "cache.h" +#include "builtin.h" +#include "dir.h" + +static const char builtin_add_usage[] = +"git-add [-n] [-v] ..."; + +static int common_prefix(const char **pathspec) +{ + const char *path, *slash, *next; + int prefix; + + if (!pathspec) + return 0; + + path = *pathspec; + slash = strrchr(path, '/'); + if (!slash) + return 0; + + prefix = slash - path + 1; + while ((next = *++pathspec) != NULL) { + int len = strlen(next); + if (len >= prefix && !memcmp(path, next, len)) + continue; + for (;;) { + if (!len) + return 0; + if (next[--len] != '/') + continue; + if (memcmp(path, next, len+1)) + continue; + prefix = len + 1; + break; + } + } + return prefix; +} + +static int match(const char **pathspec, const char *name, int namelen, int prefix) +{ + const char *match; + + name += prefix; + namelen -= prefix; + + while ((match = *pathspec++) != NULL) { + int matchlen; + + match += prefix; + matchlen = strlen(match); + if (!matchlen) + return 1; + if (!strncmp(match, name, matchlen)) { + if (match[matchlen-1] == '/') + return 1; + switch (name[matchlen]) { + case '/': case '\0': + return 1; + } + } + if (!fnmatch(match, name, 0)) + return 1; + } + return 0; +} + +static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) +{ + int i; + struct dir_entry **src, **dst; + + src = dst = dir->entries; + i = dir->nr; + while (--i >= 0) { + struct dir_entry *entry = *src++; + if (!match(pathspec, entry->name, entry->len, prefix)) { + free(entry); + continue; + } + *dst++ = entry; + } + dir->nr = dst - dir->entries; +} + +static void fill_directory(struct dir_struct *dir, const char **pathspec) +{ + const char *path, *base; + int baselen; + + /* Set up the default git porcelain excludes */ + memset(dir, 0, sizeof(*dir)); + dir->exclude_per_dir = ".gitignore"; + path = git_path("info/exclude"); + if (!access(path, R_OK)) + add_excludes_from_file(dir, path); + + /* + * Calculate common prefix for the pathspec, and + * use that to optimize the directory walk + */ + baselen = common_prefix(pathspec); + path = "."; + base = ""; + if (baselen) { + char *common = xmalloc(baselen + 1); + common = xmalloc(baselen + 1); + memcpy(common, *pathspec, baselen); + common[baselen] = 0; + path = base = common; + } + + /* Read the directory and prune it */ + read_directory(dir, path, base, baselen); + if (pathspec) + prune_directory(dir, pathspec, baselen); +} + +static int add_file_to_index(const char *path, int verbose) +{ + int size, namelen; + struct stat st; + struct cache_entry *ce; + + if (lstat(path, &st)) + die("%s: unable to stat (%s)", path, strerror(errno)); + + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) + die("%s: can only add regular files or symbolic links", path); + + namelen = strlen(path); + size = cache_entry_size(namelen); + ce = xcalloc(1, size); + memcpy(ce->name, path, namelen); + ce->ce_flags = htons(namelen); + fill_stat_cache_info(ce, &st); + + ce->ce_mode = create_ce_mode(st.st_mode); + if (!trust_executable_bit) { + /* If there is an existing entry, pick the mode bits + * from it. + */ + int pos = cache_name_pos(path, namelen); + if (pos >= 0) + ce->ce_mode = active_cache[pos]->ce_mode; + } + + if (index_path(ce->sha1, path, &st, 1)) + die("unable to index file %s", path); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD)) + die("unable to add %s to index",path); + if (verbose) + printf("add '%s'\n", path); + return 0; +} + +static struct cache_file cache_file; + +int cmd_add(int argc, const char **argv, char **envp) +{ + int i, newfd; + int verbose = 0, show_only = 0; + const char *prefix = setup_git_directory(); + const char **pathspec; + struct dir_struct dir; + + git_config(git_default_config); + + newfd = hold_index_file_for_update(&cache_file, get_index_file()); + if (newfd < 0) + die("unable to create new cachefile"); + + if (read_cache() < 0) + die("index file corrupt"); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strcmp(arg, "-n")) { + show_only = 1; + continue; + } + if (!strcmp(arg, "-v")) { + verbose = 1; + continue; + } + die(builtin_add_usage); + } + git_config(git_default_config); + pathspec = get_pathspec(prefix, argv + i); + + fill_directory(&dir, pathspec); + + if (show_only) { + const char *sep = "", *eof = ""; + for (i = 0; i < dir.nr; i++) { + printf("%s%s", sep, dir.entries[i]->name); + sep = " "; + eof = "\n"; + } + fputs(eof, stdout); + return 0; + } + + for (i = 0; i < dir.nr; i++) + add_file_to_index(dir.entries[i]->name, verbose); + + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + commit_index_file(&cache_file)) + die("Unable to write new index file"); + } + + return 0; +} diff --git a/builtin.h b/builtin.h index 7744f7d2f6..1b77f4b0ca 100644 --- a/builtin.h +++ b/builtin.h @@ -24,5 +24,6 @@ extern int cmd_count_objects(int argc, const char **argv, char **envp); extern int cmd_push(int argc, const char **argv, char **envp); extern int cmd_grep(int argc, const char **argv, char **envp); +extern int cmd_add(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index a94d9ee5a7..fac46af057 100644 --- a/git.c +++ b/git.c @@ -50,6 +50,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, + { "add", cmd_add }, }; int i; From f25933987f29070e9cd79dfddf03018010e82e80 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 May 2006 13:23:19 -0700 Subject: [PATCH 18/90] builtin-add: warn on unmatched pathspecs This is in the same spirit as what bba319b5 and 45e48120 tried to do to help users. A command such as "git add Documentaiton" with misspelled pathspecs would give a friendly reminder with this. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-add.c | 77 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index d225988e53..089c7a89b3 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -44,50 +44,89 @@ static int common_prefix(const char **pathspec) return prefix; } -static int match(const char **pathspec, const char *name, int namelen, int prefix) +static int match_one(const char *match, const char *name, int namelen) { + int matchlen; + + /* If the match was just the prefix, we matched */ + matchlen = strlen(match); + if (!matchlen) + return 1; + + /* + * If we don't match the matchstring exactly, + * we need to match by fnmatch + */ + if (strncmp(match, name, matchlen)) + return !fnmatch(match, name, 0); + + /* + * If we did match the string exactly, we still + * need to make sure that it happened on a path + * component boundary (ie either the last character + * of the match was '/', or the next character of + * the name was '/' or the terminating NUL. + */ + return match[matchlen-1] == '/' || + name[matchlen] == '/' || + !name[matchlen]; +} + +static int match(const char **pathspec, const char *name, int namelen, int prefix, char *seen) +{ + int retval; const char *match; name += prefix; namelen -= prefix; - while ((match = *pathspec++) != NULL) { - int matchlen; - + for (retval = 0; (match = *pathspec++) != NULL; seen++) { + if (retval & *seen) + continue; match += prefix; - matchlen = strlen(match); - if (!matchlen) - return 1; - if (!strncmp(match, name, matchlen)) { - if (match[matchlen-1] == '/') - return 1; - switch (name[matchlen]) { - case '/': case '\0': - return 1; - } + if (match_one(match, name, namelen)) { + retval = 1; + *seen = 1; } - if (!fnmatch(match, name, 0)) - return 1; } - return 0; + return retval; } static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { - int i; + char *seen; + int i, specs; struct dir_entry **src, **dst; + for (specs = 0; pathspec[specs]; specs++) + /* nothing */; + seen = xmalloc(specs); + memset(seen, 0, specs); + src = dst = dir->entries; i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (!match(pathspec, entry->name, entry->len, prefix)) { + if (!match(pathspec, entry->name, entry->len, prefix, seen)) { free(entry); continue; } *dst++ = entry; } dir->nr = dst - dir->entries; + + for (i = 0; i < specs; i++) { + struct stat st; + const char *match; + if (seen[i]) + continue; + + /* Existing file? We must have ignored it */ + match = pathspec[i]; + if (!lstat(match, &st)) + continue; + die("pathspec '%s' did not match any files", match); + } } static void fill_directory(struct dir_struct *dir, const char **pathspec) From bbb66c60613fe89c58d7c97a92e067add7056f4d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 May 2006 11:12:22 -0700 Subject: [PATCH 19/90] builtin-grep: workaround for non GNU grep. Of course, it still ignores the fact that not all grep's support some of the flags like -F/-L/-A/-C etc, but for those cases, the external grep itself will happily just say "unrecognized option -F" or similar. So with this change, "git grep" should handle all the flags the native grep handles, which is really quite fine. We don't _need_ to expose anything more, and if you do want our extensions, you can get them with "--uncached" and an up-to-date index. No configuration necessary, and we automatically take advantage of any native grep we have, if possible. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-grep.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/builtin-grep.c b/builtin-grep.c index 66111de514..d09ddf0485 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -453,7 +453,6 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) len = nr = 0; push_arg("grep"); - push_arg("-H"); if (opt->fixed) push_arg("-F"); if (opt->linenum) @@ -503,17 +502,35 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) push_arg("-e"); push_arg(p->pattern); } - push_arg("--"); + + /* + * To make sure we get the header printed out when we want it, + * add /dev/null to the paths to grep. This is unnecessary + * (and wrong) with "-l" or "-L", which always print out the + * name anyway. + * + * GNU grep has "-H", but this is portable. + */ + if (!opt->name_only && !opt->unmatch_name_only) + push_arg("/dev/null"); hit = 0; argc = nr; for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; + const char *name; if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode))) continue; if (!pathspec_matches(paths, ce->name)) continue; - argv[argc++] = ce->name; + name = ce->name; + if (name[0] == '-') { + int len = ce_namelen(ce); + name = xmalloc(len + 3); + memcpy(name, "./", 2); + memcpy(name + 2, ce->name, len + 1); + } + argv[argc++] = name; if (argc < MAXARGS) continue; hit += exec_grep(argc, argv); From d91d4c2c500b17d50359693a406a2c68b65330fe Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 17 May 2006 16:56:13 -0700 Subject: [PATCH 20/90] apply --cached: do not check newly added file in the working tree The --cached mode does not deal with the working tree, so we should not check it with lstat. An earlier code omitted the call to lstat but forgot to omit the check for the errno. Signed-off-by: Junio C Hamano --- apply.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apply.c b/apply.c index b3b9b40596..0ed9d132e8 100644 --- a/apply.c +++ b/apply.c @@ -1711,10 +1711,12 @@ static int check_patch(struct patch *patch) if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) { if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0) return error("%s: already exists in index", new_name); - if (!cached && !lstat(new_name, &st)) - return error("%s: already exists in working directory", new_name); - if (errno != ENOENT) - return error("%s: %s", new_name, strerror(errno)); + if (!cached) { + if (!lstat(new_name, &st)) + return error("%s: already exists in working directory", new_name); + if (errno != ENOENT) + return error("%s: %s", new_name, strerror(errno)); + } if (!patch->new_mode) { if (patch->is_new) patch->new_mode = S_IFREG | 0644; From c699f9b924d763b762df932769d91e3d053634a8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 May 2006 21:21:04 -0700 Subject: [PATCH 21/90] Remove old "git-add.sh" remnants Repeat after me: "It's now a built-in" Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 4 ++-- git-add.sh | 56 ------------------------------------------------------ 2 files changed, 2 insertions(+), 58 deletions(-) delete mode 100755 git-add.sh diff --git a/Makefile b/Makefile index e6f7794ad0..48e2a9cb22 100644 --- a/Makefile +++ b/Makefile @@ -113,7 +113,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ ### --- END CONFIGURATION SECTION --- SCRIPT_SH = \ - git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \ + git-bisect.sh git-branch.sh git-checkout.sh \ git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ git-fetch.sh \ git-format-patch.sh git-ls-remote.sh \ @@ -170,7 +170,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ - git-grep$X + git-grep$X git-add$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) diff --git a/git-add.sh b/git-add.sh deleted file mode 100755 index d6a4bc7d09..0000000000 --- a/git-add.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh - -USAGE='[-n] [-v] ...' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -show_only= -verbose= -while : ; do - case "$1" in - -n) - show_only=true - ;; - -v) - verbose=--verbose - ;; - --) - shift - break - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift -done - -# Check misspelled pathspec -case "$#" in -0) ;; -*) - git-ls-files --error-unmatch --others --cached -- "$@" >/dev/null || { - echo >&2 "Maybe you misspelled it?" - exit 1 - } - ;; -esac - -if test -f "$GIT_DIR/info/exclude" -then - git-ls-files -z \ - --exclude-from="$GIT_DIR/info/exclude" \ - --others --exclude-per-directory=.gitignore -- "$@" -else - git-ls-files -z \ - --others --exclude-per-directory=.gitignore -- "$@" -fi | -case "$show_only" in -true) - xargs -0 echo ;; -*) - git-update-index --add $verbose -z --stdin ;; -esac From be0c7e069738fbb697b0719f2252107261c9340e Mon Sep 17 00:00:00 2001 From: Elrond Date: Wed, 10 May 2006 19:37:04 +0200 Subject: [PATCH 22/90] git-cvsimport: Handle "Removed" from pserver Sometimes the pserver says "Removed" instead of "Remove-entry". Signed-off-by: Elrond Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index c0ae00bda7..8c707f2c66 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -350,7 +350,7 @@ sub _line { return $res; } elsif($line =~ s/^E //) { # print STDERR "S: $line\n"; - } elsif($line =~ /^Remove-entry /i) { + } elsif($line =~ /^(Remove-entry|Removed) /i) { $line = $self->readline(); # filename $line = $self->readline(); # OK chomp $line; From 70f75cc96a3c590ac1c54c09c39446626f155ef7 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 18 May 2006 16:58:51 +1000 Subject: [PATCH 23/90] Provide a way to flush git-diff-tree's output Gitk wants to use git-diff-tree as a filter to tell it which ids from a given list affect a set of files or directories. We don't want to fork and exec a new git-diff-tree process for each batch of ids, since there could be a lot of relatively small batches. For example, a batch could contain as many ids as fit in gitk's headline display window, i.e. 20 or so, and we would be processing a new batch every time the user scrolls that window. The --stdin flag to git-diff-tree is suitable for this, but the main difficulty is that the output of git-diff-tree gets buffered and doesn't get sent until the buffer is full. This provides a way to get git-diff-tree to flush its output buffers. If a blank line is supplied on git-diff-tree's standard input, it will flush its output buffers and then accept further input. Signed-off-by: Paul Mackerras Signed-off-by: Junio C Hamano --- diff-tree.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/diff-tree.c b/diff-tree.c index 7207867a74..69bb74b310 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -138,7 +138,10 @@ int main(int argc, const char **argv) opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | DIFF_SETUP_USE_CACHE); while (fgets(line, sizeof(line), stdin)) - diff_tree_stdin(line); + if (line[0] == '\n') + fflush(stdout); + else + diff_tree_stdin(line); return 0; } From e8f990b4e4b56f214138cc475c19e5a253e9148e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 18 May 2006 01:29:36 -0700 Subject: [PATCH 24/90] builtin-add: fix unmatched pathspec warnings. "git add Documentation/" when Documentation directory exists does not barf (as it should not), but "git add ." barfed when it did not add anything. This was because we checked for the path prefix ("Documentation/" in the former case, and an empty string in the latter case) for existence, and lstat("", &st) would say "Huh?". Signed-off-by: Junio C Hamano --- builtin-add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-add.c b/builtin-add.c index 089c7a89b3..37243f8c36 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -123,7 +123,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p /* Existing file? We must have ignored it */ match = pathspec[i]; - if (!lstat(match, &st)) + if (!match[0] || !lstat(match, &st)) continue; die("pathspec '%s' did not match any files", match); } From 8dcf39c46e2931ca02b18b1ea3a6b21f446d8de8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 18 May 2006 12:07:31 -0700 Subject: [PATCH 25/90] Prevent bogus paths from being added to the index. With this one, it's now a fatal error to try to add a pathname that cannot be added with "git add", i.e. [torvalds@g5 git]$ git add .git/config fatal: unable to add .git/config to index and [torvalds@g5 git]$ git add foo/../bar fatal: unable to add foo/../bar to index instead of the old "Ignoring path xyz" warning that would end up silently succeeding on any other paths. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- cache.h | 1 + read-cache.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ update-index.c | 64 ------------------------------------------------ 3 files changed, 67 insertions(+), 64 deletions(-) diff --git a/cache.h b/cache.h index 4b7a439253..89aa4f6c7e 100644 --- a/cache.h +++ b/cache.h @@ -142,6 +142,7 @@ extern void verify_non_filename(const char *prefix, const char *name); /* Initialize and use the cache information */ extern int read_cache(void); extern int write_cache(int newfd, struct cache_entry **cache, int entries); +extern int verify_path(const char *path); extern int cache_name_pos(const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ diff --git a/read-cache.c b/read-cache.c index a917ab0cfe..e8fa6d0812 100644 --- a/read-cache.c +++ b/read-cache.c @@ -331,6 +331,70 @@ int ce_path_match(const struct cache_entry *ce, const char **pathspec) return 0; } +/* + * We fundamentally don't like some paths: we don't want + * dot or dot-dot anywhere, and for obvious reasons don't + * want to recurse into ".git" either. + * + * Also, we don't want double slashes or slashes at the + * end that can make pathnames ambiguous. + */ +static int verify_dotfile(const char *rest) +{ + /* + * The first character was '.', but that + * has already been discarded, we now test + * the rest. + */ + switch (*rest) { + /* "." is not allowed */ + case '\0': case '/': + return 0; + + /* + * ".git" followed by NUL or slash is bad. This + * shares the path end test with the ".." case. + */ + case 'g': + if (rest[1] != 'i') + break; + if (rest[2] != 't') + break; + rest += 2; + /* fallthrough */ + case '.': + if (rest[1] == '\0' || rest[1] == '/') + return 0; + } + return 1; +} + +int verify_path(const char *path) +{ + char c; + + goto inside; + for (;;) { + if (!c) + return 1; + if (c == '/') { +inside: + c = *path++; + switch (c) { + default: + continue; + case '/': case '\0': + break; + case '.': + if (verify_dotfile(path)) + continue; + } + return 0; + } + c = *path++; + } +} + /* * Do we have another file that has the beginning components being a * proper superset of the name we're trying to add? @@ -472,6 +536,8 @@ int add_cache_entry(struct cache_entry *ce, int option) if (!ok_to_add) return -1; + if (!verify_path(ce->name)) + return -1; if (!skip_df_check && check_file_directory_conflict(ce, pos, ok_to_replace)) { diff --git a/update-index.c b/update-index.c index 3d7e02db2c..859efc7916 100644 --- a/update-index.c +++ b/update-index.c @@ -237,70 +237,6 @@ static int refresh_cache(int really) return has_errors; } -/* - * We fundamentally don't like some paths: we don't want - * dot or dot-dot anywhere, and for obvious reasons don't - * want to recurse into ".git" either. - * - * Also, we don't want double slashes or slashes at the - * end that can make pathnames ambiguous. - */ -static int verify_dotfile(const char *rest) -{ - /* - * The first character was '.', but that - * has already been discarded, we now test - * the rest. - */ - switch (*rest) { - /* "." is not allowed */ - case '\0': case '/': - return 0; - - /* - * ".git" followed by NUL or slash is bad. This - * shares the path end test with the ".." case. - */ - case 'g': - if (rest[1] != 'i') - break; - if (rest[2] != 't') - break; - rest += 2; - /* fallthrough */ - case '.': - if (rest[1] == '\0' || rest[1] == '/') - return 0; - } - return 1; -} - -static int verify_path(const char *path) -{ - char c; - - goto inside; - for (;;) { - if (!c) - return 1; - if (c == '/') { -inside: - c = *path++; - switch (c) { - default: - continue; - case '/': case '\0': - break; - case '.': - if (verify_dotfile(path)) - continue; - } - return 0; - } - c = *path++; - } -} - static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, const char *path, int stage) { From 065e0b126f47e0c280da6830956766ff2ea8cf17 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 18 May 2006 14:35:37 -0700 Subject: [PATCH 26/90] builtin-diff: do not say files are renamed when blob and file are given Signed-off-by: Junio C Hamano --- builtin-diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-diff.c b/builtin-diff.c index b0c9cade02..de81b05e32 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -122,7 +122,7 @@ static int builtin_diff_b_f(struct rev_info *revs, stuff_change(&revs->diffopt, canon_mode(st.st_mode), canon_mode(st.st_mode), blob[0].sha1, null_sha1, - blob[0].name, path); + path, path); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; From 5fb61b8dcfdf7bcec0793c071813e255d1803859 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 18 May 2006 14:19:20 -0700 Subject: [PATCH 27/90] Make "git rev-list" be a builtin This was surprisingly easy. The diff is truly minimal: rename "main()" to "cmd_rev_list()" in rev-list.c, and rename the whole file to reflect its new built-in status. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- rev-list.c => builtin-rev-list.c | 5 +++-- builtin.h | 1 + git.c | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) rename rev-list.c => builtin-rev-list.c (98%) diff --git a/Makefile b/Makefile index 9ba608c805..6efc8e0275 100644 --- a/Makefile +++ b/Makefile @@ -158,7 +158,7 @@ PROGRAMS = \ git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ - git-receive-pack$X git-rev-list$X git-rev-parse$X \ + git-receive-pack$X git-rev-parse$X \ git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ git-ssh-upload$X git-tar-tree$X git-unpack-file$X \ @@ -170,7 +170,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ - git-grep$X + git-grep$X git-rev-list$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -218,7 +218,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ - builtin-grep.o + builtin-grep.o builtin-rev-list.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/rev-list.c b/builtin-rev-list.c similarity index 98% rename from rev-list.c rename to builtin-rev-list.c index 8b0ec388fa..446802d377 100644 --- a/rev-list.c +++ b/builtin-rev-list.c @@ -7,6 +7,7 @@ #include "tree-walk.h" #include "diff.h" #include "revision.h" +#include "builtin.h" /* bits #0-15 in revision.h */ @@ -36,7 +37,7 @@ static const char rev_list_usage[] = " --bisect" ; -struct rev_info revs; +static struct rev_info revs; static int bisect_list = 0; static int show_timestamp = 0; @@ -291,7 +292,7 @@ static void mark_edges_uninteresting(struct commit_list *list) } } -int main(int argc, const char **argv) +int cmd_rev_list(int argc, const char **argv, char **envp) { struct commit_list *list; int i; diff --git a/builtin.h b/builtin.h index 7744f7d2f6..7dff121520 100644 --- a/builtin.h +++ b/builtin.h @@ -24,5 +24,6 @@ extern int cmd_count_objects(int argc, const char **argv, char **envp); extern int cmd_push(int argc, const char **argv, char **envp); extern int cmd_grep(int argc, const char **argv, char **envp); +extern int cmd_rev_list(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index a94d9ee5a7..c94e3a531b 100644 --- a/git.c +++ b/git.c @@ -50,6 +50,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, + { "rev-list", cmd_rev_list }, }; int i; From 9370bae2cef351617272aa142fbe4ce039833d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= Date: Thu, 18 May 2006 14:15:55 +0200 Subject: [PATCH 28/90] Make git-check-format-ref a builtin. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Sandström Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- builtin-check-ref-format.c | 14 ++++++++++++++ builtin.h | 1 + check-ref-format.c | 17 ----------------- git.c | 1 + 5 files changed, 19 insertions(+), 20 deletions(-) create mode 100644 builtin-check-ref-format.c delete mode 100644 check-ref-format.c diff --git a/Makefile b/Makefile index 6efc8e0275..2149fb8cea 100644 --- a/Makefile +++ b/Makefile @@ -164,13 +164,13 @@ PROGRAMS = \ git-ssh-upload$X git-tar-tree$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ git-upload-pack$X git-verify-pack$X git-write-tree$X \ - git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \ + git-update-ref$X git-symbolic-ref$X \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ - git-grep$X git-rev-list$X + git-grep$X git-rev-list$X git-check-ref-format$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -218,7 +218,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ - builtin-grep.o builtin-rev-list.o + builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c new file mode 100644 index 0000000000..4a23936aff --- /dev/null +++ b/builtin-check-ref-format.c @@ -0,0 +1,14 @@ +/* + * GIT - The information manager from hell + */ + +#include "cache.h" +#include "refs.h" +#include "builtin.h" + +int cmd_check_ref_format(int argc, const char **argv, char **envp) +{ + if (argc != 2) + usage("git check-ref-format refname"); + return !!check_ref_format(argv[1]); +} diff --git a/builtin.h b/builtin.h index 7dff121520..ff559dec7e 100644 --- a/builtin.h +++ b/builtin.h @@ -25,5 +25,6 @@ extern int cmd_count_objects(int argc, const char **argv, char **envp); extern int cmd_push(int argc, const char **argv, char **envp); extern int cmd_grep(int argc, const char **argv, char **envp); extern int cmd_rev_list(int argc, const char **argv, char **envp); +extern int cmd_check_ref_format(int argc, const char **argv, char **envp); #endif diff --git a/check-ref-format.c b/check-ref-format.c deleted file mode 100644 index a0adb3dcb3..0000000000 --- a/check-ref-format.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * GIT - The information manager from hell - */ - -#include "cache.h" -#include "refs.h" - -#include - -int main(int ac, char **av) -{ - if (ac != 2) - usage("git-check-ref-format refname"); - if (check_ref_format(av[1])) - exit(1); - return 0; -} diff --git a/git.c b/git.c index c94e3a531b..d0650bb409 100644 --- a/git.c +++ b/git.c @@ -51,6 +51,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "diff", cmd_diff }, { "grep", cmd_grep }, { "rev-list", cmd_rev_list }, + { "check-ref-format", cmd_check_ref_format } }; int i; From ff62b7f37899de2faa88cbb5be1f3bcfa246012c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= Date: Thu, 18 May 2006 14:23:59 +0200 Subject: [PATCH 29/90] SubmittingPatches: The download location of External Editor has moved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Sandström Signed-off-by: Junio C Hamano --- Documentation/SubmittingPatches | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 318b04fdeb..8601949e80 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -266,8 +266,8 @@ This recipe appears to work with the current [*1*] Thunderbird from Suse. The following Thunderbird extensions are needed: AboutConfig 0.5 http://aboutconfig.mozdev.org/ - External Editor 0.5.4 - http://extensionroom.mozdev.org/more-info/exteditor + External Editor 0.7.2 + http://globs.org/articles.php?lng=en&pg=8 1) Prepare the patch as a text file using your method of choice. From 52db0495dcb88f38590bc00607052fc5758b07d1 Mon Sep 17 00:00:00 2001 From: Tilman Sauerbeck Date: Thu, 18 May 2006 12:57:04 +0200 Subject: [PATCH 30/90] Documentation/Makefile: create tarballs for the man pages and html files [jc: rewrote by stealing from what I run to update html and man branches automatically] Signed-off-by: Tilman Sauerbeck Signed-off-by: Junio C Hamano --- Documentation/install-webdoc.sh | 8 ++++++-- Makefile | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Documentation/install-webdoc.sh b/Documentation/install-webdoc.sh index 50638c78d5..60211a5058 100755 --- a/Documentation/install-webdoc.sh +++ b/Documentation/install-webdoc.sh @@ -4,12 +4,16 @@ T="$1" for h in *.html *.txt howto/*.txt howto/*.html do - diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h" || { + if test -f "$T/$h" && + diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h" + then + :; # up to date + else echo >&2 "# install $h $T/$h" rm -f "$T/$h" mkdir -p `dirname "$T/$h"` cp "$h" "$T/$h" - } + fi done strip_leading=`echo "$T/" | sed -e 's|.|.|g'` for th in "$T"/*.html "$T"/*.txt "$T"/howto/*.txt "$T"/howto/*.html diff --git a/Makefile b/Makefile index 2149fb8cea..c33a4d2b3e 100644 --- a/Makefile +++ b/Makefile @@ -652,6 +652,25 @@ dist: git.spec git-tar-tree rpm: dist $(RPMBUILD) -ta $(GIT_TARNAME).tar.gz +htmldocs = git-htmldocs-$(GIT_VERSION) +manpages = git-manpages-$(GIT_VERSION) +dist-doc: + rm -fr .doc-tmp-dir + mkdir .doc-tmp-dir + $(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc + cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar . + gzip -n -9 -f $(htmldocs).tar + : + rm -fr .doc-tmp-dir + mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7 + $(MAKE) -C Documentation DESTDIR=. \ + man1=../.doc-tmp-dir/man1 \ + man7=../.doc-tmp-dir/man7 \ + install + cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar . + gzip -n -9 -f $(manpages).tar + rm -fr .doc-tmp-dir + ### Cleaning rules clean: @@ -659,8 +678,9 @@ clean: $(LIB_FILE) $(XDIFF_LIB) rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags - rm -rf $(GIT_TARNAME) + rm -rf $(GIT_TARNAME) .doc-tmp-dir rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz + rm -f $(htmldocs).tar $(manpages).tar $(MAKE) -C Documentation/ clean $(MAKE) -C templates clean $(MAKE) -C t/ clean From d3d8f361a8c6beb5647e0d963a1460a505324494 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 17 May 2006 12:44:40 -0600 Subject: [PATCH 31/90] Implement git-quiltimport Importing a quilt patch series into git is not very difficult but parsing the patch descriptions and all of the other minutia take a bit of effort to get right, so this automates it. Since git and quilt complement each other it makes sense to make it easy to go back and forth between the two. If a patch is encountered that it cannot derive the author from the user is asked. Signed-off-by: Junio C Hamano --- Documentation/git-quiltimport.txt | 55 ++++++++++++++++ Makefile | 2 +- git-quiltimport.sh | 106 ++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 Documentation/git-quiltimport.txt create mode 100755 git-quiltimport.sh diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt new file mode 100644 index 0000000000..c66c82c61d --- /dev/null +++ b/Documentation/git-quiltimport.txt @@ -0,0 +1,55 @@ +git-quiltimport(1) +================ + +NAME +---- +git-quiltimport - Applies a quilt patchset onto the current branch + + +SYNOPSIS +-------- +[verse] +'git-quiltimport' [--author ] [--patches ] + + +DESCRIPTION +----------- +Applies a quilt patchset onto the current git branch, preserving +the patch boundaries, patch order, and patch descriptions present +in the quilt patchset. + +For each patch the code attempts to extract the author from the +patch description. If that fails it falls back to the author +specified with --author. If the --author flag was not given +the patch description is displayed and the user is asked to +interactively enter the author of the patch. + +If a subject is not found in the patch description the patch name is +preserved as the 1 line subject in the git description. + +OPTIONS +------- +--author Author Name :: + The author name and email address to use when no author + information can be found in the patch description. + +--patches :: + The directory to find the quilt patches and the + quilt series file. + + The default for the patch directory is patches + or the value of the $QUILT_PATCHES environment + variable. + +Author +------ +Written by Eric Biederman + +Documentation +-------------- +Documentation by Eric Biederman + +GIT +--- +Part of the gitlink:git[7] suite + diff --git a/Makefile b/Makefile index 2149fb8cea..549f953b76 100644 --- a/Makefile +++ b/Makefile @@ -125,7 +125,7 @@ SCRIPT_SH = \ git-applymbox.sh git-applypatch.sh git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ git-merge-resolve.sh git-merge-ours.sh \ - git-lost-found.sh + git-lost-found.sh git-quiltimport.sh SCRIPT_PERL = \ git-archimport.perl git-cvsimport.perl git-relink.perl \ diff --git a/git-quiltimport.sh b/git-quiltimport.sh new file mode 100755 index 0000000000..dd4a198fb1 --- /dev/null +++ b/git-quiltimport.sh @@ -0,0 +1,106 @@ +#!/bin/sh +USAGE='--author --patches ' +SUBDIRECTORY_ON=Yes +. git-sh-setup + +quilt_author="" +while case "$#" in 0) break;; esac +do + case "$1" in + --au=*|--aut=*|--auth=*|--autho=*|--author=*) + quilt_author=$(expr "$1" : '-[^=]*\(.*\)') + shift + ;; + + --au|--aut|--auth|--autho|--author) + case "$#" in 1) usage ;; esac + shift + quilt_author="$1" + shift + ;; + + --pa=*|--pat=*|--patc=*|--patch=*|--patche=*|--patches=*) + QUILT_PATCHES=$(expr "$1" : '-[^=]*\(.*\)') + shift + ;; + + --pa|--pat|--patc|--patch|--patche|--patches) + case "$#" in 1) usage ;; esac + shift + QUILT_PATCHES="$1" + shift + ;; + + *) + break + ;; + esac +done + +# Quilt Author +if [ -n "$quilt_author" ] ; then + quilt_author_name=$(expr "z$quilt_author" : 'z\(.*[^ ]\) *<.*') && + quilt_author_email=$(expr "z$quilt_author" : '.*<\([^>]*\)') && + test '' != "$quilt_author_name" && + test '' != "$quilt_author_email" || + die "malformatted --author parameter" +fi + +# Quilt patch directory +: ${QUILT_PATCHES:=patches} +if ! [ -d "$QUILT_PATCHES" ] ; then + echo "The \"$QUILT_PATCHES\" directory does not exist." + exit 1 +fi + +# Temporay directories +tmp_dir=.dotest +tmp_msg="$tmp_dir/msg" +tmp_patch="$tmp_dir/patch" +tmp_info="$tmp_dir/info" + + +# Find the intial commit +commit=$(git-rev-parse HEAD) + +mkdir $tmp_dir || exit 2 +for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do + echo $patch_name + (cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3 + + # Parse the author information + export GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' "$tmp_info") + export GIT_AUTHOR_EMAIL=$(sed -ne 's/Email: //p' "$tmp_info") + while test -z "$GIT_AUTHOR_EMAIL" && test -z "$GIT_AUTHOR_NAME" ; do + if [ -n "$quilt_author" ] ; then + GIT_AUTHOR_NAME="$quilt_author_name"; + GIT_AUTHOR_EMAIL="$quilt_author_email"; + else + echo "No author found in $patch_name"; + echo "---" + cat $tmp_msg + echo -n "Author: "; + read patch_author + + echo "$patch_author" + + patch_author_name=$(expr "z$patch_author" : 'z\(.*[^ ]\) *<.*') && + patch_author_email=$(expr "z$patch_author" : '.*<\([^>]*\)') && + test '' != "$patch_author_name" && + test '' != "$patch_author_email" && + GIT_AUTHOR_NAME="$patch_author_name" && + GIT_AUTHOR_EMAIL="$patch_author_email" + fi + done + export GIT_AUTHOR_DATE=$(sed -ne 's/Date: //p' "$tmp_info") + export SUBJECT=$(sed -ne 's/Subject: //p' "$tmp_info") + if [ -z "$SUBJECT" ] ; then + SUBJECT=$(echo $patch_name | sed -e 's/.patch$//') + fi + + git-apply --index -C1 "$tmp_patch" && + tree=$(git-write-tree) && + commit=$((echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) && + git-update-ref HEAD $commit || exit 4 +done +rm -rf $tmp_dir || exit 5 From d3bd4ee1a5cc771b86f73dd0f4a2bea6f652b20a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 17 May 2006 14:10:25 -0600 Subject: [PATCH 32/90] Implement a --dry-run option to git-quiltimport Since large quilt trees like -mm can easily have patches without clear authorship information, add a --dry-run option to make the problem patches easy to find. Signed-off-by: Junio C Hamano --- Documentation/git-quiltimport.txt | 8 +++++++- git-quiltimport.sh | 24 ++++++++++++++++++------ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt index c66c82c61d..6e9a8c369a 100644 --- a/Documentation/git-quiltimport.txt +++ b/Documentation/git-quiltimport.txt @@ -9,7 +9,7 @@ git-quiltimport - Applies a quilt patchset onto the current branch SYNOPSIS -------- [verse] -'git-quiltimport' [--author ] [--patches ] +'git-quiltimport' [--dry-run] [--author ] [--patches ] DESCRIPTION @@ -29,6 +29,12 @@ preserved as the 1 line subject in the git description. OPTIONS ------- +--dry-run:: + Walk through the patches in the series and warn + if we cannot find all of the necessary information to commit + a patch. At the time of this writing only missing author + information is warned about. + --author Author Name :: The author name and email address to use when no author information can be found in the patch description. diff --git a/git-quiltimport.sh b/git-quiltimport.sh index dd4a198fb1..12d9d0cbc9 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -1,8 +1,9 @@ #!/bin/sh -USAGE='--author --patches ' +USAGE='--dry-run --author --patches ' SUBDIRECTORY_ON=Yes . git-sh-setup +dry_run="" quilt_author="" while case "$#" in 0) break;; esac do @@ -19,6 +20,11 @@ do shift ;; + --dry-run) + shift + dry_run=1 + ;; + --pa=*|--pat=*|--patc=*|--patch=*|--patche=*|--patches=*) QUILT_PATCHES=$(expr "$1" : '-[^=]*\(.*\)') shift @@ -75,8 +81,12 @@ for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do if [ -n "$quilt_author" ] ; then GIT_AUTHOR_NAME="$quilt_author_name"; GIT_AUTHOR_EMAIL="$quilt_author_email"; + elif [ -n "$dry_run" ]; then + echo "No author found in $patch_name" >&2; + GIT_AUTHOR_NAME="dry-run-not-found"; + GIT_AUTHOR_EMAIL="dry-run-not-found"; else - echo "No author found in $patch_name"; + echo "No author found in $patch_name" >&2; echo "---" cat $tmp_msg echo -n "Author: "; @@ -98,9 +108,11 @@ for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do SUBJECT=$(echo $patch_name | sed -e 's/.patch$//') fi - git-apply --index -C1 "$tmp_patch" && - tree=$(git-write-tree) && - commit=$((echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) && - git-update-ref HEAD $commit || exit 4 + if [ -z "$dry_run" ] ; then + git-apply --index -C1 "$tmp_patch" && + tree=$(git-write-tree) && + commit=$((echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) && + git-update-ref HEAD $commit || exit 4 + fi done rm -rf $tmp_dir || exit 5 From c3c8835fbb182d971d71939b9a3ec7c8b86d6caf Mon Sep 17 00:00:00 2001 From: Timo Hirvonen Date: Fri, 19 May 2006 13:03:57 +0300 Subject: [PATCH 33/90] Builtin git-init-db Basically this just renames init-db.c to builtin-init-db.c and makes some strings const. Signed-off-by: Timo Hirvonen Signed-off-by: Junio C Hamano --- Makefile | 13 ++++++------- init-db.c => builtin-init-db.c | 12 +++++++----- builtin.h | 1 + git.c | 1 + 4 files changed, 15 insertions(+), 12 deletions(-) rename init-db.c => builtin-init-db.c (95%) diff --git a/Makefile b/Makefile index 2149fb8cea..a4e96435a0 100644 --- a/Makefile +++ b/Makefile @@ -154,7 +154,7 @@ PROGRAMS = \ git-convert-objects$X git-diff-files$X \ git-diff-index$X git-diff-stages$X \ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ - git-hash-object$X git-index-pack$X git-init-db$X git-local-fetch$X \ + git-hash-object$X git-index-pack$X git-local-fetch$X \ git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ @@ -170,7 +170,8 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ - git-grep$X git-rev-list$X git-check-ref-format$X + git-grep$X git-rev-list$X git-check-ref-format$X \ + git-init-db$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -218,7 +219,8 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ - builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o + builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ + builtin-init-db.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz @@ -473,6 +475,7 @@ strip: $(PROGRAMS) git$X git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) $(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ + -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' \ $(ALL_CFLAGS) -o $@ $(filter %.c,$^) \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) @@ -565,10 +568,6 @@ git-http-push$X: revision.o http.o http-push.o $(LIB_FILE) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) -init-db.o: init-db.c - $(CC) -c $(ALL_CFLAGS) \ - -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c - $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(GITLIBS) $(DIFF_OBJS): diffcore.h diff --git a/init-db.c b/builtin-init-db.c similarity index 95% rename from init-db.c rename to builtin-init-db.c index ff294960f2..2a1384ccb0 100644 --- a/init-db.c +++ b/builtin-init-db.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "builtin.h" #ifndef DEFAULT_GIT_TEMPLATE_DIR #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates/" @@ -116,7 +117,7 @@ static void copy_templates_1(char *path, int baselen, } } -static void copy_templates(const char *git_dir, int len, char *template_dir) +static void copy_templates(const char *git_dir, int len, const char *template_dir) { char path[PATH_MAX]; char template_path[PATH_MAX]; @@ -163,7 +164,7 @@ static void copy_templates(const char *git_dir, int len, char *template_dir) closedir(dir); } -static void create_default_files(const char *git_dir, char *template_path) +static void create_default_files(const char *git_dir, const char *template_path) { unsigned len = strlen(git_dir); static char path[PATH_MAX]; @@ -234,15 +235,16 @@ static const char init_db_usage[] = * On the other hand, it might just make lookup slower and messier. You * be the judge. The default case is to have one DB per managed directory. */ -int main(int argc, char **argv) +int cmd_init_db(int argc, const char **argv, char **envp) { const char *git_dir; const char *sha1_dir; - char *path, *template_dir = NULL; + const char *template_dir = NULL; + char *path; int len, i; for (i = 1; i < argc; i++, argv++) { - char *arg = argv[1]; + const char *arg = argv[1]; if (!strncmp(arg, "--template=", 11)) template_dir = arg+11; else if (!strcmp(arg, "--shared")) diff --git a/builtin.h b/builtin.h index ff559dec7e..60541262c4 100644 --- a/builtin.h +++ b/builtin.h @@ -26,5 +26,6 @@ extern int cmd_push(int argc, const char **argv, char **envp); extern int cmd_grep(int argc, const char **argv, char **envp); extern int cmd_rev_list(int argc, const char **argv, char **envp); extern int cmd_check_ref_format(int argc, const char **argv, char **envp); +extern int cmd_init_db(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index d0650bb409..3216d311b2 100644 --- a/git.c +++ b/git.c @@ -51,6 +51,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "diff", cmd_diff }, { "grep", cmd_grep }, { "rev-list", cmd_rev_list }, + { "init-db", cmd_init_db }, { "check-ref-format", cmd_check_ref_format } }; int i; From 405e5b2fe0cf20b0eea41ca892f416c218b49f59 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 19 May 2006 09:56:35 -0700 Subject: [PATCH 34/90] Libify the index refresh logic This cleans up and libifies the "git update-index --[really-]refresh" functionality. This will be eventually required for eventually doing the "commit" and "status" commands as built-ins. It really just moves "refresh_index()" from update-index.c to read-cache.c, but it also has to change the calling convention so that the function uses a "unsigned int flags" argument instead of various static flags variables for passing down the information about whether to be quiet or not, and allow unmerged entries etc. That actually cleans up update-index.c too, since it turns out that all those flags were really specific to that one function of the index update, so they shouldn't have had file-scope visibility even before. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- cache.h | 6 +++ read-cache.c | 117 ++++++++++++++++++++++++++++++++++++++++++++ update-index.c | 128 +++---------------------------------------------- 3 files changed, 129 insertions(+), 122 deletions(-) diff --git a/cache.h b/cache.h index 4b7a439253..afa8e4f0ac 100644 --- a/cache.h +++ b/cache.h @@ -158,6 +158,12 @@ extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_o extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); +#define REFRESH_REALLY 0x0001 /* ignore_valid */ +#define REFRESH_UNMERGED 0x0002 /* allow unmerged */ +#define REFRESH_QUIET 0x0004 /* be quiet about it */ +#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ +extern int refresh_cache(unsigned int flags); + struct cache_file { struct cache_file *next; char lockfile[PATH_MAX]; diff --git a/read-cache.c b/read-cache.c index a917ab0cfe..b95edcc14c 100644 --- a/read-cache.c +++ b/read-cache.c @@ -496,6 +496,123 @@ int add_cache_entry(struct cache_entry *ce, int option) return 0; } +/* Three functions to allow overloaded pointer return; see linux/err.h */ +static inline void *ERR_PTR(long error) +{ + return (void *) error; +} + +static inline long PTR_ERR(const void *ptr) +{ + return (long) ptr; +} + +static inline long IS_ERR(const void *ptr) +{ + return (unsigned long)ptr > (unsigned long)-1000L; +} + +/* + * "refresh" does not calculate a new sha1 file or bring the + * cache up-to-date for mode/content changes. But what it + * _does_ do is to "re-match" the stat information of a file + * with the cache, so that you can refresh the cache for a + * file that hasn't been changed but where the stat entry is + * out of date. + * + * For example, you'd want to do this after doing a "git-read-tree", + * to link up the stat cache details with the proper files. + */ +static struct cache_entry *refresh_entry(struct cache_entry *ce, int really) +{ + struct stat st; + struct cache_entry *updated; + int changed, size; + + if (lstat(ce->name, &st) < 0) + return ERR_PTR(-errno); + + changed = ce_match_stat(ce, &st, really); + if (!changed) { + if (really && assume_unchanged && + !(ce->ce_flags & htons(CE_VALID))) + ; /* mark this one VALID again */ + else + return NULL; + } + + if (ce_modified(ce, &st, really)) + return ERR_PTR(-EINVAL); + + size = ce_size(ce); + updated = xmalloc(size); + memcpy(updated, ce, size); + fill_stat_cache_info(updated, &st); + + /* In this case, if really is not set, we should leave + * CE_VALID bit alone. Otherwise, paths marked with + * --no-assume-unchanged (i.e. things to be edited) will + * reacquire CE_VALID bit automatically, which is not + * really what we want. + */ + if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) + updated->ce_flags &= ~htons(CE_VALID); + + return updated; +} + +int refresh_cache(unsigned int flags) +{ + int i; + int has_errors = 0; + int really = (flags & REFRESH_REALLY) != 0; + int allow_unmerged = (flags & REFRESH_UNMERGED) != 0; + int quiet = (flags & REFRESH_QUIET) != 0; + int not_new = (flags & REFRESH_IGNORE_MISSING) != 0; + + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce, *new; + ce = active_cache[i]; + if (ce_stage(ce)) { + while ((i < active_nr) && + ! strcmp(active_cache[i]->name, ce->name)) + i++; + i--; + if (allow_unmerged) + continue; + printf("%s: needs merge\n", ce->name); + has_errors = 1; + continue; + } + + new = refresh_entry(ce, really); + if (!new) + continue; + if (IS_ERR(new)) { + if (not_new && PTR_ERR(new) == -ENOENT) + continue; + if (really && PTR_ERR(new) == -EINVAL) { + /* If we are doing --really-refresh that + * means the index is not valid anymore. + */ + ce->ce_flags &= ~htons(CE_VALID); + active_cache_changed = 1; + } + if (quiet) + continue; + printf("%s: needs update\n", ce->name); + has_errors = 1; + continue; + } + active_cache_changed = 1; + /* You can NOT just free active_cache[i] here, since it + * might not be necessarily malloc()ed but can also come + * from mmap(). */ + active_cache[i] = new; + } + return has_errors; +} + static int verify_hdr(struct cache_header *hdr, unsigned long size) { SHA_CTX c; diff --git a/update-index.c b/update-index.c index 3d7e02db2c..7d6de821e2 100644 --- a/update-index.c +++ b/update-index.c @@ -18,9 +18,6 @@ static int allow_add; static int allow_remove; static int allow_replace; -static int allow_unmerged; /* --refresh needing merge is not error */ -static int not_new; /* --refresh not having working tree files is not error */ -static int quiet; /* --refresh needing update is not error */ static int info_only; static int force_remove; static int verbose; @@ -28,23 +25,6 @@ static int mark_valid_only = 0; #define MARK_VALID 1 #define UNMARK_VALID 2 - -/* Three functions to allow overloaded pointer return; see linux/err.h */ -static inline void *ERR_PTR(long error) -{ - return (void *) error; -} - -static inline long PTR_ERR(const void *ptr) -{ - return (long) ptr; -} - -static inline long IS_ERR(const void *ptr) -{ - return (unsigned long)ptr > (unsigned long)-1000L; -} - static void report(const char *fmt, ...) { va_list vp; @@ -140,103 +120,6 @@ static int add_file_to_cache(const char *path) return 0; } -/* - * "refresh" does not calculate a new sha1 file or bring the - * cache up-to-date for mode/content changes. But what it - * _does_ do is to "re-match" the stat information of a file - * with the cache, so that you can refresh the cache for a - * file that hasn't been changed but where the stat entry is - * out of date. - * - * For example, you'd want to do this after doing a "git-read-tree", - * to link up the stat cache details with the proper files. - */ -static struct cache_entry *refresh_entry(struct cache_entry *ce, int really) -{ - struct stat st; - struct cache_entry *updated; - int changed, size; - - if (lstat(ce->name, &st) < 0) - return ERR_PTR(-errno); - - changed = ce_match_stat(ce, &st, really); - if (!changed) { - if (really && assume_unchanged && - !(ce->ce_flags & htons(CE_VALID))) - ; /* mark this one VALID again */ - else - return NULL; - } - - if (ce_modified(ce, &st, really)) - return ERR_PTR(-EINVAL); - - size = ce_size(ce); - updated = xmalloc(size); - memcpy(updated, ce, size); - fill_stat_cache_info(updated, &st); - - /* In this case, if really is not set, we should leave - * CE_VALID bit alone. Otherwise, paths marked with - * --no-assume-unchanged (i.e. things to be edited) will - * reacquire CE_VALID bit automatically, which is not - * really what we want. - */ - if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) - updated->ce_flags &= ~htons(CE_VALID); - - return updated; -} - -static int refresh_cache(int really) -{ - int i; - int has_errors = 0; - - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce, *new; - ce = active_cache[i]; - if (ce_stage(ce)) { - while ((i < active_nr) && - ! strcmp(active_cache[i]->name, ce->name)) - i++; - i--; - if (allow_unmerged) - continue; - printf("%s: needs merge\n", ce->name); - has_errors = 1; - continue; - } - - new = refresh_entry(ce, really); - if (!new) - continue; - if (IS_ERR(new)) { - if (not_new && PTR_ERR(new) == -ENOENT) - continue; - if (really && PTR_ERR(new) == -EINVAL) { - /* If we are doing --really-refresh that - * means the index is not valid anymore. - */ - ce->ce_flags &= ~htons(CE_VALID); - active_cache_changed = 1; - } - if (quiet) - continue; - printf("%s: needs update\n", ce->name); - has_errors = 1; - continue; - } - active_cache_changed = 1; - /* You can NOT just free active_cache[i] here, since it - * might not be necessarily malloc()ed but can also come - * from mmap(). */ - active_cache[i] = new; - } - return has_errors; -} - /* * We fundamentally don't like some paths: we don't want * dot or dot-dot anywhere, and for obvious reasons don't @@ -653,6 +536,7 @@ int main(int argc, const char **argv) const char *prefix = setup_git_directory(); int prefix_length = prefix ? strlen(prefix) : 0; char set_executable_bit = 0; + unsigned int refresh_flags = 0; git_config(git_default_config); @@ -673,7 +557,7 @@ int main(int argc, const char **argv) continue; } if (!strcmp(path, "-q")) { - quiet = 1; + refresh_flags |= REFRESH_QUIET; continue; } if (!strcmp(path, "--add")) { @@ -689,15 +573,15 @@ int main(int argc, const char **argv) continue; } if (!strcmp(path, "--unmerged")) { - allow_unmerged = 1; + refresh_flags |= REFRESH_UNMERGED; continue; } if (!strcmp(path, "--refresh")) { - has_errors |= refresh_cache(0); + has_errors |= refresh_cache(refresh_flags); continue; } if (!strcmp(path, "--really-refresh")) { - has_errors |= refresh_cache(1); + has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags); continue; } if (!strcmp(path, "--cacheinfo")) { @@ -770,7 +654,7 @@ int main(int argc, const char **argv) goto finish; } if (!strcmp(path, "--ignore-missing")) { - not_new = 1; + refresh_flags |= REFRESH_IGNORE_MISSING; continue; } if (!strcmp(path, "--verbose")) { From 3c6a370b0ee78114a656acba04fddf306252b9d5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 19 May 2006 16:07:51 -0700 Subject: [PATCH 35/90] Move pathspec matching from builtin-add.c into dir.c I'll use it for builtin-rm.c too. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-add.c | 82 +-------------------------------------------------- dir.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ dir.h | 3 ++ 3 files changed, 84 insertions(+), 81 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 37243f8c36..6166f66bce 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -12,86 +12,6 @@ static const char builtin_add_usage[] = "git-add [-n] [-v] ..."; -static int common_prefix(const char **pathspec) -{ - const char *path, *slash, *next; - int prefix; - - if (!pathspec) - return 0; - - path = *pathspec; - slash = strrchr(path, '/'); - if (!slash) - return 0; - - prefix = slash - path + 1; - while ((next = *++pathspec) != NULL) { - int len = strlen(next); - if (len >= prefix && !memcmp(path, next, len)) - continue; - for (;;) { - if (!len) - return 0; - if (next[--len] != '/') - continue; - if (memcmp(path, next, len+1)) - continue; - prefix = len + 1; - break; - } - } - return prefix; -} - -static int match_one(const char *match, const char *name, int namelen) -{ - int matchlen; - - /* If the match was just the prefix, we matched */ - matchlen = strlen(match); - if (!matchlen) - return 1; - - /* - * If we don't match the matchstring exactly, - * we need to match by fnmatch - */ - if (strncmp(match, name, matchlen)) - return !fnmatch(match, name, 0); - - /* - * If we did match the string exactly, we still - * need to make sure that it happened on a path - * component boundary (ie either the last character - * of the match was '/', or the next character of - * the name was '/' or the terminating NUL. - */ - return match[matchlen-1] == '/' || - name[matchlen] == '/' || - !name[matchlen]; -} - -static int match(const char **pathspec, const char *name, int namelen, int prefix, char *seen) -{ - int retval; - const char *match; - - name += prefix; - namelen -= prefix; - - for (retval = 0; (match = *pathspec++) != NULL; seen++) { - if (retval & *seen) - continue; - match += prefix; - if (match_one(match, name, namelen)) { - retval = 1; - *seen = 1; - } - } - return retval; -} - static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { char *seen; @@ -107,7 +27,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (!match(pathspec, entry->name, entry->len, prefix, seen)) { + if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) { free(entry); continue; } diff --git a/dir.c b/dir.c index d40b62e1c1..d778ecd890 100644 --- a/dir.c +++ b/dir.c @@ -11,6 +11,86 @@ #include "cache.h" #include "dir.h" +int common_prefix(const char **pathspec) +{ + const char *path, *slash, *next; + int prefix; + + if (!pathspec) + return 0; + + path = *pathspec; + slash = strrchr(path, '/'); + if (!slash) + return 0; + + prefix = slash - path + 1; + while ((next = *++pathspec) != NULL) { + int len = strlen(next); + if (len >= prefix && !memcmp(path, next, len)) + continue; + for (;;) { + if (!len) + return 0; + if (next[--len] != '/') + continue; + if (memcmp(path, next, len+1)) + continue; + prefix = len + 1; + break; + } + } + return prefix; +} + +static int match_one(const char *match, const char *name, int namelen) +{ + int matchlen; + + /* If the match was just the prefix, we matched */ + matchlen = strlen(match); + if (!matchlen) + return 1; + + /* + * If we don't match the matchstring exactly, + * we need to match by fnmatch + */ + if (strncmp(match, name, matchlen)) + return !fnmatch(match, name, 0); + + /* + * If we did match the string exactly, we still + * need to make sure that it happened on a path + * component boundary (ie either the last character + * of the match was '/', or the next character of + * the name was '/' or the terminating NUL. + */ + return match[matchlen-1] == '/' || + name[matchlen] == '/' || + !name[matchlen]; +} + +int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen) +{ + int retval; + const char *match; + + name += prefix; + namelen -= prefix; + + for (retval = 0; (match = *pathspec++) != NULL; seen++) { + if (retval & *seen) + continue; + match += prefix; + if (match_one(match, name, namelen)) { + retval = 1; + *seen = 1; + } + } + return retval; +} + void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which) { diff --git a/dir.h b/dir.h index 4f65f57085..56a1b7fce2 100644 --- a/dir.h +++ b/dir.h @@ -39,6 +39,9 @@ struct dir_struct { struct exclude_list exclude_list[3]; }; +extern int common_prefix(const char **pathspec); +extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); + extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen); extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); From 582af688157607f05447cefce8d91b26eeaee39d Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 19 May 2006 00:19:20 -0400 Subject: [PATCH 36/90] Allow pickaxe and diff-filter options to be used by git log. Handle the -S option when passed to git log such that only the appropriate commits are displayed. Also per Junio's comments, do the same for "--diff-filter", so that it too can be used as an option to git log. By default no patch or diff information is displayed. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano --- builtin-log.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/builtin-log.c b/builtin-log.c index 69f2911cb4..c4ceee0f98 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -19,6 +19,13 @@ static int cmd_log_wc(int argc, const char **argv, char **envp, rev->commit_format = CMIT_FMT_DEFAULT; rev->verbose_header = 1; argc = setup_revisions(argc, argv, rev, "HEAD"); + if (rev->always_show_header) { + if (rev->diffopt.pickaxe || rev->diffopt.filter) { + rev->always_show_header = 0; + if (rev->diffopt.output_format == DIFF_FORMAT_RAW) + rev->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT; + } + } if (argc > 1) die("unrecognized argument: %s", argv[1]); From 7c4f59d181d801c6da08aeca74e2ef78efe4cd2b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 May 2006 17:23:07 -0700 Subject: [PATCH 37/90] Fix build procedure for builtin-init-db c3c8835fbb182d971d71939b9a3ec7c8b86d6caf broke the default template location which is in builtin-init-db.o, by not supplying the compilation-time constant to the right build commands. Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3a31ce0150..4fd6520b7e 100644 --- a/Makefile +++ b/Makefile @@ -461,6 +461,7 @@ PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH)) GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR)) ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS) +ALL_CFLAGS += -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' LIB_OBJS += $(COMPAT_OBJS) export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir ### Build rules @@ -475,7 +476,6 @@ strip: $(PROGRAMS) git$X git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) $(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ - -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' \ $(ALL_CFLAGS) -o $@ $(filter %.c,$^) \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) From d9b814cc97f16daac06566a5340121c446136d22 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 19 May 2006 16:19:34 -0700 Subject: [PATCH 38/90] Add builtin "git rm" command This changes semantics very subtly, because it adds a new atomicity guarantee. In particular, if you "git rm" several files, it will now do all or nothing. The old shell-script really looped over the removed files one by one, and would basically randomly fail in the middle if "-f" was used and one of the files didn't exist in the working directory. This C builtin one will not re-write the index after each remove, but instead remove all files at once. However, that means that if "-f" is used (to also force removal of the file from the working directory), and some files have already been removed from the workspace, it won't stop in the middle in some half-way state like the old one did. So what happens is that if the _first_ file fails to be removed with "-f", we abort the whole "git rm". But once we've started removing, we don't leave anything half done. If some of the other files don't exist, we'll just ignore errors of removal from the working tree. This is only an issue with "-f", of course. I think the new behaviour is strictly an improvement, but perhaps more importantly, it is _different_. As a special case, the semantics are identical for the single-file case (which is the only one our test-suite seems to test). The other question is what to do with leading directories. The old "git rm" script didn't do anything, which is somewhat inconsistent. This one will actually clean up directories that have become empty as a result of removing the last file, but maybe we want to have a flag to decide the behaviour? Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 8 +-- builtin-rm.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git-rm.sh | 70 ------------------------ git.c | 1 + 5 files changed, 157 insertions(+), 73 deletions(-) create mode 100644 builtin-rm.c delete mode 100755 git-rm.sh diff --git a/Makefile b/Makefile index 48e2a9cb22..d4a91135c3 100644 --- a/Makefile +++ b/Makefile @@ -120,7 +120,7 @@ SCRIPT_SH = \ git-merge-one-file.sh git-parse-remote.sh \ git-prune.sh git-pull.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ - git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \ + git-resolve.sh git-revert.sh git-sh-setup.sh \ git-tag.sh git-verify-tag.sh \ git-applymbox.sh git-applypatch.sh git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ @@ -170,7 +170,8 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ - git-grep$X git-add$X + git-grep$X git-add$X git-rm$X git-rev-list$X \ + git-check-ref-format$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -218,7 +219,8 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ - builtin-grep.o builtin-add.o + builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \ + builtin-rm.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-rm.c b/builtin-rm.c new file mode 100644 index 0000000000..9014c61556 --- /dev/null +++ b/builtin-rm.c @@ -0,0 +1,150 @@ +/* + * "git rm" builtin command + * + * Copyright (C) Linus Torvalds 2006 + */ +#include "cache.h" +#include "builtin.h" +#include "dir.h" + +static const char builtin_rm_usage[] = +"git-rm [-n] [-v] [-f] ..."; + +static struct { + int nr, alloc; + const char **name; +} list; + +static void add_list(const char *name) +{ + if (list.nr >= list.alloc) { + list.alloc = alloc_nr(list.alloc); + list.name = xrealloc(list.name, list.alloc * sizeof(const char *)); + } + list.name[list.nr++] = name; +} + +static int remove_file(const char *name) +{ + int ret; + char *slash; + + ret = unlink(name); + if (!ret && (slash = strrchr(name, '/'))) { + char *n = strdup(name); + do { + n[slash - name] = 0; + name = n; + } while (!rmdir(name) && (slash = strrchr(name, '/'))); + } + return ret; +} + +static struct cache_file cache_file; + +int cmd_rm(int argc, const char **argv, char **envp) +{ + int i, newfd; + int verbose = 0, show_only = 0, force = 0; + const char *prefix = setup_git_directory(); + const char **pathspec; + char *seen; + + git_config(git_default_config); + + newfd = hold_index_file_for_update(&cache_file, get_index_file()); + if (newfd < 0) + die("unable to create new index file"); + + if (read_cache() < 0) + die("index file corrupt"); + + for (i = 1 ; i < argc ; i++) { + const char *arg = argv[i]; + + if (*arg != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strcmp(arg, "-n")) { + show_only = 1; + continue; + } + if (!strcmp(arg, "-v")) { + verbose = 1; + continue; + } + if (!strcmp(arg, "-f")) { + force = 1; + continue; + } + die(builtin_rm_usage); + } + pathspec = get_pathspec(prefix, argv + i); + + seen = NULL; + if (pathspec) { + for (i = 0; pathspec[i] ; i++) + /* nothing */; + seen = xmalloc(i); + memset(seen, 0, i); + } + + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) + continue; + add_list(ce->name); + } + + if (pathspec) { + const char *match; + for (i = 0; (match = pathspec[i]) != NULL ; i++) { + if (*match && !seen[i]) + die("pathspec '%s' did not match any files", match); + } + } + + /* + * First remove the names from the index: we won't commit + * the index unless all of them succeed + */ + for (i = 0; i < list.nr; i++) { + const char *path = list.name[i]; + printf("rm '%s'\n", path); + + if (remove_file_from_cache(path)) + die("git rm: unable to remove %s", path); + } + + /* + * Then, if we used "-f", remove the filenames from the + * workspace. If we fail to remove the first one, we + * abort the "git rm" (but once we've successfully removed + * any file at all, we'll go ahead and commit to it all: + * by then we've already committed ourself and can't fail + * in the middle) + */ + if (force) { + int removed = 0; + for (i = 0; i < list.nr; i++) { + const char *path = list.name[i]; + if (!remove_file(path)) { + removed = 1; + continue; + } + if (!removed) + die("git rm: %s: %s", path, strerror(errno)); + } + } + + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + commit_index_file(&cache_file)) + die("Unable to write new index file"); + } + + return 0; +} diff --git a/builtin.h b/builtin.h index 1b77f4b0ca..c1cb765dea 100644 --- a/builtin.h +++ b/builtin.h @@ -24,6 +24,7 @@ extern int cmd_count_objects(int argc, const char **argv, char **envp); extern int cmd_push(int argc, const char **argv, char **envp); extern int cmd_grep(int argc, const char **argv, char **envp); +extern int cmd_rm(int argc, const char **argv, char **envp); extern int cmd_add(int argc, const char **argv, char **envp); #endif diff --git a/git-rm.sh b/git-rm.sh deleted file mode 100755 index fda4541c76..0000000000 --- a/git-rm.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/sh - -USAGE='[-f] [-n] [-v] [--] ...' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -remove_files= -show_only= -verbose= -while : ; do - case "$1" in - -f) - remove_files=true - ;; - -n) - show_only=true - ;; - -v) - verbose=--verbose - ;; - --) - shift; break - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift -done - -# This is typo-proofing. If some paths match and some do not, we want -# to do nothing. -case "$#" in -0) ;; -*) - git-ls-files --error-unmatch -- "$@" >/dev/null || { - echo >&2 "Maybe you misspelled it?" - exit 1 - } - ;; -esac - -if test -f "$GIT_DIR/info/exclude" -then - git-ls-files -z \ - --exclude-from="$GIT_DIR/info/exclude" \ - --exclude-per-directory=.gitignore -- "$@" -else - git-ls-files -z \ - --exclude-per-directory=.gitignore -- "$@" -fi | -case "$show_only,$remove_files" in -true,*) - xargs -0 echo - ;; -*,true) - xargs -0 sh -c " - while [ \$# -gt 0 ]; do - file=\$1; shift - rm -- \"\$file\" && git-update-index --remove $verbose \"\$file\" - done - " inline - ;; -*) - git-update-index --force-remove $verbose -z --stdin - ;; -esac diff --git a/git.c b/git.c index fac46af057..20c0f197a3 100644 --- a/git.c +++ b/git.c @@ -50,6 +50,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, + { "rm", cmd_rm }, { "add", cmd_add }, }; int i; From 217542640ed219c980fff2b3c307c4520120f20f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 18 May 2006 18:46:44 -0700 Subject: [PATCH 39/90] built-in tar-tree and remote tar-tree This makes tar-tree a built-in. As an added bonus, you can now say: git tar-tree --remote=remote-repository [] This does not work with git-daemon yet, but should work with localhost and git over ssh transports. Signed-off-by: Junio C Hamano --- Makefile | 6 +-- tar-tree.c => builtin-tar-tree.c | 62 +++++++++++++++++++++++++- builtin-upload-tar.c | 74 ++++++++++++++++++++++++++++++++ builtin.h | 2 + git.c | 4 +- 5 files changed, 142 insertions(+), 6 deletions(-) rename tar-tree.c => builtin-tar-tree.c (85%) create mode 100644 builtin-upload-tar.c diff --git a/Makefile b/Makefile index 4fd6520b7e..f4bcec496a 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ PROGRAMS = \ git-receive-pack$X git-rev-parse$X \ git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ - git-ssh-upload$X git-tar-tree$X git-unpack-file$X \ + git-ssh-upload$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ git-upload-pack$X git-verify-pack$X git-write-tree$X \ git-update-ref$X git-symbolic-ref$X \ @@ -171,7 +171,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X + git-init-db$X git-tar-tree$X git-upload-tar$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -220,7 +220,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o + builtin-init-db.o builtin-tar-tree.o builtin-upload-tar.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/tar-tree.c b/builtin-tar-tree.c similarity index 85% rename from tar-tree.c rename to builtin-tar-tree.c index 33087366c3..e97e0af985 100644 --- a/tar-tree.c +++ b/builtin-tar-tree.c @@ -7,11 +7,14 @@ #include "commit.h" #include "strbuf.h" #include "tar.h" +#include "builtin.h" +#include "pkt-line.h" #define RECORDSIZE (512) #define BLOCKSIZE (RECORDSIZE * 20) -static const char tar_tree_usage[] = "git-tar-tree [basedir]"; +static const char tar_tree_usage[] = +"git-tar-tree [--remote=] [basedir]"; static char block[BLOCKSIZE]; static unsigned long offset; @@ -301,7 +304,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path) } } -int main(int argc, char **argv) +int generate_tar(int argc, const char **argv) { unsigned char sha1[20], tree_sha1[20]; struct commit *commit; @@ -348,3 +351,58 @@ int main(int argc, char **argv) free(current_path.buf); return 0; } + +static const char *exec = "git-upload-tar"; + +static int remote_tar(int argc, const char **argv) +{ + int fd[2], ret, len; + pid_t pid; + char buf[1024]; + char *url; + + if (argc < 3 || 4 < argc) + usage(tar_tree_usage); + + /* --remote= */ + url = strdup(argv[1]+9); + pid = git_connect(fd, url, exec); + if (pid < 0) + return 1; + + packet_write(fd[1], "want %s\n", argv[2]); + if (argv[3]) + packet_write(fd[1], "base %s\n", argv[3]); + packet_flush(fd[1]); + + len = packet_read_line(fd[0], buf, sizeof(buf)); + if (!len) + die("git-tar-tree: expected ACK/NAK, got EOF"); + if (buf[len-1] == '\n') + buf[--len] = 0; + if (strcmp(buf, "ACK")) { + if (5 < len && !strncmp(buf, "NACK ", 5)) + die("git-tar-tree: NACK %s", buf + 5); + die("git-tar-tree: protocol error"); + } + /* expect a flush */ + len = packet_read_line(fd[0], buf, sizeof(buf)); + if (len) + die("git-tar-tree: expected a flush"); + + /* Now, start reading from fd[0] and spit it out to stdout */ + ret = copy_fd(fd[0], 1); + close(fd[0]); + + ret |= finish_connect(pid); + return !!ret; +} + +int cmd_tar_tree(int argc, const char **argv, char **envp) +{ + if (argc < 2) + usage(tar_tree_usage); + if (!strncmp("--remote=", argv[1], 9)) + return remote_tar(argc, argv); + return generate_tar(argc, argv); +} diff --git a/builtin-upload-tar.c b/builtin-upload-tar.c new file mode 100644 index 0000000000..d4fa7b56c3 --- /dev/null +++ b/builtin-upload-tar.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006 Junio C Hamano + */ +#include "cache.h" +#include "pkt-line.h" +#include "exec_cmd.h" +#include "builtin.h" + +static const char upload_tar_usage[] = "git-upload-tar "; + +static int nak(const char *reason) +{ + packet_write(1, "NACK %s\n", reason); + packet_flush(1); + return 1; +} + +int cmd_upload_tar(int argc, const char **argv, char **envp) +{ + int len; + const char *dir = argv[1]; + char buf[8192]; + unsigned char sha1[20]; + char *base = NULL; + char hex[41]; + int ac; + const char *av[4]; + + if (argc != 2) + usage(upload_tar_usage); + if (strlen(dir) < sizeof(buf)-1) + strcpy(buf, dir); /* enter-repo smudges its argument */ + else + packet_write(1, "NACK insanely long repository name %s\n", dir); + if (!enter_repo(buf, 0)) { + packet_write(1, "NACK not a git archive %s\n", dir); + packet_flush(1); + return 1; + } + + len = packet_read_line(0, buf, sizeof(buf)); + if (len < 5 || strncmp("want ", buf, 5)) + return nak("expected want"); + if (buf[len-1] == '\n') + buf[--len] = 0; + if (get_sha1(buf + 5, sha1)) + return nak("expected sha1"); + strcpy(hex, sha1_to_hex(sha1)); + + len = packet_read_line(0, buf, sizeof(buf)); + if (len) { + if (len < 5 || strncmp("base ", buf, 5)) + return nak("expected (optional) base"); + if (buf[len-1] == '\n') + buf[--len] = 0; + base = strdup(buf + 5); + len = packet_read_line(0, buf, sizeof(buf)); + } + if (len) + return nak("expected flush"); + + packet_write(1, "ACK\n"); + packet_flush(1); + + ac = 0; + av[ac++] = "tar-tree"; + av[ac++] = hex; + if (base) + av[ac++] = base; + av[ac++] = NULL; + execv_git_cmd(av); + /* should it return that is an error */ + return 1; +} diff --git a/builtin.h b/builtin.h index 60541262c4..f22783c499 100644 --- a/builtin.h +++ b/builtin.h @@ -27,5 +27,7 @@ extern int cmd_grep(int argc, const char **argv, char **envp); extern int cmd_rev_list(int argc, const char **argv, char **envp); extern int cmd_check_ref_format(int argc, const char **argv, char **envp); extern int cmd_init_db(int argc, const char **argv, char **envp); +extern int cmd_tar_tree(int argc, const char **argv, char **envp); +extern int cmd_upload_tar(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 3216d311b2..fd8e9bf7f2 100644 --- a/git.c +++ b/git.c @@ -50,8 +50,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, - { "rev-list", cmd_rev_list }, { "init-db", cmd_init_db }, + { "tar-tree", cmd_tar_tree }, + { "upload-tar", cmd_upload_tar }, + { "rev-list", cmd_rev_list }, { "check-ref-format", cmd_check_ref_format } }; int i; From 845ae27bf8161063d7e2e1ca2a76b05bf5d07192 Mon Sep 17 00:00:00 2001 From: Santi Date: Fri, 19 May 2006 23:02:34 +0200 Subject: [PATCH 40/90] Document that "git add" only adds non-ignored files. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santi Béjar Signed-off-by: Junio C Hamano --- Documentation/git-add.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 5e3112943d..6342ea33e4 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -14,11 +14,13 @@ DESCRIPTION A simple wrapper for git-update-index to add files to the index, for people used to do "cvs add". +It only adds non-ignored files, to add ignored files use +"git update-index --add". OPTIONS ------- ...:: - Files to add to the index. + Files to add to the index (see gitlink:git-ls-files[1]). -n:: Don't actually add the file(s), just show if they exist. @@ -68,6 +70,7 @@ git-add git-*.sh:: See Also -------- gitlink:git-rm[1] +gitlink:git-ls-files[1] Author ------ From 8824689884a5673f803d77cda499f83b84145a06 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 May 2006 23:43:13 +0200 Subject: [PATCH 41/90] diff family: add --check option Actually, it is a diff option now, so you can say git diff --check to ask if what you are about to commit is a good patch. [jc: this also would work for fmt-patch, but the point is that the check is done before making a commit. format-patch is run from an already created commit, and that is too late to catch whitespace damaged change.] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- diff.h | 1 + 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/diff.c b/diff.c index e16e0bfc0a..af5db0ec12 100644 --- a/diff.c +++ b/diff.c @@ -397,6 +397,46 @@ static void show_stats(struct diffstat_t* data) total_files, adds, dels); } +struct checkdiff_t { + struct xdiff_emit_state xm; + const char *filename; + int lineno; +}; + +static void checkdiff_consume(void *priv, char *line, unsigned long len) +{ + struct checkdiff_t *data = priv; + + if (line[0] == '+') { + int i, spaces = 0; + + data->lineno++; + + /* check space before tab */ + for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++) + if (line[i] == ' ') + spaces++; + if (line[i - 1] == '\t' && spaces) + printf("%s:%d: space before tab:%.*s\n", + data->filename, data->lineno, (int)len, line); + + /* check white space at line end */ + if (line[len - 1] == '\n') + len--; + if (isspace(line[len - 1])) + printf("%s:%d: white space at end: %.*s\n", + data->filename, data->lineno, (int)len, line); + } else if (line[0] == ' ') + data->lineno++; + else if (line[0] == '@') { + char *plus = strchr(line, '+'); + if (plus) + data->lineno = strtol(plus, line + len, 10); + else + die("invalid diff"); + } +} + static unsigned char *deflate_it(char *data, unsigned long size, unsigned long *result_size) @@ -624,6 +664,41 @@ static void builtin_diffstat(const char *name_a, const char *name_b, } } +static void builtin_checkdiff(const char *name_a, const char *name_b, + struct diff_filespec *one, + struct diff_filespec *two) +{ + mmfile_t mf1, mf2; + struct checkdiff_t data; + + if (!two) + return; + + memset(&data, 0, sizeof(data)); + data.xm.consume = checkdiff_consume; + data.filename = name_b ? name_b : name_a; + data.lineno = 0; + + if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) + die("unable to read files to diff"); + + if (mmfile_is_binary(&mf2)) + return; + else { + /* Crazy xdl interfaces.. */ + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 0; + xecfg.flags = 0; + ecb.outf = xdiff_outf; + ecb.priv = &data; + xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + } +} + struct diff_filespec *alloc_filespec(const char *path) { int namelen = strlen(path); @@ -1180,6 +1255,25 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite); } +static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) +{ + const char *name; + const char *other; + + if (DIFF_PAIR_UNMERGED(p)) { + /* unmerged */ + return; + } + + name = p->one->path; + other = (strcmp(name, p->two->path) ? p->two->path : NULL); + + diff_fill_sha1_info(p->one); + diff_fill_sha1_info(p->two); + + builtin_checkdiff(name, other, p->one, p->two); +} + void diff_setup(struct diff_options *options) { memset(options, 0, sizeof(*options)); @@ -1205,7 +1299,8 @@ int diff_setup_done(struct diff_options *options) * recursive bits for other formats here. */ if ((options->output_format == DIFF_FORMAT_PATCH) || - (options->output_format == DIFF_FORMAT_DIFFSTAT)) + (options->output_format == DIFF_FORMAT_DIFFSTAT) || + (options->output_format == DIFF_FORMAT_CHECKDIFF)) options->recursive = 1; if (options->detect_rename && options->rename_limit < 0) @@ -1288,6 +1383,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) } else if (!strcmp(arg, "--stat")) options->output_format = DIFF_FORMAT_DIFFSTAT; + else if (!strcmp(arg, "--check")) + options->output_format = DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) options->summary = 1; else if (!strcmp(arg, "--patch-with-stat")) { @@ -1610,6 +1707,19 @@ static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o, run_diffstat(p, o, diffstat); } +static void diff_flush_checkdiff(struct diff_filepair *p, + struct diff_options *o) +{ + if (diff_unmodified_pair(p)) + return; + + if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || + (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) + return; /* no tree diffs in patch format */ + + run_checkdiff(p, o); +} + int diff_queue_is_empty(void) { struct diff_queue_struct *q = &diff_queued_diff; @@ -1740,6 +1850,9 @@ static void flush_one_pair(struct diff_filepair *p, case DIFF_FORMAT_DIFFSTAT: diff_flush_stat(p, options, diffstat); break; + case DIFF_FORMAT_CHECKDIFF: + diff_flush_checkdiff(p, options); + break; case DIFF_FORMAT_PATCH: diff_flush_patch(p, options); break; diff --git a/diff.h b/diff.h index 3027974c1e..c672277df2 100644 --- a/diff.h +++ b/diff.h @@ -153,6 +153,7 @@ extern int diff_queue_is_empty(void); #define DIFF_FORMAT_NAME 4 #define DIFF_FORMAT_NAME_STATUS 5 #define DIFF_FORMAT_DIFFSTAT 6 +#define DIFF_FORMAT_CHECKDIFF 7 extern void diff_flush(struct diff_options*); From 698ce6f87e0d6db380f7306e190e8586da184577 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 May 2006 15:40:29 +0200 Subject: [PATCH 42/90] fmt-patch: Support --attach This patch touches a couple of files, because it adds options to print a custom text just after the subject of a commit, and just after the diffstat. [jc: made "many dashes" used as the boundary leader into a single variable, to reduce the possibility of later tweaks to miscount the number of dashes to break it.] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 17 ++++++++++++++--- builtin-rev-list.c | 2 +- commit.c | 10 +++++++++- commit.h | 2 +- diff.c | 6 +++++- diff.h | 3 +++ log-tree.c | 37 +++++++++++++++++++++++++++++++++---- revision.h | 1 + show-branch.c | 2 +- 9 files changed, 68 insertions(+), 12 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 12a6d19203..c8feb0f795 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -183,7 +183,12 @@ int cmd_format_patch(int argc, const char **argv, char **envp) argv[i + 1]); output_directory = strdup(argv[i + 1]); i++; - } else + } + else if (!strcmp(argv[i], "--attach")) + rev.mime_boundary = git_version_string; + else if (!strncmp(argv[i], "--attach=", 9)) + rev.mime_boundary = argv[i] + 9; + else argv[j++] = argv[i]; } argc = j; @@ -224,8 +229,14 @@ int cmd_format_patch(int argc, const char **argv, char **envp) shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; - if (shown) - printf("-- \n%s\n\n", git_version_string); + if (shown) { + if (rev.mime_boundary) + printf("\n--%s%s--\n\n\n", + mime_boundary_leader, + rev.mime_boundary); + else + printf("-- \n%s\n\n", git_version_string); + } if (!use_stdout) fclose(stdout); } diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 7942297d13..f11dbd65c1 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -85,7 +85,7 @@ static void show_commit(struct commit *commit) static char pretty_header[16384]; pretty_print_commit(revs.commit_format, commit, ~0, pretty_header, sizeof(pretty_header), - revs.abbrev, NULL); + revs.abbrev, NULL, NULL); printf("%s%c", pretty_header, hdr_termination); } fflush(stdout); diff --git a/commit.c b/commit.c index 84558bac29..0b163d4857 100644 --- a/commit.c +++ b/commit.c @@ -498,7 +498,7 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject) { int hdr = 1, body = 0; unsigned long offset = 0; @@ -591,6 +591,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + if (after_subject) { + int slen = strlen(after_subject); + if (slen > space - offset - 1) + slen = space - offset - 1; + memcpy(buf + offset, after_subject, slen); + offset += slen; + after_subject = NULL; + } subject = NULL; } while (offset && isspace(buf[offset-1])) diff --git a/commit.h b/commit.h index 8d7514cd00..c9de1677e9 100644 --- a/commit.h +++ b/commit.h @@ -51,7 +51,7 @@ enum cmit_fmt { }; extern enum cmit_fmt get_commit_format(const char *arg); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject); +extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/diff.c b/diff.c index af5db0ec12..be459cac69 100644 --- a/diff.c +++ b/diff.c @@ -299,6 +299,7 @@ static void diffstat_consume(void *priv, char *line, unsigned long len) static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; static const char minuses[]= "----------------------------------------------------------------------"; +const char mime_boundary_leader[] = "------------"; static void show_stats(struct diffstat_t* data) { @@ -1980,7 +1981,10 @@ void diff_flush(struct diff_options *options) show_stats(diffstat); free(diffstat); diffstat = NULL; - putchar(options->line_termination); + if (options->stat_sep) + fputs(options->stat_sep, stdout); + else + putchar(options->line_termination); } for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; diff --git a/diff.h b/diff.h index c672277df2..4fc597c594 100644 --- a/diff.h +++ b/diff.h @@ -44,6 +44,7 @@ struct diff_options { int rename_limit; int setup; int abbrev; + const char *stat_sep; int nr_paths; const char **paths; @@ -52,6 +53,8 @@ struct diff_options { add_remove_fn_t add_remove; }; +extern const char mime_boundary_leader[]; + extern void diff_tree_setup_paths(const char **paths, struct diff_options *); extern void diff_tree_release_paths(struct diff_options *); extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2, diff --git a/log-tree.c b/log-tree.c index 526d578e98..7e23e42788 100644 --- a/log-tree.c +++ b/log-tree.c @@ -20,7 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra; int len; - char* subject = NULL; + char *subject = NULL, *after_subject = NULL; opt->loginfo = NULL; if (!opt->verbose_header) { @@ -52,6 +52,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) */ if (opt->commit_format == CMIT_FMT_EMAIL) { + char *sha1 = sha1_to_hex(commit->object.sha1); if (opt->total > 0) { static char buffer[64]; snprintf(buffer, sizeof(buffer), @@ -63,8 +64,36 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) else subject = "Subject: "; - printf("From %s Thu Apr 7 15:13:13 2005\n", - sha1_to_hex(commit->object.sha1)); + printf("From %s Mon Sep 17 00:00:00 2001\n", sha1); + if (opt->mime_boundary) { + static char subject_buffer[1024]; + static char buffer[1024]; + snprintf(subject_buffer, sizeof(subject_buffer) - 1, + "MIME-Version: 1.0\n" + "Content-Type: multipart/mixed;\n" + " boundary=\"%s%s\"\n" + "\n" + "This is a multi-part message in MIME " + "format.\n" + "--%s%s\n" + "Content-Type: text/plain; " + "charset=UTF-8; format=fixed\n" + "Content-Transfer-Encoding: 8bit\n\n", + mime_boundary_leader, opt->mime_boundary, + mime_boundary_leader, opt->mime_boundary); + after_subject = subject_buffer; + + snprintf(buffer, sizeof(buffer) - 1, + "--%s%s\n" + "Content-Type: text/x-patch;\n" + " name=\"%s.diff\"\n" + "Content-Transfer-Encoding: 8bit\n" + "Content-Disposition: inline;\n" + " filename=\"%s.diff\"\n\n", + mime_boundary_leader, opt->mime_boundary, + sha1, sha1); + opt->diffopt.stat_sep = buffer; + } } else { printf("%s%s", opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", @@ -81,7 +110,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) /* * And then the pretty-printed message itself */ - len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject); + len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, after_subject); printf("%s%s%s", this_header, extra, sep); } diff --git a/revision.h b/revision.h index 62759f7bc0..bdbdd235d8 100644 --- a/revision.h +++ b/revision.h @@ -59,6 +59,7 @@ struct rev_info { enum cmit_fmt commit_format; struct log_info *loginfo; int nr, total; + const char *mime_boundary; /* special limits */ int max_count; diff --git a/show-branch.c b/show-branch.c index bbe26c2e7a..684ffd187c 100644 --- a/show-branch.c +++ b/show-branch.c @@ -259,7 +259,7 @@ static void show_one_commit(struct commit *commit, int no_name) struct commit_name *name = commit->object.util; if (commit->object.parsed) pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - pretty, sizeof(pretty), 0, NULL); + pretty, sizeof(pretty), 0, NULL, NULL); else strcpy(pretty, "(unavailable)"); if (!strncmp(pretty, "[PATCH] ", 8)) From 2b1f4247ab5881cfde1d35ca3e3c48b7de8f449c Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 20 May 2006 21:54:46 -0400 Subject: [PATCH 43/90] Reference git-check-ref-format in git-branch. Its nice to have git-check-ref-format actually get mentioned in git-branch's documentation as the syntax of a ref name must conform to what is described in git-check-ref-format. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/git-branch.txt | 3 +++ Documentation/git-checkout.txt | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 72fb2f89b4..0b37e2bfc8 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -43,6 +43,9 @@ OPTIONS :: The name of the branch to create or delete. + The new branch name must pass all checks defined by + gitlink:git-check-ref-format[1]. Some of these checks + may restrict the characters allowed in a branch name. :: The new branch will be created with a HEAD equal to this. It may diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 095128906a..d82efc00d4 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -35,7 +35,10 @@ OPTIONS Force a re-read of everything. -b:: - Create a new branch and start it at . + Create a new branch named and start it at + . The new branch name must pass all checks defined + by gitlink:git-check-ref-format[1]. Some of these checks + may restrict the characters allowed in a branch name. -m:: If you have local modifications to one or more files that From 87a56cd3b1dfb651076fbdc6d3591a32d1286a03 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 20 May 2006 22:03:14 -0400 Subject: [PATCH 44/90] Elaborate on why ':' is a bad idea in a ref name. With the new cat-file syntax of 'v1.3.3:refs.c' we should mention it as part of the reason why ':' is not permitted in a ref name. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/git-check-ref-format.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt index 7dc1bdb6ef..3ea720dd00 100644 --- a/Documentation/git-check-ref-format.txt +++ b/Documentation/git-check-ref-format.txt @@ -45,6 +45,8 @@ refname expressions (see gitlink:git-rev-parse[1]). Namely: . colon `:` is used as in `srcref:dstref` to mean "use srcref\'s value and store it in dstref" in fetch and push operations. + It may also be used to select a specific object such as with + gitlink:git-cat-file[1] "git-cat-file blob v1.3.3:refs.c". GIT From 685637381a967cd7388495f97b12b7cf177abbb4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 02:48:21 -0700 Subject: [PATCH 45/90] git-format-patch: now built-in. Signed-off-by: Junio C Hamano --- Makefile | 4 +- git-format-patch.sh | 344 -------------------------------------------- git.c | 2 +- 3 files changed, 3 insertions(+), 347 deletions(-) delete mode 100755 git-format-patch.sh diff --git a/Makefile b/Makefile index 4fd6520b7e..fbb3dca2f3 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ SCRIPT_SH = \ git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \ git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ git-fetch.sh \ - git-format-patch.sh git-ls-remote.sh \ + git-ls-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \ git-prune.sh git-pull.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ @@ -171,7 +171,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X + git-init-db$X git-format-patch$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) diff --git a/git-format-patch.sh b/git-format-patch.sh deleted file mode 100755 index 8a16eadfbd..0000000000 --- a/git-format-patch.sh +++ /dev/null @@ -1,344 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Junio C Hamano -# - -USAGE='[-n | -k] [-o | --stdout] [--signoff] [--check] [--diff-options] [--attach] []' -LONG_USAGE='Prepare each commit with its patch since head forked from - head, one file per patch formatted to resemble UNIX mailbox -format, for e-mail submission or use with git-am. - -Each output file is numbered sequentially from 1, and uses the -first line of the commit message (massaged for pathname safety) -as the filename. - -When -o is specified, output files are created in ; otherwise -they are created in the current working directory. This option -is ignored if --stdout is specified. - -When -n is specified, instead of "[PATCH] Subject", the first -line is formatted as "[PATCH N/M] Subject", unless you have only -one patch. - -When --attach is specified, patches are attached, not inlined.' - -. git-sh-setup - -# Force diff to run in C locale. -LANG=C LC_ALL=C -export LANG LC_ALL - -diff_opts= -LF=' -' - -outdir=./ -while case "$#" in 0) break;; esac -do - case "$1" in - -c|--c|--ch|--che|--chec|--check) - check=t ;; - -a|--a|--au|--aut|--auth|--autho|--author|\ - -d|--d|--da|--dat|--date|\ - -m|--m|--mb|--mbo|--mbox) # now noop - ;; - --at|--att|--atta|--attac|--attach) - attach=t ;; - -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\ - --keep-subj|--keep-subje|--keep-subjec|--keep-subject) - keep_subject=t ;; - -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered) - numbered=t ;; - -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) - signoff=t ;; - --st|--std|--stdo|--stdou|--stdout) - stdout=t ;; - -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\ - --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\ - --output-direc=*|--output-direct=*|--output-directo=*|\ - --output-director=*|--output-directory=*) - outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;; - -o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\ - --output-di|--output-dir|--output-dire|--output-direc|--output-direct|\ - --output-directo|--output-director|--output-directory) - case "$#" in 1) usage ;; esac; shift - outdir="$1" ;; - -h|--h|--he|--hel|--help) - usage - ;; - -*' '* | -*"$LF"* | -*' '*) - # Ignore diff option that has whitespace for now. - ;; - -*) diff_opts="$diff_opts$1 " ;; - *) break ;; - esac - shift -done - -case "$keep_subject$numbered" in -tt) - die '--keep-subject and --numbered are incompatible.' ;; -esac - -tmp=.tmp-series$$ -trap 'rm -f $tmp-*' 0 1 2 3 15 - -series=$tmp-series -commsg=$tmp-commsg -filelist=$tmp-files - -# Backward compatible argument parsing hack. -# -# Historically, we supported: -# 1. "rev1" is equivalent to "rev1..HEAD" -# 2. "rev1..rev2" -# 3. "rev1" "rev2 is equivalent to "rev1..rev2" -# -# We want to take a sequence of "rev1..rev2" in general. -# Also, "rev1.." should mean "rev1..HEAD"; git-diff users are -# familiar with that syntax. - -case "$#,$1$2" in -1,?*..?*) - # single "rev1..rev2" - ;; -1,?*..) - # single "rev1.." should mean "rev1..HEAD" - set x "$1"HEAD - shift - ;; -1,*) - # single rev1 - set x "$1..HEAD" - shift - ;; -2,?*..?*) - # not traditional "rev1" "rev2" - ;; -2,*) - set x "$1..$2" - shift - ;; -esac - -# Now we have what we want in $@ -for revpair -do - case "$revpair" in - ?*..?*) - rev1=`expr "z$revpair" : 'z\(.*\)\.\.'` - rev2=`expr "z$revpair" : 'z.*\.\.\(.*\)'` - ;; - *) - rev1="$revpair^" - rev2="$revpair" - ;; - esac - git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 || - die "Not a valid rev $rev1 ($revpair)" - git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 || - die "Not a valid rev $rev2 ($revpair)" - git-cherry -v "$rev1" "$rev2" | - while read sign rev comment - do - case "$sign" in - '-') - echo >&2 "Merged already: $comment" - ;; - *) - echo $rev - ;; - esac - done -done >$series - -me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'` -headers=`git-repo-config --get format.headers` -case "$attach" in -"") ;; -*) - mimemagic="050802040500080604070107" -esac - -case "$outdir" in -*/) ;; -*) outdir="$outdir/" ;; -esac -test -d "$outdir" || mkdir -p "$outdir" || exit - -titleScript=' - /./d - /^$/n - s/^\[PATCH[^]]*\] *// - s/[^-a-z.A-Z_0-9]/-/g - s/\.\.\.*/\./g - s/\.*$// - s/--*/-/g - s/^-// - s/-$// - s/$/./ - p - q -' - -process_one () { - perl -w -e ' -my ($keep_subject, $num, $signoff, $headers, $mimemagic, $commsg) = @ARGV; -my ($signoff_pattern, $done_header, $done_subject, $done_separator, $signoff_seen, - $last_was_signoff); - -if ($signoff) { - $signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`; - $signoff =~ s/>.*/>/; - $signoff_pattern = quotemeta($signoff); -} - -my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat); -my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); - -sub show_date { - my ($time, $tz) = @_; - my $minutes = abs($tz); - $minutes = int($minutes / 100) * 60 + ($minutes % 100); - if ($tz < 0) { - $minutes = -$minutes; - } - my $t = $time + $minutes * 60; - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t); - return sprintf("%s, %d %s %d %02d:%02d:%02d %+05d", - $weekday_names[$wday], $mday, - $month_names[$mon], $year+1900, - $hour, $min, $sec, $tz); -} - -print "From nobody Mon Sep 17 00:00:00 2001\n"; -open FH, "git stripspace <$commsg |" or die "open $commsg pipe"; -while () { - unless ($done_header) { - if (/^$/) { - $done_header = 1; - } - elsif (/^author (.*>) (.*)$/) { - my ($author_ident, $author_date) = ($1, $2); - my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/); - $author_date = show_date($utc, $off); - - print "From: $author_ident\n"; - print "Date: $author_date\n"; - } - next; - } - unless ($done_subject) { - unless ($keep_subject) { - s/^\[PATCH[^]]*\]\s*//; - s/^/[PATCH$num] /; - } - if ($headers) { - print "$headers\n"; - } - print "Subject: $_"; - if ($mimemagic) { - print "MIME-Version: 1.0\n"; - print "Content-Type: multipart/mixed;\n"; - print " boundary=\"------------$mimemagic\"\n"; - print "\n"; - print "This is a multi-part message in MIME format.\n"; - print "--------------$mimemagic\n"; - print "Content-Type: text/plain; charset=UTF-8; format=fixed\n"; - print "Content-Transfer-Encoding: 8bit\n"; - } - $done_subject = 1; - next; - } - unless ($done_separator) { - print "\n"; - $done_separator = 1; - next if (/^$/); - } - - $last_was_signoff = 0; - if (/Signed-off-by:/i) { - if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) { - $signoff_seen = 1; - } - } - print $_; -} -if (!$signoff_seen && $signoff ne "") { - if (!$last_was_signoff) { - print "\n"; - } - print "$signoff\n"; -} -print "\n---\n\n"; -close FH or die "close $commsg pipe"; -' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg - - git-diff-tree -p --stat --summary $diff_opts "$commit" - echo - case "$mimemagic" in - '');; - *) - echo "--------------$mimemagic" - echo "Content-Type: text/x-patch;" - echo " name=\"$commit.diff\"" - echo "Content-Transfer-Encoding: 8bit" - echo "Content-Disposition: inline;" - echo " filename=\"$commit.diff\"" - echo - esac - git-diff-tree -p $diff_opts "$commit" - case "$mimemagic" in - '') - echo "-- " - echo "@@GIT_VERSION@@" - ;; - *) - echo - echo "--------------$mimemagic--" - echo - ;; - esac - echo -} - -total=`wc -l <$series | tr -dc "[0-9]"` -case "$total,$numbered" in -1,*) - numfmt='' ;; -*,t) - numfmt=`echo "$total" | wc -c` - numfmt=$(($numfmt-1)) - numfmt=" %0${numfmt}d/$total" -esac - -i=1 -while read commit -do - git-cat-file commit "$commit" | git-stripspace >$commsg - title=`sed -ne "$titleScript" <$commsg` - case "$numbered" in - '') num= ;; - *) - num=`printf "$numfmt" $i` ;; - esac - - file=`printf '%04d-%stxt' $i "$title"` - if test '' = "$stdout" - then - echo "$file" - process_one >"$outdir$file" - if test t = "$check" - then - # This is slightly modified from Andrew Morton's Perfect Patch. - # Lines you introduce should not have trailing whitespace. - # Also check for an indentation that has SP before a TAB. - grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file" - : - fi - else - echo >&2 "$file" - process_one - fi - i=`expr "$i" + 1` -done <$series diff --git a/git.c b/git.c index f4dff02bd3..ff498e674a 100644 --- a/git.c +++ b/git.c @@ -47,7 +47,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "whatchanged", cmd_whatchanged }, { "show", cmd_show }, { "push", cmd_push }, - { "fmt-patch", cmd_format_patch }, + { "format-patch", cmd_format_patch }, { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, { "grep", cmd_grep }, From 9e848163eda686093f689c25cfa9937ed2a9fdf8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 03:01:59 -0700 Subject: [PATCH 46/90] checkdiff_consume: strtol parameter fix. The second parameter is not the end of string input; it is the optional return value to retrieve where the parser stopped. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff.c b/diff.c index af5db0ec12..d3bb10e009 100644 --- a/diff.c +++ b/diff.c @@ -431,7 +431,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) else if (line[0] == '@') { char *plus = strchr(line, '+'); if (plus) - data->lineno = strtol(plus, line + len, 10); + data->lineno = strtol(plus, NULL, 10); else die("invalid diff"); } From efbff23609654ac0542d3421d19eea8dbc1f80cd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 03:16:38 -0700 Subject: [PATCH 47/90] git-rebase: use canonical A..B syntax to format-patch Signed-off-by: Junio C Hamano --- git-rebase.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-rebase.sh b/git-rebase.sh index 6ff6088d18..e6b57b8ab9 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -152,6 +152,6 @@ then exit 0 fi -git-format-patch -k --stdout --full-index "$upstream" ORIG_HEAD | +git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD | git am --binary -3 -k --resolvemsg="$RESOLVEMSG" From fbd01abf50488d8ed94c36bdd5bfc2a4ddafb8a2 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 21 May 2006 22:45:46 +0200 Subject: [PATCH 48/90] remove superflous "const" Signed-off-by: Junio C Hamano --- builtin-grep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-grep.c b/builtin-grep.c index d09ddf0485..53de8a8836 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -518,7 +518,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) argc = nr; for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; - const char *name; + char *name; if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode))) continue; if (!pathspec_matches(paths, ce->name)) From 6ba68ab2884e6c1db942e3bff63d4aa0bf354094 Mon Sep 17 00:00:00 2001 From: Yakov Lerner Date: Mon, 22 May 2006 00:37:00 +0300 Subject: [PATCH 49/90] NO_INET_NTOP and compat/inet_ntop.c for some systems (e.g. old Cygwin). For systems which lack inet_ntop(), this adds compat/inet_ntop.c, and related build constant, NO_INET_NTOP. Older Cygwin(s) lack inet_ntop(). Signed-off-by: Yakov Lerner Signed-off-by: Junio C Hamano --- Makefile | 3 + compat/inet_ntop.c | 200 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 compat/inet_ntop.c diff --git a/Makefile b/Makefile index 4fd6520b7e..efe6b12719 100644 --- a/Makefile +++ b/Makefile @@ -420,6 +420,9 @@ else ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6 endif endif +ifdef NO_INET_NTOP + LIB_OBJS += compat/inet_ntop.o +endif ifdef NO_ICONV ALL_CFLAGS += -DNO_ICONV diff --git a/compat/inet_ntop.c b/compat/inet_ntop.c new file mode 100644 index 0000000000..ec8c1bff53 --- /dev/null +++ b/compat/inet_ntop.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NS_INADDRSZ +#define NS_INADDRSZ 4 +#endif +#ifndef NS_IN6ADDRSZ +#define NS_IN6ADDRSZ 16 +#endif +#ifndef NS_INT16SZ +#define NS_INT16SZ 2 +#endif + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(src, dst, size) + const u_char *src; + char *dst; + size_t size; +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int nprinted; + + nprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); + if (nprinted < 0) + return (NULL); /* we assume "errno" was set by "snprintf()" */ + if ((size_t)nprinted > size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} + +#ifndef NO_IPV6 +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(src, dst, size) + const u_char *src; + char *dst; + size_t size; +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += snprintf(tp, sizeof tmp - (tp - tmp), "%x", words[i]); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} +#endif + +/* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ +const char * +inet_ntop(af, src, dst, size) + int af; + const void *src; + char *dst; + size_t size; +{ + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); +#ifndef NO_IPV6 + case AF_INET6: + return (inet_ntop6(src, dst, size)); +#endif + default: + errno = EAFNOSUPPORT; + return (NULL); + } + /* NOTREACHED */ +} From 67e6e5c4e79895b34d0d47ef71e1d882f06fb6f7 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 21 May 2006 16:52:34 -0400 Subject: [PATCH 50/90] tutorial: replace "whatchanged" by "log" Junio suggested changing references to git-whatchanged to git-log. Signed-off-by: J. Bruce Fields Signed-off-by: Junio C Hamano --- Documentation/tutorial.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index fa79b016c7..cd0f0df591 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -80,13 +80,13 @@ file; just remove it, then commit. At any point you can view the history of your changes using ------------------------------------------------ -$ git whatchanged +$ git log ------------------------------------------------ If you also want to see complete diffs at each step, use ------------------------------------------------ -$ git whatchanged -p +$ git log -p ------------------------------------------------ Managing branches @@ -216,7 +216,7 @@ This actually pulls changes from the branch in Bob's repository named "master". Alice could request a different branch by adding the name of the branch to the end of the git pull command line. -This merges Bob's changes into her repository; "git whatchanged" will +This merges Bob's changes into her repository; "git log" will now show the new commits. If Alice has made her own changes in the meantime, then Bob's changes will be merged in, and she will need to manually fix any conflicts. @@ -234,7 +234,7 @@ named bob-incoming. (Unlike git pull, git fetch just fetches a copy of Bob's line of development without doing any merging). Then ------------------------------------- -$ git whatchanged -p master..bob-incoming +$ git log -p master..bob-incoming ------------------------------------- shows a list of all the changes that Bob made since he branched from @@ -330,13 +330,13 @@ But you may find it more useful to see the list of commits made in the experimental branch but not in the current branch, and ------------------------------------- -git whatchanged HEAD..experimental +git log HEAD..experimental ------------------------------------- will do that, just as ------------------------------------- -git whatchanged experimental..HEAD +git log experimental..HEAD ------------------------------------- will show the list of commits made on the HEAD but not included in From f1fe3846e4236a05879d7f84b11ae8ea64dd8d0d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 21 May 2006 16:54:05 -0400 Subject: [PATCH 51/90] tutorial: expanded discussion of commit history Expand the history-browsing section of the tutorial a bit, in part to address Junio's suggestion that we mention "git grep" and Linus's complaint that people are missing the flexibility of the commandline interfaces for selecting commits. This reads a little more like a collection of examples than a "tutorial", but maybe that's what people need at this point. Signed-off-by: J. Bruce Fields Signed-off-by: Junio C Hamano --- Documentation/tutorial.txt | 175 +++++++++++++++++++++++++------------ 1 file changed, 117 insertions(+), 58 deletions(-) diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index cd0f0df591..4c298c6e71 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -288,103 +288,162 @@ Git can also be used in a CVS-like mode, with a central repository that various users push changes to; see gitlink:git-push[1] and link:cvs-migration.html[git for CVS users]. -Keeping track of history ------------------------- +Exploring history +----------------- -Git history is represented as a series of interrelated commits. The -most recent commit in the currently checked-out branch can always be -referred to as HEAD, and the "parent" of any commit can always be -referred to by appending a caret, "^", to the end of the name of the -commit. So, for example, +Git history is represented as a series of interrelated commits. We +have already seen that the git log command can list those commits. +Note that first line of each git log entry also gives a name for the +commit: ------------------------------------- -git diff HEAD^ HEAD +$ git log +commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7 +Author: Junio C Hamano +Date: Tue May 16 17:18:22 2006 -0700 + + merge-base: Clarify the comments on post processing. ------------------------------------- -shows the difference between the most-recently checked-in state of -the tree and the previous state, and +We can give this name to git show to see the details about this +commit. ------------------------------------- -git diff HEAD^^ HEAD^ +$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7 ------------------------------------- -shows the difference between that previous state and the state two -commits ago. Also, HEAD~5 can be used as a shorthand for HEAD{caret}{caret}{caret}{caret}{caret}, -and more generally HEAD~n can refer to the nth previous commit. -Commits representing merges have more than one parent, and you can -specify which parent to follow in that case; see -gitlink:git-rev-parse[1]. - -The name of a branch can also be used to refer to the most recent -commit on that branch; so you can also say things like +But there other ways to refer to commits. You can use any initial +part of the name that is long enough to uniquely identify the commit: ------------------------------------- -git diff HEAD experimental +$ git show c82a22c39c # the first few characters of the name are + # usually enough +$ git show HEAD # the tip of the current branch +$ git show experimental # the tip of the "experimental" branch ------------------------------------- -to see the difference between the most-recently committed tree in -the current branch and the most-recently committed tree in the -experimental branch. - -But you may find it more useful to see the list of commits made in -the experimental branch but not in the current branch, and +Every commit has at least one "parent" commit, which points to the +previous state of the project: ------------------------------------- -git log HEAD..experimental +$ git show HEAD^ # to see the parent of HEAD +$ git show HEAD^^ # to see the grandparent of HEAD +$ git show HEAD~4 # to see the great-great grandparent of HEAD ------------------------------------- -will do that, just as +Note that merge commits may have more than one parent: ------------------------------------- -git log experimental..HEAD +$ git show HEAD^1 # show the first parent of HEAD (same as HEAD^) +$ git show HEAD^2 # show the second parent of HEAD ------------------------------------- -will show the list of commits made on the HEAD but not included in -experimental. - -You can also give commits convenient names of your own: after running +You can also give commits names of your own; after running ------------------------------------- -$ git-tag v2.5 HEAD^^ +$ git-tag v2.5 1b2e1d63ff ------------------------------------- -you can refer to HEAD^^ by the name "v2.5". If you intend to share -this name with other people (for example, to identify a release +you can refer to 1b2e1d63ff by the name "v2.5". If you intend to +share this name with other people (for example, to identify a release version), you should create a "tag" object, and perhaps sign it; see gitlink:git-tag[1] for details. -You can revisit the old state of a tree, and make further -modifications if you wish, using git branch: the command +Any git command that needs to know a commit can take any of these +names. For example: ------------------------------------- -$ git branch stable-release v2.5 +$ git diff v2.5 HEAD # compare the current HEAD to v2.5 +$ git branch stable v2.5 # start a new branch named "stable" based + # at v2.5 +$ git reset --hard HEAD^ # reset your current branch and working + # directory its state at HEAD^ ------------------------------------- -will create a new branch named "stable-release" starting from the -commit which you tagged with the name v2.5. - -You can reset the state of any branch to an earlier commit at any -time with - -------------------------------------- -$ git reset --hard v2.5 -------------------------------------- - -This will remove all later commits from this branch and reset the -working tree to the state it had when the given commit was made. If -this branch is the only branch containing the later commits, those -later changes will be lost. Don't use "git reset" on a +Be careful with that last command: in addition to losing any changes +in the working directory, it will also remove all later commits from +this branch. If this branch is the only branch containing those +commits, they will be lost. (Also, don't use "git reset" on a publicly-visible branch that other developers pull from, as git will -be confused by history that disappears in this way. +be confused by history that disappears in this way.) + +The git grep command can search for strings in any version of your +project, so + +------------------------------------- +$ git grep "hello" v2.5 +------------------------------------- + +searches for all occurences of "hello" in v2.5. + +If you leave out the commit name, git grep will search any of the +files it manages in your current directory. So + +------------------------------------- +$ git grep "hello" +------------------------------------- + +is a quick way to search just the files that are tracked by git. + +Many git commands also take sets of commits, which can be specified +in a number of ways. Here are some examples with git log: + +------------------------------------- +$ git log v2.5..v2.6 # commits between v2.5 and v2.6 +$ git log v2.5.. # commits since v2.5 +$ git log --since="2 weeks ago" # commits from the last 2 weeks +$ git log v2.5.. Makefile # commits since v2.5 which modify + # Makefile +------------------------------------- + +You can also give git log a "range" of commits where the first is not +necessarily an ancestor of the second; for example, if the tips of +the branches "stable-release" and "master" diverged from a common +commit some time ago, then + +------------------------------------- +$ git log stable..experimental +------------------------------------- + +will list commits made in the experimental branch but not in the +stable branch, while + +------------------------------------- +$ git log experimental..stable +------------------------------------- + +will show the list of commits made on the stable branch but not +the experimental branch. + +The "git log" command has a weakness: it must present commits in a +list. When the history has lines of development that diverged and +then merged back together, the order in which "git log" presents +those commits is meaningless. + +Most projects with multiple contributors (such as the linux kernel, +or git itself) have frequent merges, and gitk does a better job of +visualizing their history. For example, + +------------------------------------- +$ gitk --since="2 weeks ago" drivers/ +------------------------------------- + +allows you to browse any commits from the last 2 weeks of commits +that modified files under the "drivers" directory. + +Finally, most commands that take filenames will optionally allow you +to precede any filename by a commit, to specify a particular version +fo the file: + +------------------------------------- +$ git diff v2.5:Makefile HEAD:Makefile.in +------------------------------------- Next Steps ---------- Some good commands to explore next: - * gitlink:git-diff[1]: This flexible command does much more than - we've seen in the few examples above. - * gitlink:git-format-patch[1], gitlink:git-am[1]: These convert series of git commits into emailed patches, and vice versa, useful for projects such as the linux kernel which rely heavily From e31952da5c52a4c1e028de634204c6bc2504771f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 21 May 2006 19:49:34 -0400 Subject: [PATCH 52/90] tutorial: add discussion of index file, object database Add a sequel to tutorial.txt which discusses the index file and the object database. Signed-off-by: J. Bruce Fields Signed-off-by: Junio C Hamano --- Documentation/Makefile | 1 + Documentation/tutorial-2.txt | 391 +++++++++++++++++++++++++++++++++++ Documentation/tutorial.txt | 28 ++- 3 files changed, 414 insertions(+), 6 deletions(-) create mode 100644 Documentation/tutorial-2.txt diff --git a/Documentation/Makefile b/Documentation/Makefile index c1af22ce04..2a08f592d9 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -7,6 +7,7 @@ MAN7_TXT=git.txt DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN7_TXT)) ARTICLES = tutorial +ARTICLES += tutorial-2 ARTICLES += core-tutorial ARTICLES += cvs-migration ARTICLES += diffcore diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt new file mode 100644 index 0000000000..a3d45ee569 --- /dev/null +++ b/Documentation/tutorial-2.txt @@ -0,0 +1,391 @@ +A tutorial introduction to git: part two +======================================== + +You should work through link:tutorial.html[A tutorial introduction to +git] before reading this tutorial. + +The goal of this tutorial is to introduce two fundamental pieces of +git's architecture--the object database and the index file--and to +provide the reader with everything necessary to understand the rest +of the git documentation. + +The git object database +----------------------- + +Let's start a new project and create a small amount of history: + +------------------------------------------------ +$ mkdir test-project +$ cd test-project +$ git init-db +defaulting to local storage area +$ echo 'hello world' > file.txt +$ git add . +$ git commit -a -m "initial commit" +Committing initial tree 92b8b694ffb1675e5975148e1121810081dbdffe +$ echo 'hello world!' >file.txt +$ git commit -a -m "add emphasis" +------------------------------------------------ + +What are the 40 digits of hex that git responded to the first commit +with? + +We saw in part one of the tutorial that commits have names like this. +It turns out that every object in the git history is stored under +such a 40-digit hex name. That name is the SHA1 hash of the object's +contents; among other things, this ensures that git will never store +the same data twice (since identical data is given an identical SHA1 +name), and that the contents of a git object will never change (since +that would change the object's name as well). + +We can ask git about this particular object with the cat-file +command--just cut-and-paste from the reply to the initial commit, to +save yourself typing all 40 hex digits: + +------------------------------------------------ +$ git cat-file -t 92b8b694ffb1675e5975148e1121810081dbdffe +tree +------------------------------------------------ + +A tree can refer to one or more "blob" objects, each corresponding to +a file. In addition, a tree can also refer to other tree objects, +thus creating a directory heirarchy. You can examine the contents of +any tree using ls-tree (remember that a long enough initial portion +of the SHA1 will also work): + +------------------------------------------------ +$ git ls-tree 92b8b694 +100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad file.txt +------------------------------------------------ + +Thus we see that this tree has one file in it. The SHA1 hash is a +reference to that file's data: + +------------------------------------------------ +$ git cat-file -t 3b18e512 +blob +------------------------------------------------ + +A "blob" is just file data, which we can also examine with cat-file: + +------------------------------------------------ +$ git cat-file blob 3b18e512 +hello world +------------------------------------------------ + +Note that this is the old file data; so the object that git named in +its response to the initial tree was a tree with a snapshot of the +directory state that was recorded by the first commit. + +All of these objects are stored under their SHA1 names inside the git +directory: + +------------------------------------------------ +$ find .git/objects/ +.git/objects/ +.git/objects/pack +.git/objects/info +.git/objects/3b +.git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad +.git/objects/92 +.git/objects/92/b8b694ffb1675e5975148e1121810081dbdffe +.git/objects/54 +.git/objects/54/196cc2703dc165cbd373a65a4dcf22d50ae7f7 +.git/objects/a0 +.git/objects/a0/423896973644771497bdc03eb99d5281615b51 +.git/objects/d0 +.git/objects/d0/492b368b66bdabf2ac1fd8c92b39d3db916e59 +.git/objects/c4 +.git/objects/c4/d59f390b9cfd4318117afde11d601c1085f241 +------------------------------------------------ + +and the contents of these files is just the compressed data plus a +header identifying their length and their type. The type is either a +blob, a tree, a commit, or a tag. We've seen a blob and a tree now, +so next we should look at a commit. + +The simplest commit to find is the HEAD commit, which we can find +from .git/HEAD: + +------------------------------------------------ +$ cat .git/HEAD +ref: refs/heads/master +------------------------------------------------ + +As you can see, this tells us which branch we're currently on, and it +tells us this by naming a file under the .git directory, which itself +contains a SHA1 name referring to a commit object, which we can +examine with cat-file: + +------------------------------------------------ +$ cat .git/refs/heads/master +c4d59f390b9cfd4318117afde11d601c1085f241 +$ git cat-file -t c4d59f39 +commit +$ git cat-file commit c4d59f39 +tree d0492b368b66bdabf2ac1fd8c92b39d3db916e59 +parent 54196cc2703dc165cbd373a65a4dcf22d50ae7f7 +author J. Bruce Fields 1143418702 -0500 +committer J. Bruce Fields 1143418702 -0500 + +add emphasis +------------------------------------------------ + +The "tree" object here refers to the new state of the tree: + +------------------------------------------------ +$ git ls-tree d0492b36 +100644 blob a0423896973644771497bdc03eb99d5281615b51 file.txt +$ git cat-file commit a0423896 +hello world! +------------------------------------------------ + +and the "parent" object refers to the previous commit: + +------------------------------------------------ +$ git-cat-file commit 54196cc2 +tree 92b8b694ffb1675e5975148e1121810081dbdffe +author J. Bruce Fields 1143414668 -0500 +committer J. Bruce Fields 1143414668 -0500 + +initial commit +------------------------------------------------ + +The tree object is the tree we examined first, and this commit is +unusual in that it lacks any parent. + +Most commits have only one parent, but it is also common for a commit +to have multiple parents. In that case the commit represents a +merge, with the parent references pointing to the heads of the merged +branches. + +Besides blobs, trees, and commits, the only remaining type of object +is a "tag", which we won't discuss here; refer to gitlink:git-tag[1] +for details. + +So now we know how git uses the object database to represent a +project's history: + + * "commit" objects refer to "tree" objects representing the + snapshot of a directory tree at a particular point in the + history, and refer to "parent" commits to show how they're + connected into the project history. + * "tree" objects represent the state of a single directory, + associating directory names to "blob" objects containing file + data and "tree" objects containing subdirectory information. + * "blob" objects contain file data without any other structure. + * References to commit objects at the head of each branch are + stored in files under .git/refs/heads/. + * The name of the current branch is stored in .git/HEAD. + +Note, by the way, that lots of commands take a tree as an argument. +But as we can see above, a tree can be referred to in many different +ways--by the SHA1 name for that tree, by the name of a commit that +refers to the tree, by the name of a branch whose head refers to that +tree, etc.--and most such commands can accept any of these names. + +In command synopses, the word "tree-ish" is sometimes used to +designate such an argument. + +The index file +-------------- + +The primary tool we've been using to create commits is "git commit +-a", which creates a commit including every change you've made to +your working tree. But what if you want to commit changes only to +certain files? Or only certain changes to certain files? + +If we look at the way commits are created under the cover, we'll see +that there are more flexible ways creating commits. + +Continuing with our test-project, let's modify file.txt again: + +------------------------------------------------ +$ echo "hello world, again" >>file.txt +------------------------------------------------ + +but this time instead of immediately making the commit, let's take an +intermediate step, and ask for diffs along the way to keep track of +what's happening: + +------------------------------------------------ +$ git diff +--- a/file.txt ++++ b/file.txt +@@ -1 +1,2 @@ + hello world! + +hello world, again +$ git update-index file.txt +$ git diff +------------------------------------------------ + +The last diff is empty, but no new commits have been made, and the +head still doesn't contain the new line: + +------------------------------------------------ +$ git-diff HEAD +diff --git a/file.txt b/file.txt +index a042389..513feba 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1,2 @@ + hello world! + +hello world, again +------------------------------------------------ + +So "git diff" is comparing against something other than the head. +The thing that it's comparing against is actually the index file, +which is stored in .git/index in a binary format, but whose contents +we can examine with ls-files: + +------------------------------------------------ +$ git ls-files --stage +100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt +$ git cat-file -t 513feba2 +blob +$ git cat-file blob 513feba2 +hello world, again +------------------------------------------------ + +So what our "git update-index" did was store a new blob and then put +a reference to it in the index file. If we modify the file again, +we'll see that the new modifications are reflected in the "git-diff" +output: + +------------------------------------------------ +$ echo 'again?' >>file.txt +$ git diff +index 513feba..ba3da7b 100644 +--- a/file.txt ++++ b/file.txt +@@ -1,2 +1,3 @@ + hello world! + hello world, again ++again? +------------------------------------------------ + +With the right arguments, git diff can also show us the difference +between the working directory and the last commit, or between the +index and the last commit: + +------------------------------------------------ +$ git diff HEAD +diff --git a/file.txt b/file.txt +index a042389..ba3da7b 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1,3 @@ + hello world! ++hello world, again ++again? +$ git diff --cached +diff --git a/file.txt b/file.txt +index a042389..513feba 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1,2 @@ + hello world! ++hello world, again +------------------------------------------------ + +At any time, we can create a new commit using "git commit" (without +the -a option), and verify that the state committed only includes the +changes stored in the index file, not the additional change that is +still only in our working tree: + +------------------------------------------------ +$ git commit -m "repeat" +$ git diff HEAD +diff --git a/file.txt b/file.txt +index 513feba..ba3da7b 100644 +--- a/file.txt ++++ b/file.txt +@@ -1,2 +1,3 @@ + hello world! + hello world, again ++again? +------------------------------------------------ + +So by default "git commit" uses the index to create the commit, not +the working tree; the -a option to commit tells it to first update +the index with all changes in the working tree. + +Finally, it's worth looking at the effect of "git add" on the index +file: + +------------------------------------------------ +$ echo "goodbye, world" >closing.txt +$ git add closing.txt +------------------------------------------------ + +The effect of the "git add" was to add one entry to the index file: + +------------------------------------------------ +$ git ls-files --stage +100644 8b9743b20d4b15be3955fc8d5cd2b09cd2336138 0 closing.txt +100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt +------------------------------------------------ + +And, as you can see with cat-file, this new entry refers to the +current contents of the file: + +------------------------------------------------ +$ git cat-file blob a6b11f7a +goodbye, word +------------------------------------------------ + +The "status" command is a useful way to get a quick summary of the +situation: + +------------------------------------------------ +$ git status +# +# Updated but not checked in: +# (will commit) +# +# new file: closing.txt +# +# +# Changed but not updated: +# (use git-update-index to mark for commit) +# +# modified: file.txt +# +------------------------------------------------ + +Since the current state of closing.txt is cached in the index file, +it is listed as "updated but not checked in". Since file.txt has +changes in the working directory that aren't reflected in the index, +it is marked "changed but not updated". At this point, running "git +commit" would create a commit that added closing.txt (with its new +contents), but that didn't modify file.txt. + +Also, note that a bare "git diff" shows the changes to file.txt, but +not the addition of closing.txt, because the version of closing.txt +in the index file is identical to the one in the working directory. + +In addition to being the staging area for new commits, the index file +is also populated from the object database when checking out a +branch, and is used to hold the trees involved in a merge operation. +See the link:core-tutorial.txt[core tutorial] and the relevant man +pages for details. + +What next? +---------- + +At this point you should know everything necessary to read the man +pages for any of the git commands; one good place to start would be +with the commands mentioned in link:everday.html[Everyday git]. You +should be able to find any unknown jargon in the +link:glossary.html[Glosssay]. + +The link:cvs-migration.html[CVS migration] document explains how to +import a CVS repository into git, and shows how to use git in a +CVS-like way. + +For some interesting examples of git use, see the +link:howto-index.html[howtos]. + +For git developers, the link:core-tutorial.html[Core tutorial] goes +into detail on the lower-level git mechanisms involved in, for +example, creating a new commit. diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index 4c298c6e71..79781adf4f 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -442,7 +442,25 @@ $ git diff v2.5:Makefile HEAD:Makefile.in Next Steps ---------- -Some good commands to explore next: +This tutorial should be enough to perform basic distributed revision +control for your projects. However, to fully understand the depth +and power of git you need to understand two simple ideas on which it +is based: + + * The object database is the rather elegant system used to + store the history of your project--files, directories, and + commits. + + * The index file is a cache of the state of a directory tree, + used to create commits, check out working directories, and + hold the various trees involved in a merge. + +link:tutorial-2.html[Part two of this tutorial] explains the object +database, the index file, and a few other odds and ends that you'll +need to make the most of git. + +If you don't want to consider with that right away, a few other +digressions that may be interesting at this point are: * gitlink:git-format-patch[1], gitlink:git-am[1]: These convert series of git commits into emailed patches, and vice versa, @@ -456,8 +474,6 @@ Some good commands to explore next: smart enough to perform a close-to-optimal search even in the case of complex non-linear history with lots of merged branches. -Other good starting points include link:everyday.html[Everday GIT -with 20 Commands Or So] and link:cvs-migration.html[git for CVS -users]. Also, link:core-tutorial.html[A short git tutorial] gives an -introduction to lower-level git commands for advanced users and -developers. + * link:everyday.html[Everday GIT with 20 Commands Or So] + + * link:cvs-migration.html[git for CVS users]. From d5e3d60c200b214e36eacfb3de21044a7efd1971 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 17:37:25 -0700 Subject: [PATCH 53/90] tutorial-2: typofix in examples. Signed-off-by: Junio C Hamano --- Documentation/tutorial-2.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt index a3d45ee569..7de91aa735 100644 --- a/Documentation/tutorial-2.txt +++ b/Documentation/tutorial-2.txt @@ -214,7 +214,7 @@ $ git diff +++ b/file.txt @@ -1 +1,2 @@ hello world! - +hello world, again ++hello world, again $ git update-index file.txt $ git diff ------------------------------------------------ @@ -230,7 +230,7 @@ index a042389..513feba 100644 +++ b/file.txt @@ -1 +1,2 @@ hello world! - +hello world, again ++hello world, again ------------------------------------------------ So "git diff" is comparing against something other than the head. From cdd406e389bd6e0b2ad9394340a366c0c5ae27fc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 16 May 2006 02:29:42 -0700 Subject: [PATCH 54/90] CMIT_FMT_EMAIL: Q-encode Subject: and display-name part of From: fields. By convention, the commit message and the author/committer names in the commit objects are UTF-8 encoded. When formatting for e-mails, Q-encode them according to RFC 2047. While we are at it, generate the content-type and content-transfer-encoding headers as well. Signed-off-by: Junio C Hamano --- commit.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/commit.c b/commit.c index 0b163d4857..7597fba70a 100644 --- a/commit.c +++ b/commit.c @@ -422,6 +422,46 @@ static int get_one_line(const char *msg, unsigned long len) return ret; } +static int is_rfc2047_special(char ch) +{ + return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_')); +} + +static int add_rfc2047(char *buf, const char *line, int len) +{ + char *bp = buf; + int i, needquote; + static const char q_utf8[] = "=?utf-8?q?"; + + for (i = needquote = 0; !needquote && i < len; i++) { + unsigned ch = line[i]; + if (ch & 0x80) + needquote++; + if ((i + 1 < len) && + (ch == '=' && line[i+1] == '?')) + needquote++; + } + if (!needquote) + return sprintf(buf, "%.*s", len, line); + + memcpy(bp, q_utf8, sizeof(q_utf8)-1); + bp += sizeof(q_utf8)-1; + for (i = 0; i < len; i++) { + unsigned ch = line[i]; + if (is_rfc2047_special(ch)) { + sprintf(bp, "=%02X", ch); + bp += 3; + } + else if (ch == ' ') + *bp++ = '_'; + else + *bp++ = ch; + } + memcpy(bp, "?=", 2); + bp += 2; + return bp - buf; +} + static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line) { char *date; @@ -440,12 +480,26 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c tz = strtol(date, NULL, 10); if (fmt == CMIT_FMT_EMAIL) { - what = "From"; + char *name_tail = strchr(line, '<'); + int display_name_length; + if (!name_tail) + return 0; + while (line < name_tail && isspace(name_tail[-1])) + name_tail--; + display_name_length = name_tail - line; filler = ""; + strcpy(buf, "From: "); + ret = strlen(buf); + ret += add_rfc2047(buf + ret, line, display_name_length); + memcpy(buf + ret, name_tail, namelen - display_name_length); + ret += namelen - display_name_length; + buf[ret++] = '\n'; + } + else { + ret = sprintf(buf, "%s: %.*s%.*s\n", what, + (fmt == CMIT_FMT_FULLER) ? 4 : 0, + filler, namelen, line); } - ret = sprintf(buf, "%s: %.*s%.*s\n", what, - (fmt == CMIT_FMT_FULLER) ? 4 : 0, - filler, namelen, line); switch (fmt) { case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); @@ -584,13 +638,24 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit int slen = strlen(subject); memcpy(buf + offset, subject, slen); offset += slen; + offset += add_rfc2047(buf + offset, line, linelen); + } + else { + memset(buf + offset, ' ', indent); + memcpy(buf + offset + indent, line, linelen); + offset += linelen + indent; } - memset(buf + offset, ' ', indent); - memcpy(buf + offset + indent, line, linelen); - offset += linelen + indent; buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + if (subject) { + static const char header[] = + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n"; + memcpy(buf + offset, header, sizeof(header)-1); + offset += sizeof(header)-1; + subject = NULL; + } if (after_subject) { int slen = strlen(after_subject); if (slen > space - offset - 1) From c831da664799eb4dbf082a843c34c66889548768 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 23:55:00 -0700 Subject: [PATCH 55/90] builtin format-patch: squelch content-type for 7-bit ASCII When --attach is not used, usually we do not say Content-Type: and fluff, but if the commit message is not 7-bit ASCII, mark it as "text/plain; charset=UTF-8". This unclutters output somewhat. Signed-off-by: Junio C Hamano --- commit.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/commit.c b/commit.c index 7597fba70a..94f470b75c 100644 --- a/commit.c +++ b/commit.c @@ -559,10 +559,24 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; + int plain_non_ascii = 0; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; + /* After-subject is used to pass in Content-Type: multipart + * MIME header; in that case we do not have to do the + * plaintext content type even if the commit message has + * non 7-bit ASCII character. Otherwise, check if we need + * to say this is not a 7-bit ASCII. + */ + if (fmt == CMIT_FMT_EMAIL && !after_subject) { + int i; + for (i = 0; !plain_non_ascii && msg[i] && i < len; i++) + if (msg[i] & 0x80) + plain_non_ascii = 1; + } + for (;;) { const char *line = msg; int linelen = get_one_line(msg, len); @@ -648,13 +662,12 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; - if (subject) { + if (subject && plain_non_ascii) { static const char header[] = "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n"; memcpy(buf + offset, header, sizeof(header)-1); offset += sizeof(header)-1; - subject = NULL; } if (after_subject) { int slen = strlen(after_subject); From 5e363541d0431b640e4bbbafb2941d1c64fa13c4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 22 May 2006 00:31:02 -0700 Subject: [PATCH 56/90] diff: minor option combination fix. output_format == DIFFSTAT and with_stat == true does not make sense, and the way the code is structured it causes trouble. Avoid it. Signed-off-by: Junio C Hamano --- diff.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/diff.c b/diff.c index d3bb10e009..1601783679 100644 --- a/diff.c +++ b/diff.c @@ -1303,6 +1303,14 @@ int diff_setup_done(struct diff_options *options) (options->output_format == DIFF_FORMAT_CHECKDIFF)) options->recursive = 1; + /* + * These combinations do not make sense. + */ + if (options->output_format == DIFF_FORMAT_RAW) + options->with_raw = 0; + if (options->output_format == DIFF_FORMAT_DIFFSTAT) + options->with_stat = 0; + if (options->detect_rename && options->rename_limit < 0) options->rename_limit = diff_rename_limit_default; if (options->setup & DIFF_SETUP_USE_CACHE) { From 9546010b7bbc25e037029a7dd83de9c57b7a935c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 11 May 2006 15:28:44 -0700 Subject: [PATCH 57/90] fetch-pack: output refs in the order they were given on the command line. Currently, fetched refs are output in the order the remote side happened to send them. This changes the order to match the order of refs that were given on the command line. To the existing core callers (git-fetch and git-clone) this does not make any difference, but for other Porcelain use, it would be more intuitive. Signed-off-by: Junio C Hamano --- connect.c | 2 +- fetch-pack.c | 64 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/connect.c b/connect.c index 6a8f8a6a24..54f7bf7915 100644 --- a/connect.c +++ b/connect.c @@ -100,7 +100,7 @@ int path_match(const char *path, int nr, char **match) if (pathlen > len && path[pathlen - len - 1] != '/') continue; *s = 0; - return 1; + return (i + 1); } return 0; } diff --git a/fetch-pack.c b/fetch-pack.c index a3bcad016f..8daa93d024 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -262,22 +262,58 @@ static void mark_recent_complete_commits(unsigned long cutoff) static void filter_refs(struct ref **refs, int nr_match, char **match) { - struct ref *prev, *current, *next; + struct ref **return_refs; + struct ref *newlist = NULL; + struct ref **newtail = &newlist; + struct ref *ref, *next; + struct ref *fastarray[32]; - for (prev = NULL, current = *refs; current; current = next) { - next = current->next; - if ((!memcmp(current->name, "refs/", 5) && - check_ref_format(current->name + 5)) || - (!fetch_all && - !path_match(current->name, nr_match, match))) { - if (prev == NULL) - *refs = next; - else - prev->next = next; - free(current); - } else - prev = current; + if (nr_match && !fetch_all) { + if (ARRAY_SIZE(fastarray) < nr_match) + return_refs = xcalloc(nr_match, sizeof(struct ref *)); + else { + return_refs = fastarray; + memset(return_refs, 0, sizeof(struct ref *) * nr_match); + } } + else + return_refs = NULL; + + for (ref = *refs; ref; ref = next) { + next = ref->next; + if (!memcmp(ref->name, "refs/", 5) && + check_ref_format(ref->name + 5)) + ; /* trash */ + else if (fetch_all) { + *newtail = ref; + ref->next = NULL; + newtail = &ref->next; + continue; + } + else { + int order = path_match(ref->name, nr_match, match); + if (order) { + return_refs[order-1] = ref; + continue; /* we will link it later */ + } + } + free(ref); + } + + if (!fetch_all) { + int i; + for (i = 0; i < nr_match; i++) { + ref = return_refs[i]; + if (ref) { + *newtail = ref; + ref->next = NULL; + newtail = &ref->next; + } + } + if (return_refs != fastarray) + free(return_refs); + } + *refs = newlist; } static int everything_local(struct ref **refs, int nr_match, char **match) From 5126f35a54f179d125775e6d18213f76aa7856b3 Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Mon, 22 May 2006 12:09:14 +0200 Subject: [PATCH 58/90] git help: remove whatchanged from list of common commands whatchanged is replaced by git log now. Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano --- generate-cmdlist.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh index 6c59dbd68f..ec1eda20de 100755 --- a/generate-cmdlist.sh +++ b/generate-cmdlist.sh @@ -37,7 +37,6 @@ show-branch status tag verify-tag -whatchanged EOF while read cmd do From 884e3134a0002af1f8842768873fe9d041ed9a76 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 22 May 2006 15:34:40 -0700 Subject: [PATCH 59/90] Tutorial #2: broken link fix. Signed-off-by: Junio C Hamano --- Documentation/tutorial-2.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt index 7de91aa735..08d3453e5c 100644 --- a/Documentation/tutorial-2.txt +++ b/Documentation/tutorial-2.txt @@ -375,7 +375,7 @@ What next? At this point you should know everything necessary to read the man pages for any of the git commands; one good place to start would be -with the commands mentioned in link:everday.html[Everyday git]. You +with the commands mentioned in link:everyday.html[Everyday git]. You should be able to find any unknown jargon in the link:glossary.html[Glosssay]. From e3008464e70d25b4926b97b1f86951d7a3cb938d Mon Sep 17 00:00:00 2001 From: Sean Date: Mon, 22 May 2006 20:36:34 -0400 Subject: [PATCH 60/90] Avoid segfault in diff --stat rename output. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff.c b/diff.c index 1601783679..3a2a175c7e 100644 --- a/diff.c +++ b/diff.c @@ -237,7 +237,7 @@ static char *pprint_rename(const char *a, const char *b) if (a_midlen < 0) a_midlen = 0; if (b_midlen < 0) b_midlen = 0; - name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7); + name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7); sprintf(name, "%.*s{%.*s => %.*s}%s", pfx_length, a, a_midlen, a + pfx_length, From f803eec51b6fe812c523c8f6474e029163b307e8 Mon Sep 17 00:00:00 2001 From: Yakov Lerner Date: Mon, 22 May 2006 22:34:00 +0300 Subject: [PATCH 61/90] Problem: 'trap...exit' causes error message when /bin/sh is ash. Problem: 'trap...exit' causes error message when /bin/sh is ash. Fix: Change 'trap...exit' to 'trap...0' like in other scripts. Signed-off-by: Yakov Lerner Signed-off-by: Junio C Hamano --- git-clone.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-clone.sh b/git-clone.sh index 227245c865..d96894d4c2 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -199,7 +199,7 @@ dir="$2" [ -e "$dir" ] && echo "$dir already exists." && usage mkdir -p "$dir" && D=$(cd "$dir" && pwd) && -trap 'err=$?; cd ..; rm -r "$D"; exit $err' exit +trap 'err=$?; cd ..; rm -r "$D"; exit $err' 0 case "$bare" in yes) GIT_DIR="$D" ;; *) GIT_DIR="$D/.git" ;; @@ -407,5 +407,5 @@ Pull: refs/heads/$head_points_at:$origin_track" && fi rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD" -trap - exit +trap - 0 From f396f01f11208789875b61e4e0e3239b04f9e38d Mon Sep 17 00:00:00 2001 From: Martin Langhoff Date: Tue, 23 May 2006 00:45:47 +1200 Subject: [PATCH 62/90] cvsimport: minor fixups Cleanup @skipped after it's used. Close a fhandle. Removing suspects one at a time. Signed-off-by: Martin Langhoff Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 8c707f2c66..282646af35 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -650,6 +650,8 @@ my $commit = sub { "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), "git-commit-tree", $tree,@par); die "Cannot exec git-commit-tree: $!\n"; + + close OUT; } $pw->writer(); $pr->reader(); @@ -661,6 +663,7 @@ my $commit = sub { if (@skipped) { $logmsg .= "\n\n\nSKIPPED:\n\t"; $logmsg .= join("\n\t", @skipped) . "\n"; + @skipped = (); } print $pw "$logmsg\n" From c4b16f8d7786c2a9655636779ce4e3e89f0df86c Mon Sep 17 00:00:00 2001 From: Martin Langhoff Date: Tue, 23 May 2006 00:45:39 +1200 Subject: [PATCH 63/90] cvsimport: replace anonymous sub ref with a normal sub commit() does not need to be an anonymous subreference. Keep it simple. Signed-off-by: Martin Langhoff Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 282646af35..d257e668d6 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -563,7 +563,7 @@ my $state = 0; my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg); my(@old,@new,@skipped); -my $commit = sub { +sub commit { my $pid; while(@old) { my @o2; @@ -852,7 +852,7 @@ while() { } elsif($state == 9 and /^\s*$/) { $state = 10; } elsif(($state == 9 or $state == 10) and /^-+$/) { - &$commit(); + commit(); $state = 1; } elsif($state == 11 and /^-+$/) { $state = 1; @@ -862,7 +862,7 @@ while() { print "* UNKNOWN LINE * $_\n"; } } -&$commit() if $branch and $state != 11; +commit() if $branch and $state != 11; unlink($git_index); From 1100ac81a90b03e0c037a286569a340decf7489b Mon Sep 17 00:00:00 2001 From: Sean Date: Mon, 22 May 2006 00:39:52 -0400 Subject: [PATCH 64/90] Change GIT-VERSION-GEN to call git commands with "git" not "git-". GIT-VERSION-GEN can incorrectly return a default version of "v1.3.GIT" because it tries to execute git commands using the "git-cmd" format that expects all git commands to be in the $PATH. Convert these to "git cmd" format so that a proper answer is returned even when the git commands have been moved out of the $PATH and into a $gitexecdir. Signed-off-by: Junio C Hamano --- GIT-VERSION-GEN | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 7fcefcd7c4..a461518cde 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -5,7 +5,7 @@ DEF_VER=v1.3.GIT # First try git-describe, then see if there is a version file # (included in release tarballs), then default -if VN=$(git-describe --abbrev=4 HEAD 2>/dev/null); then +if VN=$(git describe --abbrev=4 HEAD 2>/dev/null); then VN=$(echo "$VN" | sed -e 's/-/./g'); elif test -f version then @@ -16,7 +16,7 @@ fi VN=$(expr "$VN" : v*'\(.*\)') -dirty=$(sh -c 'git-diff-index --name-only HEAD' 2>/dev/null) || dirty= +dirty=$(sh -c 'git diff-index --name-only HEAD' 2>/dev/null) || dirty= case "$dirty" in '') ;; From 7f7e6eacf999cb53771426e561589f721e6c9974 Mon Sep 17 00:00:00 2001 From: Sean Date: Mon, 22 May 2006 00:42:59 -0400 Subject: [PATCH 65/90] Install git builtins into gitexecdir rather than bindir. Moving "git-cmd" commands out of the path and into a special git exec path, should include the builtins. [jc: fixed the case where bindir == gitexecdir - ln -f fails with a complaint that src and dst are the same, likewise for the fallback cp.] Signed-off-by: Junio C Hamano --- Makefile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index efe6b12719..5423b7a79b 100644 --- a/Makefile +++ b/Makefile @@ -627,7 +627,14 @@ install: all $(MAKE) -C templates install $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)' $(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)' - $(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(bindir_SQ)/$p' && ln '$(DESTDIR_SQ)$(bindir_SQ)/git$X' '$(DESTDIR_SQ)$(bindir_SQ)/$p' ;) + if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \ + then \ + ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \ + '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' || \ + cp '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \ + '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \ + fi + $(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) install-doc: $(MAKE) -C Documentation install From 06918348de86774d0fad19c7076747b8182d1c74 Mon Sep 17 00:00:00 2001 From: Martin Langhoff Date: Mon, 22 May 2006 23:38:08 +1200 Subject: [PATCH 66/90] cvsimport: introduce -L option to workaround memory leaks Signed-off-by: Martin Langhoff Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index d257e668d6..6c232c0d4b 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -29,7 +29,7 @@ use IPC::Open2; $SIG{'PIPE'}="IGNORE"; $ENV{'TZ'}="UTC"; -our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S); +our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L); my (%conv_author_name, %conv_author_email); sub usage() { @@ -85,7 +85,7 @@ sub write_author_info($) { close ($f); } -getopts("hivmkuo:d:p:C:z:s:M:P:A:S:") or usage(); +getopts("hivmkuo:d:p:C:z:s:M:P:A:S:L:") or usage(); usage if $opt_h; @ARGV <= 1 or usage(); @@ -719,6 +719,7 @@ sub commit { } }; +my $commitcount = 1; while() { chomp; if($state == 0 and /^-+$/) { @@ -852,6 +853,9 @@ while() { } elsif($state == 9 and /^\s*$/) { $state = 10; } elsif(($state == 9 or $state == 10) and /^-+$/) { + if ($opt_L && $commitcount++ >= $opt_L) { + last; + } commit(); $state = 1; } elsif($state == 11 and /^-+$/) { From 4adcea995e97361d0900aaf27c60fad0b03b9ad1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 22 May 2006 19:28:37 -0700 Subject: [PATCH 67/90] cvsimport: repack every kilo-commits. Signed-off-by: Linus Torvalds Acked-by: Martin Langhoff Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 6c232c0d4b..712cdc0e32 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -853,10 +853,14 @@ while() { } elsif($state == 9 and /^\s*$/) { $state = 10; } elsif(($state == 9 or $state == 10) and /^-+$/) { - if ($opt_L && $commitcount++ >= $opt_L) { + $commitcount++; + if ($opt_L && $commitcount > $opt_L) { last; } commit(); + if (($commitcount & 1023) == 0) { + system("git repack -a -d"); + } $state = 1; } elsif($state == 11 and /^-+$/) { $state = 1; From 443f8338b9e248353a7095a1096684f1ed106c66 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Mon, 22 May 2006 23:02:06 +0200 Subject: [PATCH 68/90] git status: skip empty directories, and add -u to show all untracked files By default, we use --others --directory to show uninteresting directories (to get user's attention) without their contents (to unclutter output). Showing empty directories do not make sense, so pass --no-empty-directory when we do so. Giving -u (or --untracked) disables this uncluttering to let the user get all untracked files. Signed-off-by: Junio C Hamano --- git-commit.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/git-commit.sh b/git-commit.sh index 6ef1a9dedc..6785826fef 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Linus Torvalds # Copyright (c) 2006 Junio C Hamano -USAGE='[-a] [-s] [-v] [--no-verify] [-m | -F | (-C|-c) ) [--amend] [-e] [--author ] [[-i | -o] ...]' +USAGE='[-a] [-s] [-v] [--no-verify] [-m | -F | (-C|-c) ] [-u] [--amend] [-e] [--author ] [[-i | -o] ...]' SUBDIRECTORY_OK=Yes . git-sh-setup @@ -134,13 +134,17 @@ run_status () { report "Changed but not updated" \ "use git-update-index to mark for commit" + option="" + if test -z "$untracked_files"; then + option="--directory --no-empty-directory" + fi if test -f "$GIT_DIR/info/exclude" then - git-ls-files -z --others --directory \ + git-ls-files -z --others $option \ --exclude-from="$GIT_DIR/info/exclude" \ --exclude-per-directory=.gitignore else - git-ls-files -z --others --directory \ + git-ls-files -z --others $option \ --exclude-per-directory=.gitignore fi | perl -e '$/ = "\0"; @@ -203,6 +207,7 @@ verbose= signoff= force_author= only_include_assumed= +untracked_files= while case "$#" in 0) break;; esac do case "$1" in @@ -340,6 +345,12 @@ do verbose=t shift ;; + -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|--untracked|\ + --untracked-|--untracked-f|--untracked-fi|--untracked-fil|--untracked-file|\ + --untracked-files) + untracked_files=t + shift + ;; --) shift break From 6a1871e174fee1757713df7a3d776dd3813e7ad8 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 23 May 2006 03:27:45 -0400 Subject: [PATCH 69/90] cvsimport: use git-update-index --index-info This should reduce the number of git-update-index forks required per commit. We now do adds/removes in one call, and we are no longer forced to deal with argv limitations. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 712cdc0e32..90ca018261 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -565,29 +565,19 @@ my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg); my(@old,@new,@skipped); sub commit { my $pid; - while(@old) { - my @o2; - if(@old > 55) { - @o2 = splice(@old,0,50); - } else { - @o2 = @old; - @old = (); - } - system("git-update-index","--force-remove","--",@o2); - die "Cannot remove files: $?\n" if $?; - } - while(@new) { - my @n2; - if(@new > 12) { - @n2 = splice(@new,0,10); - } else { - @n2 = @new; - @new = (); - } - system("git-update-index","--add", - (map { ('--cacheinfo', @$_) } @n2)); - die "Cannot add files: $?\n" if $?; - } + + open(my $fh, '|-', qw(git-update-index -z --index-info)) + or die "unable to open git-update-index: $!"; + print $fh + (map { "0 0000000000000000000000000000000000000000\t$_\0" } + @old), + (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" } + @new) + or die "unable to write to git-update-index: $!"; + close $fh + or die "unable to write to git-update-index: $!"; + $? and die "git-update-index reported error: $?"; + @old = @new = (); $pid = open(C,"-|"); die "Cannot fork: $!" unless defined $pid; From e73aefe4fdba0d161d9878642c69b40d83a0204c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 23 May 2006 03:27:46 -0400 Subject: [PATCH 70/90] cvsimport: cleanup commit function This change attempts to clean up the commit function to make it a bit easier to read (or at least the first half of it). It also improves robustness and performance. Specifically: - report get_headref errors on opening ref unless the error is ENOENT - use regex to check for sha1 instead of length - use lexically scoped filehandles which get cleaned up automagically - check for error on both 'print' and 'close' (since output is buffered) - avoid "fork, do some perl, then exec" in commit(). It's not necessary, and we probably end up COW'ing parts of the perl process. Plus the code is much smaller because we can use open2() - avoid calling strftime over and over (mainly a readability cleanup) Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 154 +++++++++++++++++++-------------------------- 1 file changed, 66 insertions(+), 88 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 90ca018261..f0e4d2422b 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -23,7 +23,7 @@ use File::Basename qw(basename dirname); use Time::Local; use IO::Socket; use IO::Pipe; -use POSIX qw(strftime dup2); +use POSIX qw(strftime dup2 :errno_h); use IPC::Open2; $SIG{'PIPE'}="IGNORE"; @@ -429,21 +429,24 @@ sub getwd() { return $pwd; } - -sub get_headref($$) { - my $name = shift; - my $git_dir = shift; - my $sha; - - if (open(C,"$git_dir/refs/heads/$name")) { - chomp($sha = ); - close(C); - length($sha) == 40 - or die "Cannot get head id for $name ($sha): $!\n"; - } - return $sha; +sub is_sha1 { + my $s = shift; + return $s =~ /^[a-f0-9]{40}$/; } +sub get_headref ($$) { + my $name = shift; + my $git_dir = shift; + + my $f = "$git_dir/refs/heads/$name"; + if(open(my $fh, $f)) { + chomp(my $r = <$fh>); + is_sha1($r) or die "Cannot get head id for $name ($r): $!"; + return $r; + } + die "unable to open $f: $!" unless $! == POSIX::ENOENT; + return undef; +} -d $git_tree or mkdir($git_tree,0777) @@ -561,90 +564,67 @@ unless($pid) { my $state = 0; -my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg); -my(@old,@new,@skipped); -sub commit { - my $pid; - +sub update_index (\@\@) { + my $old = shift; + my $new = shift; open(my $fh, '|-', qw(git-update-index -z --index-info)) or die "unable to open git-update-index: $!"; print $fh (map { "0 0000000000000000000000000000000000000000\t$_\0" } - @old), + @$old), (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" } - @new) + @$new) or die "unable to write to git-update-index: $!"; close $fh or die "unable to write to git-update-index: $!"; $? and die "git-update-index reported error: $?"; - @old = @new = (); +} - $pid = open(C,"-|"); - die "Cannot fork: $!" unless defined $pid; - unless($pid) { - exec("git-write-tree"); - die "Cannot exec git-write-tree: $!\n"; - } - chomp(my $tree = ); - length($tree) == 40 - or die "Cannot get tree id ($tree): $!\n"; - close(C) +sub write_tree () { + open(my $fh, '-|', qw(git-write-tree)) + or die "unable to open git-write-tree: $!"; + chomp(my $tree = <$fh>); + is_sha1($tree) + or die "Cannot get tree id ($tree): $!"; + close($fh) or die "Error running git-write-tree: $?\n"; print "Tree ID $tree\n" if $opt_v; + return $tree; +} - my $parent = ""; - if(open(C,"$git_dir/refs/heads/$last_branch")) { - chomp($parent = ); - close(C); - length($parent) == 40 - or die "Cannot get parent id ($parent): $!\n"; - print "Parent ID $parent\n" if $opt_v; - } +my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg); +my(@old,@new,@skipped); +sub commit { + update_index(@old, @new); + @old = @new = (); + my $tree = write_tree(); + my $parent = get_headref($last_branch, $git_dir); + print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v; - my $pr = IO::Pipe->new() or die "Cannot open pipe: $!\n"; - my $pw = IO::Pipe->new() or die "Cannot open pipe: $!\n"; - $pid = fork(); - die "Fork: $!\n" unless defined $pid; - unless($pid) { - $pr->writer(); - $pw->reader(); - open(OUT,">&STDOUT"); - dup2($pw->fileno(),0); - dup2($pr->fileno(),1); - $pr->close(); - $pw->close(); + my @commit_args; + push @commit_args, ("-p", $parent) if $parent; - my @par = (); - @par = ("-p",$parent) if $parent; - - # loose detection of merges - # based on the commit msg - foreach my $rx (@mergerx) { - if ($logmsg =~ $rx) { - my $mparent = $1; - if ($mparent eq 'HEAD') { $mparent = $opt_o }; - if ( -e "$git_dir/refs/heads/$mparent") { - $mparent = get_headref($mparent, $git_dir); - push @par, '-p', $mparent; - print OUT "Merge parent branch: $mparent\n" if $opt_v; - } - } + # loose detection of merges + # based on the commit msg + foreach my $rx (@mergerx) { + next unless $logmsg =~ $rx && $1; + my $mparent = $1 eq 'HEAD' ? $opt_o : $1; + if(my $sha1 = get_headref($mparent, $git_dir)) { + push @commit_args, '-p', $mparent; + print "Merge parent branch: $mparent\n" if $opt_v; } - - exec("env", - "GIT_AUTHOR_NAME=$author_name", - "GIT_AUTHOR_EMAIL=$author_email", - "GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), - "GIT_COMMITTER_NAME=$author_name", - "GIT_COMMITTER_EMAIL=$author_email", - "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), - "git-commit-tree", $tree,@par); - die "Cannot exec git-commit-tree: $!\n"; - - close OUT; } - $pw->writer(); - $pr->reader(); + + my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)); + my $pid = open2(my $commit_read, my $commit_write, + 'env', + "GIT_AUTHOR_NAME=$author_name", + "GIT_AUTHOR_EMAIL=$author_email", + "GIT_AUTHOR_DATE=$commit_date", + "GIT_COMMITTER_NAME=$author_name", + "GIT_COMMITTER_EMAIL=$author_email", + "GIT_COMMITTER_DATE=$commit_date", + 'git-commit-tree', $tree, @commit_args); # compatibility with git2cvs substr($logmsg,32767) = "" if length($logmsg) > 32767; @@ -656,16 +636,14 @@ sub commit { @skipped = (); } - print $pw "$logmsg\n" + print($commit_write "$logmsg\n") && close($commit_write) or die "Error writing to git-commit-tree: $!\n"; - $pw->close(); - print "Committed patch $patchset ($branch ".strftime("%Y-%m-%d %H:%M:%S",gmtime($date)).")\n" if $opt_v; - chomp(my $cid = <$pr>); - length($cid) == 40 - or die "Cannot get commit id ($cid): $!\n"; + print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v; + chomp(my $cid = <$commit_read>); + is_sha1($cid) or die "Cannot get commit id ($cid): $!\n"; print "Commit ID $cid\n" if $opt_v; - $pr->close(); + close($commit_read); waitpid($pid,0); die "Error running git-commit-tree: $?\n" if $?; From 55cad8429954c7d08d0ce86155e7f9adf2f4c6ad Mon Sep 17 00:00:00 2001 From: Martin Langhoff Date: Tue, 23 May 2006 20:08:58 +1200 Subject: [PATCH 71/90] cvsimport: introduce _fetchfile() method and used a 1M buffer to read() File retrieval from the socket is now moved to _fetchfile() and we now cap reads at 1MB. This should limit the memory growth of the cvsimport process. Signed-off-by: Martin Langhoff Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index f0e4d2422b..41ee9a608d 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -315,15 +315,7 @@ sub _line { chomp $cnt; die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/; $line=""; - $res=0; - while($cnt) { - my $buf; - my $num = $self->{'socketi'}->read($buf,$cnt); - die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0; - print $fh $buf; - $res += $num; - $cnt -= $num; - } + $res = $self->_fetchfile($fh, $cnt); } elsif($line =~ s/^ //) { print $fh $line; $res += length($line); @@ -335,14 +327,7 @@ sub _line { chomp $cnt; die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1; $line=""; - while($cnt) { - my $buf; - my $num = $self->{'socketi'}->read($buf,$cnt); - die "S: Mbinary $cnt: $num: $!\n" if not defined $num or $num<=0; - print $fh $buf; - $res += $num; - $cnt -= $num; - } + $res += $self->_fetchfile($fh, $cnt); } else { chomp $line; if($line eq "ok") { @@ -384,6 +369,23 @@ sub file { return ($name, $res); } +sub _fetchfile { + my ($self, $fh, $cnt) = @_; + my $res; + my $bufsize = 1024 * 1024; + while($cnt) { + if ($bufsize > $cnt) { + $bufsize = $cnt; + } + my $buf; + my $num = $self->{'socketi'}->read($buf,$bufsize); + die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0; + print $fh $buf; + $res += $num; + $cnt -= $num; + } + return $res; +} package main; From 405053d2d98c613d028795df439de657981e0711 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Mon, 22 May 2006 15:46:25 +0200 Subject: [PATCH 72/90] Add git-quiltimport to .gitignore. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b5959d6311..199cc310a2 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ git-prune git-prune-packed git-pull git-push +git-quiltimport git-read-tree git-rebase git-receive-pack From 0864f26421b3c599b462bc867de948d14b268d76 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:29 +0200 Subject: [PATCH 73/90] Builtin git-ls-files. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- ls-files.c => builtin-ls-files.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) rename ls-files.c => builtin-ls-files.c (99%) diff --git a/Makefile b/Makefile index 5423b7a79b..c540d7d9f1 100644 --- a/Makefile +++ b/Makefile @@ -155,7 +155,7 @@ PROGRAMS = \ git-diff-index$X git-diff-stages$X \ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ - git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \ + git-ls-tree$X git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ git-receive-pack$X git-rev-parse$X \ @@ -171,7 +171,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X + git-init-db$X git-ls-files$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -220,7 +220,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o + builtin-init-db.o builtin-ls-files.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/ls-files.c b/builtin-ls-files.c similarity index 99% rename from ls-files.c rename to builtin-ls-files.c index 4a4af1ca3b..3a0c5f2150 100644 --- a/ls-files.c +++ b/builtin-ls-files.c @@ -10,6 +10,7 @@ #include "cache.h" #include "quote.h" +#include "builtin.h" static int abbrev = 0; static int show_deleted = 0; @@ -648,7 +649,7 @@ static const char ls_files_usage[] = "[ --exclude-per-directory= ] [--full-name] [--abbrev] " "[--] []*"; -int main(int argc, const char **argv) +int cmd_ls_files(int argc, const char **argv, char** envp) { int i; int exc_given = 0; diff --git a/builtin.h b/builtin.h index 60541262c4..a0713d3747 100644 --- a/builtin.h +++ b/builtin.h @@ -27,5 +27,6 @@ extern int cmd_grep(int argc, const char **argv, char **envp); extern int cmd_rev_list(int argc, const char **argv, char **envp); extern int cmd_check_ref_format(int argc, const char **argv, char **envp); extern int cmd_init_db(int argc, const char **argv, char **envp); +extern int cmd_ls_files(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 3216d311b2..9cfa9ebced 100644 --- a/git.c +++ b/git.c @@ -52,7 +52,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "grep", cmd_grep }, { "rev-list", cmd_rev_list }, { "init-db", cmd_init_db }, - { "check-ref-format", cmd_check_ref_format } + { "check-ref-format", cmd_check_ref_format }, + { "ls-files", cmd_ls_files } }; int i; From aae01bda7f6d3224cf6b2ce0aa9aa668ce35d0b7 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:30 +0200 Subject: [PATCH 74/90] Builtin git-ls-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- ls-tree.c => builtin-ls-tree.c | 5 +++-- builtin.h | 1 + git.c | 3 ++- 4 files changed, 9 insertions(+), 6 deletions(-) rename ls-tree.c => builtin-ls-tree.c (96%) diff --git a/Makefile b/Makefile index c540d7d9f1..2afd089a0d 100644 --- a/Makefile +++ b/Makefile @@ -155,7 +155,7 @@ PROGRAMS = \ git-diff-index$X git-diff-stages$X \ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ - git-ls-tree$X git-mailinfo$X git-merge-base$X \ + git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ git-receive-pack$X git-rev-parse$X \ @@ -171,7 +171,7 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X git-ls-files$X + git-init-db$X git-ls-files$X git-ls-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -220,7 +220,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o builtin-ls-files.o + builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/ls-tree.c b/builtin-ls-tree.c similarity index 96% rename from ls-tree.c rename to builtin-ls-tree.c index f2b3bc1231..48385d59f6 100644 --- a/ls-tree.c +++ b/builtin-ls-tree.c @@ -7,6 +7,7 @@ #include "blob.h" #include "tree.h" #include "quote.h" +#include "builtin.h" static int line_termination = '\n'; #define LS_RECURSIVE 1 @@ -15,7 +16,7 @@ static int line_termination = '\n'; #define LS_NAME_ONLY 8 static int abbrev = 0; static int ls_options = 0; -const char **pathspec; +static const char **pathspec; static int chomp_prefix = 0; static const char *prefix; @@ -84,7 +85,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, return retval; } -int main(int argc, const char **argv) +int cmd_ls_tree(int argc, const char **argv, char **envp) { unsigned char sha1[20]; struct tree *tree; diff --git a/builtin.h b/builtin.h index a0713d3747..951f206372 100644 --- a/builtin.h +++ b/builtin.h @@ -28,5 +28,6 @@ extern int cmd_rev_list(int argc, const char **argv, char **envp); extern int cmd_check_ref_format(int argc, const char **argv, char **envp); extern int cmd_init_db(int argc, const char **argv, char **envp); extern int cmd_ls_files(int argc, const char **argv, char **envp); +extern int cmd_ls_tree(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 9cfa9ebced..8574775418 100644 --- a/git.c +++ b/git.c @@ -53,7 +53,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "rev-list", cmd_rev_list }, { "init-db", cmd_init_db }, { "check-ref-format", cmd_check_ref_format }, - { "ls-files", cmd_ls_files } + { "ls-files", cmd_ls_files }, + { "ls-tree", cmd_ls_tree } }; int i; From 56d1398ad305498faf57d6e433f97ad393d7909e Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:31 +0200 Subject: [PATCH 75/90] Builtin git-tar-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 8 +++++--- tar-tree.c => builtin-tar-tree.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) rename tar-tree.c => builtin-tar-tree.c (99%) diff --git a/Makefile b/Makefile index 2afd089a0d..a3164f8ef0 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ PROGRAMS = \ git-receive-pack$X git-rev-parse$X \ git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ - git-ssh-upload$X git-tar-tree$X git-unpack-file$X \ + git-ssh-upload$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ git-upload-pack$X git-verify-pack$X git-write-tree$X \ git-update-ref$X git-symbolic-ref$X \ @@ -171,7 +171,8 @@ PROGRAMS = \ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ - git-init-db$X git-ls-files$X git-ls-tree$X + git-init-db$X git-ls-files$X git-ls-tree$X \ + git-tar-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -220,7 +221,8 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ - builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o + builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ + builtin-tar-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/tar-tree.c b/builtin-tar-tree.c similarity index 99% rename from tar-tree.c rename to builtin-tar-tree.c index 33087366c3..6ada04ce38 100644 --- a/tar-tree.c +++ b/builtin-tar-tree.c @@ -7,6 +7,7 @@ #include "commit.h" #include "strbuf.h" #include "tar.h" +#include "builtin.h" #define RECORDSIZE (512) #define BLOCKSIZE (RECORDSIZE * 20) @@ -301,7 +302,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path) } } -int main(int argc, char **argv) +int cmd_tar_tree(int argc, const char **argv, char** envp) { unsigned char sha1[20], tree_sha1[20]; struct commit *commit; diff --git a/builtin.h b/builtin.h index 951f206372..d210543948 100644 --- a/builtin.h +++ b/builtin.h @@ -29,5 +29,6 @@ extern int cmd_check_ref_format(int argc, const char **argv, char **envp); extern int cmd_init_db(int argc, const char **argv, char **envp); extern int cmd_ls_files(int argc, const char **argv, char **envp); extern int cmd_ls_tree(int argc, const char **argv, char **envp); +extern int cmd_tar_tree(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 8574775418..c253e60953 100644 --- a/git.c +++ b/git.c @@ -54,7 +54,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "init-db", cmd_init_db }, { "check-ref-format", cmd_check_ref_format }, { "ls-files", cmd_ls_files }, - { "ls-tree", cmd_ls_tree } + { "ls-tree", cmd_ls_tree }, + { "tar-tree", cmd_tar_tree } }; int i; From d147e501f37a596e73a430ce46f125f83e06aa07 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:32 +0200 Subject: [PATCH 76/90] Builtin git-read-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- read-tree.c => builtin-read-tree.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) rename read-tree.c => builtin-read-tree.c (99%) diff --git a/Makefile b/Makefile index a3164f8ef0..1aebcf5978 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ PROGRAMS = \ git-hash-object$X git-index-pack$X git-local-fetch$X \ git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ - git-peek-remote$X git-prune-packed$X git-read-tree$X \ + git-peek-remote$X git-prune-packed$X \ git-receive-pack$X git-rev-parse$X \ git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ @@ -172,7 +172,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ - git-tar-tree$X + git-tar-tree$X git-read-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -222,7 +222,7 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ - builtin-tar-tree.o + builtin-tar-tree.o builtin-read-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/read-tree.c b/builtin-read-tree.c similarity index 99% rename from read-tree.c rename to builtin-read-tree.c index 82e2a9a4d3..ec40d013c4 100644 --- a/read-tree.c +++ b/builtin-read-tree.c @@ -11,6 +11,7 @@ #include "tree.h" #include #include +#include "builtin.h" static int reset = 0; static int merge = 0; @@ -763,7 +764,7 @@ static const char read_tree_usage[] = "git-read-tree ( | -m [--aggressive] static struct cache_file cache_file; -int main(int argc, char **argv) +int cmd_read_tree(int argc, const char **argv, char **envp) { int i, newfd, stage = 0; unsigned char sha1[20]; diff --git a/builtin.h b/builtin.h index d210543948..88b3523c25 100644 --- a/builtin.h +++ b/builtin.h @@ -30,5 +30,6 @@ extern int cmd_init_db(int argc, const char **argv, char **envp); extern int cmd_ls_files(int argc, const char **argv, char **envp); extern int cmd_ls_tree(int argc, const char **argv, char **envp); extern int cmd_tar_tree(int argc, const char **argv, char **envp); +extern int cmd_read_tree(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index c253e60953..300e2b269c 100644 --- a/git.c +++ b/git.c @@ -55,7 +55,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "check-ref-format", cmd_check_ref_format }, { "ls-files", cmd_ls_files }, { "ls-tree", cmd_ls_tree }, - { "tar-tree", cmd_tar_tree } + { "tar-tree", cmd_tar_tree }, + { "read-tree", cmd_read_tree } }; int i; From 6d96ac18e52aca19ff1087ffa64e2d616cc75c6f Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:33 +0200 Subject: [PATCH 77/90] Builtin git-commit-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- commit-tree.c => builtin-commit-tree.c | 5 +++-- builtin.h | 1 + git.c | 3 ++- 4 files changed, 9 insertions(+), 6 deletions(-) rename commit-tree.c => builtin-commit-tree.c (96%) diff --git a/Makefile b/Makefile index 1aebcf5978..a1bee61e57 100644 --- a/Makefile +++ b/Makefile @@ -150,7 +150,7 @@ SIMPLE_PROGRAMS = \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ git-apply$X git-cat-file$X \ - git-checkout-index$X git-clone-pack$X git-commit-tree$X \ + git-checkout-index$X git-clone-pack$X \ git-convert-objects$X git-diff-files$X \ git-diff-index$X git-diff-stages$X \ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ @@ -172,7 +172,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ - git-tar-tree$X git-read-tree$X + git-tar-tree$X git-read-tree$X git-commit-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -222,7 +222,7 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ - builtin-tar-tree.o builtin-read-tree.o + builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/commit-tree.c b/builtin-commit-tree.c similarity index 96% rename from commit-tree.c rename to builtin-commit-tree.c index 0320036e80..ec082bf754 100644 --- a/commit-tree.c +++ b/builtin-commit-tree.c @@ -6,6 +6,7 @@ #include "cache.h" #include "commit.h" #include "tree.h" +#include "builtin.h" #define BLOCKING (1ul << 14) @@ -76,7 +77,7 @@ static int new_parent(int idx) return 1; } -int main(int argc, char **argv) +int cmd_commit_tree(int argc, const char **argv, char **envp) { int i; int parents = 0; @@ -98,7 +99,7 @@ int main(int argc, char **argv) check_valid(tree_sha1, tree_type); for (i = 2; i < argc; i += 2) { - char *a, *b; + const char *a, *b; a = argv[i]; b = argv[i+1]; if (!b || strcmp(a, "-p")) usage(commit_tree_usage); diff --git a/builtin.h b/builtin.h index 88b3523c25..c6b07d9a65 100644 --- a/builtin.h +++ b/builtin.h @@ -31,5 +31,6 @@ extern int cmd_ls_files(int argc, const char **argv, char **envp); extern int cmd_ls_tree(int argc, const char **argv, char **envp); extern int cmd_tar_tree(int argc, const char **argv, char **envp); extern int cmd_read_tree(int argc, const char **argv, char **envp); +extern int cmd_commit_tree(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 300e2b269c..4c2c062e36 100644 --- a/git.c +++ b/git.c @@ -56,7 +56,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "ls-files", cmd_ls_files }, { "ls-tree", cmd_ls_tree }, { "tar-tree", cmd_tar_tree }, - { "read-tree", cmd_read_tree } + { "read-tree", cmd_read_tree }, + { "commit-tree", cmd_commit_tree } }; int i; From ac6245e31a359200b65bfdd910bba9a0fbe90c11 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:34 +0200 Subject: [PATCH 78/90] Builtin git-apply. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 8 +++++--- apply.c => builtin-apply.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) rename apply.c => builtin-apply.c (99%) diff --git a/Makefile b/Makefile index a1bee61e57..3da576838f 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,7 @@ SIMPLE_PROGRAMS = \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ - git-apply$X git-cat-file$X \ + git-cat-file$X \ git-checkout-index$X git-clone-pack$X \ git-convert-objects$X git-diff-files$X \ git-diff-index$X git-diff-stages$X \ @@ -172,7 +172,8 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-count-objects$X git-diff$X git-push$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ - git-tar-tree$X git-read-tree$X git-commit-tree$X + git-tar-tree$X git-read-tree$X git-commit-tree$X \ + git-apply$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -222,7 +223,8 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ - builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o + builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \ + builtin-apply.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/apply.c b/builtin-apply.c similarity index 99% rename from apply.c rename to builtin-apply.c index 0ed9d132e8..4056b9d67b 100644 --- a/apply.c +++ b/builtin-apply.c @@ -11,6 +11,7 @@ #include "quote.h" #include "blob.h" #include "delta.h" +#include "builtin.h" // --check turns on checking that the working tree matches the // files that are being modified, but doesn't apply the patch @@ -2151,7 +2152,7 @@ static int git_apply_config(const char *var, const char *value) } -int main(int argc, char **argv) +int cmd_apply(int argc, const char **argv, char **envp) { int i; int read_stdin = 1; diff --git a/builtin.h b/builtin.h index c6b07d9a65..d6ff88ebc1 100644 --- a/builtin.h +++ b/builtin.h @@ -32,5 +32,6 @@ extern int cmd_ls_tree(int argc, const char **argv, char **envp); extern int cmd_tar_tree(int argc, const char **argv, char **envp); extern int cmd_read_tree(int argc, const char **argv, char **envp); extern int cmd_commit_tree(int argc, const char **argv, char **envp); +extern int cmd_apply(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 4c2c062e36..f44e08b9ce 100644 --- a/git.c +++ b/git.c @@ -57,7 +57,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "ls-tree", cmd_ls_tree }, { "tar-tree", cmd_tar_tree }, { "read-tree", cmd_read_tree }, - { "commit-tree", cmd_commit_tree } + { "commit-tree", cmd_commit_tree }, + { "apply", cmd_apply } }; int i; From 51ce34b9923d9b119ac53414584f80e05520abea Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:35 +0200 Subject: [PATCH 79/90] Builtin git-show-branch. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- show-branch.c => builtin-show-branch.c | 7 ++++--- builtin.h | 1 + git.c | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) rename show-branch.c => builtin-show-branch.c (99%) diff --git a/Makefile b/Makefile index 3da576838f..69377e37b4 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ PROGRAMS = \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X \ git-receive-pack$X git-rev-parse$X \ - git-send-pack$X git-show-branch$X git-shell$X \ + git-send-pack$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ git-ssh-upload$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ @@ -173,7 +173,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ git-tar-tree$X git-read-tree$X git-commit-tree$X \ - git-apply$X + git-apply$X git-show-branch$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -224,7 +224,7 @@ BUILTIN_OBJS = \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \ - builtin-apply.o + builtin-apply.o builtin-show-branch.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/show-branch.c b/builtin-show-branch.c similarity index 99% rename from show-branch.c rename to builtin-show-branch.c index 268c57b180..3af24e767b 100644 --- a/show-branch.c +++ b/builtin-show-branch.c @@ -3,13 +3,14 @@ #include "cache.h" #include "commit.h" #include "refs.h" +#include "builtin.h" static const char show_branch_usage[] = "git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [...]"; static int default_num = 0; static int default_alloc = 0; -static char **default_arg = NULL; +static const char **default_arg = NULL; #define UNINTERESTING 01 @@ -548,7 +549,7 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n) return 0; } -int main(int ac, char **av) +int cmd_show_branch(int ac, const char **av, char **envp) { struct commit *rev[MAX_REVS], *commit; struct commit_list *list = NULL, *seen = NULL; @@ -581,7 +582,7 @@ int main(int ac, char **av) } while (1 < ac && av[1][0] == '-') { - char *arg = av[1]; + const char *arg = av[1]; if (!strcmp(arg, "--")) { ac--; av++; break; diff --git a/builtin.h b/builtin.h index d6ff88ebc1..01882ec93d 100644 --- a/builtin.h +++ b/builtin.h @@ -33,5 +33,6 @@ extern int cmd_tar_tree(int argc, const char **argv, char **envp); extern int cmd_read_tree(int argc, const char **argv, char **envp); extern int cmd_commit_tree(int argc, const char **argv, char **envp); extern int cmd_apply(int argc, const char **argv, char **envp); +extern int cmd_show_branch(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index f44e08b9ce..d29505c4c9 100644 --- a/git.c +++ b/git.c @@ -58,7 +58,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "tar-tree", cmd_tar_tree }, { "read-tree", cmd_read_tree }, { "commit-tree", cmd_commit_tree }, - { "apply", cmd_apply } + { "apply", cmd_apply }, + { "show-branch", cmd_show_branch } }; int i; From e8cc9cd98e2ecd7fd8bb03e725d470405c8e2b94 Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Tue, 23 May 2006 14:15:36 +0200 Subject: [PATCH 80/90] Builtin git-diff-files, git-diff-index, git-diff-stages, and git-diff-tree. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- Makefile | 12 ++++++------ diff-files.c => builtin-diff-files.c | 3 ++- diff-index.c => builtin-diff-index.c | 3 ++- diff-stages.c => builtin-diff-stages.c | 3 ++- diff-tree.c => builtin-diff-tree.c | 3 ++- builtin.h | 5 +++++ git.c | 6 +++++- 7 files changed, 24 insertions(+), 11 deletions(-) rename diff-files.c => builtin-diff-files.c (94%) rename diff-index.c => builtin-diff-index.c (91%) rename diff-stages.c => builtin-diff-stages.c (96%) rename diff-tree.c => builtin-diff-tree.c (97%) diff --git a/Makefile b/Makefile index 69377e37b4..fc5f98b908 100644 --- a/Makefile +++ b/Makefile @@ -151,9 +151,7 @@ SIMPLE_PROGRAMS = \ PROGRAMS = \ git-cat-file$X \ git-checkout-index$X git-clone-pack$X \ - git-convert-objects$X git-diff-files$X \ - git-diff-index$X git-diff-stages$X \ - git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \ + git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ git-mailinfo$X git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ @@ -173,7 +171,8 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-grep$X git-rev-list$X git-check-ref-format$X \ git-init-db$X git-ls-files$X git-ls-tree$X \ git-tar-tree$X git-read-tree$X git-commit-tree$X \ - git-apply$X git-show-branch$X + git-apply$X git-show-branch$X git-diff-files$X \ + git-diff-index$X git-diff-stages$X git-diff-tree$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -223,8 +222,9 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \ - builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \ - builtin-apply.o builtin-show-branch.o + builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \ + builtin-apply.o builtin-show-branch.o builtin-diff-files.o \ + builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/diff-files.c b/builtin-diff-files.c similarity index 94% rename from diff-files.c rename to builtin-diff-files.c index b9d193d506..cebda828ee 100644 --- a/diff-files.c +++ b/builtin-diff-files.c @@ -7,12 +7,13 @@ #include "diff.h" #include "commit.h" #include "revision.h" +#include "builtin.h" static const char diff_files_usage[] = "git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [] [...]" COMMON_DIFF_OPTIONS_HELP; -int main(int argc, const char **argv) +int cmd_diff_files(int argc, const char **argv, char **envp) { struct rev_info rev; int silent = 0; diff --git a/diff-index.c b/builtin-diff-index.c similarity index 91% rename from diff-index.c rename to builtin-diff-index.c index 8c9f60173b..1958580d82 100644 --- a/diff-index.c +++ b/builtin-diff-index.c @@ -2,13 +2,14 @@ #include "diff.h" #include "commit.h" #include "revision.h" +#include "builtin.h" static const char diff_cache_usage[] = "git-diff-index [-m] [--cached] " "[] [...]" COMMON_DIFF_OPTIONS_HELP; -int main(int argc, const char **argv) +int cmd_diff_index(int argc, const char **argv, char **envp) { struct rev_info rev; int cached = 0; diff --git a/diff-stages.c b/builtin-diff-stages.c similarity index 96% rename from diff-stages.c rename to builtin-diff-stages.c index dcd20e79e4..7c157ca889 100644 --- a/diff-stages.c +++ b/builtin-diff-stages.c @@ -4,6 +4,7 @@ #include "cache.h" #include "diff.h" +#include "builtin.h" static struct diff_options diff_options; @@ -54,7 +55,7 @@ static void diff_stages(int stage1, int stage2, const char **pathspec) } } -int main(int ac, const char **av) +int cmd_diff_stages(int ac, const char **av, char **envp) { int stage1, stage2; const char *prefix = setup_git_directory(); diff --git a/diff-tree.c b/builtin-diff-tree.c similarity index 97% rename from diff-tree.c rename to builtin-diff-tree.c index 69bb74b310..cc53b81ac4 100644 --- a/diff-tree.c +++ b/builtin-diff-tree.c @@ -2,6 +2,7 @@ #include "diff.h" #include "commit.h" #include "log-tree.h" +#include "builtin.h" static struct rev_info log_tree_opt; @@ -58,7 +59,7 @@ static const char diff_tree_usage[] = " --root include the initial commit as diff against /dev/null\n" COMMON_DIFF_OPTIONS_HELP; -int main(int argc, const char **argv) +int cmd_diff_tree(int argc, const char **argv, char **envp) { int nr_sha1; char line[1000]; diff --git a/builtin.h b/builtin.h index 01882ec93d..7620984624 100644 --- a/builtin.h +++ b/builtin.h @@ -34,5 +34,10 @@ extern int cmd_read_tree(int argc, const char **argv, char **envp); extern int cmd_commit_tree(int argc, const char **argv, char **envp); extern int cmd_apply(int argc, const char **argv, char **envp); extern int cmd_show_branch(int argc, const char **argv, char **envp); +extern int cmd_diff_files(int argc, const char **argv, char **envp); +extern int cmd_diff_index(int argc, const char **argv, char **envp); +extern int cmd_diff_stages(int argc, const char **argv, char **envp); +extern int cmd_diff_tree(int argc, const char **argv, char **envp); + #endif diff --git a/git.c b/git.c index d29505c4c9..874974874b 100644 --- a/git.c +++ b/git.c @@ -59,7 +59,11 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "read-tree", cmd_read_tree }, { "commit-tree", cmd_commit_tree }, { "apply", cmd_apply }, - { "show-branch", cmd_show_branch } + { "show-branch", cmd_show_branch }, + { "diff-files", cmd_diff_files }, + { "diff-index", cmd_diff_index }, + { "diff-stages", cmd_diff_stages }, + { "diff-tree", cmd_diff_tree } }; int i; From e7332f96b32bedbc39ff6ff7e845c902056099f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Engelmann?= Date: Tue, 23 May 2006 20:19:04 +0200 Subject: [PATCH 81/90] remove the artificial restriction tagsize < 8kb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Engelmann Signed-off-by: Junio C Hamano --- cache.h | 1 + mktag.c | 19 +++++++++---------- sha1_file.c | 46 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/cache.h b/cache.h index afa8e4f0ac..b19d84470b 100644 --- a/cache.h +++ b/cache.h @@ -154,6 +154,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int); extern int ce_modified(struct cache_entry *ce, struct stat *st, int); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type); +extern int read_pipe(int fd, char** return_buf, unsigned long* return_size); extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); diff --git a/mktag.c b/mktag.c index 23288781cf..f1598dbd21 100644 --- a/mktag.c +++ b/mktag.c @@ -45,7 +45,7 @@ static int verify_tag(char *buffer, unsigned long size) unsigned char sha1[20]; const char *object, *type_line, *tag_line, *tagger_line; - if (size < 64 || size > MAXSIZE-1) + if (size < 64) return -1; buffer[size] = 0; @@ -105,8 +105,8 @@ static int verify_tag(char *buffer, unsigned long size) int main(int argc, char **argv) { - unsigned long size; - char buffer[MAXSIZE]; + unsigned long size = 4096; + char *buffer = malloc(size); unsigned char result_sha1[20]; if (argc != 1) @@ -114,13 +114,9 @@ int main(int argc, char **argv) setup_git_directory(); - // Read the signature - size = 0; - for (;;) { - int ret = xread(0, buffer + size, MAXSIZE - size); - if (ret <= 0) - break; - size += ret; + if (read_pipe(0, &buffer, &size)) { + free(buffer); + die("could not read from stdin"); } // Verify it for some basic sanity: it needs to start with "object \ntype\ntagger " @@ -129,6 +125,9 @@ int main(int argc, char **argv) if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0) die("unable to write tag file"); + + free(buffer); + printf("%s\n", sha1_to_hex(result_sha1)); return 0; } diff --git a/sha1_file.c b/sha1_file.c index 223001033c..e444d9df1b 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1645,16 +1645,24 @@ int has_sha1_file(const unsigned char *sha1) return find_sha1_file(sha1, &st) ? 1 : 0; } -int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) +/* + * reads from fd as long as possible into a supplied buffer of size bytes. + * If neccessary the buffer's size is increased using realloc() + * + * returns 0 if anything went fine and -1 otherwise + * + * NOTE: both buf and size may change, but even when -1 is returned + * you still have to free() it yourself. + */ +int read_pipe(int fd, char** return_buf, unsigned long* return_size) { - unsigned long size = 4096; - char *buf = malloc(size); - int iret, ret; + char* buf = *return_buf; + unsigned long size = *return_size; + int iret; unsigned long off = 0; - unsigned char hdr[50]; - int hdrlen; + do { - iret = read(fd, buf + off, size - off); + iret = xread(fd, buf + off, size - off); if (iret > 0) { off += iret; if (off == size) { @@ -1663,16 +1671,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) } } } while (iret > 0); - if (iret < 0) { + + *return_buf = buf; + *return_size = off; + + if (iret < 0) + return -1; + return 0; +} + +int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) +{ + unsigned long size = 4096; + char *buf = malloc(size); + int ret; + unsigned char hdr[50]; + int hdrlen; + + if (read_pipe(fd, &buf, &size)) { free(buf); return -1; } + if (!type) type = blob_type; if (write_object) - ret = write_sha1_file(buf, off, type, sha1); + ret = write_sha1_file(buf, size, type, sha1); else { - write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen); + write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen); ret = 0; } free(buf); From cfba045930423551ec9936fa15cbea7c04a841cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Engelmann?= Date: Tue, 23 May 2006 20:20:09 +0200 Subject: [PATCH 82/90] add more informative error messages to git-mktag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Engelmann Signed-off-by: Junio C Hamano --- mktag.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/mktag.c b/mktag.c index f1598dbd21..f0fe5285b2 100644 --- a/mktag.c +++ b/mktag.c @@ -46,41 +46,45 @@ static int verify_tag(char *buffer, unsigned long size) const char *object, *type_line, *tag_line, *tagger_line; if (size < 64) - return -1; + return error("wanna fool me ? you obviously got the size wrong !\n"); + buffer[size] = 0; /* Verify object line */ object = buffer; if (memcmp(object, "object ", 7)) - return -1; + return error("char%d: does not start with \"object \"\n", 0); + if (get_sha1_hex(object + 7, sha1)) - return -1; + return error("char%d: could not get SHA1 hash\n", 7); /* Verify type line */ type_line = object + 48; if (memcmp(type_line - 1, "\ntype ", 6)) - return -1; + return error("char%d: could not find \"\\ntype \"\n", 47); /* Verify tag-line */ tag_line = strchr(type_line, '\n'); if (!tag_line) - return -1; + return error("char%td: could not find next \"\\n\"\n", type_line - buffer); tag_line++; if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n') - return -1; + return error("char%td: no \"tag \" found\n", tag_line - buffer); /* Get the actual type */ typelen = tag_line - type_line - strlen("type \n"); if (typelen >= sizeof(type)) - return -1; + return error("char%td: type too long\n", type_line+5 - buffer); + memcpy(type, type_line+5, typelen); type[typelen] = 0; /* Verify that the object matches */ if (get_sha1_hex(object + 7, sha1)) - return -1; + return error("char%d: could not get SHA1 hash but this is really odd since i got it before !\n", 7); + if (verify_object(sha1, type)) - return -1; + return error("char%d: could not verify object %s\n", 7, sha1); /* Verify the tag-name: we don't allow control characters or spaces in it */ tag_line += 4; @@ -90,14 +94,14 @@ static int verify_tag(char *buffer, unsigned long size) break; if (c > ' ') continue; - return -1; + return error("char%td: could not verify tag name\n", tag_line - buffer); } /* Verify the tagger line */ tagger_line = tag_line; if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n')) - return -1; + return error("char%td: could not find \"tagger\"\n", tagger_line - buffer); /* The actual stuff afterwards we don't care about.. */ return 0; From 61efa5e300386978dd440716260c94e951a493b4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 23 May 2006 16:30:39 -0700 Subject: [PATCH 83/90] cvsimport: do not barf on creation of an empty file. When the server says "created this file whose length is empty", we mistakenly said "oops, the server did not say a sensible thing". Fix it. Spotted and fixed by Linus, acked by Martin. Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 41ee9a608d..60fc86a5be 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -371,7 +371,7 @@ sub file { } sub _fetchfile { my ($self, $fh, $cnt) = @_; - my $res; + my $res = 0; my $bufsize = 1024 * 1024; while($cnt) { if ($bufsize > $cnt) { From 62bf0d962963794e9fbcdfdd43419b060d5d245f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 23 May 2006 16:59:44 -0400 Subject: [PATCH 84/90] cvsimport: set up commit environment in perl instead of using env Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 60fc86a5be..af331d9c43 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -618,14 +618,13 @@ sub commit { } my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)); + $ENV{GIT_AUTHOR_NAME} = $author_name; + $ENV{GIT_AUTHOR_EMAIL} = $author_email; + $ENV{GIT_AUTHOR_DATE} = $commit_date; + $ENV{GIT_COMMITTER_NAME} = $author_name; + $ENV{GIT_COMMITTER_EMAIL} = $author_email; + $ENV{GIT_COMMITTER_DATE} = $commit_date; my $pid = open2(my $commit_read, my $commit_write, - 'env', - "GIT_AUTHOR_NAME=$author_name", - "GIT_AUTHOR_EMAIL=$author_email", - "GIT_AUTHOR_DATE=$commit_date", - "GIT_COMMITTER_NAME=$author_name", - "GIT_COMMITTER_EMAIL=$author_email", - "GIT_COMMITTER_DATE=$commit_date", 'git-commit-tree', $tree, @commit_args); # compatibility with git2cvs From 304dac15486b6d86d31aea1ca736c2c61f17e902 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 23 May 2006 19:23:40 -0700 Subject: [PATCH 85/90] git-svn: starting a 1.1.0-pre development version Some not-very-well-tested changes coming... Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index de13a96b8a..ea7bfc22e4 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION $GIT_SVN_INDEX $GIT_SVN $GIT_DIR $REV_DIR/; $AUTHOR = 'Eric Wong '; -$VERSION = '1.0.0'; +$VERSION = '1.1.0-pre'; use Cwd qw/abs_path/; $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); From 36f5b1f0c800a23b9755a0214c1c3250f34f2486 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 23 May 2006 19:23:41 -0700 Subject: [PATCH 86/90] git-svn: ignore expansion of svn:keywords Unlike my earlier test patch, this also checks svn:eol-style and makes sure it's applied to working copy updates. This is definitely more correct than my original attempt at killing keyword expansions, but I still haven't tested it enough to know. Feedback would be much appreciated. Also changed assert_svn_wc_clean() to only work on the svn working copy. This requires a separate call to assert_tree() to check wc integrity against git in preparation for another change I'm planning. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/Makefile | 1 + contrib/git-svn/git-svn.perl | 106 ++++++++++++--- contrib/git-svn/t/lib-git-svn.sh | 39 ++++++ contrib/git-svn/t/t0000-contrib-git-svn.sh | 43 +----- .../git-svn/t/t0001-contrib-git-svn-props.sh | 125 ++++++++++++++++++ 5 files changed, 254 insertions(+), 60 deletions(-) create mode 100644 contrib/git-svn/t/lib-git-svn.sh create mode 100644 contrib/git-svn/t/t0001-contrib-git-svn-props.sh diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index acedf7305e..48f60b3a0d 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -30,6 +30,7 @@ git-svn.html : git-svn.txt -f ../../Documentation/asciidoc.conf $< test: git-svn cd t && $(SHELL) ./t0000-contrib-git-svn.sh + cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh clean: rm -f git-svn *.xml *.html *.1 diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index ea7bfc22e4..b3e0684c44 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -39,6 +39,10 @@ my $_svn_co_url_revs; my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'branch|b=s' => \@_branch_from, 'authors-file|A=s' => \$_authors ); + +# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome: +my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" ); + my %cmd = ( fetch => [ \&fetch, "Download new revisions from SVN", { 'revision|r=s' => \$_revision, %fc_opts } ], @@ -207,7 +211,7 @@ sub rebuild { push @svn_up, '--ignore-externals' unless $_no_ignore_ext; sys(@svn_up,"-r$newest_rev"); $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX; - git_addremove(); + index_changes(); exec('git-write-tree'); } waitpid $pid, 0; @@ -249,7 +253,7 @@ sub fetch { chdir $SVN_WC or croak $!; read_uuid(); $last_commit = git_commit($base, @parents); - assert_svn_wc_clean($base->{revision}, $last_commit); + assert_tree($last_commit); } else { chdir $SVN_WC or croak $!; read_uuid(); @@ -259,16 +263,20 @@ sub fetch { push @svn_up, '--ignore-externals' unless $_no_ignore_ext; my $last = $base; while (my $log_msg = next_log_entry($svn_log)) { - assert_svn_wc_clean($last->{revision}, $last_commit); + assert_tree($last_commit); if ($last->{revision} >= $log_msg->{revision}) { croak "Out of order: last >= current: ", "$last->{revision} >= $log_msg->{revision}\n"; } + # Revert is needed for cases like: + # https://svn.musicpd.org/Jamming/trunk (r166:167), but + # I can't seem to reproduce something like that on a test... + sys(qw/svn revert -R ./); + assert_svn_wc_clean($last->{revision}); sys(@svn_up,"-r$log_msg->{revision}"); $last_commit = git_commit($log_msg, $last_commit, @parents); $last = $log_msg; } - assert_svn_wc_clean($last->{revision}, $last_commit); unless (-e "$GIT_DIR/refs/heads/master") { sys(qw(git-update-ref refs/heads/master),$last_commit); } @@ -314,7 +322,6 @@ sub commit { $svn_current_rev = svn_commit_tree($svn_current_rev, $c); } print "Done committing ",scalar @revs," revisions to SVN\n"; - } sub show_ignore { @@ -367,13 +374,11 @@ sub setup_git_svn { } sub assert_svn_wc_clean { - my ($svn_rev, $treeish) = @_; + my ($svn_rev) = @_; croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/); - croak "$treeish is not a sha1!\n" unless ($treeish =~ /^$sha1$/o); my $lcr = svn_info('.')->{'Last Changed Rev'}; if ($svn_rev != $lcr) { print STDERR "Checking for copy-tree ... "; - # use my @diff = grep(/^Index: /,(safe_qx(qw(svn diff), "-r$lcr:$svn_rev"))); if (@diff) { @@ -389,7 +394,6 @@ sub assert_svn_wc_clean { print STDERR $_ foreach @status; croak; } - assert_tree($treeish); } sub assert_tree { @@ -416,7 +420,7 @@ sub assert_tree { unlink $tmpindex or croak $!; } $ENV{GIT_INDEX_FILE} = $tmpindex; - git_addremove(); + index_changes(1); chomp(my $tree = `git-write-tree`); if ($old_index) { $ENV{GIT_INDEX_FILE} = $old_index; @@ -426,6 +430,7 @@ sub assert_tree { if ($tree ne $expected) { croak "Tree mismatch, Got: $tree, Expected: $expected\n"; } + unlink $tmpindex; } sub parse_diff_tree { @@ -562,7 +567,8 @@ sub precommit_check { sub svn_checkout_tree { my ($svn_rev, $treeish) = @_; my $from = file_to_s("$REV_DIR/$svn_rev"); - assert_svn_wc_clean($svn_rev,$from); + assert_svn_wc_clean($svn_rev); + assert_tree($from); print "diff-tree $from $treeish\n"; my $pid = open my $diff_fh, '-|'; defined $pid or croak $!; @@ -852,13 +858,75 @@ sub svn_info { sub sys { system(@_) == 0 or croak $? } -sub git_addremove { - system( "git-diff-files --name-only -z ". - " | git-update-index --remove -z --stdin && ". - "git-ls-files -z --others ". - "'--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude'". - " | git-update-index --add -z --stdin" - ) == 0 or croak $? +sub eol_cp { + my ($from, $to) = @_; + my $es = safe_qx(qw/svn propget svn:eol-style/, $to); + open my $rfd, '<', $from or croak $!; + binmode $rfd or croak $!; + open my $wfd, '>', $to or croak $!; + binmode $wfd or croak $!; + + my $eol = $EOL{$es} or undef; + if ($eol) { + print "$eol: $from => $to\n"; + } + my $buf; + while (1) { + my ($r, $w, $t); + defined($r = sysread($rfd, $buf, 4096)) or croak $!; + return unless $r; + $buf =~ s/(?:\015|\012|\015\012)/$eol/gs if $eol; + for ($w = 0; $w < $r; $w += $t) { + $t = syswrite($wfd, $buf, $r - $w, $w) or croak $!; + } + } +} + +sub do_update_index { + my ($z_cmd, $cmd, $no_text_base) = @_; + + my $z = open my $p, '-|'; + defined $z or croak $!; + unless ($z) { exec @$z_cmd or croak $! } + + my $pid = open my $ui, '|-'; + defined $pid or croak $!; + unless ($pid) { + exec('git-update-index',"--$cmd",'-z','--stdin') or croak $!; + } + local $/ = "\0"; + while (my $x = <$p>) { + chomp $x; + if (!$no_text_base && lstat $x && ! -l _ && + safe_qx(qw/svn propget svn:keywords/,$x)) { + my $mode = -x _ ? 0755 : 0644; + my ($v,$d,$f) = File::Spec->splitpath($x); + my $tb = File::Spec->catfile($d, '.svn', 'tmp', + 'text-base',"$f.svn-base"); + $tb =~ s#^/##; + unless (-f $tb) { + $tb = File::Spec->catfile($d, '.svn', + 'text-base',"$f.svn-base"); + $tb =~ s#^/##; + } + unlink $x or croak $!; + eol_cp($tb, $x); + chmod(($mode &~ umask), $x) or croak $!; + } + print $ui $x,"\0"; + } + close $ui or croak $!; +} + +sub index_changes { + my $no_text_base = shift; + do_update_index([qw/git-diff-files --name-only -z/], + 'remove', + $no_text_base); + do_update_index([qw/git-ls-files -z --others/, + "--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude"], + 'add', + $no_text_base); } sub s_to_file { @@ -936,7 +1004,7 @@ sub git_commit { defined $pid or croak $!; if ($pid == 0) { $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX; - git_addremove(); + index_changes(); chomp(my $tree = `git-write-tree`); croak if $?; if (exists $tree_map{$tree}) { diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh new file mode 100644 index 0000000000..a98e9d164d --- /dev/null +++ b/contrib/git-svn/t/lib-git-svn.sh @@ -0,0 +1,39 @@ +PATH=$PWD/../:$PATH +if test -d ../../../t +then + cd ../../../t +else + echo "Must be run in contrib/git-svn/t" >&2 + exit 1 +fi + +. ./test-lib.sh + +GIT_DIR=$PWD/.git +GIT_SVN_DIR=$GIT_DIR/git-svn +SVN_TREE=$GIT_SVN_DIR/tree + +svnadmin >/dev/null 2>&1 +if test $? != 1 +then + test_expect_success 'skipping contrib/git-svn test' : + test_done + exit +fi + +svn >/dev/null 2>&1 +if test $? != 1 +then + test_expect_success 'skipping contrib/git-svn test' : + test_done + exit +fi + +svnrepo=$PWD/svnrepo + +set -e + +svnadmin create $svnrepo +svnrepo="file://$svnrepo/test-git-svn" + + diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index 80ad3573db..8b3a0d9029 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -3,48 +3,10 @@ # Copyright (c) 2006 Eric Wong # - -PATH=$PWD/../:$PATH test_description='git-svn tests' -if test -d ../../../t -then - cd ../../../t -else - echo "Must be run in contrib/git-svn/t" >&2 - exit 1 -fi - -. ./test-lib.sh - -GIT_DIR=$PWD/.git -GIT_SVN_DIR=$GIT_DIR/git-svn -SVN_TREE=$GIT_SVN_DIR/tree - -svnadmin >/dev/null 2>&1 -if test $? != 1 -then - test_expect_success 'skipping contrib/git-svn test' : - test_done - exit -fi - -svn >/dev/null 2>&1 -if test $? != 1 -then - test_expect_success 'skipping contrib/git-svn test' : - test_done - exit -fi - -svnrepo=$PWD/svnrepo - -set -e - -svnadmin create $svnrepo -svnrepo="file://$svnrepo/test-git-svn" +. ./lib-git-svn.sh mkdir import - cd import echo foo > foo @@ -55,10 +17,9 @@ mkdir -p bar echo 'zzz' > bar/zzz echo '#!/bin/sh' > exec.sh chmod +x exec.sh -svn import -m 'import for git-svn' . $svnrepo >/dev/null +svn import -m 'import for git-svn' . "$svnrepo" >/dev/null cd .. - rm -rf import test_expect_success \ diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh new file mode 100644 index 0000000000..6fa7889e9a --- /dev/null +++ b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh @@ -0,0 +1,125 @@ +#!/bin/sh +# +# Copyright (c) 2006 Eric Wong +# + +test_description='git-svn property tests' +. ./lib-git-svn.sh + +mkdir import + +a_crlf= +a_lf= +a_cr= +a_ne_crlf= +a_ne_lf= +a_ne_cr= +a_empty= +a_empty_lf= +a_empty_cr= +a_empty_crlf= + +cd import + cat >> kw.c <<'' +/* Make it look like somebody copied a file from CVS into SVN: */ +/* $Id: kw.c,v 1.1.1.1 1994/03/06 00:00:00 eric Exp $ */ + + printf "Hello\r\nWorld\r\n" > crlf + a_crlf=`git-hash-object -w crlf` + printf "Hello\rWorld\r" > cr + a_cr=`git-hash-object -w cr` + printf "Hello\nWorld\n" > lf + a_lf=`git-hash-object -w lf` + + printf "Hello\r\nWorld" > ne_crlf + a_ne_crlf=`git-hash-object -w ne_crlf` + printf "Hello\nWorld" > ne_lf + a_ne_lf=`git-hash-object -w ne_lf` + printf "Hello\rWorld" > ne_cr + a_ne_cr=`git-hash-object -w ne_cr` + + touch empty + a_empty=`git-hash-object -w empty` + printf "\n" > empty_lf + a_empty_lf=`git-hash-object -w empty_lf` + printf "\r" > empty_cr + a_empty_cr=`git-hash-object -w empty_cr` + printf "\r\n" > empty_crlf + a_empty_crlf=`git-hash-object -w empty_crlf` + + svn import -m 'import for git-svn' . "$svnrepo" >/dev/null +cd .. + +rm -rf import +svn co "$svnrepo" test_wc + +cd test_wc + echo 'Greetings' >> kw.c + svn commit -m 'Not yet an $Id$' + svn up + + echo 'Hello world' >> kw.c + svn commit -m 'Modified file, but still not yet an $Id$' + svn up + + svn propset svn:keywords Id kw.c + svn commit -m 'Propset $Id$' + svn up +cd .. + +git-svn init "$svnrepo" +git-svn fetch + +git checkout -b mybranch remotes/git-svn +echo 'Hi again' >> kw.c +name='test svn:keywords ignoring' + +git commit -a -m "$name" +git-svn commit remotes/git-svn..mybranch +git pull . remotes/git-svn + +expect='/* $Id$ */' +got="`sed -ne 2p kw.c`" +test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'" + +cd test_wc + svn propset svn:eol-style CR empty + svn propset svn:eol-style CR crlf + svn propset svn:eol-style CR ne_crlf + svn commit -m 'propset CR on crlf files' + svn up +cd .. + +git-svn fetch +git pull . remotes/git-svn + +svn co "$svnrepo" new_wc +for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf +do + test_expect_success "Comparing $i" "cmp $i new_wc/$i" +done + + +cd test_wc + printf '$Id$\rHello\rWorld\r' > cr + printf '$Id$\rHello\rWorld' > ne_cr + a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git-hash-object --stdin` + a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git-hash-object --stdin` + svn propset svn:eol-style CRLF cr + svn propset svn:eol-style CRLF ne_cr + svn propset svn:keywords Id cr + svn propset svn:keywords Id ne_cr + svn commit -m 'propset CRLF on cr files' + svn up +cd .. + +git-svn fetch +git pull . remotes/git-svn + +b_cr="`git-hash-object cr`" +b_ne_cr="`git-hash-object ne_cr`" + +test_expect_success 'CRLF + $Id$' "test '$a_cr' = '$b_cr'" +test_expect_success 'CRLF + $Id$ (no newline)' "test '$a_ne_cr' = '$b_ne_cr'" + +test_done From fad70686b25c414b6224406d42a4dfb7c4136e85 Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 23 May 2006 05:34:23 -0400 Subject: [PATCH 87/90] --summary output should print immediately after stats. Currently the summary is displayed after the patch. Fix this so that the output order is stat-summary-patch. As a consequence of the way this is coded, the --summary option will only actually display summary data if combined with either the --stat or --patch-with-stat option. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano --- diff.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/diff.c b/diff.c index 3a2a175c7e..77c09a87b0 100644 --- a/diff.c +++ b/diff.c @@ -1988,6 +1988,9 @@ void diff_flush(struct diff_options *options) show_stats(diffstat); free(diffstat); diffstat = NULL; + if (options->summary) + for (i = 0; i < q->nr; i++) + diff_summary(q->queue[i]); putchar(options->line_termination); } for (i = 0; i < q->nr; i++) { @@ -2001,7 +2004,7 @@ void diff_flush(struct diff_options *options) } for (i = 0; i < q->nr; i++) { - if (options->summary) + if (diffstat && options->summary) diff_summary(q->queue[i]); diff_free_filepair(q->queue[i]); } From f81daefe56b3c97b93a851e1ada14eeca0dea47a Mon Sep 17 00:00:00 2001 From: Timo Hirvonen Date: Wed, 24 May 2006 14:08:46 +0300 Subject: [PATCH 88/90] Builtin git-cat-file Signed-off-by: Timo Hirvonen Signed-off-by: Junio C Hamano --- Makefile | 6 +++--- cat-file.c => builtin-cat-file.c | 3 ++- builtin.h | 1 + git.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) rename cat-file.c => builtin-cat-file.c (97%) diff --git a/Makefile b/Makefile index 7e6517f62b..dbf19c6277 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,6 @@ SIMPLE_PROGRAMS = \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ - git-cat-file$X \ git-checkout-index$X git-clone-pack$X \ git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ @@ -174,7 +173,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X \ git-ls-files$X git-ls-tree$X \ git-read-tree$X git-commit-tree$X \ git-apply$X git-show-branch$X git-diff-files$X \ - git-diff-index$X git-diff-stages$X git-diff-tree$X + git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -228,7 +227,8 @@ BUILTIN_OBJS = \ builtin-ls-files.o builtin-ls-tree.o \ builtin-read-tree.o builtin-commit-tree.o \ builtin-apply.o builtin-show-branch.o builtin-diff-files.o \ - builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o + builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \ + builtin-cat-file.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/cat-file.c b/builtin-cat-file.c similarity index 97% rename from cat-file.c rename to builtin-cat-file.c index 7413feed78..8ab136e981 100644 --- a/cat-file.c +++ b/builtin-cat-file.c @@ -7,6 +7,7 @@ #include "exec_cmd.h" #include "tag.h" #include "tree.h" +#include "builtin.h" static void flush_buffer(const char *buf, unsigned long size) { @@ -93,7 +94,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long return 0; } -int main(int argc, char **argv) +int cmd_cat_file(int argc, const char **argv, char **envp) { unsigned char sha1[20]; char type[20]; diff --git a/builtin.h b/builtin.h index 714b97578c..738ec3d945 100644 --- a/builtin.h +++ b/builtin.h @@ -42,5 +42,6 @@ extern int cmd_diff_files(int argc, const char **argv, char **envp); extern int cmd_diff_index(int argc, const char **argv, char **envp); extern int cmd_diff_stages(int argc, const char **argv, char **envp); extern int cmd_diff_tree(int argc, const char **argv, char **envp); +extern int cmd_cat_file(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 5a884bb07a..10ea934bcf 100644 --- a/git.c +++ b/git.c @@ -68,7 +68,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "diff-files", cmd_diff_files }, { "diff-index", cmd_diff_index }, { "diff-stages", cmd_diff_stages }, - { "diff-tree", cmd_diff_tree } + { "diff-tree", cmd_diff_tree }, + { "cat-file", cmd_cat_file } }; int i; From 4d548150ace0816dd5fe678cdbde75b13d5e5249 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 24 May 2006 08:30:54 -0700 Subject: [PATCH 89/90] Clean up sha1 file writing This cleans up and future-proofs the sha1 file writing in sha1_file.c. In particular, instead of doing a simple "write()" call and just verifying that it succeeds (or - as in one place - just assuming it does), it uses "write_buffer()" to write data to the file descriptor while correctly checking for partial writes, EINTR etc. It also splits up write_sha1_to_fd() to be a lot more readable: if we need to re-create the compressed object, we do so in a separate helper function, making the logic a whole lot more modular and obvious. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- sha1_file.c | 145 +++++++++++++++++++++++++++++----------------------- 1 file changed, 81 insertions(+), 64 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index e444d9df1b..f77c18934a 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1399,6 +1399,25 @@ int move_temp_to_file(const char *tmpfile, char *filename) return 0; } +static int write_buffer(int fd, const void *buf, size_t len) +{ + while (len) { + ssize_t size; + + size = write(fd, buf, len); + if (!size) + return error("file write: disk full"); + if (size < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return error("file write error (%s)", strerror(errno)); + } + len -= size; + buf += size; + } + return 0; +} + int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) { int size; @@ -1465,8 +1484,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha deflateEnd(&stream); size = stream.total_out; - if (write(fd, compressed, size) != size) - die("unable to write file"); + if (write_buffer(fd, compressed, size) < 0) + die("unable to write sha1 file"); fchmod(fd, 0444); close(fd); free(compressed); @@ -1474,73 +1493,70 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha return move_temp_to_file(tmpfile, filename); } +/* + * We need to unpack and recompress the object for writing + * it out to a different file. + */ +static void *repack_object(const unsigned char *sha1, unsigned long *objsize) +{ + size_t size; + z_stream stream; + unsigned char *unpacked; + unsigned long len; + char type[20]; + char hdr[50]; + int hdrlen; + void *buf; + + // need to unpack and recompress it by itself + unpacked = read_packed_sha1(sha1, type, &len); + + hdrlen = sprintf(hdr, "%s %lu", type, len) + 1; + + /* Set it up */ + memset(&stream, 0, sizeof(stream)); + deflateInit(&stream, Z_BEST_COMPRESSION); + size = deflateBound(&stream, len + hdrlen); + buf = xmalloc(size); + + /* Compress it */ + stream.next_out = buf; + stream.avail_out = size; + + /* First header.. */ + stream.next_in = (void *)hdr; + stream.avail_in = hdrlen; + while (deflate(&stream, 0) == Z_OK) + /* nothing */; + + /* Then the data itself.. */ + stream.next_in = unpacked; + stream.avail_in = len; + while (deflate(&stream, Z_FINISH) == Z_OK) + /* nothing */; + deflateEnd(&stream); + free(unpacked); + + *objsize = stream.total_out; + return buf; +} + int write_sha1_to_fd(int fd, const unsigned char *sha1) { - ssize_t size; + int retval; unsigned long objsize; - int posn = 0; - void *map = map_sha1_file_internal(sha1, &objsize); - void *buf = map; - void *temp_obj = NULL; - z_stream stream; + void *buf = map_sha1_file_internal(sha1, &objsize); - if (!buf) { - unsigned char *unpacked; - unsigned long len; - char type[20]; - char hdr[50]; - int hdrlen; - // need to unpack and recompress it by itself - unpacked = read_packed_sha1(sha1, type, &len); - - hdrlen = sprintf(hdr, "%s %lu", type, len) + 1; - - /* Set it up */ - memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, Z_BEST_COMPRESSION); - size = deflateBound(&stream, len + hdrlen); - temp_obj = buf = xmalloc(size); - - /* Compress it */ - stream.next_out = buf; - stream.avail_out = size; - - /* First header.. */ - stream.next_in = (void *)hdr; - stream.avail_in = hdrlen; - while (deflate(&stream, 0) == Z_OK) - /* nothing */; - - /* Then the data itself.. */ - stream.next_in = unpacked; - stream.avail_in = len; - while (deflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; - deflateEnd(&stream); - free(unpacked); - - objsize = stream.total_out; + if (buf) { + retval = write_buffer(fd, buf, objsize); + munmap(buf, objsize); + return retval; } - do { - size = write(fd, buf + posn, objsize - posn); - if (size <= 0) { - if (!size) { - fprintf(stderr, "write closed\n"); - } else { - perror("write "); - } - return -1; - } - posn += size; - } while (posn < objsize); - - if (map) - munmap(map, objsize); - if (temp_obj) - free(temp_obj); - - return 0; + buf = repack_object(sha1, &objsize); + retval = write_buffer(fd, buf, objsize); + free(buf); + return retval; } int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, @@ -1579,7 +1595,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, SHA1_Update(&c, discard, sizeof(discard) - stream.avail_out); } while (stream.avail_in && ret == Z_OK); - write(local, buffer, *bufposn - stream.avail_in); + if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0) + die("unable to write sha1 file"); memmove(buffer, buffer + *bufposn - stream.avail_in, stream.avail_in); *bufposn = stream.avail_in; From 84c667ff97da2a3864ef7952b9f438d133ce35ef Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 24 May 2006 16:42:38 -0700 Subject: [PATCH 90/90] fetch.c: remove an unused variable and dead code. Funnily enough, this variable was never assigned ever since it was introduced, and has been protecting some code that has never been executed. Signed-off-by: Junio C Hamano --- fetch.c | 20 +++----------------- fetch.h | 3 --- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/fetch.c b/fetch.c index 73bde07aea..f7f8902580 100644 --- a/fetch.c +++ b/fetch.c @@ -9,8 +9,6 @@ const char *write_ref = NULL; -const unsigned char *current_ref = NULL; - int get_tree = 0; int get_history = 0; int get_all = 0; @@ -205,19 +203,12 @@ static int mark_complete(const char *path, const unsigned char *sha1) int pull(char *target) { unsigned char sha1[20]; - int fd = -1; save_commit_buffer = 0; track_object_refs = 0; - if (write_ref && current_ref) { - fd = lock_ref_sha1(write_ref, current_ref); - if (fd < 0) - return -1; - } - if (!get_recover) { + if (!get_recover) for_each_ref(mark_complete); - } if (interpret_target(target, sha1)) return error("Could not interpret %s as something to pull", @@ -227,12 +218,7 @@ int pull(char *target) if (loop()) return -1; - if (write_ref) { - if (current_ref) { - write_ref_sha1(write_ref, fd, sha1); - } else { - write_ref_sha1_unlocked(write_ref, sha1); - } - } + if (write_ref) + write_ref_sha1_unlocked(write_ref, sha1); return 0; } diff --git a/fetch.h b/fetch.h index 9837a3d035..001a6b8e2d 100644 --- a/fetch.h +++ b/fetch.h @@ -25,9 +25,6 @@ extern int fetch_ref(char *ref, unsigned char *sha1); /* If set, the ref filename to write the target value to. */ extern const char *write_ref; -/* If set, the hash that the current value of write_ref must be. */ -extern const unsigned char *current_ref; - /* Set to fetch the target tree. */ extern int get_tree;