From 62e09ce998dd7f6b844deb650101c743a5c4ce50 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Fri, 20 Jul 2007 01:42:28 +0200 Subject: [PATCH 001/107] Make git tag a builtin. This replaces the script "git-tag.sh" with "builtin-tag.c". The existing test suite for "git tag" guarantees the compatibility with the features provided by the script version. There are some minor changes in the behaviour of "git tag" here: "git tag -v" now can get more than one tag to verify, like "git tag -d" does, "git tag" with no arguments prints all tags, more like "git branch" does, and "git tag -n" also prints all tags with annotations (without needing -l). Tests and documentation were also updated to reflect these changes. The program is currently calling the script "git verify-tag" for verify. This can be changed porting it to C and calling its functions directly from builtin-tag.c. Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 8 +- Makefile | 3 +- builtin-tag.c | 450 ++++++++++++++++++++++ builtin.h | 1 + git-tag.sh => contrib/examples/git-tag.sh | 0 git.c | 1 + t/t7004-tag.sh | 88 ++++- 7 files changed, 538 insertions(+), 13 deletions(-) create mode 100644 builtin-tag.c rename git-tag.sh => contrib/examples/git-tag.sh (100%) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index aee2c1bdc7..119117f0bd 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git-tag' [-a | -s | -u ] [-f] [-m | -F ] [] 'git-tag' -d ... 'git-tag' [-n []] -l [] -'git-tag' -v +'git-tag' -v ... DESCRIPTION ----------- @@ -23,7 +23,7 @@ Unless `-f` is given, the tag must not yet exist in If one of `-a`, `-s`, or `-u ` is passed, the command creates a 'tag' object, and requires the tag message. Unless -`-m ` is given, an editor is started for the user to type +`-m ` or `-F ` is given, an editor is started for the user to type in the tag message. Otherwise just the SHA1 object name of the commit object is @@ -59,15 +59,17 @@ OPTIONS Delete existing tags with the given names. -v:: - Verify the gpg signature of given the tag + Verify the gpg signature of the given tag names. -n :: specifies how many lines from the annotation, if any, are printed when using -l. The default is not to print any annotation lines. + If no number is given to `-n`, only the first line is printed. -l :: List tags with names that match the given pattern (or all if no pattern is given). + Typing "git tag" without arguments, also lists all tags. -m :: Use the given tag message (instead of prompting) diff --git a/Makefile b/Makefile index 73b487fba9..8db6646245 100644 --- a/Makefile +++ b/Makefile @@ -206,7 +206,7 @@ SCRIPT_SH = \ git-pull.sh git-rebase.sh git-rebase--interactive.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ git-sh-setup.sh \ - git-tag.sh git-verify-tag.sh \ + git-verify-tag.sh \ git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ git-merge-resolve.sh git-merge-ours.sh \ @@ -361,6 +361,7 @@ BUILTIN_OBJS = \ builtin-show-branch.o \ builtin-stripspace.o \ builtin-symbolic-ref.o \ + builtin-tag.o \ builtin-tar-tree.o \ builtin-unpack-objects.o \ builtin-update-index.o \ diff --git a/builtin-tag.c b/builtin-tag.c new file mode 100644 index 0000000000..507f51076d --- /dev/null +++ b/builtin-tag.c @@ -0,0 +1,450 @@ +/* + * Builtin "git tag" + * + * Copyright (c) 2007 Kristian Høgsberg , + * Carlos Rica + * Based on git-tag.sh and mktag.c by Linus Torvalds. + */ + +#include "cache.h" +#include "builtin.h" +#include "refs.h" +#include "tag.h" +#include "run-command.h" + +static const char builtin_tag_usage[] = + "git-tag [-n []] -l [] | [-a | -s | -u ] [-f | -d | -v] [-m | -F ] []"; + +static char signingkey[1000]; + +static void launch_editor(const char *path, char **buffer, unsigned long *len) +{ + const char *editor, *terminal; + struct child_process child; + const char *args[3]; + int fd; + + editor = getenv("VISUAL"); + if (!editor) + editor = getenv("EDITOR"); + + terminal = getenv("TERM"); + if (!editor && (!terminal || !strcmp(terminal, "dumb"))) { + fprintf(stderr, + "Terminal is dumb but no VISUAL nor EDITOR defined.\n" + "Please supply the message using either -m or -F option.\n"); + exit(1); + } + + if (!editor) + editor = "vi"; + + memset(&child, 0, sizeof(child)); + child.argv = args; + args[0] = editor; + args[1] = path; + args[2] = NULL; + + if (run_command(&child)) + die("There was a problem with the editor %s.", editor); + + fd = open(path, O_RDONLY); + if (fd < 0) + die("could not open '%s': %s", path, strerror(errno)); + if (read_fd(fd, buffer, len)) { + free(*buffer); + die("could not read message file '%s': %s", + path, strerror(errno)); + } + close(fd); +} + +struct tag_filter { + const char *pattern; + int lines; +}; + +#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----" + +static int show_reference(const char *refname, const unsigned char *sha1, + int flag, void *cb_data) +{ + struct tag_filter *filter = cb_data; + + if (!fnmatch(filter->pattern, refname, 0)) { + int i; + unsigned long size; + enum object_type type; + char *buf, *sp, *eol; + size_t len; + + if (!filter->lines) { + printf("%s\n", refname); + return 0; + } + printf("%-15s ", refname); + + sp = buf = read_sha1_file(sha1, &type, &size); + if (!buf || !size) + return 0; + /* skip header */ + while (sp + 1 < buf + size && + !(sp[0] == '\n' && sp[1] == '\n')) + sp++; + /* only take up to "lines" lines, and strip the signature */ + for (i = 0, sp += 2; i < filter->lines && sp < buf + size && + prefixcmp(sp, PGP_SIGNATURE "\n"); + i++) { + if (i) + printf("\n "); + eol = memchr(sp, '\n', size - (sp - buf)); + len = eol ? eol - sp : size - (sp - buf); + fwrite(sp, len, 1, stdout); + if (!eol) + break; + sp = eol + 1; + } + putchar('\n'); + free(buf); + } + + return 0; +} + +static int list_tags(const char *pattern, int lines) +{ + struct tag_filter filter; + char *newpattern; + + if (pattern == NULL) + pattern = ""; + + /* prepend/append * to the shell pattern: */ + newpattern = xmalloc(strlen(pattern) + 3); + sprintf(newpattern, "*%s*", pattern); + + filter.pattern = newpattern; + filter.lines = lines; + + for_each_tag_ref(show_reference, (void *) &filter); + + free(newpattern); + + return 0; +} + +typedef int (*func_tag)(const char *name, const char *ref, + const unsigned char *sha1); + +static int do_tag_names(const char **argv, func_tag fn) +{ + const char **p; + char ref[PATH_MAX]; + int had_error = 0; + unsigned char sha1[20]; + + for (p = argv; *p; p++) { + if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p) + >= sizeof(ref)) { + error("tag name too long: %.*s...", 50, *p); + had_error = 1; + continue; + } + if (!resolve_ref(ref, sha1, 1, NULL)) { + error("tag '%s' not found.", *p); + had_error = 1; + continue; + } + if (fn(*p, ref, sha1)) + had_error = 1; + } + return had_error; +} + +static int delete_tag(const char *name, const char *ref, + const unsigned char *sha1) +{ + if (delete_ref(ref, sha1)) + return 1; + printf("Deleted tag '%s'\n", name); + return 0; +} + +static int verify_tag(const char *name, const char *ref, + const unsigned char *sha1) +{ + const char *argv_verify_tag[] = {"git-verify-tag", + "-v", "SHA1_HEX", NULL}; + argv_verify_tag[2] = sha1_to_hex(sha1); + + if (run_command_v_opt(argv_verify_tag, 0)) + return error("could not verify the tag '%s'", name); + return 0; +} + +static ssize_t do_sign(char *buffer, size_t size, size_t max) +{ + struct child_process gpg; + const char *args[4]; + char *bracket; + int len; + + if (!*signingkey) { + if (strlcpy(signingkey, git_committer_info(1), + sizeof(signingkey)) >= sizeof(signingkey)) + return error("committer info too long."); + bracket = strchr(signingkey, '>'); + if (bracket) + bracket[1] = '\0'; + } + + memset(&gpg, 0, sizeof(gpg)); + gpg.argv = args; + gpg.in = -1; + gpg.out = -1; + args[0] = "gpg"; + args[1] = "-bsau"; + args[2] = signingkey; + args[3] = NULL; + + if (start_command(&gpg)) + return error("could not run gpg."); + + write_or_die(gpg.in, buffer, size); + close(gpg.in); + gpg.close_in = 0; + len = read_in_full(gpg.out, buffer + size, max - size); + + finish_command(&gpg); + + if (len == max - size) + return error("could not read the entire signature from gpg."); + + return size + len; +} + +static const char tag_template[] = + "\n" + "#\n" + "# Write a tag message\n" + "#\n"; + +static int git_tag_config(const char *var, const char *value) +{ + if (!strcmp(var, "user.signingkey")) { + if (!value) + die("user.signingkey without value"); + if (strlcpy(signingkey, value, sizeof(signingkey)) + >= sizeof(signingkey)) + die("user.signingkey value too long"); + return 0; + } + + return git_default_config(var, value); +} + +#define MAX_SIGNATURE_LENGTH 1024 +/* message must be NULL or allocated, it will be reallocated and freed */ +static void create_tag(const unsigned char *object, const char *tag, + char *message, int sign, unsigned char *result) +{ + enum object_type type; + char header_buf[1024], *buffer; + int header_len, max_size; + unsigned long size; + + type = sha1_object_info(object, NULL); + if (type <= 0) + die("bad object type."); + + header_len = snprintf(header_buf, sizeof(header_buf), + "object %s\n" + "type %s\n" + "tag %s\n" + "tagger %s\n\n", + sha1_to_hex(object), + typename(type), + tag, + git_committer_info(1)); + + if (header_len >= sizeof(header_buf)) + die("tag header too big."); + + if (!message) { + char *path; + int fd; + + /* write the template message before editing: */ + path = xstrdup(git_path("TAG_EDITMSG")); + fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (fd < 0) + die("could not create file '%s': %s", + path, strerror(errno)); + write_or_die(fd, tag_template, strlen(tag_template)); + close(fd); + + launch_editor(path, &buffer, &size); + + unlink(path); + free(path); + } + else { + buffer = message; + size = strlen(message); + } + + size = stripspace(buffer, size, 1); + + if (!message && !size) + die("no tag message?"); + + /* insert the header and add the '\n' if needed: */ + max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1; + buffer = xrealloc(buffer, max_size); + if (size) + buffer[size++] = '\n'; + memmove(buffer + header_len, buffer, size); + memcpy(buffer, header_buf, header_len); + size += header_len; + + if (sign) { + size = do_sign(buffer, size, max_size); + if (size < 0) + die("unable to sign the tag"); + } + + if (write_sha1_file(buffer, size, tag_type, result) < 0) + die("unable to write tag file"); + free(buffer); +} + +int cmd_tag(int argc, const char **argv, const char *prefix) +{ + unsigned char object[20], prev[20]; + int annotate = 0, sign = 0, force = 0, lines = 0; + char *message = NULL; + char ref[PATH_MAX]; + const char *object_ref, *tag; + int i; + struct ref_lock *lock; + + git_config(git_tag_config); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (arg[0] != '-') + break; + if (!strcmp(arg, "-a")) { + annotate = 1; + continue; + } + if (!strcmp(arg, "-s")) { + annotate = 1; + sign = 1; + continue; + } + if (!strcmp(arg, "-f")) { + force = 1; + continue; + } + if (!strcmp(arg, "-n")) { + if (i + 1 == argc || *argv[i + 1] == '-') + /* no argument */ + lines = 1; + else + lines = isdigit(*argv[++i]) ? + atoi(argv[i]) : 1; + continue; + } + if (!strcmp(arg, "-m")) { + annotate = 1; + i++; + if (i == argc) + die("option -m needs an argument."); + message = xstrdup(argv[i]); + continue; + } + if (!strcmp(arg, "-F")) { + unsigned long len; + int fd; + + annotate = 1; + i++; + if (i == argc) + die("option -F needs an argument."); + + if (!strcmp(argv[i], "-")) + fd = 0; + else { + fd = open(argv[i], O_RDONLY); + if (fd < 0) + die("could not open '%s': %s", + argv[i], strerror(errno)); + } + len = 1024; + message = xmalloc(len); + if (read_fd(fd, &message, &len)) { + free(message); + die("cannot read %s", argv[i]); + } + continue; + } + if (!strcmp(arg, "-u")) { + annotate = 1; + sign = 1; + i++; + if (i == argc) + die("option -u needs an argument."); + if (strlcpy(signingkey, argv[i], sizeof(signingkey)) + >= sizeof(signingkey)) + die("argument to option -u too long"); + continue; + } + if (!strcmp(arg, "-l")) { + return list_tags(argv[i + 1], lines); + } + if (!strcmp(arg, "-d")) { + return do_tag_names(argv + i + 1, delete_tag); + } + if (!strcmp(arg, "-v")) { + return do_tag_names(argv + i + 1, verify_tag); + } + usage(builtin_tag_usage); + } + + if (i == argc) { + if (annotate) + usage(builtin_tag_usage); + return list_tags(NULL, lines); + } + tag = argv[i++]; + + object_ref = i < argc ? argv[i] : "HEAD"; + if (i + 1 < argc) + die("too many params"); + + if (get_sha1(object_ref, object)) + die("Failed to resolve '%s' as a valid ref.", object_ref); + + if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) >= sizeof(ref)) + die("tag name too long: %.*s...", 50, tag); + if (check_ref_format(ref)) + die("'%s' is not a valid tag name.", tag); + + if (!resolve_ref(ref, prev, 1, NULL)) + hashclr(prev); + else if (!force) + die("tag '%s' already exists", tag); + + if (annotate) + create_tag(object, tag, message, sign, object); + + lock = lock_any_ref_for_update(ref, prev, 0); + if (!lock) + die("%s: cannot lock the ref", ref); + if (write_ref_sha1(lock, object, NULL) < 0) + die("%s: cannot update the ref", ref); + + return 0; +} diff --git a/builtin.h b/builtin.h index 4cc228dace..ac7417f7c5 100644 --- a/builtin.h +++ b/builtin.h @@ -70,6 +70,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix); extern int cmd_show_branch(int argc, const char **argv, const char *prefix); extern int cmd_stripspace(int argc, const char **argv, const char *prefix); extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix); +extern int cmd_tag(int argc, const char **argv, const char *prefix); extern int cmd_tar_tree(int argc, const char **argv, const char *prefix); extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix); extern int cmd_update_index(int argc, const char **argv, const char *prefix); diff --git a/git-tag.sh b/contrib/examples/git-tag.sh similarity index 100% rename from git-tag.sh rename to contrib/examples/git-tag.sh diff --git a/git.c b/git.c index a647f9c61e..eb9e5ca972 100644 --- a/git.c +++ b/git.c @@ -363,6 +363,7 @@ static void handle_internal_command(int argc, const char **argv) { "show", cmd_show, RUN_SETUP | USE_PAGER }, { "stripspace", cmd_stripspace }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, + { "tag", cmd_tag, RUN_SETUP }, { "tar-tree", cmd_tar_tree }, { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP }, diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 17de2a90e6..a0be164619 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -5,7 +5,7 @@ test_description='git-tag -Basic tests for operations with tags.' +Tests for operations with tags.' . ./test-lib.sh @@ -16,11 +16,15 @@ tag_exists () { } # todo: git tag -l now returns always zero, when fixed, change this test -test_expect_success 'listing all tags in an empty tree should succeed' \ - 'git tag -l' +test_expect_success 'listing all tags in an empty tree should succeed' ' + git tag -l && + git tag +' -test_expect_success 'listing all tags in an empty tree should output nothing' \ - 'test `git-tag -l | wc -l` -eq 0' +test_expect_success 'listing all tags in an empty tree should output nothing' ' + test `git-tag -l | wc -l` -eq 0 && + test `git-tag | wc -l` -eq 0 +' test_expect_failure 'looking for a tag in an empty tree should fail' \ 'tag_exists mytag' @@ -49,11 +53,15 @@ test_expect_success 'creating a tag using default HEAD should succeed' ' git tag mytag ' -test_expect_success 'listing all tags if one exists should succeed' \ - 'git-tag -l' +test_expect_success 'listing all tags if one exists should succeed' ' + git-tag -l && + git-tag +' -test_expect_success 'listing all tags if one exists should output that tag' \ - 'test `git-tag -l` = mytag' +test_expect_success 'listing all tags if one exists should output that tag' ' + test `git-tag -l` = mytag && + test `git-tag` = mytag +' # pattern matching: @@ -165,6 +173,8 @@ test_expect_success 'listing all tags should print them ordered' ' git tag v1.0 && git tag t210 && git tag -l > actual && + git diff expect actual && + git tag > actual && git diff expect actual ' @@ -264,6 +274,10 @@ test_expect_failure \ 'trying to verify a non-annotated and non-signed tag should fail' \ 'git-tag -v non-annotated-tag' +test_expect_failure \ + 'trying to verify many non-annotated or unknown tags, should fail' \ + 'git-tag -v unknown-tag1 non-annotated-tag unknown-tag2' + # creating annotated tags: get_tag_msg () { @@ -306,6 +320,18 @@ test_expect_success \ git diff expect actual ' +cat >inputmsg <expect +cat inputmsg >>expect +test_expect_success 'creating an annotated tag with -F - should succeed' ' + git-tag -F - stdin-annotated-tag actual && + git diff expect actual +' + # blank and empty messages: get_tag_header empty-annotated-tag $commit commit $time >expect @@ -551,6 +577,12 @@ test_expect_success \ ! git-tag -v file-annotated-tag ' +test_expect_success \ + 'trying to verify two annotated non-signed tags should fail' ' + tag_exists annotated-tag file-annotated-tag && + ! git-tag -v annotated-tag file-annotated-tag +' + # creating and verifying signed tags: gpg --version >/dev/null @@ -589,9 +621,47 @@ test_expect_success 'creating a signed tag with -m message should succeed' ' git diff expect actual ' +cat >sigmsgfile <expect +cat sigmsgfile >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success \ + 'creating a signed tag with -F messagefile should succeed' ' + git-tag -s -F sigmsgfile file-signed-tag && + get_tag_msg file-signed-tag >actual && + git diff expect actual +' + +cat >siginputmsg <expect +cat siginputmsg >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success 'creating a signed tag with -F - should succeed' ' + git-tag -s -F - stdin-signed-tag actual && + git diff expect actual +' + test_expect_success 'verifying a signed tag should succeed' \ 'git-tag -v signed-tag' +test_expect_success 'verifying two signed tags in one command should succeed' \ + 'git-tag -v signed-tag file-signed-tag' + +test_expect_success \ + 'verifying many signed and non-signed tags should fail' ' + ! git-tag -v signed-tag annotated-tag && + ! git-tag -v file-annotated-tag file-signed-tag && + ! git-tag -v annotated-tag file-signed-tag file-annotated-tag && + ! git-tag -v signed-tag annotated-tag file-signed-tag +' + test_expect_success 'verifying a forged tag should fail' ' forged=$(git cat-file tag signed-tag | sed -e "s/signed-tag/forged-tag/" | From 4d87b9c5db2590f7616fedfc0a538fc78f528e14 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 20 Jul 2007 13:06:09 +0100 Subject: [PATCH 002/107] launch_editor(): Heed GIT_EDITOR and core.editor settings In the commit 'Add GIT_EDITOR environment and core.editor configuration variables', this was done for the shell scripts. Port it over to builtin-tag's version of launch_editor(), which is just about to be refactored into editor.c. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-tag.c | 10 +++++++--- cache.h | 2 ++ config.c | 5 +++++ environment.c | 1 + 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/builtin-tag.c b/builtin-tag.c index 507f51076d..81d37ce901 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -24,7 +24,11 @@ static void launch_editor(const char *path, char **buffer, unsigned long *len) const char *args[3]; int fd; - editor = getenv("VISUAL"); + editor = getenv("GIT_EDITOR"); + if (!editor && editor_program) + editor = editor_program; + if (!editor) + editor = getenv("VISUAL"); if (!editor) editor = getenv("EDITOR"); @@ -249,9 +253,9 @@ static void create_tag(const unsigned char *object, const char *tag, char *message, int sign, unsigned char *result) { enum object_type type; - char header_buf[1024], *buffer; + char header_buf[1024], *buffer = NULL; int header_len, max_size; - unsigned long size; + unsigned long size = 0; type = sha1_object_info(object, NULL); if (type <= 0) diff --git a/cache.h b/cache.h index 53801b8089..67b1af16d9 100644 --- a/cache.h +++ b/cache.h @@ -560,6 +560,8 @@ extern char *pager_program; extern int pager_in_use; extern int pager_use_color; +extern char *editor_program; + /* base85 */ int decode_85(char *dst, const char *line, int linelen); void encode_85(char *buf, const unsigned char *data, int bytes); diff --git a/config.c b/config.c index f89a611915..103b4fcc61 100644 --- a/config.c +++ b/config.c @@ -426,6 +426,11 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.editor")) { + editor_program = xstrdup(value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } diff --git a/environment.c b/environment.c index f83fb9e448..35d3f4b595 100644 --- a/environment.c +++ b/environment.c @@ -33,6 +33,7 @@ size_t delta_base_cache_limit = 16 * 1024 * 1024; char *pager_program; int pager_in_use; int pager_use_color = 1; +char *editor_program; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ static const char *git_dir; From e317cfafd247b279055e9ee64a6a982043bd06e7 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Sat, 21 Jul 2007 14:13:12 +0200 Subject: [PATCH 003/107] builtin-tag.c: Fix two memory leaks and minor notation changes. A repeated call to read_sha1_file was not freing memory when the buffer was allocated but returned size was zero. Also, now the program does not allow many -F or -m options, which was a bug too because it was not freing the memory allocated for any previous -F or -m options. Tests are provided for ensuring that only one option -F or -m is given. Also, another test is shipped here, to check that "git tag" fails when a non-existing file is passed to the -F option, something that git-tag.sh allowed creating the tag with an empty message. Signed-off-by: Carlos Rica Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-tag.c | 38 ++++++++++++++++++++++---------------- t/t7004-tag.sh | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/builtin-tag.c b/builtin-tag.c index 81d37ce901..d6d38ad123 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -89,14 +89,19 @@ static int show_reference(const char *refname, const unsigned char *sha1, printf("%-15s ", refname); sp = buf = read_sha1_file(sha1, &type, &size); - if (!buf || !size) + if (!buf) return 0; + if (!size) { + free(buf); + return 0; + } /* skip header */ while (sp + 1 < buf + size && !(sp[0] == '\n' && sp[1] == '\n')) sp++; /* only take up to "lines" lines, and strip the signature */ - for (i = 0, sp += 2; i < filter->lines && sp < buf + size && + for (i = 0, sp += 2; + i < filter->lines && sp < buf + size && prefixcmp(sp, PGP_SIGNATURE "\n"); i++) { if (i) @@ -137,10 +142,10 @@ static int list_tags(const char *pattern, int lines) return 0; } -typedef int (*func_tag)(const char *name, const char *ref, +typedef int (*each_tag_name_fn)(const char *name, const char *ref, const unsigned char *sha1); -static int do_tag_names(const char **argv, func_tag fn) +static int for_each_tag_name(const char **argv, each_tag_name_fn fn) { const char **p; char ref[PATH_MAX]; @@ -195,7 +200,7 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max) if (!*signingkey) { if (strlcpy(signingkey, git_committer_info(1), - sizeof(signingkey)) >= sizeof(signingkey)) + sizeof(signingkey)) > sizeof(signingkey) - 1) return error("committer info too long."); bracket = strchr(signingkey, '>'); if (bracket) @@ -258,7 +263,7 @@ static void create_tag(const unsigned char *object, const char *tag, unsigned long size = 0; type = sha1_object_info(object, NULL); - if (type <= 0) + if (type <= OBJ_NONE) die("bad object type."); header_len = snprintf(header_buf, sizeof(header_buf), @@ -271,7 +276,7 @@ static void create_tag(const unsigned char *object, const char *tag, tag, git_committer_info(1)); - if (header_len >= sizeof(header_buf)) + if (header_len > sizeof(header_buf) - 1) die("tag header too big."); if (!message) { @@ -366,6 +371,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) i++; if (i == argc) die("option -m needs an argument."); + if (message) + die("only one -F or -m option is allowed."); message = xstrdup(argv[i]); continue; } @@ -377,6 +384,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) i++; if (i == argc) die("option -F needs an argument."); + if (message) + die("only one -F or -m option is allowed."); if (!strcmp(argv[i], "-")) fd = 0; @@ -405,15 +414,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("argument to option -u too long"); continue; } - if (!strcmp(arg, "-l")) { + if (!strcmp(arg, "-l")) return list_tags(argv[i + 1], lines); - } - if (!strcmp(arg, "-d")) { - return do_tag_names(argv + i + 1, delete_tag); - } - if (!strcmp(arg, "-v")) { - return do_tag_names(argv + i + 1, verify_tag); - } + if (!strcmp(arg, "-d")) + return for_each_tag_name(argv + i + 1, delete_tag); + if (!strcmp(arg, "-v")) + return for_each_tag_name(argv + i + 1, verify_tag); usage(builtin_tag_usage); } @@ -431,7 +437,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (get_sha1(object_ref, object)) die("Failed to resolve '%s' as a valid ref.", object_ref); - if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) >= sizeof(ref)) + if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) > sizeof(ref) - 1) die("tag name too long: %.*s...", 50, tag); if (check_ref_format(ref)) die("'%s' is not a valid tag name.", tag); diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index a0be164619..c4fa4461f7 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -332,6 +332,33 @@ test_expect_success 'creating an annotated tag with -F - should succeed' ' git diff expect actual ' +test_expect_success \ + 'trying to create a tag with a non-existing -F file should fail' ' + ! test -f nonexistingfile && + ! tag_exists notag && + ! git-tag -F nonexistingfile notag && + ! tag_exists notag +' + +test_expect_success \ + 'trying to create tags giving many -m or -F options should fail' ' + echo "message file 1" >msgfile1 && + echo "message file 2" >msgfile2 && + ! tag_exists msgtag && + ! git-tag -m "message 1" -m "message 2" msgtag && + ! tag_exists msgtag && + ! git-tag -F msgfile1 -F msgfile2 msgtag && + ! tag_exists msgtag && + ! git-tag -m "message 1" -F msgfile1 msgtag && + ! tag_exists msgtag && + ! git-tag -F msgfile1 -m "message 1" msgtag && + ! tag_exists msgtag && + ! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag && + ! tag_exists msgtag && + ! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag && + ! tag_exists msgtag +' + # blank and empty messages: get_tag_header empty-annotated-tag $commit commit $time >expect @@ -648,6 +675,14 @@ test_expect_success 'creating a signed tag with -F - should succeed' ' git diff expect actual ' +test_expect_success \ + 'trying to create a signed tag with non-existing -F file should fail' ' + ! test -f nonexistingfile && + ! tag_exists nosigtag && + ! git-tag -s -F nonexistingfile nosigtag && + ! tag_exists nosigtag +' + test_expect_success 'verifying a signed tag should succeed' \ 'git-tag -v signed-tag' From 2ae68fcb785a617793813abcea19893e13e436b0 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Fri, 27 Jul 2007 06:07:34 +0200 Subject: [PATCH 004/107] Make verify-tag a builtin. This replaces "git-verify-tag.sh" with "builtin-verify-tag.c". Testing relies on the "git tag -v" tests calling this command. A temporary file is needed when calling to gpg, because git is already creating detached signatures (gpg option -b) to sign tags (instead of leaving gpg to add the signature to the file by itself), and those signatures need to be supplied in a separate file to be verified by gpg. The program uses git_mkstemp to create that temporary file needed by gpg, instead of the previously used "$GIT_DIR/.tmp-vtag", in order to allow the command to be used in read-only repositories, and also prevent other instances of git to read or remove the same file. Signal SIGPIPE is ignored because the program sometimes was terminated because that signal when writing the input for gpg. The command now can receive many tag names to be verified. Documentation is also updated here to reflect this new behaviour. Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- Documentation/git-verify-tag.txt | 4 +- Makefile | 2 +- builtin-verify-tag.c | 110 ++++++++++++++++++ builtin.h | 1 + .../examples/git-verify-tag.sh | 0 git.c | 1 + 6 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 builtin-verify-tag.c rename git-verify-tag.sh => contrib/examples/git-verify-tag.sh (100%) diff --git a/Documentation/git-verify-tag.txt b/Documentation/git-verify-tag.txt index 48d17fd9c4..ac7fb19154 100644 --- a/Documentation/git-verify-tag.txt +++ b/Documentation/git-verify-tag.txt @@ -3,11 +3,11 @@ git-verify-tag(1) NAME ---- -git-verify-tag - Check the GPG signature of tag +git-verify-tag - Check the GPG signature of tags SYNOPSIS -------- -'git-verify-tag' +'git-verify-tag' ... DESCRIPTION ----------- diff --git a/Makefile b/Makefile index 8db6646245..98670bbd71 100644 --- a/Makefile +++ b/Makefile @@ -206,7 +206,6 @@ SCRIPT_SH = \ git-pull.sh git-rebase.sh git-rebase--interactive.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ git-sh-setup.sh \ - git-verify-tag.sh \ git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ git-merge-resolve.sh git-merge-ours.sh \ @@ -368,6 +367,7 @@ BUILTIN_OBJS = \ builtin-update-ref.o \ builtin-upload-archive.o \ builtin-verify-pack.o \ + builtin-verify-tag.o \ builtin-write-tree.o \ builtin-show-ref.o \ builtin-pack-refs.o diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c new file mode 100644 index 0000000000..dfcfcd0455 --- /dev/null +++ b/builtin-verify-tag.c @@ -0,0 +1,110 @@ +/* + * Builtin "git verify-tag" + * + * Copyright (c) 2007 Carlos Rica + * + * Based on git-verify-tag.sh + */ +#include "cache.h" +#include "builtin.h" +#include "tag.h" +#include "run-command.h" +#include + +static const char builtin_verify_tag_usage[] = + "git-verify-tag [-v|--verbose] ..."; + +#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----" + +static int run_gpg_verify(const char *buf, unsigned long size, int verbose) +{ + struct child_process gpg; + const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL}; + char path[PATH_MAX], *eol; + size_t len; + int fd, ret; + + fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX"); + if (fd < 0) + return error("could not create temporary file '%s': %s", + path, strerror(errno)); + if (write_in_full(fd, buf, size) < 0) + return error("failed writing temporary file '%s': %s", + path, strerror(errno)); + close(fd); + + /* find the length without signature */ + len = 0; + while (len < size && prefixcmp(buf + len, PGP_SIGNATURE "\n")) { + eol = memchr(buf + len, '\n', size - len); + len += eol ? eol - (buf + len) + 1 : size - len; + } + if (verbose) + write_in_full(1, buf, len); + + memset(&gpg, 0, sizeof(gpg)); + gpg.argv = args_gpg; + gpg.in = -1; + gpg.out = 1; + args_gpg[2] = path; + if (start_command(&gpg)) + return error("could not run gpg."); + + write_in_full(gpg.in, buf, len); + close(gpg.in); + gpg.close_in = 0; + ret = finish_command(&gpg); + + unlink(path); + + return ret; +} + +static int verify_tag(const char *name, int verbose) +{ + enum object_type type; + unsigned char sha1[20]; + char *buf; + unsigned long size; + int ret; + + if (get_sha1(name, sha1)) + return error("tag '%s' not found.", name); + + type = sha1_object_info(sha1, NULL); + if (type != OBJ_TAG) + return error("%s: cannot verify a non-tag object of type %s.", + name, typename(type)); + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return error("%s: unable to read file.", name); + + ret = run_gpg_verify(buf, size, verbose); + + free(buf); + return ret; +} + +int cmd_verify_tag(int argc, const char **argv, const char *prefix) +{ + int i = 1, verbose = 0, had_error = 0; + + git_config(git_default_config); + + if (argc == 1) + usage(builtin_verify_tag_usage); + + if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) { + verbose = 1; + i++; + } + + /* sometimes the program was terminated because this signal + * was received in the process of writing the gpg input: */ + signal(SIGPIPE, SIG_IGN); + while (i < argc) + if (verify_tag(argv[i++], verbose)) + had_error = 1; + return had_error; +} diff --git a/builtin.h b/builtin.h index ac7417f7c5..bb720004af 100644 --- a/builtin.h +++ b/builtin.h @@ -77,6 +77,7 @@ extern int cmd_update_index(int argc, const char **argv, const char *prefix); extern int cmd_update_ref(int argc, const char **argv, const char *prefix); extern int cmd_upload_archive(int argc, const char **argv, const char *prefix); extern int cmd_upload_tar(int argc, const char **argv, const char *prefix); +extern int cmd_verify_tag(int argc, const char **argv, const char *prefix); extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_whatchanged(int argc, const char **argv, const char *prefix); extern int cmd_write_tree(int argc, const char **argv, const char *prefix); diff --git a/git-verify-tag.sh b/contrib/examples/git-verify-tag.sh similarity index 100% rename from git-verify-tag.sh rename to contrib/examples/git-verify-tag.sh diff --git a/git.c b/git.c index eb9e5ca972..230e50611f 100644 --- a/git.c +++ b/git.c @@ -369,6 +369,7 @@ static void handle_internal_command(int argc, const char **argv) { "update-index", cmd_update_index, RUN_SETUP }, { "update-ref", cmd_update_ref, RUN_SETUP }, { "upload-archive", cmd_upload_archive }, + { "verify-tag", cmd_verify_tag, RUN_SETUP }, { "version", cmd_version }, { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER }, { "write-tree", cmd_write_tree, RUN_SETUP }, From f653aee5a37b909e772d612eb7e226f09fd2f3d3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 23 Jul 2007 12:58:27 +0100 Subject: [PATCH 005/107] Teach "git stripspace" the --strip-comments option With --strip-comments (or short -s), git stripspace now removes lines beginning with a '#', too. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/git-stripspace.txt | 5 ++++- builtin-stripspace.c | 7 ++++++- t/t0030-stripspace.sh | 5 +++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt index 1306d7bab7..5212358306 100644 --- a/Documentation/git-stripspace.txt +++ b/Documentation/git-stripspace.txt @@ -8,7 +8,7 @@ git-stripspace - Filter out empty lines SYNOPSIS -------- -'git-stripspace' < +'git-stripspace' [-s | --strip-comments] < DESCRIPTION ----------- @@ -16,6 +16,9 @@ Remove multiple empty lines, and empty lines at beginning and end. OPTIONS ------- +-s\|--strip-comments:: + In addition to empty lines, also strip lines starting with '#'. + :: Byte stream to act on. diff --git a/builtin-stripspace.c b/builtin-stripspace.c index 55716873dc..916355ca5d 100644 --- a/builtin-stripspace.c +++ b/builtin-stripspace.c @@ -76,6 +76,11 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) { char *buffer; unsigned long size; + int strip_comments = 0; + + if (argc > 1 && (!strcmp(argv[1], "-s") || + !strcmp(argv[1], "--strip-comments"))) + strip_comments = 1; size = 1024; buffer = xmalloc(size); @@ -84,7 +89,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) die("could not read the input"); } - size = stripspace(buffer, size, 0); + size = stripspace(buffer, size, strip_comments); write_or_die(1, buffer, size); if (size) putc('\n', stdout); diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index b1c900379b..cad95f35ad 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -392,4 +392,9 @@ test_expect_success \ git diff expect actual ' +test_expect_success 'strip comments, too' ' + test ! -z "$(echo "# comment" | git stripspace)" && + test -z "$(echo "# comment" | git stripspace -s)" +' + test_done From 3244729aac7515ccda651d40f41cef94517ac089 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 27 Jul 2007 22:30:15 +1000 Subject: [PATCH 006/107] gitk: Add a context menu for file list entries At the moment this just has two entries, which allow you to add the file that you clicked on to the list of filenames to highlight, or replace the list with the file. Signed-off-by: Paul Mackerras --- gitk | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/gitk b/gitk index f74ce51379..6c2be3b727 100755 --- a/gitk +++ b/gitk @@ -879,6 +879,7 @@ proc makewindow {} { bind $cflist <1> {sel_flist %W %x %y; break} bind $cflist {sel_flist %W %x %y; break} bind $cflist {treeclick %W %x %y} + bind $cflist {pop_flist_menu %W %X %Y %x %y} set maincursor [. cget -cursor] set textcursor [$ctext cget -cursor] @@ -916,6 +917,14 @@ proc makewindow {} { -command cobranch $headctxmenu add command -label "Remove this branch" \ -command rmbranch + + global flist_menu + set flist_menu .flistctxmenu + menu $flist_menu -tearoff 0 + $flist_menu add command -label "Highlight this too" \ + -command {flist_hl 0} + $flist_menu add command -label "Highlight this only" \ + -command {flist_hl 1} } # mouse-2 makes all windows scan vertically, but only the one @@ -1499,6 +1508,33 @@ proc sel_flist {w x y} { } } +proc pop_flist_menu {w X Y x y} { + global ctext cflist cmitmode flist_menu flist_menu_file + global treediffs diffids + + set l [lindex [split [$w index "@$x,$y"] "."] 0] + if {$l <= 1} return + if {$cmitmode eq "tree"} { + set e [linetoelt $l] + if {[string index $e end] eq "/"} return + } else { + set e [lindex $treediffs($diffids) [expr {$l-2}]] + } + set flist_menu_file $e + tk_popup $flist_menu $X $Y +} + +proc flist_hl {only} { + global flist_menu_file highlight_files + + set x [shellquote $flist_menu_file] + if {$only || $highlight_files eq {}} { + set highlight_files $x + } else { + append highlight_files " " $x + } +} + # Functions for adding and removing shell-type quoting proc shellquote {str} { From 72a4f4b657846af6c65360d92aa09d8b7f3dfbb0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 1 Aug 2007 10:03:37 -0700 Subject: [PATCH 007/107] connect: accept file:// URL scheme We might make it something like: "if you use an url, we don't default to local", so the difference would be that git clone file:///directory/to/repo would work the way it does now, but git clone /directory/to/repo would default to "-l" behaviour. That kind of would make sense (and should be easy to implement. This adds support for "file://" URL to underlying connect codepath to make it happen. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- connect.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/connect.c b/connect.c index 715cdc0223..ae49c5a367 100644 --- a/connect.c +++ b/connect.c @@ -145,6 +145,8 @@ static enum protocol get_protocol(const char *name) return PROTO_SSH; if (!strcmp(name, "ssh+git")) return PROTO_SSH; + if (!strcmp(name, "file")) + return PROTO_LOCAL; die("I don't handle protocol '%s'", name); } @@ -498,13 +500,13 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags) end = host; path = strchr(end, c); - if (c == ':') { - if (path) { + if (path) { + if (c == ':') { protocol = PROTO_SSH; *path++ = '\0'; - } else - path = host; - } + } + } else + path = end; if (!path || !*path) die("No path specified. See 'man git-pull' for valid url syntax"); From 3d5c418ff56645e13bdbd8c9f7d84fdaf7c8494b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 1 Aug 2007 23:42:36 -0700 Subject: [PATCH 008/107] git-clone: aggressively optimize local clone behaviour. This changes the behaviour of cloning from a repository on the local machine, by defaulting to "-l" (use hardlinks to share files under .git/objects) and making "-l" a no-op. A new option, --no-hardlinks, is also added to cause file-level copy of files under .git/objects while still avoiding the normal "pack to pipe, then receive and index pack" network transfer overhead. The old behaviour of local cloning without -l nor -s is availble by specifying the source repository with the newly introduced file:///path/to/repo.git/ syntax (i.e. "same as network" cloning). * With --no-hardlinks (i.e. have all .git/objects/ copied via cpio) would not catch the source repository corruption, and also risks corrupted recipient repository if an alpha-particle hits memory cell while indexing and resolving deltas. As long as the recipient is created uncorrupted, you have a good back-up. * same-as-network is expensive, but it would catch the breakage of the source repository. It still risks corrupted recipient repository due to hardware failure. As long as the recipient is created uncorrupted, you have a good back-up. * The new default on the same filesystem, as long as the source repository is healthy, it is very likely that the recipient would be, too. Also it is very cheap. You do not get any back-up benefit, though. None of the method is resilient against the source repository corruption, so let's discount that from the comparison. Then the difference with and without --no-hardlinks matters primarily if you value the back-up benefit or not. If you want to use the cloned repository as a back-up, then it is cheaper to do a clone with --no-hardlinks and two git-fsck (source before clone, recipient after clone) than same-as-network clone, especially as you are likely to do a git-fsck on the recipient if you are so paranoid anyway. Which leads me to believe that being able to use file:/// is probably a good idea, if only for testability, but probably of little practical value. We default to hardlinked clone for everyday use, and paranoids can use --no-hardlinks as a way to make a back-up. Signed-off-by: Junio C Hamano --- Documentation/git-clone.txt | 18 +++++++++-- Documentation/urls.txt | 16 ++++++---- git-clone.sh | 64 ++++++++++++++++++++----------------- t/t5500-fetch-pack.sh | 2 +- t/t5700-clone-reference.sh | 2 +- t/t5701-clone-local.sh | 17 ++++++++++ 6 files changed, 79 insertions(+), 40 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index a0a10e3e26..227f092e26 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -9,7 +9,8 @@ git-clone - Clone a repository into a new directory SYNOPSIS -------- [verse] -'git-clone' [--template=] [-l [-s]] [-q] [-n] [--bare] +'git-clone' [--template=] + [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [-o ] [-u ] [--reference ] [--depth ] [] @@ -40,8 +41,19 @@ OPTIONS this flag bypasses normal "git aware" transport mechanism and clones the repository by making a copy of HEAD and everything under objects and refs directories. - The files under .git/objects/ directory are hardlinked - to save space when possible. + The files under `.git/objects/` directory are hardlinked + to save space when possible. This is now the default when + the source repository is specified with `/path/to/repo` + syntax, so it essentially is a no-op option. To force + copying instead of hardlinking (which may be desirable + if you are trying to make a back-up of your repository), + but still avoid the usual "git aware" transport + mechanism, `--no-hardlinks` can be used. + +--no-hardlinks:: + Optimize the cloning process from a repository on a + local filesystem by copying files under `.git/objects` + directory. --shared:: -s:: diff --git a/Documentation/urls.txt b/Documentation/urls.txt index 781df4174b..b38145faff 100644 --- a/Documentation/urls.txt +++ b/Documentation/urls.txt @@ -15,11 +15,11 @@ to name the remote repository: - ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git =============================================================== -SSH is the default transport protocol. You can optionally specify -which user to log-in as, and an alternate, scp-like syntax is also -supported. Both syntaxes support username expansion, -as does the native git protocol. The following three are -identical to the last three above, respectively: +SSH is the default transport protocol over the network. You can +optionally specify which user to log-in as, and an alternate, +scp-like syntax is also supported. Both syntaxes support +username expansion, as does the native git protocol. The following +three are identical to the last three above, respectively: =============================================================== - {startsb}user@{endsb}host.xz:/path/to/repo.git/ @@ -27,8 +27,12 @@ identical to the last three above, respectively: - {startsb}user@{endsb}host.xz:path/to/repo.git =============================================================== -To sync with a local directory, use: +To sync with a local directory, you can use: =============================================================== - /path/to/repo.git/ +- file:///path/to/repo.git/ =============================================================== + +They are mostly equivalent, except when cloning. See +gitlink:git-clone[1] for details. diff --git a/git-clone.sh b/git-clone.sh index 09225540e6..4c9b1c9710 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -87,7 +87,7 @@ Perhaps git-update-server-info needs to be run there?" quiet= local=no -use_local=no +use_local_hardlink=yes local_shared=no unset template no_checkout= @@ -108,9 +108,13 @@ while no_checkout=yes ;; *,--na|*,--nak|*,--nake|*,--naked|\ *,-b|*,--b|*,--ba|*,--bar|*,--bare) bare=yes ;; - *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;; + *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) + use_local_hardlink=yes ;; + *,--no-h|*,--no-ha|*,--no-har|*,--no-hard|*,--no-hardl|\ + *,--no-hardli|*,--no-hardlin|*,--no-hardlink|*,--no-hardlinks) + use_local_hardlink=no ;; *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) - local_shared=yes; use_local=yes ;; + local_shared=yes; ;; 1,--template) usage ;; *,--template) shift; template="--template=$1" ;; @@ -249,34 +253,36 @@ fi rm -f "$GIT_DIR/CLONE_HEAD" # We do local magic only when the user tells us to. -case "$local,$use_local" in -yes,yes) +case "$local" in +yes) ( cd "$repo/objects" ) || - die "-l flag seen but repository '$repo' is not local." + die "cannot chdir to local '$repo/objects'." - case "$local_shared" in - no) - # See if we can hardlink and drop "l" if not. - sample_file=$(cd "$repo" && \ - find objects -type f -print | sed -e 1q) - - # objects directory should not be empty since we are cloning! - test -f "$repo/$sample_file" || exit - - l= - if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null - then - l=l - fi && - rm -f "$GIT_DIR/objects/sample" && - cd "$repo" && - find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1 - ;; - yes) - mkdir -p "$GIT_DIR/objects/info" - echo "$repo/objects" >> "$GIT_DIR/objects/info/alternates" - ;; - esac + if test "$local_shared" = yes + then + mkdir -p "$GIT_DIR/objects/info" + echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates" + else + l= && + if test "$use_local_hardlink" = yes + then + # See if we can hardlink and drop "l" if not. + sample_file=$(cd "$repo" && \ + find objects -type f -print | sed -e 1q) + # objects directory should not be empty because + # we are cloning! + test -f "$repo/$sample_file" || exit + if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null + then + rm -f "$GIT_DIR/objects/sample" + l=l + else + echo >&2 "Warning: -l asked but cannot hardlink to $repo" + fi + fi && + cd "$repo" && + find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1 + fi git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 ;; *) diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 7da515361a..7b6798d8b5 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -129,7 +129,7 @@ pull_to_client 2nd "B" $((64*3)) pull_to_client 3rd "A" $((1*3)) # old fails -test_expect_success "clone shallow" "git-clone --depth 2 . shallow" +test_expect_success "clone shallow" "git-clone --depth 2 file://`pwd`/. shallow" (cd shallow; git count-objects -v) > count.shallow diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index 6d43252593..4e93aaab02 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -51,7 +51,7 @@ diff expected current' cd "$base_dir" test_expect_success 'cloning with reference (no -l -s)' \ -'git clone --reference B A D' +'git clone --reference B file://`pwd`/A D' cd "$base_dir" diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh index b0933274db..a3026ec4fc 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5701-clone-local.sh @@ -43,4 +43,21 @@ test_expect_success 'local clone from x.git that does not exist' ' fi ' +test_expect_success 'With -no-hardlinks, local will make a copy' ' + cd "$D" && + git clone --bare --no-hardlinks x w && + cd w && + linked=$(find objects -type f ! -links 1 | wc -l) && + test "$linked" = 0 +' + +test_expect_success 'Even without -l, local will make a hardlink' ' + cd "$D" && + rm -fr w && + git clone -l --bare x w && + cd w && + copied=$(find objects -type f -links 1 | wc -l) && + test "$copied" = 0 +' + test_done From 7fd53fce1c574f6a4940eedf36383a4e9ed7ae6a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 02:04:37 -0700 Subject: [PATCH 009/107] git-completion: add "git stash" This is a new addition to 1.5.3; let's teach it to the completion before the final release. [sp: Added missing git-stash completion configuration] Signed-off-by: Junio C Hamano Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f2b10fa5f6..82b9ed40d8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -972,6 +972,11 @@ _git_show () __git_complete_file } +_git_stash () +{ + __gitcomp 'list show apply clear' +} + _git () { local i c=1 command __git_dir @@ -1028,6 +1033,7 @@ _git () shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; + stash) _git_stash ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; esac @@ -1073,6 +1079,7 @@ complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show +complete -o default -o nospace -F _git_stash git-stash complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged From 33f243308589120a700f084b54ee597ce6c26069 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 3 Aug 2007 19:50:42 +0200 Subject: [PATCH 010/107] gitweb: Fix handling of $file_name in feed generation The commit b6093a5c, by Robert Fitzsimons: "gitweb: Change atom, rss actions to use parse_commits." forgot to pass $file_name parameter to parse_commits subroutine. If git_feed is provided a file name, it ought to show only the history affecting that file or a directory. The title was being set correctly, but all commits from history were being shown. Signed-off-by: Steven Walter Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 077eb2f4ca..f282a677aa 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5366,7 +5366,7 @@ sub git_feed { # log/feed of current (HEAD) branch, log of given branch, history of file/directory my $head = $hash || 'HEAD'; - my @commitlist = parse_commits($head, 150); + my @commitlist = parse_commits($head, 150, 0, undef, $file_name); my %latest_commit; my %latest_date; From 2ec39edad9c3be74b5fed5ab5c122493e6a53c66 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 20:19:09 -0700 Subject: [PATCH 011/107] INSTALL: add warning on docbook-xsl 1.72 and 1.73 Signed-off-by: Junio C Hamano --- INSTALL | 4 ++++ .../docbook-xsl-manpages-charmap.patch | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 contrib/patches/docbook-xsl-manpages-charmap.patch diff --git a/INSTALL b/INSTALL index 79e71b6922..c62b12c288 100644 --- a/INSTALL +++ b/INSTALL @@ -116,3 +116,7 @@ Issues of note: would instead give you a copy of what you see at: http://www.kernel.org/pub/software/scm/git/docs/ + + It has been reported that docbook-xsl version 1.72 and 1.73 are + buggy; 1.72 misformats manual pages for callouts, and 1.73 needs + the patch in contrib/patches/docbook-xsl-manpages-charmap.patch diff --git a/contrib/patches/docbook-xsl-manpages-charmap.patch b/contrib/patches/docbook-xsl-manpages-charmap.patch new file mode 100644 index 0000000000..f2b08b4f4a --- /dev/null +++ b/contrib/patches/docbook-xsl-manpages-charmap.patch @@ -0,0 +1,21 @@ +From: Ismail Dönmez + +Trying to build the documentation with docbook-xsl 1.73 may result in +the following error. This patch fixes it. + +$ xmlto -m callouts.xsl man git-add.xml +runtime error: file +file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line +129 element call-template +The called template 'read-character-map' was not found. + +--- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap 2007-07-23 16:24:23.000000000 +0100 ++++ docbook-xsl-1.73.0/manpages/docbook.xsl 2007-07-23 16:25:16.000000000 +0100 +@@ -37,6 +37,7 @@ + + + ++ + + + From d5538b418d658d507f7fb9f359b31152a9b5f8e0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 4 Aug 2007 02:06:52 -0400 Subject: [PATCH 012/107] Document GIT_SSH environment variable alongside other variables The GIT_SSH environment variable has survived for quite a while without being documented, but has been mentioned on list and on my day-job repositories can only be accessed via magic supplied through the wonderous hack that is GIT_SSH. Advertising it alongside other "low level magic" such as GIT_PAGER and GIT_MERGE_VERBOSITY will certainly help others who need to spread their own pixie dust to make things work. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/git.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/git.txt b/Documentation/git.txt index 4c4d1746e0..18f8b6a0a1 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -421,6 +421,22 @@ other to an empty string or to the value "cat", git will not launch a pager. +'GIT_SSH':: + If this environment variable is set then gitlink:git-fetch[1] + and gitlink:git-push[1] will use this command instead + of `ssh` when they need to connect to a remote system. + The 'GIT_SSH' command will be given exactly two arguments: + the 'username@host' (or just 'host') from the URL and the + shell command to execute on that remote system. ++ +To pass options to the program that you want to list in GIT_SSH +you will need to wrap the program and options into a shell script, +then set GIT_SSH to refer to the shell script. ++ +Usually it is easier to configure any desired options through your +personal `.ssh/config` file. Please consult your ssh documentation +for further details. + 'GIT_FLUSH':: If this environment variable is set to "1", then commands such as git-blame (in incremental mode), git-rev-list, git-log, From 936800bb559afb8e24d79d5fab555bf061a4a88c Mon Sep 17 00:00:00 2001 From: "Randal L. Schwartz" Date: Sat, 4 Aug 2007 01:57:29 -0700 Subject: [PATCH 013/107] add "test-absolute-path" to .gitignore New file requires new ignore. Signed-off-by: Junio C Hamano --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 20ee642420..63c918c667 100644 --- a/.gitignore +++ b/.gitignore @@ -148,6 +148,7 @@ git-write-tree git-core-*/?* gitk-wish gitweb/gitweb.cgi +test-absolute-path test-chmtime test-date test-delta From 4465f410d6a8fa7abc5436aac6f58196f2b618d8 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 4 Aug 2007 23:20:07 +0100 Subject: [PATCH 014/107] checkout-index needs a working tree Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git.c b/git.c index 25b8274d3e..f8c4545208 100644 --- a/git.c +++ b/git.c @@ -315,7 +315,8 @@ static void handle_internal_command(int argc, const char **argv) { "branch", cmd_branch, RUN_SETUP }, { "bundle", cmd_bundle }, { "cat-file", cmd_cat_file, RUN_SETUP }, - { "checkout-index", cmd_checkout_index, RUN_SETUP }, + { "checkout-index", cmd_checkout_index, + RUN_SETUP | NEED_WORK_TREE}, { "check-ref-format", cmd_check_ref_format }, { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE }, { "cherry", cmd_cherry, RUN_SETUP }, From 64a476e691cb08c2f8bd6d40cc88fb8ccb9ed955 Mon Sep 17 00:00:00 2001 From: Jyotirmoy Bhattacharya Date: Sun, 5 Aug 2007 10:52:15 +0530 Subject: [PATCH 015/107] Fixed git-push manpage In git-push it is the remote repository and not the local repository which is fast forwarded. The description of the -f option in the git-push manpage gets it the other way round. Signed-off-by: Jyotirmoy Bhattacharya Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 74a0da1ed4..0dd9caf867 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -79,7 +79,7 @@ the remote repository. -f, \--force:: Usually, the command refuses to update a remote ref that is - not a descendant of the local ref used to overwrite it. + not an ancestor of the local ref used to overwrite it. This flag disables the check. This can cause the remote repository to lose commits; use it with care. From 936492d3cf96817f03182712ca14eb4744c721ef Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 22:13:09 -0700 Subject: [PATCH 016/107] unpack-trees.c: assume submodules are clean during check-out Sven originally raised this issue: If you have a submodule checked out and you go back (or forward) to a revision of the supermodule that contains a different revision of the submodule and then switch to another revision, it will complain that the submodule is not uptodate, because git simply didn't update the submodule in the first move. The current policy is to consider it is perfectly normal that checked-out submodule is out-of-sync wrt the supermodule index. At least until we introduce a superproject repository configuration option that says "in this repository, I do care about this submodule and at any time I move around in the superproject, recursively check out the submodule to match", it is a reasonable policy, as we currently do not recursively checkout the submodules at all. The most extreme case of this policy is that the superproject index knows about the submodule but the subdirectory does not even have to be checked out. The function verify_uptodate(), called during the two-way merge aka branch switching, is about "make sure the filesystem entity that corresponds to this cache entry is up to date, lest we lose the local modifications". As we explicitly allow submodule checkout to drift from the supermodule index entry, the check should say "Ok, for submodules, not matching is the norm" for now. Later when we have the ability to mark "I care about this submodule to be always in sync with the superproject" (thereby implementing automatic recursive checkout and perhaps diff, among other things), we should check if the submodule in question is marked as such and perform the current test. Acked-by: Lars Hjemli Acked-by: Sven Verdoolaege Signed-off-by: Junio C Hamano --- unpack-trees.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/unpack-trees.c b/unpack-trees.c index 3b32718436..dfd985b0ef 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -407,6 +407,15 @@ static void verify_uptodate(struct cache_entry *ce, unsigned changed = ce_match_stat(ce, &st, 1); if (!changed) return; + /* + * NEEDSWORK: the current default policy is to allow + * submodule to be out of sync wrt the supermodule + * index. This needs to be tightened later for + * submodules that are marked to be automatically + * checked out. + */ + if (S_ISGITLINK(ntohl(ce->ce_mode))) + return; errno = 0; } if (errno == ENOENT) From 00d8c5180dde7434930bfbdf20bc296e9241e4fc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 4 Aug 2007 23:48:27 -0700 Subject: [PATCH 017/107] Fix install-doc-quick target The script starts in a subdirectory of the source directory to muck with a branch whose structure does not have anything to do with the actual work tree. Go up to the top to make it clear that we operate on the whole tree. It also exported GIT_DIR without any good reason. Remove it. Signed-off-by: Junio C Hamano --- Documentation/install-doc-quick.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh index e6601bdd82..07d227f093 100755 --- a/Documentation/install-doc-quick.sh +++ b/Documentation/install-doc-quick.sh @@ -7,7 +7,7 @@ mandir="$2" SUBDIRECTORY_OK=t USAGE=' ' . git-sh-setup -export GIT_DIR +cd_to_toplevel test -z "$mandir" && usage if ! git rev-parse --verify "$head^0" >/dev/null; then @@ -18,6 +18,8 @@ fi GIT_INDEX_FILE=`pwd`/.quick-doc.index export GIT_INDEX_FILE rm -f "$GIT_INDEX_FILE" +trap 'rm -f "$GIT_INDEX_FILE"' 0 + git read-tree $head git checkout-index -a -f --prefix="$mandir"/ From 0eb4f7cdf85273c88feb95c677a808cee9cfd859 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 5 Aug 2007 17:18:52 -0400 Subject: [PATCH 018/107] user-manual: update for new default --track behavior Update documentation to reflect the --track default. That change seems to have happened in the 1.5.3 -rc's, so bump the "for version x.y.z or newer" warning as well. Signed-off-by: J. Bruce Fields --- Documentation/user-manual.txt | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 0071cd070e..c0820e9036 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1,4 +1,4 @@ -Git User's Manual (for version 1.5.1 or newer) +Git User's Manual (for version 1.5.3 or newer) ______________________________________________ @@ -1667,24 +1667,19 @@ one step: $ git pull origin master ------------------------------------------------- -In fact, "origin" is normally the default repository to pull from, -and the default branch is normally the HEAD of the remote repository, -so often you can accomplish the above with just +In fact, if you have "master" checked out, then by default "git pull" +merges from the HEAD branch of the origin repository. So often you can +accomplish the above with just a simple ------------------------------------------------- $ git pull ------------------------------------------------- -See the descriptions of the branch..remote and branch..merge -options in gitlink:git-config[1] to learn how to control these defaults -depending on the current branch. Also note that the --track option to -gitlink:git-branch[1] and gitlink:git-checkout[1] can be used to -automatically set the default remote branch to pull from at the time -that a branch is created: - -------------------------------------------------- -$ git checkout --track -b maint origin/maint -------------------------------------------------- +More generally, a branch that is created from a remote branch will pull +by default from that branch. See the descriptions of the +branch..remote and branch..merge options in +gitlink:git-config[1], and the discussion of the --track option in +gitlink:git-checkout[1], to learn how to control these defaults. In addition to saving you keystrokes, "git pull" also helps you by producing a default commit message documenting the branch and From 7a7cc594ca294a58b6d7ae5aa50a65378538e875 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 15:22:59 -0700 Subject: [PATCH 019/107] user-manual: mention git stash Mention the git-stash command as a way to temporarily set aside work in progress. Signed-off-by: J. Bruce Fields --- Documentation/user-manual.txt | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index c0820e9036..9efe85ce2f 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1484,6 +1484,38 @@ $ git show HEAD^:path/to/file which will display the given version of the file. +[[interrupted-work]] +Temporarily setting aside work in progress +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While you are in the middle of working on something complicated, you +find an unrelated but obvious and trivial bug. You would like to fix it +before continuing. You can use gitlink:git-stash[1] to save the current +state of your work, and after fixing the bug (or, optionally after doing +so on a different branch and then coming back), unstash the +work-in-progress changes. + +------------------------------------------------ +$ git stash "work in progress for foo feature" +------------------------------------------------ + +This command will save your changes away to the `stash`, and +reset your working tree and the index to match the tip of your +current branch. Then you can make your fix as usual. + +------------------------------------------------ +... edit and test ... +$ git commit -a -m "blorpl: typofix" +------------------------------------------------ + +After that, you can go back to what you were working on with +`git stash apply`: + +------------------------------------------------ +$ git stash apply +------------------------------------------------ + + [[ensuring-good-performance]] Ensuring good performance ------------------------- From 407c0c87e15b3cf60347f4fc0bcdb4d239de4163 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 5 Aug 2007 18:12:37 -0400 Subject: [PATCH 020/107] user-manual: mention git-gui The git gui project seems to be still in early stages, but at a point where it's worth mentioning as an alternative way of creating commits. One feature of interest is the ability to manipulate individual diff hunks. However, people have found that feature not to be easily discoverable from the user-interface. Pending some ui improvements, a parenthetical hint here may help. (Thanks to Steffen Prohask and Junio Hamano for suggesting the language.) Signed-off-by: J. Bruce Fields --- Documentation/user-manual.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 9efe85ce2f..f89952ad84 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1079,6 +1079,11 @@ $ git diff HEAD # difference between HEAD and working tree; what $ git status # a brief per-file summary of the above. ------------------------------------------------- +You can also use gitlink:git-gui[1] to create commits, view changes in +the index and the working tree files, and individually select diff hunks +for inclusion in the index (by right-clicking on the diff hunk and +choosing "Stage Hunk For Commit"). + [[creating-good-commit-messages]] Creating good commit messages ----------------------------- @@ -2506,8 +2511,10 @@ $ gitk origin..mywork & And browse through the list of patches in the mywork branch using gitk, applying them (possibly in a different order) to mywork-new using -cherry-pick, and possibly modifying them as you go using commit ---amend. +cherry-pick, and possibly modifying them as you go using commit --amend. +The git-gui[1] command may also help as it allows you to individually +select diff hunks for inclusion in the index (by right-clicking on the +diff hunk and choosing "Stage Hunk for Commit"). Another technique is to use git-format-patch to create a series of patches, then reset the state to before the patches: From 5f42ac921fe06bbb80df82d8fa5cb15701ec2f60 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 5 Aug 2007 19:16:09 -0400 Subject: [PATCH 021/107] documentation: use the word "index" in the git-add manual page It was a neat trick to show that you could introduce the git-add manual page without using the word "index", and it was certainly an improvement over the previous man page (which started out "A simple wrapper for git-update-index to add files to the index..."). But it's possible to use the standard terminology without sacrificing user-friendliness. So, rewrite to use the word "index" when appropriate. Signed-off-by: J. Bruce Fields --- Documentation/git-add.txt | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index a0c9f68580..37077b523e 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -3,7 +3,7 @@ git-add(1) NAME ---- -git-add - Add file contents to the changeset to be committed next +git-add - Add file contents to the index SYNOPSIS -------- @@ -11,24 +11,27 @@ SYNOPSIS DESCRIPTION ----------- -All the changed file contents to be committed together in a single set -of changes must be "added" with the 'add' command before using the -'commit' command. This is not only for adding new files. Even modified -files must be added to the set of changes about to be committed. +This command adds the current content of new or modified files to the +index, thus staging that content for inclusion in the next commit. -This command can be performed multiple times before a commit. The added -content corresponds to the state of specified file(s) at the time the -'add' command is used. This means the 'commit' command will not consider -subsequent changes to already added content if it is not added again before -the commit. +The "index" holds a snapshot of the content of the working tree, and it +is this snapshot that is taken as the contents of the next commit. Thus +after making any changes to the working directory, and before running +the commit command, you must use the 'add' command to add any new or +modified files to the index. -The 'git status' command can be used to obtain a summary of what is included -for the next commit. +This command can be performed multiple times before a commit. It only +adds the content of the specified file(s) at the time the add command is +run; if you want subsequent changes included in the next commit, then +you must run 'git add' again to add the new content to the index. -This command can be used to add ignored files with `-f` (force) -option, but they have to be -explicitly and exactly specified from the command line. File globbing -and recursive behaviour do not add ignored files. +The 'git status' command can be used to obtain a summary of which +files have changes that are staged for the next commit. + +The 'add' command can be used to add ignored files with `-f` (force) +option, but they have to be explicitly and exactly specified from the +command line. File globbing and recursive behaviour do not add ignored +files. Please see gitlink:git-commit[1] for alternative ways to add content to a commit. From a76c2acb28146f5630592f2ba738c0ebf0f3c1c4 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 6 Aug 2007 00:34:02 -0400 Subject: [PATCH 022/107] documentation: use the word "index" in the git-commit man page As with git-add, I think previous updates to the git-commit man page did indeed help make it more user-friendly. But I think the banishment of the word "index" from the description goes too far; reinstate its use, to simplify some of the language slightly and smooth the transition to other documentation. Signed-off-by: J. Bruce Fields Signed-off-by: Junio C Hamano --- Documentation/git-commit.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 627994eb97..63599d3828 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -15,26 +15,26 @@ SYNOPSIS DESCRIPTION ----------- -Use 'git commit' when you want to record your changes into the repository -along with a log message describing what the commit is about. All changes -to be committed must be explicitly identified using one of the following -methods: +Use 'git commit' to store the current contents of the index in a new +commit along with a log message describing the changes you have made. +The content to be added can be specified in several ways: 1. by using gitlink:git-add[1] to incrementally "add" changes to the - next commit before using the 'commit' command (Note: even modified + index before using the 'commit' command (Note: even modified files must be "added"); -2. by using gitlink:git-rm[1] to identify content removal for the next - commit, again before using the 'commit' command; +2. by using gitlink:git-rm[1] to remove files from the working tree + and the index, again before using the 'commit' command; -3. by directly listing files containing changes to be committed as arguments - to the 'commit' command, in which cases only those files alone will be - considered for the commit; +3. by listing files as arguments to the 'commit' command, in which + case the commit will ignore changes staged in the index, and instead + record the current content of the listed files; -4. by using the -a switch with the 'commit' command to automatically "add" - changes from all known files i.e. files that have already been committed - before, and to automatically "rm" files that have been - removed from the working tree, and perform the actual commit. +4. by using the -a switch with the 'commit' command to automatically + "add" changes from all known files (i.e. all files that are already + listed in the index) and to automatically "rm" files in the index + that have been removed from the working tree, and then perform the + actual commit; 5. by using the --interactive switch with the 'commit' command to decide one by one which files should be part of the commit, before finalizing the From 33a798c880f9a8bed1fe95531349e5e5ef1e0cd2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 6 Aug 2007 00:20:06 -0700 Subject: [PATCH 023/107] setup.c:verify_non_filename(): don't die unnecessarily while disambiguating If you have a working tree _file_ "foo", attempt to refer to a branch "foo/bar" without -- to disambiguate, like this: $ git log foo/bar tried to make sure that foo/bar cannot be naming a working tree file "foo/bar" (in which case we would say "which one do you want? A rev or a working tree file? clarify with -- please"). We run lstat("foo/bar") to check that. If it does not succeed, there is no ambiguity. That is good. But we also checked the error status for the lstat() and expected it to fail with ENOENT. In this particular case, however, it fails with ENOTDIR. That should be treated as "expected error" as well. Signed-off-by: Junio C Hamano --- setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.c b/setup.c index a45ea8309a..2b8e8c0e5e 100644 --- a/setup.c +++ b/setup.c @@ -103,7 +103,7 @@ void verify_non_filename(const char *prefix, const char *arg) if (!lstat(name, &st)) die("ambiguous argument '%s': both revision and filename\n" "Use '--' to separate filenames from revisions", arg); - if (errno != ENOENT) + if (errno != ENOENT && errno != ENOTDIR) die("'%s': %s", arg, strerror(errno)); } From 93969438dca50c7f0039fcf35e7ab82776d4122f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 4 Aug 2007 21:48:08 -0700 Subject: [PATCH 024/107] apply: remove directory that becomes empty by renaming the last file away We attempt to remove directory that becomes empty after removal of a file. We should do the same when we rename an existing file away. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-apply.c b/builtin-apply.c index 490e23ef40..9ee9393662 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2489,7 +2489,7 @@ static void write_out_one_result(struct patch *patch, int phase) * thing: remove the old, write the new */ if (phase == 0) - remove_file(patch, 0); + remove_file(patch, patch->is_rename); if (phase == 1) create_file(patch); } From 7d4aef4027e6a95fa112fa7b069f945cad294a23 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Mon, 6 Aug 2007 01:16:43 -0700 Subject: [PATCH 025/107] Documentation/git-svn: how to clone a git-svn-created repository These instructions tell you how to create a clone of a repository created with git-svn, that can in turn be used with git-svn. Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 0a210e4bea..816340b944 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -435,6 +435,26 @@ Tracking and contributing to an entire Subversion-managed project # of dcommit/rebase/show-ignore should be the same as above. ------------------------------------------------------------------------ +The initial 'git-svn clone' can be quite time-consuming +(especially for large Subversion repositories). If multiple +people (or one person with multiple machines) want to use +git-svn to interact with the same Subversion repository, you can +do the initial 'git-svn clone' to a repository on a server and +have each person clone that repository with 'git clone': + +------------------------------------------------------------------------ +# Do the initial import on a server + ssh server "cd /pub && git-svn clone http://svn.foo.org/project +# Clone locally + git clone server:/pub/project +# Tell git-svn which branch contains the Subversion commits + git update-ref refs/remotes/git-svn origin/master +# Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server) + git-svn init http://svn.foo.org/project +# Pull the latest changes from Subversion + git-svn rebase +------------------------------------------------------------------------ + REBASE VS. PULL/MERGE --------------------- From 3f0a8f3c015f5f2502228a72d8c944d9e700c559 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Mon, 6 Aug 2007 14:15:30 +0000 Subject: [PATCH 026/107] git-am: initialize variable $resume on startup git-am expects the variable $resume to be empty or unset, which might not be the case if $resume is set in the user's environment. So initialize it to an empty value on startup. The problem was noticed by Pierre Habouzit and reported through http://bugs.debian.org/435807 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- git-am.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-am.sh b/git-am.sh index 6cf0eeee71..b5ed8ca15c 100755 --- a/git-am.sh +++ b/git-am.sh @@ -103,7 +103,8 @@ It does not apply to blobs recorded in its index." } prec=4 -dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg= +dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= +resolvemsg= resume= git_apply_opt= while case "$#" in 0) break;; esac From cad3a2056d2aef3f57c3bb0eccc10614ae69926f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 6 Aug 2007 21:08:43 -0700 Subject: [PATCH 027/107] pager: find out pager setting from configuration It was very unfortunate that we added core.pager setting to the configuration file; even when the underlying command does not care if there is no git repository is involved (think "git diff --no-index"), the user would now rightfully want the configuration setting to be honored, which means we would need to read the configuration file before we launch the pager. This is a minimum change in the sense that it restores the old behaviour of not even reading config in setup_git_directory(), but have the core.pager honored when we know it matters. Note that this does not cover "git -p --git-dir where command"; the -p option immediately trigger the pager settings before we even see --git-dir to learn where the configuration file is, so we will end up reading the configuration from the place where we would _normally_ find the git repository. Signed-off-by: Junio C Hamano --- pager.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pager.c b/pager.c index 3bfed02599..8bac9d9903 100644 --- a/pager.c +++ b/pager.c @@ -31,8 +31,11 @@ void setup_pager(void) if (!isatty(1)) return; - if (!pager) + if (!pager) { + if (!pager_program) + git_config(git_default_config); pager = pager_program; + } if (!pager) pager = getenv("PAGER"); if (!pager) From 87027ae4494cd09c428e85b85337fed45a42e29c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 6 Aug 2007 21:15:15 -0700 Subject: [PATCH 028/107] Fix "make GZ=1 quick-install-doc" The basic idea is from Mark Levedahl. I do not use GZ=1 nor quick-install-doc myself (there obviously is a chicken-and-egg issue with quick-install-doc for me). Signed-off-by: Junio C Hamano --- Documentation/install-doc-quick.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh index 07d227f093..5433cf8ced 100755 --- a/Documentation/install-doc-quick.sh +++ b/Documentation/install-doc-quick.sh @@ -24,10 +24,8 @@ git read-tree $head git checkout-index -a -f --prefix="$mandir"/ if test -n "$GZ"; then - cd "$mandir" - for i in `git ls-tree -r --name-only $head` - do - gzip < $i > $i.gz && rm $i - done + git ls-tree -r --name-only $head | + xargs printf "$mandir/%s\n" | + xargs gzip -f fi rm -f "$GIT_INDEX_FILE" From 5b56aaa29e9d7c0371b6d47bd8a6b12a0c4292dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=1B=2CAv=1B=28Bnig?= Date: Mon, 6 Aug 2007 22:34:50 +0200 Subject: [PATCH 029/107] send-email: teach sanitize_address to do rfc2047 quoting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this patch I'm not able to properly send emails as I have a non-ascii character in my name. I removed the _rfc822 suffix from the function name as it now does more than rfc822 quoting. I dug through rfc822 to do the double quoting right. Only if that is not possible rfc2047 quoting is applied. Signed-off-by: Uwe Kleine-K,Av(Bnig Cc: Jakub Narebski Signed-off-by: Junio C Hamano --- git-send-email.perl | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index f43f92f957..39e433b76b 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -289,7 +289,7 @@ sub expand_aliases { } @to = expand_aliases(@to); -@to = (map { sanitize_address_rfc822($_) } @to); +@to = (map { sanitize_address($_) } @to); @initial_cc = expand_aliases(@initial_cc); @bcclist = expand_aliases(@bcclist); @@ -459,22 +459,41 @@ sub unquote_rfc2047 { return "$_"; } -# If an address contains a . in the name portion, the name must be quoted. -sub sanitize_address_rfc822 +# use the simplest quoting being able to handle the recipient +sub sanitize_address { my ($recipient) = @_; - my ($recipient_name) = ($recipient =~ /^(.*?)\s+@,;:\\".\000-\037\177]/) { + $recipient_name =~ s/(["\\\r])/\\$1/; + $recipient_name = "\"$recipient_name\""; + } + + return "$recipient_name $recipient_addr"; + } sub send_message { my @recipients = unique_email_list(@to); - @cc = (map { sanitize_address_rfc822($_) } @cc); + @cc = (map { sanitize_address($_) } @cc); my $to = join (",\n\t", @recipients); @recipients = unique_email_list(@recipients,@cc,@bcclist); @recipients = (map { extract_valid_address($_) } @recipients); @@ -489,7 +508,7 @@ sub send_message if ($cc ne '') { $ccline = "\nCc: $cc"; } - $from = sanitize_address_rfc822($from); + $from = sanitize_address($from); make_message_id(); my $header = "From: $from From f9935bf9312dd6681e5051fbdce03204b6458c73 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Mon, 6 Aug 2007 14:56:32 +0200 Subject: [PATCH 030/107] Documentation/git-commit.txt: correct bad list formatting. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- Documentation/git-commit.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 63599d3828..e54fb12103 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -19,6 +19,7 @@ Use 'git commit' to store the current contents of the index in a new commit along with a log message describing the changes you have made. The content to be added can be specified in several ways: + 1. by using gitlink:git-add[1] to incrementally "add" changes to the index before using the 'commit' command (Note: even modified files must be "added"); From 4cf3ef97406ea10261cf6faa487520861f8b424b Mon Sep 17 00:00:00 2001 From: Steven Grimm Date: Mon, 6 Aug 2007 22:57:47 -0700 Subject: [PATCH 031/107] Add a note about the index being updated by git-status in some cases Signed-off-by: Steven Grimm Signed-off-by: Junio C Hamano --- Documentation/git-status.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 6f16eb0328..8fd0fc6236 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -27,6 +27,13 @@ The command takes the same set of options as `git-commit`; it shows what would be committed if the same options are given to `git-commit`. +If any paths have been touched in the working tree (that is, +their modification times have changed) but their contents and +permissions are identical to those in the index file, the command +updates the index file. Running `git-status` can thus speed up +subsequent operations such as `git-diff` if the working tree +contains many paths that have been touched but not modified. + OUTPUT ------ From e2c6de1c62373bd4aecf00e91ea7982c8fd6807e Mon Sep 17 00:00:00 2001 From: Steve Hoelzer Date: Tue, 7 Aug 2007 12:39:03 -0500 Subject: [PATCH 032/107] git-stash documentation: stash numbering starts at zero, not one Signed-off-by: Steve Hoelzer Signed-off-by: Junio C Hamano --- Documentation/git-stash.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 17121ade56..14e76d7745 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -29,8 +29,8 @@ you create one. The latest stash you created is stored in `$GIT_DIR/refs/stash`; older stashes are found in the reflog of this reference and can be named using -the usual reflog syntax (e.g. `stash@\{1}` is the most recently -created stash, `stash@\{2}` is the one before it, `stash@\{2.hours.ago}` +the usual reflog syntax (e.g. `stash@\{0}` is the most recently +created stash, `stash@\{1}` is the one before it, `stash@\{2.hours.ago}` is also possible). OPTIONS @@ -61,7 +61,7 @@ show []:: stashed state and its original parent. When no `` is given, shows the latest one. By default, the command shows the diffstat, but it will accept any format known to `git-diff` (e.g., `git-stash show - -p stash@\{2}` to view the second most recent stash in patch form). + -p stash@\{1}` to view the second most recent stash in patch form). apply []:: From 3671757546ce78bb04611aa5481c2aa77a1ffc90 Mon Sep 17 00:00:00 2001 From: Steve Hoelzer Date: Tue, 7 Aug 2007 12:38:29 -0500 Subject: [PATCH 033/107] git-stash documentation: add missing backtick Signed-off-by: Steve Hoelzer Signed-off-by: Junio C Hamano --- Documentation/git-stash.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 14e76d7745..05f40cff6c 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -45,7 +45,7 @@ save:: list:: List the stashes that you currently have. Each 'stash' is listed - with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1} is + with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is the one before, etc.), the name of the branch that was current when the stash was made, and a short description of the commit the stash was based on. From 74276ec6f2d2123714066cee24b26a629930e7a8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 7 Aug 2007 12:28:00 +0200 Subject: [PATCH 034/107] git-p4: Fix support for symlinks. Detect symlinks as file type, set the git file mode accordingly and strip off the trailing newline in the p4 print output. Make the mode handling a bit more readable at the same time. Signed-off-by: Simon Hausmann Acked-by: Brian Swetland Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 41e86e76cb..3cbb2da221 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -839,16 +839,20 @@ class P4Sync(Command): if file["action"] == "delete": self.gitStream.write("D %s\n" % relPath) else: - mode = 644 - if file["type"].startswith("x"): - mode = 755 - data = file['data'] + mode = "644" + if file["type"].startswith("x"): + mode = "755" + elif file["type"] == "symlink": + mode = "120000" + # p4 print on a symlink contains "target\n", so strip it off + data = data[:-1] + if self.isWindows and file["type"].endswith("text"): data = data.replace("\r\n", "\n") - self.gitStream.write("M %d inline %s\n" % (mode, relPath)) + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) self.gitStream.write("data %s\n" % len(data)) self.gitStream.write(data) self.gitStream.write("\n") From aec2196a671de8ef6ba38622869dc862c971df17 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 8 Aug 2007 13:41:46 -0700 Subject: [PATCH 035/107] Reorder the list of commands in the manual. The basic idea was proposed by Steve Hoelzer; in order to make the list easier to search, we keep the command list in the script that generates it with "sort -d". Signed-off-by: Junio C Hamano --- Documentation/cmd-list.perl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index 2143995ece..4ee76eaf99 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -68,6 +68,8 @@ for my $cat (qw(ancillaryinterrogators } } +# The following list is sorted with "sort -d" to make it easier +# to find entry in the resulting git.html manual page. __DATA__ git-add mainporcelain git-am mainporcelain @@ -80,9 +82,9 @@ git-blame ancillaryinterrogators git-branch mainporcelain git-bundle mainporcelain git-cat-file plumbinginterrogators -git-checkout-index plumbingmanipulators -git-checkout mainporcelain git-check-attr purehelpers +git-checkout mainporcelain +git-checkout-index plumbingmanipulators git-check-ref-format purehelpers git-cherry ancillaryinterrogators git-cherry-pick mainporcelain @@ -91,6 +93,7 @@ git-clean mainporcelain git-clone mainporcelain git-commit mainporcelain git-commit-tree plumbingmanipulators +git-config ancillarymanipulators git-convert-objects ancillarymanipulators git-count-objects ancillaryinterrogators git-cvsexportcommit foreignscminterface @@ -98,9 +101,9 @@ git-cvsimport foreignscminterface git-cvsserver foreignscminterface git-daemon synchingrepositories git-describe mainporcelain +git-diff mainporcelain git-diff-files plumbinginterrogators git-diff-index plumbinginterrogators -git-diff mainporcelain git-diff-tree plumbinginterrogators git-fast-import ancillarymanipulators git-fetch mainporcelain @@ -130,13 +133,13 @@ git-ls-remote plumbinginterrogators git-ls-tree plumbinginterrogators git-mailinfo purehelpers git-mailsplit purehelpers +git-merge mainporcelain git-merge-base plumbinginterrogators git-merge-file plumbingmanipulators git-merge-index plumbingmanipulators -git-merge mainporcelain git-merge-one-file purehelpers -git-merge-tree ancillaryinterrogators git-mergetool ancillarymanipulators +git-merge-tree ancillaryinterrogators git-mktag plumbingmanipulators git-mktree plumbingmanipulators git-mv mainporcelain @@ -157,9 +160,8 @@ git-rebase mainporcelain git-receive-pack synchelpers git-reflog ancillarymanipulators git-relink ancillarymanipulators -git-repack ancillarymanipulators -git-config ancillarymanipulators git-remote ancillarymanipulators +git-repack ancillarymanipulators git-request-pull foreignscminterface git-rerere ancillaryinterrogators git-reset mainporcelain From ea99c3ae0e2cecaa0950532385319d60c59e97e0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 8 Aug 2007 17:06:55 +0200 Subject: [PATCH 036/107] git-p4: Fix git-p4 submit to include only changed files in the perforce submit template. Parse the files section in the "p4 change -o" output and remove lines with file changes in unrelated depot paths. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3cbb2da221..805d632a68 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -390,6 +390,30 @@ class P4Submit(Command): return result + def prepareSubmitTemplate(self): + # remove lines in the Files section that show changes to files outside the depot path we're committing into + template = "" + inFilesSection = False + for line in read_pipe_lines("p4 change -o"): + if inFilesSection: + if line.startswith("\t"): + # path starts and ends with a tab + path = line[1:] + lastTab = path.rfind("\t") + if lastTab != -1: + path = path[:lastTab] + if not path.startswith(self.depotPath): + continue + else: + inFilesSection = False + else: + if line.startswith("Files:"): + inFilesSection = True + + template += line + + return template + def applyCommit(self, id): if self.directSubmit: print "Applying local change in working directory/index" @@ -467,7 +491,7 @@ class P4Submit(Command): logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() - template = read_pipe("p4 change -o") + template = self.prepareSubmitTemplate() if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) @@ -558,24 +582,24 @@ class P4Submit(Command): return False [upstream, settings] = findUpstreamBranchPoint() - depotPath = settings['depot-paths'][0] + self.depotPath = settings['depot-paths'][0] if len(self.origin) == 0: self.origin = upstream if self.verbose: print "Origin branch is " + self.origin - if len(depotPath) == 0: + if len(self.depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - self.clientPath = p4Where(depotPath) + self.clientPath = p4Where(self.depotPath) if len(self.clientPath) == 0: - print "Error: Cannot locate perforce checkout of %s in client view" % depotPath + print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath sys.exit(128) - print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) + print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() if self.directSubmit: From b50be1d80f4c151447d2ac96eaeb1c4d76ed4ef5 Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Wed, 8 Aug 2007 23:26:10 -0500 Subject: [PATCH 037/107] cvsserver: Fix for work trees git-cvsserver used checkout-index internally for commit and annotate. Since a work tree is required for this to function now, this was breaking. Work around this by defining GIT_WORK_TREE=. in the appropriate places. Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index ae7d511589..13dbd27a80 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1196,6 +1196,7 @@ sub req_ci $log->info("Lockless commit start, basing commit on '$tmpdir', index file is '$file_index'"); $ENV{GIT_DIR} = $state->{CVSROOT} . "/"; + $ENV{GIT_WORK_TREE} = "."; $ENV{GIT_INDEX_FILE} = $file_index; # Remember where the head was at the beginning. @@ -1721,6 +1722,7 @@ sub req_annotate $log->info("Temp checkoutdir creation successful, basing annotate session work on '$tmpdir', index file is '$file_index'"); $ENV{GIT_DIR} = $state->{CVSROOT} . "/"; + $ENV{GIT_WORK_TREE} = "."; $ENV{GIT_INDEX_FILE} = $file_index; chdir $tmpdir; From 3955d994dec590fb7c586b35094d25af92065c9d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 10 Aug 2007 00:47:28 -0700 Subject: [PATCH 038/107] Fix formatting of git-blame documentation. blame-options.txt did not format multi-paragraph option description correctly. Signed-off-by: Junio C Hamano --- Documentation/blame-options.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index a46bf6ce70..17379f0576 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -64,11 +64,11 @@ of lines before or after the line given by . assigns blame to the lines that were moved down (i.e. A) to the child commit. With this option, both groups of lines are blamed on the parent. - - is optional but it is the lower bound on the number of - alphanumeric characters that git must detect as moving - within a file for it to associate those lines with the parent - commit. ++ + is optional but it is the lower bound on the number of +alphanumeric characters that git must detect as moving +within a file for it to associate those lines with the parent +commit. -C||:: In addition to `-M`, detect lines copied from other @@ -77,11 +77,11 @@ of lines before or after the line given by . around across files. When this option is given twice, the command looks for copies from all other files in the parent for the commit that creates the file in addition. - - is optional but it is the lower bound on the number of - alphanumeric characters that git must detect as moving - between files for it to associate those lines with the parent - commit. ++ + is optional but it is the lower bound on the number of +alphanumeric characters that git must detect as moving +between files for it to associate those lines with the parent +commit. -h, --help:: Show help message. From b767c792fa202539cfb9bba36f46c62bcbf7c987 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:09 -0400 Subject: [PATCH 039/107] Teach update-paranoid how to store ACLs organized by groups In some applications of this paranoid update hook the set of ACL rules that need to be applied to a user can be large, and the number of users that those rules must also be applied to can be more than a handful of individuals. Rather than repeating the same rules multiple times (once for each user) we now allow users to be members of groups, where the group supplies the list of ACL rules. For various reasons we don't depend on the underlying OS groups and instead perform our own group handling. Users can be made a member of one or more groups by setting the user.memberOf property within the "users/$who.acl" file: [user] memberOf = developer memberOf = administrator This will cause the hook to also parse the "groups/$groupname.acl" file for each value of user.memberOf, and merge any allow rules that match the current repository with the user's own private rules (if they had any). Since some rules are basically the same but may have a component differ based on the individual user, any user.* key may be inserted into a rule using the "${user.foo}" syntax. The allow rule does not match if the user does not define one (and exactly one) value for the key "foo". Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 60 +++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 5ee1835c80..fb2aca3628 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -118,22 +118,29 @@ sub info ($) { print STDERR "-Info- $_[0]\n" if $debug; } -sub parse_config ($$) { - my ($data, $fn) = @_; - info "Loading $fn"; - open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn); +sub git_value (@) { + open(T,'-|','git',@_); local $_ = ; chop; close T; $_; +} + +sub parse_config ($$$$) { + my $data = shift; + local $ENV{GIT_DIR} = shift; + my $br = shift; + my $fn = shift; + info "Loading $br:$fn"; + open(I,'-|','git','cat-file','blob',"$br:$fn"); my $section = ''; while () { chomp; if (/^\s*$/ || /^\s*#/) { } elsif (/^\[([a-z]+)\]$/i) { - $section = $1; + $section = lc $1; } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) { - $section = "$1.$2"; + $section = join('.',lc $1,$2); } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) { - push @{$data->{"$section.$1"}}, $2; + push @{$data->{join('.',$section,lc $1)}}, $2; } else { - deny "bad config file line $. in $fn"; + deny "bad config file line $. in $br:$fn"; } } close I; @@ -202,11 +209,6 @@ sub check_committers (@) { } } -sub git_value (@) { - open(T,'-|','git',@_); local $_ = ; chop; close T; - $_; -} - deny "No GIT_DIR inherited from caller" unless $git_dir; deny "Need a ref name" unless $ref; deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; @@ -231,13 +233,39 @@ $op = 'U' if ($op eq 'R' && $ref =~ m,^heads/, && $old eq git_value('merge-base',$old,$new)); -# Load the user's ACL file. +# Load the user's ACL file. Expand groups (user.memberof) one level. { my %data = ('user.committer' => []); - parse_config(\%data, "$acl_branch:users/$this_user.acl"); + parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl"); + + %data = ( + 'user.committer' => $data{'user.committer'}, + 'user.memberof' => [], + ); + parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl"); + %user_committer = map {$_ => $_} @{$data{'user.committer'}}; - my $rules = $data{"repository.$repository_name.allow"} || []; + my $rule_key = "repository.$repository_name.allow"; + my $rules = $data{$rule_key} || []; + + foreach my $group (@{$data{'user.memberof'}}) { + my %g; + parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl"); + my $group_rules = $g{$rule_key}; + push @$rules, @$group_rules if $group_rules; + } + +RULE: foreach (@$rules) { + while (/\${user\.([a-z][a-zA-Z0-9]+)}/) { + my $k = lc $1; + my $v = $data{"user.$k"}; + next RULE unless defined $v; + next RULE if @$v != 1; + next RULE unless defined $v->[0]; + s/\${user\.$k}/$v->[0]/g; + } + if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { my $ops = $1; my $ref = $2; From d47eed3272311acf16f136c49b0bb341c9a6e39c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:12 -0400 Subject: [PATCH 040/107] Teach the update-paranoid to look at file differences In some applications of the update hook a user may be allowed to modify a branch, but only if the file level difference is also an allowed change. This is the commonly requested feature of allowing users to modify only certain files. A new repository.*.allow syntax permits granting the three basic file level operations: A: file is added relative to the other tree M: file exists in both trees, but its SHA-1 or mode differs D: file is removed relative to the other tree on a per-branch and path-name basis. The user must also have a branch level allow line already granting them access to create, rewind or update (CRU) that branch before the hook will consult any file level rules. In order for a branch change to succeed _all_ files that differ relative to some base (by default the old value of this branch, but it can also be any valid tree-ish) must be allowed by file level allow rules. A push is rejected if any diff exists that is not covered by at least one allow rule. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 112 +++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 7 deletions(-) diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index fb2aca3628..84ed452480 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -102,6 +102,8 @@ my ($this_user) = getpwuid $<; # REAL_USER_ID my $repository_name; my %user_committer; my @allow_rules; +my @path_rules; +my %diff_cache; sub deny ($) { print STDERR "-Deny- $_[0]\n" if $debug; @@ -122,6 +124,13 @@ sub git_value (@) { open(T,'-|','git',@_); local $_ = ; chop; close T; $_; } +sub match_string ($$) { + my ($acl_n, $ref) = @_; + ($acl_n eq $ref) + || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) + || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:); +} + sub parse_config ($$$$) { my $data = shift; local $ENV{GIT_DIR} = shift; @@ -209,6 +218,31 @@ sub check_committers (@) { } } +sub load_diff ($) { + my $base = shift; + my $d = $diff_cache{$base}; + unless ($d) { + local $/ = "\0"; + open(T,'-|','git','diff-tree', + '-r','--name-status','-z', + $base,$new) or return undef; + my %this_diff; + while () { + my $op = $_; + chop $op; + + my $path = ; + chop $path; + + $this_diff{$path} = $op; + } + close T or return undef; + $d = \%this_diff; + $diff_cache{$base} = $d; + } + return $d; +} + deny "No GIT_DIR inherited from caller" unless $git_dir; deny "Need a ref name" unless $ref; deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; @@ -266,7 +300,19 @@ RULE: s/\${user\.$k}/$v->[0]/g; } - if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { + if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) { + my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4); + $ops =~ s/ //g; + $pth =~ s/\\\\/\\/g; + $ref =~ s/\\\\/\\/g; + push @path_rules, [$ops, $pth, $ref, $bst]; + } elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) { + my ($ops, $pth, $ref) = ($1, $2, $3); + $ops =~ s/ //g; + $pth =~ s/\\\\/\\/g; + $ref =~ s/\\\\/\\/g; + push @path_rules, [$ops, $pth, $ref, $old]; + } elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { my $ops = $1; my $ref = $2; $ops =~ s/ //g; @@ -300,13 +346,65 @@ foreach my $acl_entry (@allow_rules) { next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen. next unless $acl_n; next unless $op =~ /^[$acl_ops]$/; + next unless match_string $acl_n, $ref; - grant "Allowed by: $acl_ops for $acl_n" - if ( - ($acl_n eq $ref) - || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) - || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:) - ); + # Don't test path rules on branch deletes. + # + grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D'; + + # Aggregate matching path rules; allow if there aren't + # any matching this ref. + # + my %pr; + foreach my $p_entry (@path_rules) { + my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; + next unless $p_ref; + push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref; + } + grant "Allowed by: $acl_ops for $acl_n" unless %pr; + + # Allow only if all changes against a single base are + # allowed by file path rules. + # + my @bad; + foreach my $p_bst (keys %pr) { + my $diff_ref = load_diff $p_bst; + deny "Cannot difference trees." unless ref $diff_ref; + + my %fd = %$diff_ref; + foreach my $p_entry (@{$pr{$p_bst}}) { + my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; + next unless $p_ops =~ /^[AMD]+$/; + next unless $p_n; + + foreach my $f_n (keys %fd) { + my $f_op = $fd{$f_n}; + next unless $f_op; + next unless $f_op =~ /^[$p_ops]$/; + delete $fd{$f_n} if match_string $p_n, $f_n; + } + last unless %fd; + } + + if (%fd) { + push @bad, [$p_bst, \%fd]; + } else { + # All changes relative to $p_bst were allowed. + # + grant "Allowed by: $acl_ops for $acl_n diff $p_bst"; + } + } + + foreach my $bad_ref (@bad) { + my ($p_bst, $fd) = @$bad_ref; + print STDERR "\n"; + print STDERR "Not allowed to make the following changes:\n"; + print STDERR "(base: $p_bst)\n"; + foreach my $f_n (sort keys %$fd) { + print STDERR " $fd->{$f_n} $f_n\n"; + } + } + deny "You are not permitted to $op $ref"; } close A; deny "You are not permitted to $op $ref"; From cabead982b7cf40f8930e6e38327fc396b7fef81 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:16 -0400 Subject: [PATCH 041/107] Use the empty tree for base diff in paranoid-update on new branches We have to load a tree difference for the purpose of testing file patterns. But if our branch is being created and there is no specific base to difference against in the rule our base will be '0'x40. This is (usually) not a valid tree-ish object in a Git repository, so there's nothing to difference against. Instead of creating the empty tree and running git-diff against that we just take the output of `ls-tree -r --name-only` and mark every returned pathname as an add. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 84ed452480..068fa37083 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -223,20 +223,31 @@ sub load_diff ($) { my $d = $diff_cache{$base}; unless ($d) { local $/ = "\0"; - open(T,'-|','git','diff-tree', - '-r','--name-status','-z', - $base,$new) or return undef; my %this_diff; - while () { - my $op = $_; - chop $op; + if ($base =~ /^0{40}$/) { + open(T,'-|','git','ls-tree', + '-r','--name-only','-z', + $new) or return undef; + while () { + chop; + $this_diff{$_} = 'A'; + } + close T or return undef; + } else { + open(T,'-|','git','diff-tree', + '-r','--name-status','-z', + $base,$new) or return undef; + while () { + my $op = $_; + chop $op; - my $path = ; - chop $path; + my $path = ; + chop $path; - $this_diff{$path} = $op; + $this_diff{$path} = $op; + } + close T or return undef; } - close T or return undef; $d = \%this_diff; $diff_cache{$base} = $d; } From 155197e6e770599a5ed7a33a33f2916032184dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Aug 2007 15:27:57 +0200 Subject: [PATCH 042/107] send-email: rfc822 forbids using without a non-empty "phrase" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Email::Valid does respect this considering such a mailbox specification invalid. b06c6bc831cbb9e9eb82fd3ffd5a2b674cd940d0 addressed the issue, but only if Email::Valid is available. Signed-off-by: Uwe Kleine-König Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 39e433b76b..a02ab9694f 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -408,8 +408,8 @@ sub extract_valid_address { # check for a local address: return $address if ($address =~ /^($local_part_regexp)$/); + $address =~ s/^\s*<(.*)>\s*$/$1/; if ($have_email_valid) { - $address =~ s/^\s*<(.*)>\s*$/$1/; return scalar Email::Valid->address($address); } else { # less robust/correct than the monster regexp in Email::Valid, From 94638f89f5bcd1f92b86ebf988f7974167c3fd27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Aug 2007 15:27:58 +0200 Subject: [PATCH 043/107] send-email: get all the quoting of realnames right MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - when sending several mails I got a slightly different behaviour for the first mail compared to the second to last one. The reason is that $from was assigned in line 608 and was not reset when beginning to handle the next mail. - Email::Valid can only handle properly quoted real names, so quote arguments to extract_valid_address. This patch cleans up variable naming to better differentiate between sender of the mail and it's author. Signed-off-by: Uwe Kleine-König Signed-off-by: Junio C Hamano --- git-send-email.perl | 50 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index a02ab9694f..69559b289a 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -137,7 +137,7 @@ my $compose_filename = ".msg.$$"; # Variables we fill in automatically, or via prompting: my (@to,@cc,@initial_cc,@bcclist,@xh, - $initial_reply_to,$initial_subject,@files,$from,$compose,$time); + $initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time); my $smtp_server; my $envelope_sender; @@ -179,7 +179,7 @@ if (!@bcclist or !$bcclist[0]) { # Begin by accumulating all the variables (defined above), that we will end up # needing, first, from the command line: -my $rc = GetOptions("from=s" => \$from, +my $rc = GetOptions("sender|from=s" => \$sender, "in-reply-to=s" => \$initial_reply_to, "subject=s" => \$initial_subject, "to=s" => \@to, @@ -216,8 +216,8 @@ foreach my $entry (@bcclist) { # Now, let's fill any that aren't set in with defaults: -my ($author) = $repo->ident_person('author'); -my ($committer) = $repo->ident_person('committer'); +my ($repoauthor) = $repo->ident_person('author'); +my ($repocommitter) = $repo->ident_person('committer'); my %aliases; my @alias_files = $repo->config('sendemail.aliasesfile'); @@ -254,17 +254,17 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) { } } -($from) = expand_aliases($from) if defined $from; +($sender) = expand_aliases($sender) if defined $sender; my $prompting = 0; -if (!defined $from) { - $from = $author || $committer; +if (!defined $sender) { + $sender = $repoauthor || $repocommitter; do { - $_ = $term->readline("Who should the emails appear to be from? [$from] "); + $_ = $term->readline("Who should the emails appear to be from? [$sender] "); } while (!defined $_); - $from = $_ if ($_); - print "Emails will be sent from: ", $from, "\n"; + $sender = $_ if ($_); + print "Emails will be sent from: ", $sender, "\n"; $prompting++; } @@ -330,7 +330,7 @@ if ($compose) { # effort to have it be unique open(C,">",$compose_filename) or die "Failed to open for writing $compose_filename: $!"; - print C "From $from # This line is ignored.\n"; + print C "From $sender # This line is ignored.\n"; printf C "Subject: %s\n\n", $initial_subject; printf C <code, ' ', ($smtp->message =~ /\n([^\n]+\n)$/s), "\n"; @@ -582,7 +582,7 @@ $subject = $initial_subject; foreach my $t (@files) { open(F,"<",$t) or die "can't open file $t"; - my $author_not_sender = undef; + my $author = undef; @cc = @initial_cc; @xh = (); my $input_format = undef; @@ -604,12 +604,11 @@ foreach my $t (@files) { $subject = $1; } elsif (/^(Cc|From):\s+(.*)$/) { - if (unquote_rfc2047($2) eq $from) { - $from = $2; + if (unquote_rfc2047($2) eq $sender) { next if ($suppress_from); } elsif ($1 eq 'From') { - $author_not_sender = $2; + $author = unquote_rfc2047($2); } printf("(mbox) Adding cc: %s from line '%s'\n", $2, $_) unless $quiet; @@ -653,9 +652,8 @@ foreach my $t (@files) { } } close F; - if (defined $author_not_sender) { - $author_not_sender = unquote_rfc2047($author_not_sender); - $message = "From: $author_not_sender\n\n$message"; + if (defined $author) { + $message = "From: $author\n\n$message"; } From f1ec6b22a8c1ab1cca0f1875f85aea5d2434e5a6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 10 Aug 2007 00:49:27 -0700 Subject: [PATCH 044/107] Fix an illustration in git-rev-parse.txt This hides the backslash at the end of line from AsciiDoc toolchain by introducing a trailing whitespace on one line in an illustration in git-rev-parse.txt. Signed-off-by: Junio C Hamano --- Documentation/git-rev-parse.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index eea9c9cfe9..4b4d229e60 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -224,7 +224,7 @@ left-to-right. G H I J \ / \ / D E F - \ | / \ + \ | / \ \ | / | \|/ | B C From 524e5ffcf41a67ec113a9c3730ddc8fb8d3317f5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 10 Aug 2007 00:49:26 -0700 Subject: [PATCH 045/107] tweak manpage formatting This attempts to force fixed-font in manpages for literal blocks. I have tested this with docbook 1.71 and it seems to work as expected. Signed-off-by: Junio C Hamano --- Documentation/callouts.xsl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl index 6a361a2136..7941af7878 100644 --- a/Documentation/callouts.xsl +++ b/Documentation/callouts.xsl @@ -27,4 +27,17 @@ + + .RS + + + + + + .ft C .nf + + .fi .ft + .RE + + From 7efeb8f09866ddd09485c0e6f371a6cbba3d2a0a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 5 Aug 2007 14:12:53 +0100 Subject: [PATCH 046/107] Reinstate the old behaviour when GIT_DIR is set and GIT_WORK_TREE is unset The old behaviour was to unilaterally default to the cwd is the work tree when GIT_DIR was set, but GIT_WORK_TREE wasn't, no matter if we are inside the GIT_DIR, or if GIT_DIR is actually something like ../../../.git. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- setup.c | 52 +++++++++----------------------------------- t/t1500-rev-parse.sh | 6 +++-- 2 files changed, 14 insertions(+), 44 deletions(-) diff --git a/setup.c b/setup.c index b55b82c99e..06004f1587 100644 --- a/setup.c +++ b/setup.c @@ -189,53 +189,21 @@ int is_inside_work_tree(void) } /* - * If no worktree was given, and we are outside of a default work tree, - * now is the time to set it. - * - * In other words, if the user calls git with something like - * - * git --git-dir=/some/where/else/.git bla - * - * default to /some/where/else as working directory; if the specified - * git-dir does not end in "/.git", the cwd is used as working directory. + * set_work_tree() is only ever called if you set GIT_DIR explicitely. + * The old behaviour (which we retain here) is to set the work tree root + * to the cwd, unless overridden by the config, the command line, or + * GIT_WORK_TREE. */ -const char *set_work_tree(const char *dir) +static const char *set_work_tree(const char *dir) { - char dir_buffer[PATH_MAX], *rel = NULL; - static char buffer[PATH_MAX + 1]; - int len, suffix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1; + char buffer[PATH_MAX + 1]; - /* strip the variable 'dir' of the postfix "/.git" if it has it */ - len = strlen(dir); - if (len > suffix_len && - !strcmp(dir + len - suffix_len, "/" DEFAULT_GIT_DIR_ENVIRONMENT)) { - if ((len - suffix_len) >= sizeof(dir_buffer)) - die("directory name too long"); - memcpy(dir_buffer, dir, len - suffix_len); - dir_buffer[len - suffix_len] = '\0'; - - /* are we inside the default work tree? */ - rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer); - } - - /* if rel is set, the cwd is _not_ the current working tree */ - if (rel && *rel) { - if (!is_absolute_path(dir)) - set_git_dir(make_absolute_path(dir)); - dir = dir_buffer; - if (chdir(dir)) - die("cannot chdir to %s: %s", dir, strerror(errno)); - else - strcat(rel, "/"); - inside_git_dir = 0; - } else { - rel = NULL; - dir = getcwd(buffer, sizeof(buffer)); - } - git_work_tree_cfg = xstrdup(dir); + if (!getcwd(buffer, sizeof(buffer))) + die ("Could not get the current working directory"); + git_work_tree_cfg = xstrdup(buffer); inside_work_tree = 1; - return rel; + return NULL; } /* diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index bea40cba8d..e474b3f1d5 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -28,6 +28,8 @@ test_rev_parse() { [ $# -eq 0 ] && return } +# label is-bare is-inside-git is-inside-work prefix + test_rev_parse toplevel false false true '' cd .git || exit 1 @@ -53,13 +55,13 @@ export GIT_DIR=../.git export GIT_CONFIG="$(pwd)"/../.git/config git config core.bare false -test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/ +test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true '' git config core.bare true test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false '' git config --unset core.bare -test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/ +test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true '' mv ../.git ../repo.git || exit 1 export GIT_DIR=../repo.git From 933bf40a5c6328b6c022b636f45a6f2c48c3838e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 9 Aug 2007 22:21:29 -0700 Subject: [PATCH 047/107] Start moving unpack-trees to "struct tree_desc" This doesn't actually change any real code, but it changes the interface to unpack_trees() to take an array of "struct tree_desc" entries, the same way the tree-walk.c functions do. The reason for this is that we would be much better off if we can do the tree-unpacking using the generic "traverse_trees()" functionality instead of having to the special "unpack" infrastructure. This really is a pretty minimal diff, just to change the calling convention. It passes all the tests, and looks sane. There were only two users of "unpack_trees()": builtin-read-tree and merge-recursive, and I tried to keep the changes minimal. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-read-tree.c | 26 ++++++++++++++++++-------- merge-recursive.c | 16 +++++++++++----- unpack-trees.c | 25 +++++++++---------------- unpack-trees.h | 2 +- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/builtin-read-tree.c b/builtin-read-tree.c index a3b17a3bd9..1967d100f2 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -13,14 +13,19 @@ #include "dir.h" #include "builtin.h" -static struct object_list *trees; +static int nr_trees; +static struct tree *trees[4]; static int list_tree(unsigned char *sha1) { - struct tree *tree = parse_tree_indirect(sha1); + struct tree *tree; + + if (nr_trees >= 4) + return -1; + tree = parse_tree_indirect(sha1); if (!tree) return -1; - object_list_append(&tree->object, &trees); + trees[nr_trees++] = tree; return 0; } @@ -76,11 +81,10 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree) static void prime_cache_tree(void) { - struct tree *tree = (struct tree *)trees->item; - if (!tree) + if (!nr_trees) return; active_cache_tree = cache_tree(); - prime_cache_tree_rec(active_cache_tree, tree); + prime_cache_tree_rec(active_cache_tree, trees[0]); } @@ -92,6 +96,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) { int i, newfd, stage = 0; unsigned char sha1[20]; + struct tree_desc t[3]; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); @@ -258,7 +263,12 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) opts.head_idx = 1; } - unpack_trees(trees, &opts); + for (i = 0; i < nr_trees; i++) { + struct tree *tree = trees[i]; + parse_tree(tree); + init_tree_desc(t+i, tree->buffer, tree->size); + } + unpack_trees(nr_trees, t, &opts); /* * When reading only one tree (either the most basic form, @@ -266,7 +276,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) * valid cache-tree because the index must match exactly * what came from the tree. */ - if (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) { + if (nr_trees && !opts.prefix && (!opts.merge || (stage == 2))) { cache_tree_free(&active_cache_tree); prime_cache_tree(); } diff --git a/merge-recursive.c b/merge-recursive.c index c8539ec0ba..f7d1b84999 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -216,13 +216,19 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, */ static int index_only = 0; +static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) +{ + parse_tree(tree); + init_tree_desc(desc, tree->buffer, tree->size); +} + static int git_merge_trees(int index_only, struct tree *common, struct tree *head, struct tree *merge) { int rc; - struct object_list *trees = NULL; + struct tree_desc t[3]; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); @@ -234,11 +240,11 @@ static int git_merge_trees(int index_only, opts.head_idx = 2; opts.fn = threeway_merge; - object_list_append(&common->object, &trees); - object_list_append(&head->object, &trees); - object_list_append(&merge->object, &trees); + init_tree_desc_from_tree(t+0, common); + init_tree_desc_from_tree(t+1, head); + init_tree_desc_from_tree(t+2, merge); - rc = unpack_trees(trees, &opts); + rc = unpack_trees(3, t, &opts); cache_tree_free(&active_cache_tree); return rc; } diff --git a/unpack-trees.c b/unpack-trees.c index dfd985b0ef..5d1ffd1a32 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -16,19 +16,13 @@ struct tree_entry_list { const unsigned char *sha1; }; -static struct tree_entry_list *create_tree_entry_list(struct tree *tree) +static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc) { - struct tree_desc desc; struct name_entry one; struct tree_entry_list *ret = NULL; struct tree_entry_list **list_p = &ret; - if (!tree->object.parsed) - parse_tree(tree); - - init_tree_desc(&desc, tree->buffer, tree->size); - - while (tree_entry(&desc, &one)) { + while (tree_entry(desc, &one)) { struct tree_entry_list *entry; entry = xmalloc(sizeof(struct tree_entry_list)); @@ -173,9 +167,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, if (S_ISDIR(posns[i]->mode)) { struct tree *tree = lookup_tree(posns[i]->sha1); + struct tree_desc t; any_dirs = 1; parse_tree(tree); - subposns[i] = create_tree_entry_list(tree); + init_tree_desc(&t, tree->buffer, tree->size); + subposns[i] = create_tree_entry_list(&t); posns[i] = posns[i]->next; src[i + o->merge] = o->df_conflict_entry; continue; @@ -331,12 +327,10 @@ static void check_updates(struct cache_entry **src, int nr, stop_progress(&progress);; } -int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) +int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) { - unsigned len = object_list_length(trees); struct tree_entry_list **posns; int i; - struct object_list *posn = trees; struct tree_entry_list df_conflict_list; static struct cache_entry *dfc; @@ -356,10 +350,9 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) if (len) { posns = xmalloc(len * sizeof(struct tree_entry_list *)); - for (i = 0; i < len; i++) { - posns[i] = create_tree_entry_list((struct tree *) posn->item); - posn = posn->next; - } + for (i = 0; i < len; i++) + posns[i] = create_tree_entry_list(t+i); + if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "", o, &df_conflict_list)) return -1; diff --git a/unpack-trees.h b/unpack-trees.h index fee7da4382..9cd39a28a9 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -26,7 +26,7 @@ struct unpack_trees_options { struct cache_entry *df_conflict_entry; }; -extern int unpack_trees(struct object_list *trees, +extern int unpack_trees(unsigned n, struct tree_desc *t, struct unpack_trees_options *options); int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o); From 63c21c494fc91eda339d06b3a466c4241afffc81 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 10 Aug 2007 11:32:42 -0700 Subject: [PATCH 048/107] Revert "tweak manpage formatting" This reverts commit 524e5ffcf41a67ec113a9c3730ddc8fb8d3317f5. It is reported that this change breaks formatting with docbook 1.69. --- Documentation/callouts.xsl | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl index 7941af7878..6a361a2136 100644 --- a/Documentation/callouts.xsl +++ b/Documentation/callouts.xsl @@ -27,17 +27,4 @@ - - .RS - - - - - - .ft C .nf - - .fi .ft - .RE - - From af3785dc5a76f4d5ddb8039e33c322e0e8b60e72 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 9 Aug 2007 13:42:50 -0700 Subject: [PATCH 049/107] Optimize "diff --cached" performance. The read_tree() function is called only from the call chain to run "git diff --cached" (this includes the internal call made by git-runstatus to run_diff_index()). The function vacates stage without any funky "merge" magic. The caller then goes and compares stage #1 entries from the tree with stage #0 entries from the original index. When adding the cache entries this way, it used the general purpose add_cache_entry(). This function looks for an existing entry to replace or if there is none to find where to insert the new entry, resolves D/F conflict and all the other things. For the purpose of reading entries into an empty stage, none of that processing is needed. We can instead append everything and then sort the result at the end. This commit changes read_tree() to first make sure that there is no existing cache entries at specified stage, and if that is the case, it runs add_cache_entry() with ADD_CACHE_JUST_APPEND flag (new), and then sort the resulting cache using qsort(). This new flag tells add_cache_entry() to omit all the checks such as "Does this path already exist? Does adding this path remove other existing entries because it turns a directory to a file?" and instead append the given cache entry straight at the end of the active cache. The caller of course is expected to sort the resulting cache at the end before using the result. Signed-off-by: Junio C Hamano --- cache.h | 1 + read-cache.c | 20 +++++++++++++-- tree.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/cache.h b/cache.h index e97af18eea..e5276e6add 100644 --- a/cache.h +++ b/cache.h @@ -258,6 +258,7 @@ extern int index_name_pos(struct index_state *, 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 */ #define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */ +#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option); extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern int remove_index_entry_at(struct index_state *, int pos); diff --git a/read-cache.c b/read-cache.c index e060392d1d..865369df0e 100644 --- a/read-cache.c +++ b/read-cache.c @@ -665,7 +665,7 @@ static int check_file_directory_conflict(struct index_state *istate, return retval + has_dir_name(istate, ce, pos, ok_to_replace); } -int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option) +static int add_index_entry_with_check(struct index_state *istate, struct cache_entry *ce, int option) { int pos; int ok_to_add = option & ADD_CACHE_OK_TO_ADD; @@ -707,6 +707,22 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags)); pos = -pos-1; } + return pos + 1; +} + +int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option) +{ + int pos; + + if (option & ADD_CACHE_JUST_APPEND) + pos = istate->cache_nr; + else { + int ret; + ret = add_index_entry_with_check(istate, ce, option); + if (ret <= 0) + return ret; + pos = ret - 1; + } /* Make sure the array is big enough .. */ if (istate->cache_nr == istate->cache_alloc) { @@ -717,7 +733,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti /* Add it in.. */ istate->cache_nr++; - if (istate->cache_nr > pos) + if (istate->cache_nr > pos + 1) memmove(istate->cache + pos + 1, istate->cache + pos, (istate->cache_nr - pos - 1) * sizeof(ce)); diff --git a/tree.c b/tree.c index 04fe653a8e..8c0819fa72 100644 --- a/tree.c +++ b/tree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "cache-tree.h" #include "tree.h" #include "blob.h" #include "commit.h" @@ -7,7 +8,7 @@ const char *tree_type = "tree"; -static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) +static int read_one_entry_opt(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, int opt) { int len; unsigned int size; @@ -25,7 +26,23 @@ static int read_one_entry(const unsigned char *sha1, const char *base, int basel memcpy(ce->name, base, baselen); memcpy(ce->name + baselen, pathname, len+1); hashcpy(ce->sha1, sha1); - return add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); + return add_cache_entry(ce, opt); +} + +static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) +{ + return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage, + ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); +} + +/* + * This is used when the caller knows there is no existing entries at + * the stage that will conflict with the entry being added. + */ +static int read_one_entry_quick(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) +{ + return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage, + ADD_CACHE_JUST_APPEND); } static int match_tree_entry(const char *base, int baselen, const char *path, unsigned int mode, const char **paths) @@ -119,9 +136,55 @@ int read_tree_recursive(struct tree *tree, return 0; } +static int cmp_cache_name_compare(const void *a_, const void *b_) +{ + const struct cache_entry *ce1, *ce2; + + ce1 = *((const struct cache_entry **)a_); + ce2 = *((const struct cache_entry **)b_); + return cache_name_compare(ce1->name, ntohs(ce1->ce_flags), + ce2->name, ntohs(ce2->ce_flags)); +} + int read_tree(struct tree *tree, int stage, const char **match) { - return read_tree_recursive(tree, "", 0, stage, match, read_one_entry); + read_tree_fn_t fn = NULL; + int i, err; + + /* + * Currently the only existing callers of this function all + * call it with stage=1 and after making sure there is nothing + * at that stage; we could always use read_one_entry_quick(). + * + * But when we decide to straighten out git-read-tree not to + * use unpack_trees() in some cases, this will probably start + * to matter. + */ + + /* + * See if we have cache entry at the stage. If so, + * do it the original slow way, otherwise, append and then + * sort at the end. + */ + for (i = 0; !fn && i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce_stage(ce) == stage) + fn = read_one_entry; + } + + if (!fn) + fn = read_one_entry_quick; + err = read_tree_recursive(tree, "", 0, stage, match, fn); + if (fn == read_one_entry || err) + return err; + + /* + * Sort the cache entry -- we need to nuke the cache tree, though. + */ + cache_tree_free(&active_cache_tree); + qsort(active_cache, active_nr, sizeof(active_cache[0]), + cmp_cache_name_compare); + return 0; } struct tree *lookup_tree(const unsigned char *sha1) From 22631473e0b7a33356587ba3e38a9b4cc4dba2f1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 10 Aug 2007 09:51:58 -0700 Subject: [PATCH 050/107] Fix "git commit directory/" performance anomaly This trivial patch avoids re-hashing files that are already clean in the index. This mirrors what commit 0781b8a9b2fe760fc4ed519a3a26e4b9bd6ccffe did for "git add .", only for "git commit ." instead. This improves the cold-cache case immensely, since we don't need to bring in all the file contents, just the index and any files dirty in the index. Before: [torvalds@woody linux]$ time git commit . real 1m49.537s user 0m3.892s sys 0m2.432s After: [torvalds@woody linux]$ time git commit . real 0m14.273s user 0m1.312s sys 0m0.516s (both after doing a "echo 3 > /proc/sys/vm/drop_caches" to get cold-cache behaviour - even with the index optimization git still has to "lstat()" all the files, so with a truly cold cache, bringing all the inodes in will take some time). [jc: trivial "return 0;" fixed] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-update-index.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/builtin-update-index.c b/builtin-update-index.c index 509369e9e7..a7a4574f2b 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -86,9 +86,15 @@ static int process_lstat_error(const char *path, int err) static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st) { - int option, size = cache_entry_size(len); - struct cache_entry *ce = xcalloc(1, size); + int option, size; + struct cache_entry *ce; + /* Was the old index entry already up-to-date? */ + if (old && !ce_stage(old) && !ce_match_stat(old, st, 0)) + return 0; + + size = cache_entry_size(len); + ce = xcalloc(1, size); memcpy(ce->name, path, len); ce->ce_flags = htons(len); fill_stat_cache_info(ce, st); From b48d5a050afa30402c8281c223df9d7e58b4493c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 10 Aug 2007 12:15:54 -0700 Subject: [PATCH 051/107] Move old index entry removal from "unpack_trees()" into the individual functions This makes no changes to current code, but it allows the individual merge functions to decide what to do about the old entry. They might decide to update it in place, rather than force them to always delete and re-add it. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 29 +++++++++++++++++++++++------ unpack-trees.h | 11 ++++++----- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index 5d1ffd1a32..5e457b0bc9 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -58,10 +58,17 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2) return ret; } +static inline void remove_entry(int remove) +{ + if (remove >= 0) + remove_cache_entry_at(remove); +} + static int unpack_trees_rec(struct tree_entry_list **posns, int len, const char *base, struct unpack_trees_options *o, struct tree_entry_list *df_conflict_list) { + int remove; int baselen = strlen(base); int src_size = len + 1; int i_stk = i_stk; @@ -145,10 +152,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, subposns = xcalloc(len, sizeof(struct tree_list_entry *)); + remove = -1; if (cache_name && !strcmp(cache_name, first)) { any_files = 1; src[0] = active_cache[o->pos]; - remove_cache_entry_at(o->pos); + remove = o->pos; } for (i = 0; i < len; i++) { @@ -214,13 +222,14 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, printf("\n"); } #endif - ret = o->fn(src, o); + ret = o->fn(src, o, remove); #if DBRT_DEBUG > 1 printf("Added %d entries\n", ret); #endif o->pos += ret; } else { + remove_entry(remove); for (i = 0; i < src_size; i++) { if (src[i]) { add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); @@ -641,7 +650,8 @@ static void show_stage_entry(FILE *o, #endif int threeway_merge(struct cache_entry **stages, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *index; struct cache_entry *head; @@ -657,6 +667,7 @@ int threeway_merge(struct cache_entry **stages, int no_anc_exists = 1; int i; + remove_entry(remove); for (i = 1; i < o->head_idx; i++) { if (!stages[i] || stages[i] == o->df_conflict_entry) any_anc_missing = 1; @@ -809,12 +820,14 @@ int threeway_merge(struct cache_entry **stages, * */ int twoway_merge(struct cache_entry **src, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *current = src[0]; struct cache_entry *oldtree = src[1]; struct cache_entry *newtree = src[2]; + remove_entry(remove); if (o->merge_size != 2) return error("Cannot do a twoway merge of %d trees", o->merge_size); @@ -868,11 +881,13 @@ int twoway_merge(struct cache_entry **src, * stage0 does not have anything there. */ int bind_merge(struct cache_entry **src, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; + remove_entry(remove); if (o->merge_size != 1) return error("Cannot do a bind merge of %d trees\n", o->merge_size); @@ -891,11 +906,13 @@ int bind_merge(struct cache_entry **src, * - take the stat information from stage0, take the data from stage1 */ int oneway_merge(struct cache_entry **src, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; + remove_entry(remove); if (o->merge_size != 1) return error("Cannot do a oneway merge of %d trees", o->merge_size); diff --git a/unpack-trees.h b/unpack-trees.h index 9cd39a28a9..5517faafad 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -4,7 +4,8 @@ struct unpack_trees_options; typedef int (*merge_fn_t)(struct cache_entry **src, - struct unpack_trees_options *options); + struct unpack_trees_options *options, + int remove); struct unpack_trees_options { int reset; @@ -29,9 +30,9 @@ struct unpack_trees_options { extern int unpack_trees(unsigned n, struct tree_desc *t, struct unpack_trees_options *options); -int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o); -int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o); -int bind_merge(struct cache_entry **src, struct unpack_trees_options *o); -int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o); +int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int); +int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int); +int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int); +int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int); #endif From 288f072ec033cf917eed949119428db3626ddc71 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 10 Aug 2007 12:21:20 -0700 Subject: [PATCH 052/107] Optimize the common cases of git-read-tree This optimizes bind_merge() and oneway_merge() to not unnecessarily remove and re-add the old index entries when they can just get replaced by updated ones. This makes these operations much faster for large trees (where "large" is in the 50,000+ file range), because we don't unnecessarily move index entries around in the index array all the time. Using the "bummer" tree (a test-tree with 100,000 files) we get: Before: [torvalds@woody bummer]$ time git commit -m"Change one file" 50/500 real 0m9.470s user 0m8.729s sys 0m0.476s After: [torvalds@woody bummer]$ time git commit -m"Change one file" 50/500 real 0m1.173s user 0m0.720s sys 0m0.452s so for large trees this is easily very noticeable indeed. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index 5e457b0bc9..d57b91c1b2 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -887,7 +887,6 @@ int bind_merge(struct cache_entry **src, struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; - remove_entry(remove); if (o->merge_size != 1) return error("Cannot do a bind merge of %d trees\n", o->merge_size); @@ -912,13 +911,14 @@ int oneway_merge(struct cache_entry **src, struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; - remove_entry(remove); if (o->merge_size != 1) return error("Cannot do a oneway merge of %d trees", o->merge_size); - if (!a) + if (!a) { + remove_entry(remove); return deleted_entry(old, old, o); + } if (old && same(old, a)) { if (o->reset) { struct stat st; From d699676dda5fdf0996601006c3bac2a9c077a862 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 10 Aug 2007 12:31:20 -0700 Subject: [PATCH 053/107] Optimize the two-way merge of git-read-tree too This trivially optimizes the two-way merge case of git-read-tree too, which affects switching branches. When you have tons and tons of files in your repository, but there are only small differences in the branches (maybe just a couple of files changed), the biggest cost of the branch switching was actually just the index calculations. This fixes it (timings for switching between the "testing" and "master" branches in the 100,000 file testing-repo-from-hell, where the branches only differ in one small file). Before: [torvalds@woody bummer]$ time git checkout master real 0m9.919s user 0m8.461s sys 0m0.264s After: [torvalds@woody bummer]$ time git checkout testing real 0m0.576s user 0m0.348s sys 0m0.228s so it's easily an order of magnitude different. This concludes the series. I think we could/should do the three-way merge too (to speed up merges), but I'm lazy. Somebody else can do it. The rule is very simple: you need to remove the old entry if: - you want to remove the file entirely - you replace it with a "merge conflict" entry (ie a non-stage-0 entry) and you can avoid removing it if you either - keep the old one - or resolve it to a new one. and these rules should all be valid for the three-way case too. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index d57b91c1b2..fda7729f1d 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -827,7 +827,6 @@ int twoway_merge(struct cache_entry **src, struct cache_entry *oldtree = src[1]; struct cache_entry *newtree = src[2]; - remove_entry(remove); if (o->merge_size != 2) return error("Cannot do a twoway merge of %d trees", o->merge_size); @@ -850,6 +849,7 @@ int twoway_merge(struct cache_entry **src, } else if (oldtree && !newtree && same(current, oldtree)) { /* 10 or 11 */ + remove_entry(remove); return deleted_entry(oldtree, current, o); } else if (oldtree && newtree && @@ -859,6 +859,7 @@ int twoway_merge(struct cache_entry **src, } else { /* all other failures */ + remove_entry(remove); if (oldtree) reject_merge(oldtree); if (current) @@ -870,8 +871,8 @@ int twoway_merge(struct cache_entry **src, } else if (newtree) return merged_entry(newtree, current, o); - else - return deleted_entry(oldtree, current, o); + remove_entry(remove); + return deleted_entry(oldtree, current, o); } /* From 7fa8254f9476de24661e93b2a90c6ce30dc10006 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 8 Aug 2007 17:01:49 -0700 Subject: [PATCH 054/107] allow git-bundle to create bottomless bundle While "git bundle" was a useful way to sneakernet incremental changes, we did not allow: $ git bundle create v2.6.20.bndl v2.6.20 to create a bundle that contains the whole history to a well-known good revision. Such a bundle can be mirrored everywhere, and people can prime their repository with it to reduce the load on the repository that serves near the tip of the development. Signed-off-by: Junio C Hamano --- builtin-bundle.c | 10 +++++++++- t/t5510-fetch.sh | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/builtin-bundle.c b/builtin-bundle.c index 6ae5ab04c9..cb439ca465 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -208,6 +208,10 @@ static int create_bundle(struct bundle_header *header, const char *path, struct rev_info revs; struct child_process rls; + /* + * NEEDSWORK: this should use something like lock-file + * to create temporary that is cleaned up upon error. + */ bundle_fd = (!strcmp(path, "-") ? 1 : open(path, O_CREAT | O_EXCL | O_WRONLY, 0666)); if (bundle_fd < 0) @@ -267,8 +271,12 @@ static int create_bundle(struct bundle_header *header, const char *path, * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. + * + * Non commit objects such as tags and blobs do not have + * this issue as they are not affected by those extra + * constraints. */ - if (!(e->item->flags & SHOWN)) { + if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); continue; diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 426017e1d0..439430f569 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -145,4 +145,12 @@ test_expect_success 'bundle does not prerequisite objects' ' test 4 = $(git verify-pack -v bundle.pack | wc -l) ' +test_expect_success 'bundle should be able to create a full history' ' + + cd "$D" && + git tag -a -m '1.0' v1.0 master && + git bundle create bundle4 v1.0 + +' + test_done From c06793a4ed1bf81902c324d1ed88dd055c3aa468 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 8 Aug 2007 22:04:06 -0700 Subject: [PATCH 055/107] allow git-bundle to create bottomless bundle Mark Levedahl writes: > Junio C Hamano wrote: >> While "git bundle" was a useful way to sneakernet incremental >> changes, we did not allow: >> > Thanks - I've been thinking for months I could fix this bug, never > figured it out and didn't want to nag Dscho one more time. I confirm > that this allows creation of bundles with arbitrary refs, not just > those under refs/heads. Yahoo! Actually, there is another bug nearby. If you do: git bundle create v2.6-20-v2.6.22.bndl v2.6.20..v2.6.22 the bundle records that it requires v2.6.20^0 commit (correct) and gives you tag v2.6.22 (incorrect); the bug is that the object it lists in fact is the commit v2.6.22^0, not the tag. This is because the revision range operation .. is always about set of commits, but the code near where my patch touches does not validate that the sha1 value obtained from dwim_ref() against the commit object name e->item->sha1 before placing the head information in the commit. The attached patch attempts to fix this problem. Signed-off-by: Junio C Hamano --- builtin-bundle.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/builtin-bundle.c b/builtin-bundle.c index cb439ca465..2d0e106c08 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -279,8 +279,41 @@ static int create_bundle(struct bundle_header *header, const char *path, if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); + free(ref); continue; } + /* + * If you run "git bundle create bndl v1.0..v2.0", the + * name of the positive ref is "v2.0" but that is the + * commit that is referenced by the tag, and not the tag + * itself. + */ + if (hashcmp(sha1, e->item->sha1)) { + /* + * Is this the positive end of a range expressed + * in terms of a tag (e.g. v2.0 from the range + * "v1.0..v2.0")? + */ + struct commit *one = lookup_commit_reference(sha1); + struct object *obj; + + if (e->item == &(one->object)) { + /* + * Need to include e->name as an + * independent ref to the pack-objects + * input, so that the tag is included + * in the output; otherwise we would + * end up triggering "empty bundle" + * error. + */ + obj = parse_object(sha1); + obj->flags |= SHOWN; + add_pending_object(&revs, obj, e->name); + } + free(ref); + continue; + } + ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); From 442b67a55972e69a054eb1206bbbdf044532130a Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Fri, 10 Aug 2007 18:29:49 -0400 Subject: [PATCH 056/107] builtin-bundle.c - use stream buffered input for rev-list git-bundle create on cygwin was nearly unusable due to 1 character at a time (unbuffered) reading from an exec'ed process. Fix by using fdopen to get a buffered stream. Results for "time git bundle create test.bdl v1.0.3..v1.5.2" are: before this patch: cygwin linux real 1m38.828s 0m3.578s user 0m12.122s 0m2.896s sys 1m28.215s 0m0.692s after this patch: real 0m3.688s 0m2.835s user 0m3.075s 0m2.731s sys 0m1.075s 0m0.149s Signed-off-by: Mark Levedahl Signed-off-by: Junio C Hamano --- builtin-bundle.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/builtin-bundle.c b/builtin-bundle.c index 2d0e106c08..b954213f70 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -207,6 +207,7 @@ static int create_bundle(struct bundle_header *header, const char *path, char buffer[1024]; struct rev_info revs; struct child_process rls; + FILE *rls_fout; /* * NEEDSWORK: this should use something like lock-file @@ -236,10 +237,11 @@ static int create_bundle(struct bundle_header *header, const char *path, rls.git_cmd = 1; if (start_command(&rls)) return -1; - while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) { + rls_fout = fdopen(rls.out, "r"); + while (fgets(buffer, sizeof(buffer), rls_fout)) { unsigned char sha1[20]; if (buffer[0] == '-') { - write_or_die(bundle_fd, buffer, i); + write_or_die(bundle_fd, buffer, strlen(buffer)); if (!get_sha1_hex(buffer + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; @@ -250,6 +252,7 @@ static int create_bundle(struct bundle_header *header, const char *path, object->flags |= SHOWN; } } + fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); From 21a02980f9025c3a338fb897796542ddef8707d1 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Fri, 10 Aug 2007 20:39:24 -0400 Subject: [PATCH 057/107] builtin-bundle - use buffered reads for bundle header This eliminates all use of byte-at-a-time reading of data in this function: as Junio noted, a bundle file is seekable so we can reset the file position to the first part of the pack-file using lseek after reading the header. Signed-off-by: Mark Levedahl Signed-off-by: Junio C Hamano --- builtin-bundle.c | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/builtin-bundle.c b/builtin-bundle.c index b954213f70..f4b4f034f4 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -44,38 +44,21 @@ struct bundle_header { struct ref_list references; }; -/* this function returns the length of the string */ -static int read_string(int fd, char *buffer, int size) -{ - int i; - for (i = 0; i < size - 1; i++) { - ssize_t count = xread(fd, buffer + i, 1); - if (count < 0) - return error("Read error: %s", strerror(errno)); - if (count == 0) { - i--; - break; - } - if (buffer[i] == '\n') - break; - } - buffer[i + 1] = '\0'; - return i + 1; -} - /* returns an fd */ static int read_header(const char *path, struct bundle_header *header) { char buffer[1024]; - int fd = open(path, O_RDONLY); + int fd; + long fpos; + FILE *ffd = fopen(path, "rb"); - if (fd < 0) + if (!ffd) return error("could not open '%s'", path); - if (read_string(fd, buffer, sizeof(buffer)) < 0 || + if (!fgets(buffer, sizeof(buffer), ffd) || strcmp(buffer, bundle_signature)) { - close(fd); + fclose(ffd); return error("'%s' does not look like a v2 bundle file", path); } - while (read_string(fd, buffer, sizeof(buffer)) > 0 + while (fgets(buffer, sizeof(buffer), ffd) && buffer[0] != '\n') { int is_prereq = buffer[0] == '-'; int offset = is_prereq ? 1 : 0; @@ -97,6 +80,12 @@ static int read_header(const char *path, struct bundle_header *header) { add_to_ref_list(sha1, isspace(delim) ? buffer + 41 + offset : "", list); } + fpos = ftell(ffd); + fclose(ffd); + fd = open(path, O_RDONLY); + if (fd < 0) + return error("could not open '%s'", path); + lseek(fd, fpos, SEEK_SET); return fd; } From cbbb218f8bd219d79907623a9304496ee69d8abd Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Fri, 10 Aug 2007 15:06:22 +0200 Subject: [PATCH 058/107] Fix filehandle leak in "git branch -D" On Windows (it can't touch open files in any way) the following fails: git branch -D branch1 branch2 if the both branches are in packed-refs. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- refs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/refs.c b/refs.c index fac6548001..09a2c87fc2 100644 --- a/refs.c +++ b/refs.c @@ -869,6 +869,7 @@ static int repack_without_ref(const char *refname) die("too long a refname '%s'", list->name); write_or_die(fd, line, len); } + close(fd); return commit_lock_file(&packlock); } From 566b5c057c452d04605805ea2f7af210c6fb9b59 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 10 Aug 2007 12:53:51 -0700 Subject: [PATCH 059/107] Optimize the three-way merge of git-read-tree As mentioned, the three-way case *should* be as trivial as the following. It passes all the tests, and I verified that a conflicting merge in the 100,000 file horror-case merged correctly (with the conflict markers) in 0.687 seconds with this, so it works, but I'm lazy and somebody else should double-check it [jc: followed all three-way merge codepaths and verified it removes when it should]. Without this patch, the merge took 8.355 seconds, so this patch really does make a huge difference for merge performance with lots and lots of files, and we're not talking percentages, we're talking orders-of-magnitude differences! Now "unpack_trees()" is just fast enough that we don't need to avoid it (although it's probably still a good idea to eventually convert it to use the traverse_trees() infrastructure some day - just to avoid having extraneous tree traversal functions). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index fda7729f1d..ccfeb6e245 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -667,7 +667,6 @@ int threeway_merge(struct cache_entry **stages, int no_anc_exists = 1; int i; - remove_entry(remove); for (i = 1; i < o->head_idx; i++) { if (!stages[i] || stages[i] == o->df_conflict_entry) any_anc_missing = 1; @@ -730,8 +729,10 @@ int threeway_merge(struct cache_entry **stages, } /* #1 */ - if (!head && !remote && any_anc_missing) + if (!head && !remote && any_anc_missing) { + remove_entry(remove); return 0; + } /* Under the new "aggressive" rule, we resolve mostly trivial * cases that we historically had git-merge-one-file resolve. @@ -763,6 +764,7 @@ int threeway_merge(struct cache_entry **stages, if ((head_deleted && remote_deleted) || (head_deleted && remote && remote_match) || (remote_deleted && head && head_match)) { + remove_entry(remove); if (index) return deleted_entry(index, index, o); else if (ce && !head_deleted) @@ -785,6 +787,7 @@ int threeway_merge(struct cache_entry **stages, verify_uptodate(index, o); } + remove_entry(remove); o->nontrivial_merge = 1; /* #2, #3, #4, #6, #7, #9, #10, #11. */ From 4739809cd0ea12a8de006f9f086fdff9285189b8 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Mon, 6 Aug 2007 12:22:57 +0200 Subject: [PATCH 060/107] Add support for an info version of the user manual These patches use docbook2x in order to create an info version of the git user manual. No existing Makefile targets (including "all") are touched, so you need to explicitly say make info sudo make install-info to get git.info created and installed. If the info target directory does not already contain a "dir" file, no directory entry is created. This facilitates $(DESTDIR)-based installations. The same could be achieved with sudo make INSTALL_INFO=: install-info explicitly. perl is used for patching up sub-par file and directory information in the Texinfo file. It would be cleaner to place the respective info straight into user-manual.txt or the conversion configurations, but I find myself unable to find out how to do this with Asciidoc/Texinfo. Signed-off-by: David Kastrup --- Documentation/Makefile | 24 +++++++++++++++++++++++- Documentation/fix-texi.perl | 15 +++++++++++++++ Makefile | 6 ++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100755 Documentation/fix-texi.perl diff --git a/Documentation/Makefile b/Documentation/Makefile index 443114b046..76a15ff520 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -44,6 +44,11 @@ INSTALL?=install RM ?= rm -f DOC_REF = origin/man +infodir?=$(prefix)/share/info +MAKEINFO=makeinfo +INSTALL_INFO=install-info +DOCBOOK2X_TEXI=docbook2x-texi + -include ../config.mak.autogen -include ../config.mak @@ -67,6 +72,8 @@ man1: $(DOC_MAN1) man5: $(DOC_MAN5) man7: $(DOC_MAN7) +info: git.info + install: man $(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(INSTALL) -d -m755 $(DESTDIR)$(man5dir) @@ -75,6 +82,14 @@ install: man $(INSTALL) -m644 $(DOC_MAN5) $(DESTDIR)$(man5dir) $(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir) +install-info: info + $(INSTALL) -d -m755 $(DESTDIR)$(infodir) + $(INSTALL) -m644 git.info $(DESTDIR)$(infodir) + if test -r $(DESTDIR)$(infodir)/dir; then \ + $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\ + else \ + echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \ + fi ../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE $(MAKE) -C ../ GIT-VERSION-FILE @@ -110,7 +125,7 @@ cmd-list.made: cmd-list.perl $(MAN1_TXT) git.7 git.html: git.txt core-intro.txt clean: - $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 howto-index.txt howto/*.html doc.dep + $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 *.texi *.texi+ howto-index.txt howto/*.html doc.dep $(RM) $(cmds_txt) *.made %.html : %.txt @@ -138,6 +153,13 @@ XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css user-manual.html: user-manual.xml xsltproc $(XSLTOPTS) -o $@ $(XSLT) $< +git.info: user-manual.xml + $(RM) $@ $*.texi $*.texi+ + $(DOCBOOK2X_TEXI) user-manual.xml --to-stdout >$*.texi+ + perl fix-texi.perl <$*.texi+ >$*.texi + $(MAKEINFO) --no-split $*.texi + $(RM) $*.texi $*.texi+ + howto-index.txt: howto-index.sh $(wildcard howto/*.txt) $(RM) $@+ $@ sh ./howto-index.sh $(wildcard howto/*.txt) >$@+ diff --git a/Documentation/fix-texi.perl b/Documentation/fix-texi.perl new file mode 100755 index 0000000000..ff7d78f620 --- /dev/null +++ b/Documentation/fix-texi.perl @@ -0,0 +1,15 @@ +#!/usr/bin/perl -w + +while (<>) { + if (/^\@setfilename/) { + $_ = "\@setfilename git.info\n"; + } elsif (/^\@direntry/) { + print '@dircategory Development +@direntry +* Git: (git). A fast distributed revision control system +@end direntry +'; } + unless (/^\@direntry/../^\@end direntry/) { + print; + } +} diff --git a/Makefile b/Makefile index 2f3b9b23e3..b685c7e39e 100644 --- a/Makefile +++ b/Makefile @@ -913,6 +913,9 @@ perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS doc: $(MAKE) -C Documentation all +info: + $(MAKE) -C Documentation info + TAGS: $(RM) TAGS $(FIND) . -name '*.[hcS]' -print | xargs etags -a @@ -1005,6 +1008,9 @@ endif install-doc: $(MAKE) -C Documentation install +install-info: + $(MAKE) -C Documentation install-info + quick-install-doc: $(MAKE) -C Documentation quick-install From 98e79f63be7e2cf043bd3150ae9ac0c8d118ce61 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Tue, 7 Aug 2007 12:02:12 +0200 Subject: [PATCH 061/107] INSTALL: explain info installation and dependencies. Signed-off-by: David Kastrup --- INSTALL | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/INSTALL b/INSTALL index c62b12c288..289b046a44 100644 --- a/INSTALL +++ b/INSTALL @@ -5,8 +5,8 @@ Normally you can just do "make" followed by "make install", and that will install the git programs in your own ~/bin/ directory. If you want to do a global install, you can do - $ make prefix=/usr all doc ;# as yourself - # make prefix=/usr install install-doc ;# as root + $ make prefix=/usr all doc info ;# as yourself + # make prefix=/usr install install-doc install-info ;# as root (or prefix=/usr/local, of course). Just like any program suite that uses $prefix, the built results have some paths encoded, @@ -91,9 +91,13 @@ Issues of note: - To build and install documentation suite, you need to have the asciidoc/xmlto toolchain. Because not many people are inclined to install the tools, the default build target - ("make all") does _not_ build them. The documentation is - written for AsciiDoc 7, but "make ASCIIDOC8=YesPlease doc" - will let you format with AsciiDoc 8. + ("make all") does _not_ build them. + + Building and installing the info file additionally requires + makeinfo and docbook2X. Version 0.8.3 is known to work. + + The documentation is written for AsciiDoc 7, but "make + ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8. Alternatively, pre-formatted documentation are available in "html" and "man" branches of the git repository itself. For From f9286765b2c409e5b88efe8c20a2634d6842bc5f Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Mon, 6 Aug 2007 15:05:56 +0200 Subject: [PATCH 062/107] Documentation/Makefile: remove cmd-list.made before redirecting to it. If cmd-list.made has been created by a previous run as root, output redirection to it will fail. So remove it before regeneration. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- Documentation/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/Makefile b/Documentation/Makefile index 76a15ff520..fbefe9a45b 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -119,6 +119,7 @@ cmds_txt = cmds-ancillaryinterrogators.txt \ $(cmds_txt): cmd-list.made cmd-list.made: cmd-list.perl $(MAN1_TXT) + $(RM) $@ perl ./cmd-list.perl date >$@ From 67a4f1a7f5c778ffa23d1e562feb4cc6d52c9414 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 12 Aug 2007 17:23:47 +1000 Subject: [PATCH 063/107] gitk: Fix bug causing the "can't unset idinlist(...)" error Under some circumstances, having duplicate parents in a commit could trigger a "can't unset idinlist" Tcl error. This fixes the cause (the logic in layoutrows could end up putting the same commit into rowidlist twice) and also puts a catch around the unset to ignore the error. Thanks to Jeff King for coming up with a test script to generate a repo that shows the problem. Signed-off-by: Paul Mackerras --- gitk | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gitk b/gitk index 6c2be3b727..44b04f017a 100755 --- a/gitk +++ b/gitk @@ -2880,6 +2880,7 @@ proc layoutrows {row endrow last} { } elseif {!$idinlist($p)} { lappend oldolds $p } + set idinlist($p) 1 } set nev [expr {[llength $idlist] + [llength $newolds] + [llength $oldolds] - $maxwidth + 1}] @@ -2952,12 +2953,10 @@ proc layoutrows {row endrow last} { lset offs $col {} } foreach i $newolds { - set idinlist($i) 1 set idrowranges($i) $id } incr col $l foreach oid $oldolds { - set idinlist($oid) 1 set idlist [linsert $idlist $col $oid] set offs [linsert $offs $col $o] makeuparrow $oid $col $row $o @@ -2998,7 +2997,7 @@ proc layouttail {} { set col [expr {[llength $idlist] - 1}] set id [lindex $idlist $col] addextraid $id $row - unset idinlist($id) + catch {unset idinlist($id)} lappend idrowranges($id) $id lappend rowrangelist $idrowranges($id) unset idrowranges($id) From bd441de4df68c4144425b1c18d323fd90f3f9617 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Tue, 7 Aug 2007 21:40:34 -0400 Subject: [PATCH 064/107] [PATCH] gitk: Enable selected patch text on Windows On windows, mouse input follows the keyboard focus, so to allow selecting text from the patch canvas we must not shift focus back to the top level. This change has no negative impact on X, so we don't explicitly test for Win32 on this change. This provides similar selection capability as already available using X-Windows. Signed-off-by: Mark Levedahl Signed-off-by: Paul Mackerras --- gitk | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index 44b04f017a..32206a68f0 100755 --- a/gitk +++ b/gitk @@ -964,8 +964,8 @@ proc bindkey {ev script} { # set the focus back to the toplevel for any click outside # the entry widgets proc click {w} { - global entries - foreach e $entries { + global ctext entries + foreach e [concat $entries $ctext] { if {$w == $e} return } focus . @@ -4600,6 +4600,7 @@ proc sellastline {} { proc selnextline {dir} { global selectedline + focus . if {![info exists selectedline]} return set l [expr {$selectedline + $dir}] unmarkmatches @@ -4680,6 +4681,7 @@ proc godo {elt} { proc goback {} { global history historyindex + focus . if {$historyindex > 1} { incr historyindex -1 @@ -4693,6 +4695,7 @@ proc goback {} { proc goforw {} { global history historyindex + focus . if {$historyindex < [llength $history]} { set cmd [lindex $history $historyindex] From 314c30936f505f70534c619a48d99afb93451cb2 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Tue, 7 Aug 2007 21:40:35 -0400 Subject: [PATCH 065/107] [PATCH] gitk: Handle MouseWheel events on Windows Windows, unlike X-Windows, sends mousewheel events by default to the window that has keyboard focus and uses the MouseWheel event to do so. The window to be scrolled must be able to take focus, but gitk's panels are disabled so cannot take focus. For all these reasons, a different design is needed to use the mousewheel on Windows. The approach here is to bind the mousewheel events to the top level window and redirect them based upon the current mouse position. Signed-off-by: Mark Levedahl Signed-off-by: Paul Mackerras --- gitk | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index 32206a68f0..9000383235 100755 --- a/gitk +++ b/gitk @@ -823,8 +823,13 @@ proc makewindow {} { pack .ctop -fill both -expand 1 bindall <1> {selcanvline %W %x %y} #bindall {selcanvline %W %x %y} - bindall "allcanvs yview scroll -5 units" - bindall "allcanvs yview scroll 5 units" + if {[tk windowingsystem] == "win32"} { + bind . { windows_mousewheel_redirector %W %X %Y %D } + bind $ctext { windows_mousewheel_redirector %W %X %Y %D ; break } + } else { + bindall "allcanvs yview scroll -5 units" + bindall "allcanvs yview scroll 5 units" + } bindall <2> "canvscan mark %W %x %y" bindall "canvscan dragto %W %x %y" bindkey selfirstline @@ -927,6 +932,24 @@ proc makewindow {} { -command {flist_hl 1} } +# Windows sends all mouse wheel events to the current focused window, not +# the one where the mouse hovers, so bind those events here and redirect +# to the correct window +proc windows_mousewheel_redirector {W X Y D} { + global canv canv2 canv3 + set w [winfo containing -displayof $W $X $Y] + if {$w ne ""} { + set u [expr {$D < 0 ? 5 : -5}] + if {$w == $canv || $w == $canv2 || $w == $canv3} { + allcanvs yview scroll $u units + } else { + catch { + $w yview scroll $u units + } + } + } +} + # mouse-2 makes all windows scan vertically, but only the one # the cursor is in scans horizontally proc canvscan {op w x y} { From 062d671f57a422863416ee4c746ef74c1cc45c19 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 29 Jul 2007 22:28:40 +0200 Subject: [PATCH 066/107] [PATCH] gitk: Continue and show error message in new repos If there is no commit made yet, gitk just dumps a Tcl error on stderr, which sometimes is hard to see. Noticed when gitk was run from Xfce file manager (thunar's custom action). Signed-off-by: Alex Riesen Signed-off-by: Paul Mackerras --- gitk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitk b/gitk index 9000383235..b0a76dd225 100755 --- a/gitk +++ b/gitk @@ -427,7 +427,7 @@ proc readrefs {} { lappend idotherrefs($id) $name } } - close $refd + catch {close $refd} set mainhead {} set mainheadid {} catch { From 6c87d60cc6202d4de5ac6d136394602feefeafc6 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 29 Jul 2007 22:29:45 +0200 Subject: [PATCH 067/107] [PATCH] gitk: Show an error and exit if no .git could be found This is to help people starting gitk from graphical file managers where the stderr output is hidden. Signed-off-by: Alex Riesen Signed-off-by: Paul Mackerras --- gitk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gitk b/gitk index b0a76dd225..769c79ab07 100755 --- a/gitk +++ b/gitk @@ -7636,7 +7636,10 @@ catch {source ~/.gitk} font create optionfont -family sans-serif -size -12 # check that we can find a .git directory somewhere... -set gitdir [gitdir] +if {[catch {set gitdir [gitdir]}]} { + show_error {} . "Cannot find a git repository here." + exit 1 +} if {![file isdirectory $gitdir]} { show_error {} . "Cannot find the git directory \"$gitdir\"." exit 1 From 7b459a1c1cc5401295e3adb12031e39e35712f4a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 13 Aug 2007 14:52:00 +1000 Subject: [PATCH 068/107] gitk: Fix bug introduced in commit 67a4f1a7 In fixing the "can't unset idinlist" error, I moved the setting of idinlist into the loop that splits the parents into "new" parents (i.e. those of which this is the first child) and "old" parents. Unfortunately this is incorrect in the case where we hit the break statement a few lines further down, since when we come back in, we'll see idinlist($p) set for some parents that aren't in the list. This fixes it by moving the loop that sets up newolds and oldolds further down. Signed-off-by: Paul Mackerras --- gitk | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/gitk b/gitk index 769c79ab07..666a545751 100755 --- a/gitk +++ b/gitk @@ -2895,18 +2895,12 @@ proc layoutrows {row endrow last} { set offs [lindex $rowoffsets $row] while {$row < $endrow} { set id [lindex $displayorder $row] - set oldolds {} - set newolds {} + set nev [expr {[llength $idlist] - $maxwidth + 1}] foreach p [lindex $parentlist $row] { - if {![info exists idinlist($p)]} { - lappend newolds $p - } elseif {!$idinlist($p)} { - lappend oldolds $p + if {![info exists idinlist($p)] || !$idinlist($p)} { + incr nev } - set idinlist($p) 1 } - set nev [expr {[llength $idlist] + [llength $newolds] - + [llength $oldolds] - $maxwidth + 1}] if {$nev > 0} { if {!$last && $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break @@ -2925,12 +2919,22 @@ proc layoutrows {row endrow last} { if {[incr nev -1] <= 0} break continue } - set rowchk($id) [expr {$row + $r}] + set rowchk($i) [expr {$row + $r}] } } lset rowidlist $row $idlist lset rowoffsets $row $offs } + set oldolds {} + set newolds {} + foreach p [lindex $parentlist $row] { + if {![info exists idinlist($p)]} { + lappend newolds $p + } elseif {!$idinlist($p)} { + lappend oldolds $p + } + set idinlist($p) 1 + } set col [lsearch -exact $idlist $id] if {$col < 0} { set col [llength $idlist] From a69b2d1a8bf335ddbf0929c609b2daa523c7ede0 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 13 Aug 2007 15:02:02 +1000 Subject: [PATCH 069/107] gitk: Fix bug causing Tcl error when updating graph If "Show nearby tags" is turned off, selecting "Update" from the File menu will cause a Tcl error. This fixes it. The problem was that we were calling regetallcommits unconditionally, but it assumed that getallcommits had been called previously. This also restructures {re,}getallcommits to be a bit simpler. Signed-off-by: Paul Mackerras --- gitk | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/gitk b/gitk index 666a545751..57617d58b0 100755 --- a/gitk +++ b/gitk @@ -296,7 +296,7 @@ proc readcommit {id} { proc updatecommits {} { global viewdata curview phase displayorder - global children commitrow selectedline thickerline + global children commitrow selectedline thickerline showneartags if {$phase ne {}} { stop_rev_list @@ -313,7 +313,9 @@ proc updatecommits {} { catch {unset viewdata($n)} readrefs changedrefs - regetallcommits + if {$showneartags} { + getallcommits + } showview $n } @@ -6199,17 +6201,13 @@ proc rmbranch {} { proc getallcommits {} { global allcommits allids nbmp nextarc seeds - set allids {} - set nbmp 0 - set nextarc 0 - set allcommits 0 - set seeds {} - regetallcommits -} - -# Called when the graph might have changed -proc regetallcommits {} { - global allcommits seeds + if {![info exists allcommits]} { + set allids {} + set nbmp 0 + set nextarc 0 + set allcommits 0 + set seeds {} + } set cmd [concat | git rev-list --all --parents] foreach id $seeds { From 09afcd6933e8497c997205dda71d718e62b4de62 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 11 Aug 2007 12:22:47 +0200 Subject: [PATCH 070/107] git.el: Add support for interactive diffs. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f6102fc344..214b75cf93 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -965,7 +965,13 @@ Return the list of files that haven't been handled." (defun git-diff-file-idiff () "Perform an interactive diff on the current file." (interactive) - (error "Interactive diffs not implemented yet.")) + (let ((files (git-marked-files-state 'added 'deleted 'modified))) + (unless (eq 1 (length files)) + (error "Cannot perform an interactive diff on multiple files.")) + (let* ((filename (car (git-get-filenames files))) + (buff1 (find-file-noselect filename)) + (buff2 (git-run-command-buffer (concat filename ".~HEAD~") "cat-file" "blob" (concat "HEAD:" filename)))) + (ediff-buffers buff1 buff2)))) (defun git-log-file () "Display a log of changes to the marked file(s)." From 8fdc39729b9aaa02e89eca9d7964182a72052665 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 11 Aug 2007 12:23:21 +0200 Subject: [PATCH 071/107] git.el: Always set the current directory in the git-diff buffer. This allows jumping to the correct file with the diff-mode commands. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 214b75cf93..be44e06c45 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -912,10 +912,12 @@ Return the list of files that haven't been handled." (defun git-setup-diff-buffer (buffer) "Setup a buffer for displaying a diff." - (with-current-buffer buffer - (diff-mode) - (goto-char (point-min)) - (setq buffer-read-only t)) + (let ((dir default-directory)) + (with-current-buffer buffer + (diff-mode) + (goto-char (point-min)) + (setq default-directory dir) + (setq buffer-read-only t))) (display-buffer buffer) (shrink-window-if-larger-than-buffer)) From 77b258f436874bdd1caecd4b3c9c63e3d49bd147 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 11 Aug 2007 14:08:20 -0700 Subject: [PATCH 072/107] t3404: fix "fake-editor" Here-text to create fake-editor did not use <<\EOF but < --- t/t3404-rebase-interactive.sh | 36 ++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index a9b552ff08..40d6799ed6 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -61,29 +61,31 @@ test_expect_success 'setup' ' git tag I ' -cat > fake-editor.sh << EOF +cat > fake-editor.sh <<\EOF #!/bin/sh -test "\$1" = .git/COMMIT_EDITMSG && { - test -z "\$FAKE_COMMIT_MESSAGE" || echo "\$FAKE_COMMIT_MESSAGE" > "\$1" - test -z "\$FAKE_COMMIT_AMEND" || echo "\$FAKE_COMMIT_AMEND" >> "\$1" +case "$1" in +*/COMMIT_EDITMSG) + test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" + test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1" exit -} -test -z "\$EXPECT_COUNT" || - test "\$EXPECT_COUNT" = \$(grep -ve "^#" -e "^$" < "\$1" | wc -l) || + ;; +esac +test -z "$EXPECT_COUNT" || + test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) || exit -test -z "\$FAKE_LINES" && exit -grep -v "^#" < "\$1" > "\$1".tmp -rm "\$1" -cat "\$1".tmp +test -z "$FAKE_LINES" && exit +grep -v '^#' < "$1" > "$1".tmp +rm -f "$1" +cat "$1".tmp action=pick -for line in \$FAKE_LINES; do - case \$line in +for line in $FAKE_LINES; do + case $line in squash) - action="\$line";; + action="$line";; *) - echo sed -n "\${line}s/^pick/\$action/p" - sed -n "\${line}p" < "\$1".tmp - sed -n "\${line}s/^pick/\$action/p" < "\$1".tmp >> "\$1" + echo sed -n "${line}s/^pick/$action/p" + sed -n "${line}p" < "$1".tmp + sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" action=pick;; esac done From d616813d75b888b7c29bbad19808fe5cffa5380c Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 11 Aug 2007 23:59:01 +0200 Subject: [PATCH 073/107] git-add: Add support for --refresh option. This allows to refresh only a subset of the project files, based on the specified pathspecs. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- Documentation/git-add.txt | 6 +++++- builtin-add.c | 28 +++++++++++++++++++++++++++- cache.h | 4 ++-- read-cache.c | 6 +++++- t/t3700-add.sh | 12 ++++++++++++ 5 files changed, 51 insertions(+), 5 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 4af3a9b0d7..dee38f8250 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -7,7 +7,7 @@ git-add - Add file contents to the index SYNOPSIS -------- -'git-add' [-n] [-v] [-f] [--interactive | -i] [-u] [--] ... +'git-add' [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] ... DESCRIPTION ----------- @@ -66,6 +66,10 @@ OPTIONS command line. If no paths are specified, all tracked files are updated. +\--refresh:: + Don't add the file(s), but only refresh their stat() + information in the index. + \--:: This option can be used to separate command-line options from the list of files, (useful when filenames might be mistaken diff --git a/builtin-add.c b/builtin-add.c index de5c108f8f..82c806acf0 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -123,6 +123,23 @@ static void update(int verbose, const char **files) run_diff_files(&rev, 0); } +static void refresh(int verbose, const char **pathspec) +{ + char *seen; + int i, specs; + + for (specs = 0; pathspec[specs]; specs++) + /* nothing */; + seen = xcalloc(specs, 1); + if (read_cache() < 0) + die("index file corrupt"); + refresh_index(&the_index, verbose ? 0 : REFRESH_QUIET, pathspec, seen); + for (i = 0; i < specs; i++) { + if (!seen[i]) + die("pathspec '%s' did not match any files", pathspec[i]); + } +} + static int git_add_config(const char *var, const char *value) { if (!strcmp(var, "core.excludesfile")) { @@ -143,7 +160,7 @@ static const char ignore_warning[] = int cmd_add(int argc, const char **argv, const char *prefix) { int i, newfd; - int verbose = 0, show_only = 0, ignored_too = 0; + int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; const char **pathspec; struct dir_struct dir; int add_interactive = 0; @@ -191,6 +208,10 @@ int cmd_add(int argc, const char **argv, const char *prefix) take_worktree_changes = 1; continue; } + if (!strcmp(arg, "--refresh")) { + refresh_only = 1; + continue; + } usage(builtin_add_usage); } @@ -206,6 +227,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) } pathspec = get_pathspec(prefix, argv + i); + if (refresh_only) { + refresh(verbose, pathspec); + goto finish; + } + fill_directory(&dir, pathspec, ignored_too); if (show_only) { diff --git a/cache.h b/cache.h index 4507404240..c7e00e7b05 100644 --- a/cache.h +++ b/cache.h @@ -173,7 +173,7 @@ extern struct index_state the_index; #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos)) #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose)) -#define refresh_cache(flags) refresh_index(&the_index, flags) +#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) #define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really)) #define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really)) #endif @@ -278,7 +278,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); #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_index(struct index_state *, unsigned int flags); +extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen); struct lock_file { struct lock_file *next; diff --git a/read-cache.c b/read-cache.c index 865369df0e..8b1c94e0e3 100644 --- a/read-cache.c +++ b/read-cache.c @@ -7,6 +7,7 @@ #include "cache.h" #include "cache-tree.h" #include "refs.h" +#include "dir.h" /* Index extensions. * @@ -798,7 +799,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, return updated; } -int refresh_index(struct index_state *istate, unsigned int flags) +int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, char *seen) { int i; int has_errors = 0; @@ -824,6 +825,9 @@ int refresh_index(struct index_state *istate, unsigned int flags) continue; } + if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen)) + continue; + new = refresh_cache_ent(istate, ce, really, &cache_errno); if (new == ce) continue; diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 213e9249da..a328bf57eb 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -143,4 +143,16 @@ test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over st git ls-files --stage | grep "^120000 .* 0 symlink$" ' +test_expect_success 'git add --refresh' ' + >foo && git add foo && git commit -a -m "commit all" && + test -z "`git diff-index HEAD -- foo`" && + git read-tree HEAD && + case "`git diff-index HEAD -- foo`" in + :100644" "*"M foo") echo ok;; + *) echo fail; (exit 1);; + esac && + git add --refresh -- foo && + test -z "`git diff-index HEAD -- foo`" +' + test_done From 95eb6853af73ba91188a481a882076fa2639f15f Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Sun, 12 Aug 2007 11:24:30 -0400 Subject: [PATCH 074/107] t3902 - skip test if file system doesn't support HT in names Windows / cygwin don't support HT, LF, or TAB in file name so this test is meaningless there. Signed-off-by: Mark Levedahl Signed-off-by: Junio C Hamano --- t/t3902-quoted.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh index 63f950b179..245fb3babd 100755 --- a/t/t3902-quoted.sh +++ b/t/t3902-quoted.sh @@ -7,6 +7,12 @@ test_description='quoted output' . ./test-lib.sh +P1='pathname with HT' +: >"$P1" 2>&1 && test -f "$P1" && rm -f "$P1" || { + echo >&2 'Filesystem does not support HT in names' + test_done +} + FN='濱野' GN='純' HT=' ' From 4b37666ccbab015e75fda745c29a2f5d58a54482 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 12 Aug 2007 10:44:06 -0700 Subject: [PATCH 075/107] builtin-bundle create - use lock_file "git bundle create" left an invalid, partially written bundle if an error occured during creation. Signed-off-by: Junio C Hamano --- builtin-bundle.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/builtin-bundle.c b/builtin-bundle.c index f4b4f034f4..1b650069c9 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -189,7 +189,9 @@ static int list_heads(struct bundle_header *header, int argc, const char **argv) static int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { + static struct lock_file lock; int bundle_fd = -1; + int bundle_to_stdout; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(5 * sizeof(const char *)); int i, ref_count = 0; @@ -198,14 +200,11 @@ static int create_bundle(struct bundle_header *header, const char *path, struct child_process rls; FILE *rls_fout; - /* - * NEEDSWORK: this should use something like lock-file - * to create temporary that is cleaned up upon error. - */ - bundle_fd = (!strcmp(path, "-") ? 1 : - open(path, O_CREAT | O_EXCL | O_WRONLY, 0666)); - if (bundle_fd < 0) - return error("Could not create '%s': %s", path, strerror(errno)); + bundle_to_stdout = !strcmp(path, "-"); + if (bundle_to_stdout) + bundle_fd = 1; + else + bundle_fd = hold_lock_file_for_update(&lock, path, 1); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); @@ -341,6 +340,9 @@ static int create_bundle(struct bundle_header *header, const char *path, } if (finish_command(&rls)) return error ("pack-objects died"); + close(bundle_fd); + if (!bundle_to_stdout) + commit_lock_file(&lock); return 0; } From 89d07f750a3f878c42b3de96ee93dc571b42b230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 12 Aug 2007 19:46:55 +0200 Subject: [PATCH 076/107] diff: don't run pager if user asked for a diff style exit code As Wincent Colaiuta found out, it's a bit unexpected for git diff to start a pager even when the --quiet option is specified. The problem is that the pager hides the return code -- which is the only output we're interested in in this case. Push pager setup down into builtin-diff.c and don't start the pager if --exit-code or --quiet (which implies --exit-code) was specified. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin-diff.c | 6 ++++++ git.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin-diff.c b/builtin-diff.c index b48121e6e2..8dc17b0dd7 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -235,6 +235,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix) rev.diffopt.allow_external = 1; rev.diffopt.recursive = 1; + /* If the user asked for our exit code then don't start a + * pager or we would end up reporting its exit code instead. + */ + if (!rev.diffopt.exit_with_status) + setup_pager(); + /* Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ diff --git a/git.c b/git.c index e5daae0f95..cab0e7227d 100644 --- a/git.c +++ b/git.c @@ -325,7 +325,7 @@ static void handle_internal_command(int argc, const char **argv) { "config", cmd_config }, { "count-objects", cmd_count_objects, RUN_SETUP }, { "describe", cmd_describe, RUN_SETUP }, - { "diff", cmd_diff, USE_PAGER }, + { "diff", cmd_diff }, { "diff-files", cmd_diff_files }, { "diff-index", cmd_diff_index, RUN_SETUP }, { "diff-tree", cmd_diff_tree, RUN_SETUP }, From 7da660f4372f4113184f782634c0fafb2cf46cef Mon Sep 17 00:00:00 2001 From: "Reece H. Dunn" Date: Mon, 13 Aug 2007 19:40:50 +0100 Subject: [PATCH 077/107] git-p4: Fix the sorting of changelists when cloning a Perforce repository. When performing a git-p4 clone operation on a Perforce repository, where the changelists change in order of magnitude (e.g. 100 to 1000), the set of changes to import from is not sorted properly. This is because the data in the list is strings not integers. The other place where this is done already converts the value to an integer, so it is not affected. Acked-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 805d632a68..6d0106237a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1322,7 +1322,7 @@ class P4Sync(Command): for line in output: changeNum = line.split(" ")[1] - changes.append(changeNum) + changes.append(int(changeNum)) changes.sort() From 70f64148bf7ca5136f2aa18c41b3b8f4bebfd138 Mon Sep 17 00:00:00 2001 From: Brian Gernhardt Date: Sat, 11 Aug 2007 15:24:29 -0400 Subject: [PATCH 078/107] Fix t5701-clone-local for white space from wc Signed-off-by: Brian Gernhardt Signed-off-by: Junio C Hamano --- t/t5701-clone-local.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh index a3026ec4fc..56f9d8ae73 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5701-clone-local.sh @@ -48,7 +48,7 @@ test_expect_success 'With -no-hardlinks, local will make a copy' ' git clone --bare --no-hardlinks x w && cd w && linked=$(find objects -type f ! -links 1 | wc -l) && - test "$linked" = 0 + test 0 = $linked ' test_expect_success 'Even without -l, local will make a hardlink' ' @@ -57,7 +57,7 @@ test_expect_success 'Even without -l, local will make a hardlink' ' git clone -l --bare x w && cd w && copied=$(find objects -type f -links 1 | wc -l) && - test "$copied" = 0 + test 0 = $copied ' test_done From 0476786e645ae18096d6a63de5fd4b756071b522 Mon Sep 17 00:00:00 2001 From: Alberto Bertogli Date: Tue, 14 Aug 2007 01:03:18 -0300 Subject: [PATCH 079/107] Allow git-svnimport to take "" as the trunk directory. Some repositories started with the trunk in "/" and then moved it to the standard "trunk/" location. On these repositories, the correct thing would be to call git-svnimport -T "", but because of the way the options are handled, it uses the default "trunk" instead of the given empty string. This patch fixes that behaviour. Reported by Leandro Lucarella . Signed-off-by: Alberto Bertogli Signed-off-by: Junio C Hamano --- git-svnimport.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-svnimport.perl b/git-svnimport.perl index b73d6494d8..8c17fb5ae2 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -49,7 +49,7 @@ getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:R:uv") or usage(); usage if $opt_h; my $tag_name = $opt_t || "tags"; -my $trunk_name = $opt_T || "trunk"; +my $trunk_name = defined $opt_T ? $opt_T : "trunk"; my $branch_name = $opt_b || "branches"; my $project_name = $opt_P || ""; $project_name = "/" . $project_name if ($project_name); From b2bc9a30981ace8c18d1a73b3e10153efc5c20f7 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Sat, 11 Aug 2007 15:36:28 +0200 Subject: [PATCH 080/107] git-sh-setup.sh: make GIT_DIR absolute Quite a few of the scripts are rather careless about using GIT_DIR while changing directories. Some try their hands (with different likelihood of success) in making GIT_DIR absolute. This patch lets git-sh-setup.sh cater for absolute directories (in a way that should work reliably also with non-Unix path names) and removes the respective kludges in git-filter-branch.sh and git-instaweb.sh. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- git-filter-branch.sh | 7 ------- git-instaweb.sh | 8 +------- git-sh-setup.sh | 12 +++++++++++- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index b5fa44920d..c42e4512cf 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -170,13 +170,6 @@ do esac done < "$tempdir"/backup-refs -case "$GIT_DIR" in -/*) - ;; -*) - GIT_DIR="$(pwd)/../../$GIT_DIR" - ;; -esac export GIT_DIR GIT_WORK_TREE=. # These refs should be updated if their heads were rewritten diff --git a/git-instaweb.sh b/git-instaweb.sh index cbc7418e35..b79c6b6a42 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -8,13 +8,7 @@ USAGE='[--start] [--stop] [--restart] . git-sh-setup -case "$GIT_DIR" in -/*) - fqgitdir="$GIT_DIR" ;; -*) - fqgitdir="$PWD/$GIT_DIR" ;; -esac - +fqgitdir="$GIT_DIR" local="`git config --bool --get instaweb.local`" httpd="`git config --get instaweb.httpd`" browser="`git config --get instaweb.browser`" diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 8cbd153b62..185c5c6c95 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -116,6 +116,16 @@ then exit $exit } else - GIT_DIR=$(git rev-parse --git-dir) || exit + GIT_DIR=$(git rev-parse --git-dir) || { + exit=$? + echo >&2 "Failed to find a valid git directory." + exit $exit + } fi + +test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || { + echo >&2 "Unable to determine absolute path of git directory" + exit 1 +} + : ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"} From 30c5cd312472574f6a8d8c82ba7c4af76d43ff7c Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Mon, 13 Aug 2007 07:38:11 +0200 Subject: [PATCH 081/107] Add a test for git-commit being confused by relative GIT_DIR Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- t/t2050-git-dir-relative.sh | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100755 t/t2050-git-dir-relative.sh diff --git a/t/t2050-git-dir-relative.sh b/t/t2050-git-dir-relative.sh new file mode 100755 index 0000000000..88f268b9d7 --- /dev/null +++ b/t/t2050-git-dir-relative.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +test_description='check problems with relative GIT_DIR + +This test creates a working tree state with a file and subdir: + + top (committed several times) + subdir (a subdirectory) + +It creates a commit-hook and tests it, then moves .git +into the subdir while keeping the worktree location, +and tries commits from the top and the subdir, checking +that the commit-hook still gets called.' + +. ./test-lib.sh + +COMMIT_FILE="$(pwd)/output" +export COMMIT_FILE + +test_expect_success 'Setting up post-commit hook' ' +mkdir -p .git/hooks && +echo >.git/hooks/post-commit "#!/bin/sh +touch \"\${COMMIT_FILE}\" +echo Post commit hook was called." && +chmod +x .git/hooks/post-commit' + +test_expect_success 'post-commit hook used ordinarily' ' +echo initial >top && +git-add top +git-commit -m initial && +test -r "${COMMIT_FILE}" +' + +rm -rf "${COMMIT_FILE}" +mkdir subdir +mv .git subdir + +test_expect_success 'post-commit-hook created and used from top dir' ' +echo changed >top && +git --git-dir subdir/.git add top && +git --git-dir subdir/.git commit -m topcommit && +test -r "${COMMIT_FILE}" +' + +rm -rf "${COMMIT_FILE}" + +test_expect_success 'post-commit-hook from sub dir' ' +echo changed again >top +cd subdir && +git --git-dir .git --work-tree .. add ../top && +git --git-dir .git --work-tree .. commit -m subcommit && +test -r "${COMMIT_FILE}" +' + +test_done From 180787c48f21c40a047ae3ee5432821d7e033e44 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Tue, 14 Aug 2007 00:05:50 +0200 Subject: [PATCH 082/107] Improved hint on how to set identity The first thing we teach in the tutorial is to set the default identity in $HOME/.gitconfig using "git config --global". The suggestion in the error message should match the order, while hinting that per repository identity can later be configured differently. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- ident.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ident.c b/ident.c index 6612d17eba..9b2a852cb0 100644 --- a/ident.c +++ b/ident.c @@ -185,11 +185,11 @@ static const char *env_hint = "\n" "Run\n" "\n" -" git config user.email \"you@email.com\"\n" -" git config user.name \"Your Name\"\n" +" git config --global user.email \"you@email.com\"\n" +" git config --global user.name \"Your Name\"\n" "\n" -"To set the identity in this repository.\n" -"Add --global to set your account\'s default\n" +"to set your account\'s default identity.\n" +"Omit --global to set the identity only in this repository.\n" "\n"; const char *fmt_ident(const char *name, const char *email, From fb13227e089f22dc31a3b1624559153821056848 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 13:33:31 -0700 Subject: [PATCH 083/107] git-diff: squelch "empty" diffs After starting to edit a working tree file but later when your edit ends up identical to the original (this can also happen when you ran a wholesale regexp replace with something like "perl -i" that does not actually modify many of the paths), "git diff" between the index and the working tree outputs many "empty" diffs that show "diff --git" headers and nothing else, because these paths are stat-dirty. While it was a way to warn the user that the earlier action of the user made the index ineffective as an optimization mechanism, it was felt too loud for the purpose of warning even to experienced users, and also resulted in confusing people new to git. This replaces the "empty" diffs with a single warning message at the end. Having many such paths hurts performance, and you can run "git-update-index --refresh" to update the lstat(2) information recorded in the index in such a case. "git-status" does so as a side effect, and that is more familiar to the end-user, so we recommend it to them. The change affects only "git diff" that outputs patch text, because that is where the annoyance of too many "empty" diff is most strongly felt, and because the warning message can be safely ignored by downstream tools without getting mistaken as part of the patch. For the low-level "git diff-files" and "git diff-index", the traditional behaviour is retained. Signed-off-by: Junio C Hamano --- builtin-diff.c | 8 ++++++++ diff.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ diff.h | 1 + 3 files changed, 61 insertions(+) diff --git a/builtin-diff.c b/builtin-diff.c index 8dc17b0dd7..6ed7b6842e 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -222,6 +222,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) prefix = setup_git_directory_gently(&nongit); git_config(git_diff_ui_config); init_revisions(&rev, prefix); + rev.diffopt.skip_stat_unmatch = 1; if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix)) argc = 0; @@ -344,5 +345,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix) ent, ents); if (rev.diffopt.exit_with_status) result = rev.diffopt.has_changes; + + if ((rev.diffopt.output_format & DIFF_FORMAT_PATCH) + && (1 < rev.diffopt.skip_stat_unmatch)) + printf("Warning: %d path%s touched but unmodified. " + "Consider running git-status.\n", + rev.diffopt.skip_stat_unmatch - 1, + rev.diffopt.skip_stat_unmatch == 2 ? "" : "s"); return result; } diff --git a/diff.c b/diff.c index a5fc56bdad..f884de77ac 100644 --- a/diff.c +++ b/diff.c @@ -3143,11 +3143,63 @@ static void diffcore_apply_filter(const char *filter) *q = outq; } +static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) +{ + int i; + struct diff_queue_struct *q = &diff_queued_diff; + struct diff_queue_struct outq; + outq.queue = NULL; + outq.nr = outq.alloc = 0; + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + + /* + * 1. Entries that come from stat info dirtyness + * always have both sides (iow, not create/delete), + * one side of the object name is unknown, with + * the same mode and size. Keep the ones that + * do not match these criteria. They have real + * differences. + * + * 2. At this point, the file is known to be modified, + * with the same mode and size, and the object + * name of one side is unknown. Need to inspect + * the identical contents. + */ + if (!DIFF_FILE_VALID(p->one) || /* (1) */ + !DIFF_FILE_VALID(p->two) || + (p->one->sha1_valid && p->two->sha1_valid) || + (p->one->mode != p->two->mode) || + diff_populate_filespec(p->one, 1) || + diff_populate_filespec(p->two, 1) || + (p->one->size != p->two->size) || + + diff_populate_filespec(p->one, 0) || /* (2) */ + diff_populate_filespec(p->two, 0) || + memcmp(p->one->data, p->two->data, p->one->size)) + diff_q(&outq, p); + else { + /* + * The caller can subtract 1 from skip_stat_unmatch + * to determine how many paths were dirty only + * due to stat info mismatch. + */ + diffopt->skip_stat_unmatch++; + diff_free_filepair(p); + } + } + free(q->queue); + *q = outq; +} + void diffcore_std(struct diff_options *options) { if (options->quiet) return; + if (options->skip_stat_unmatch && !options->find_copies_harder) + diffcore_skip_stat_unmatch(options); if (options->break_opt != -1) diffcore_break(options->break_opt); if (options->detect_rename) diff --git a/diff.h b/diff.h index 9fd6d447d4..de21f8ecd0 100644 --- a/diff.h +++ b/diff.h @@ -65,6 +65,7 @@ struct diff_options { int context; int break_opt; int detect_rename; + int skip_stat_unmatch; int line_termination; int output_format; int pickaxe_opts; From 9fa3465d6be83c08ed24762c82eb33cb005729f3 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Fri, 20 Jul 2007 20:15:13 +0200 Subject: [PATCH 084/107] Add --log-size to git log to print message size With this option git-log prints log message size just before the corresponding message. Porcelain tools could use this to speedup parsing of git-log output. Note that size refers to log message only. If also patch content is shown its size is not included. In case it is not possible to know the size upfront size value is set to zero. Signed-off-by: Marco Costalba Signed-off-by: Junio C Hamano --- Documentation/git-log.txt | 7 +++++++ log-tree.c | 3 +++ revision.c | 4 ++++ revision.h | 1 + 4 files changed, 15 insertions(+) diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 63c1dbe812..5a90f65b5e 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -64,6 +64,13 @@ include::pretty-options.txt[] --follow:: Continue listing the history of a file beyond renames. +--log-size:: + Before the log message print out its size in bytes. Intended + mainly for porcelain tools consumption. If git is unable to + produce a valid value size is set to zero. + Note that only message is considered, if also a diff is shown + its size is not included. + ...:: Show only commits that affect the specified paths. diff --git a/log-tree.c b/log-tree.c index 8624d5a39c..a6423718e7 100644 --- a/log-tree.c +++ b/log-tree.c @@ -295,6 +295,9 @@ void show_log(struct rev_info *opt, const char *sep) if (opt->add_signoff) len = append_signoff(&msgbuf, &msgbuf_len, len, opt->add_signoff); + if (opt->show_log_size) + printf("log size %i\n", len); + printf("%s%s%s", msgbuf, extra, sep); free(msgbuf); } diff --git a/revision.c b/revision.c index 038693caba..7d32a89b0e 100644 --- a/revision.c +++ b/revision.c @@ -1150,6 +1150,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch die("unknown date format %s", arg); continue; } + if (!strcmp(arg, "--log-size")) { + revs->show_log_size = 1; + continue; + } /* * Grepping the commit log diff --git a/revision.h b/revision.h index f46b4d55a2..98a0a8f3fa 100644 --- a/revision.h +++ b/revision.h @@ -81,6 +81,7 @@ struct rev_info { const char *log_reencode; const char *subject_prefix; int no_inline; + int show_log_size; /* Filter by commit log message */ struct grep_opt *grep_filter; From b798671fa935492ce511766bc99fb26b2892499b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 14 Aug 2007 15:33:07 -0700 Subject: [PATCH 085/107] merge-recursive: do not rudely die on binary merge When you try to merge a path that involves binary file-level merge, merge-recursive died rudely without cleaning up its own mess. A files added by the merge were left in the working tree, but the index was not written out (because it just punted and died), so it was cumbersome for the user to retry it by first running "git reset --hard". This changes merge-recursive to still warn but do the "binary" merge for such a path; leave the "our" version in the working tree, but still keep the path unmerged so that the user can sort it out. Signed-off-by: Junio C Hamano --- merge-recursive.c | 51 +++++++++++++++++-------------- t/t6027-merge-binary.sh | 67 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 23 deletions(-) create mode 100755 t/t6027-merge-binary.sh diff --git a/merge-recursive.c b/merge-recursive.c index f7d1b84999..5326d7c97a 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -677,6 +677,26 @@ struct ll_merge_driver { /* * Built-in low-levels */ +static int ll_binary_merge(const struct ll_merge_driver *drv_unused, + const char *path_unused, + mmfile_t *orig, + mmfile_t *src1, const char *name1, + mmfile_t *src2, const char *name2, + mmbuffer_t *result) +{ + /* + * The tentative merge result is "ours" for the final round, + * or common ancestor for an internal merge. Still return + * "conflicted merge" status. + */ + mmfile_t *stolen = index_only ? orig : src1; + + result->ptr = stolen->ptr; + result->size = stolen->size; + stolen->ptr = NULL; + return 1; +} + static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, const char *path_unused, mmfile_t *orig, @@ -687,10 +707,15 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, xpparam_t xpp; if (buffer_is_binary(orig->ptr, orig->size) || - buffer_is_binary(src1->ptr, src1->size) || - buffer_is_binary(src2->ptr, src2->size)) - return error("Cannot merge binary files: %s vs. %s\n", + buffer_is_binary(src1->ptr, src1->size) || + buffer_is_binary(src2->ptr, src2->size)) { + warning("Cannot merge binary files: %s vs. %s\n", name1, name2); + return ll_binary_merge(drv_unused, path_unused, + orig, src1, name1, + src2, name2, + result); + } memset(&xpp, 0, sizeof(xpp)); return xdl_merge(orig, @@ -743,26 +768,6 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused, return 0; } -static int ll_binary_merge(const struct ll_merge_driver *drv_unused, - const char *path_unused, - mmfile_t *orig, - mmfile_t *src1, const char *name1, - mmfile_t *src2, const char *name2, - mmbuffer_t *result) -{ - /* - * The tentative merge result is "ours" for the final round, - * or common ancestor for an internal merge. Still return - * "conflicted merge" status. - */ - mmfile_t *stolen = index_only ? orig : src1; - - result->ptr = stolen->ptr; - result->size = stolen->size; - stolen->ptr = NULL; - return 1; -} - #define LL_BINARY_MERGE 0 #define LL_TEXT_MERGE 1 #define LL_UNION_MERGE 2 diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh new file mode 100755 index 0000000000..a7358f75b1 --- /dev/null +++ b/t/t6027-merge-binary.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +test_description='ask merge-recursive to merge binary files' + +. ./test-lib.sh + +test_expect_success setup ' + + cat ../test4012.png >m && + git add m && + git ls-files -s | sed -e "s/ 0 / 1 /" >E1 && + test_tick && + git commit -m "initial" && + + git branch side && + echo frotz >a && + git add a && + echo nitfol >>m && + git add a m && + git ls-files -s a >E0 && + git ls-files -s m | sed -e "s/ 0 / 3 /" >E3 && + test_tick && + git commit -m "master adds some" && + + git checkout side && + echo rezrov >>m && + git add m && + git ls-files -s m | sed -e "s/ 0 / 2 /" >E2 && + test_tick && + git commit -m "side modifies" && + + git tag anchor && + + cat E0 E1 E2 E3 >expect +' + +test_expect_success resolve ' + + rm -f a* m* && + git reset --hard anchor && + + if git merge -s resolve master + then + echo Oops, should not have succeeded + false + else + git ls-files -s >current + diff -u current expect + fi +' + +test_expect_success recursive ' + + rm -f a* m* && + git reset --hard anchor && + + if git merge -s recursive master + then + echo Oops, should not have succeeded + false + else + git ls-files -s >current + diff -u current expect + fi +' + +test_done From eef427a09ce7fcbdc54c73ae363cbab331eccd88 Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Tue, 14 Aug 2007 16:42:37 -0300 Subject: [PATCH 086/107] Avoid ambiguous error message if pack.idx header is wrong Print the index version when an error occurs so the user knows what type of header (and size) we thought the index should have had. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Junio C Hamano --- sha1_file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index aca741b79c..b219d4d5f2 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -497,7 +497,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p) */ if (idx_size != 4*256 + nr * 24 + 20 + 20) { munmap(idx_map, idx_size); - return error("wrong index file size in %s", path); + return error("wrong index v1 file size in %s", path); } } else if (version == 2) { /* @@ -519,7 +519,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p) max_size += (nr - 1)*8; if (idx_size < min_size || idx_size > max_size) { munmap(idx_map, idx_size); - return error("wrong index file size in %s", path); + return error("wrong index v2 file size in %s", path); } if (idx_size != min_size) { /* make sure we can deal with large pack offsets */ From f21a47b27ceef8ff2f6f218d5b7266d5fd9e2e93 Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Tue, 14 Aug 2007 16:44:53 -0300 Subject: [PATCH 087/107] Introduces xmkstemp() This is a wrapper for mkstemp() that performs error checking and calls die() when an error occur. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Junio C Hamano --- git-compat-util.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/git-compat-util.h b/git-compat-util.h index 362e040f52..ca0a597a28 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -303,6 +303,16 @@ static inline FILE *xfdopen(int fd, const char *mode) return stream; } +static inline int xmkstemp(char *template) +{ + int fd; + + fd = mkstemp(template); + if (fd < 0) + die("Unable to create temporary file: %s", strerror(errno)); + return fd; +} + static inline size_t xsize_t(off_t len) { return (size_t)len; From 7647b17f1d7a98362f8bdbe48b30d94ed655229c Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Tue, 14 Aug 2007 16:45:58 -0300 Subject: [PATCH 088/107] Use xmkstemp() instead of mkstemp() xmkstemp() performs error checking and prints a standard error message when an error occur. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 4 +--- fast-import.c | 8 ++------ index-pack.c | 2 +- merge-recursive.c | 4 +--- pack-write.c | 2 +- unpack-file.c | 4 +--- 6 files changed, 7 insertions(+), 17 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 5e9d1fd86c..51a850e111 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -586,7 +586,7 @@ static off_t write_one(struct sha1file *f, static int open_object_dir_tmp(const char *path) { snprintf(tmpname, sizeof(tmpname), "%s/%s", get_object_directory(), path); - return mkstemp(tmpname); + return xmkstemp(tmpname); } /* forward declaration for write_pack_file */ @@ -612,8 +612,6 @@ static void write_pack_file(void) f = sha1fd(1, ""); } else { int fd = open_object_dir_tmp("tmp_pack_XXXXXX"); - if (fd < 0) - die("unable to create %s: %s\n", tmpname, strerror(errno)); pack_tmp_name = xstrdup(tmpname); f = sha1fd(fd, pack_tmp_name); } diff --git a/fast-import.c b/fast-import.c index 99a19d89e8..170cccdceb 100644 --- a/fast-import.c +++ b/fast-import.c @@ -663,9 +663,7 @@ static void start_packfile(void) snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_pack_XXXXXX", get_object_directory()); - pack_fd = mkstemp(tmpfile); - if (pack_fd < 0) - die("Can't create %s: %s", tmpfile, strerror(errno)); + pack_fd = xmkstemp(tmpfile); p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2); strcpy(p->pack_name, tmpfile); p->pack_fd = pack_fd; @@ -727,9 +725,7 @@ static char *create_index(void) snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_idx_XXXXXX", get_object_directory()); - idx_fd = mkstemp(tmpfile); - if (idx_fd < 0) - die("Can't create %s: %s", tmpfile, strerror(errno)); + idx_fd = xmkstemp(tmpfile); f = sha1fd(idx_fd, tmpfile); sha1write(f, array, 256 * sizeof(int)); SHA1_Init(&ctx); diff --git a/index-pack.c b/index-pack.c index 8403c36b63..db58e05041 100644 --- a/index-pack.c +++ b/index-pack.c @@ -114,7 +114,7 @@ static const char *open_pack_file(const char *pack_name) static char tmpfile[PATH_MAX]; snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_pack_XXXXXX", get_object_directory()); - output_fd = mkstemp(tmpfile); + output_fd = xmkstemp(tmpfile); pack_name = xstrdup(tmpfile); } else output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600); diff --git a/merge-recursive.c b/merge-recursive.c index 5326d7c97a..16f6a0f98b 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -782,9 +782,7 @@ static void create_temp(mmfile_t *src, char *path) int fd; strcpy(path, ".merge_file_XXXXXX"); - fd = mkstemp(path); - if (fd < 0) - die("unable to create temp-file"); + fd = xmkstemp(path); if (write_in_full(fd, src->ptr, src->size) != src->size) die("unable to write temp-file"); close(fd); diff --git a/pack-write.c b/pack-write.c index 1cf5f7c9f0..e59b197e5e 100644 --- a/pack-write.c +++ b/pack-write.c @@ -45,7 +45,7 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec static char tmpfile[PATH_MAX]; snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_idx_XXXXXX", get_object_directory()); - fd = mkstemp(tmpfile); + fd = xmkstemp(tmpfile); index_name = xstrdup(tmpfile); } else { unlink(index_name); diff --git a/unpack-file.c b/unpack-file.c index 25c56b374a..65c66eb0bf 100644 --- a/unpack-file.c +++ b/unpack-file.c @@ -14,9 +14,7 @@ static char *create_temp_file(unsigned char *sha1) die("unable to read blob object %s", sha1_to_hex(sha1)); strcpy(path, ".merge_file_XXXXXX"); - fd = mkstemp(path); - if (fd < 0) - die("unable to create temp-file"); + fd = xmkstemp(path); if (write_in_full(fd, buf, size) != size) die("unable to write temp-file"); close(fd); From 6d2d9e8666ef72c8878af4822e243ca33b6752a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Wed, 15 Aug 2007 00:41:00 +0200 Subject: [PATCH 089/107] diff: squelch empty diffs even more When we compare two non-tracked files, or explicitly specify --no-index, the suggestion to run git-status is not helpful. The patch adds a new diff_options bitfield member, no_index, that is used instead of the special value of -2 of the rev_info field max_count to indicate that the index is not to be used. This makes it possible to pass that flag down to diffcore_skip_stat_unmatch(), which only has one diff_options parameter. This could even become a cleanup if we removed all assignments of max_count to a value of -2 (viz. replacement of a magic value with a self-documenting field name) but I didn't dare to do that so late in the rc game.. The no_index bit, if set, then tells diffcore_skip_stat_unmatch() to not account for any skipped stat-mismatches, which avoids the suggestion to run git-status. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- diff-lib.c | 8 ++++++-- diff.c | 3 ++- diff.h | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index 92c0e39ad6..f5568c3b36 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -189,6 +189,7 @@ static int handle_diff_files_args(struct rev_info *revs, !strcmp(argv[1], "--no-index")) { revs->max_count = -2; revs->diffopt.exit_with_status = 1; + revs->diffopt.no_index = 1; } else if (!strcmp(argv[1], "-q")) *silent = 1; @@ -204,8 +205,10 @@ static int handle_diff_files_args(struct rev_info *revs, */ read_cache(); if (!is_in_index(revs->diffopt.paths[0]) || - !is_in_index(revs->diffopt.paths[1])) + !is_in_index(revs->diffopt.paths[1])) { revs->max_count = -2; + revs->diffopt.no_index = 1; + } } /* @@ -293,6 +296,7 @@ int setup_diff_no_index(struct rev_info *revs, else revs->diffopt.paths = argv + argc - 2; revs->diffopt.nr_paths = 2; + revs->diffopt.no_index = 1; revs->max_count = -2; return 0; } @@ -304,7 +308,7 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) if (handle_diff_files_args(revs, argc, argv, &silent_on_removed)) return -1; - if (revs->max_count == -2) { + if (revs->diffopt.no_index) { if (revs->diffopt.nr_paths != 2) return error("need two files/directories with --no-index"); if (queue_diff(&revs->diffopt, revs->diffopt.paths[0], diff --git a/diff.c b/diff.c index f884de77ac..97cc5bc085 100644 --- a/diff.c +++ b/diff.c @@ -3185,7 +3185,8 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) * to determine how many paths were dirty only * due to stat info mismatch. */ - diffopt->skip_stat_unmatch++; + if (!diffopt->no_index) + diffopt->skip_stat_unmatch++; diff_free_filepair(p); } } diff --git a/diff.h b/diff.h index de21f8ecd0..4546aad219 100644 --- a/diff.h +++ b/diff.h @@ -60,6 +60,7 @@ struct diff_options { color_diff_words:1, has_changes:1, quiet:1, + no_index:1, allow_external:1, exit_with_status:1; int context; From 6b06d518caa6ef98cc0efb796713d0a0693895bf Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Tue, 14 Aug 2007 08:18:38 -0500 Subject: [PATCH 090/107] Add read_cache to builtin-check-attr We can now read .gitattributes files out of the index, but the index must be loaded for this to work. Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- builtin-check-attr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builtin-check-attr.c b/builtin-check-attr.c index 9d77f76ff1..d94973379c 100644 --- a/builtin-check-attr.c +++ b/builtin-check-attr.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "cache.h" #include "attr.h" #include "quote.h" @@ -10,6 +11,10 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) struct git_attr_check *check; int cnt, i, doubledash; + if (read_cache() < 0) { + die("invalid cache"); + } + doubledash = -1; for (i = 1; doubledash < 0 && i < argc; i++) { if (!strcmp(argv[i], "--")) From a44131181a13eb599ed35647709692b1699706eb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 14 Aug 2007 01:40:45 -0700 Subject: [PATCH 091/107] attr.c: refactoring This splits out a common routine that parses a single line of attribute file and adds it to the attr_stack. It should not change any behaviour, other than attrs array in the attr_stack structure is now grown with alloc_nr() macro, instead of one by one, which relied on xrealloc() to give enough slack to be efficient enough. Signed-off-by: Junio C Hamano --- attr.c | 69 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/attr.c b/attr.c index a0712543b2..8d778f1d26 100644 --- a/attr.c +++ b/attr.c @@ -257,6 +257,7 @@ static struct attr_stack { struct attr_stack *prev; char *origin; unsigned num_matches; + unsigned alloc; struct match_attr **attrs; } *attr_stack; @@ -287,6 +288,26 @@ static const char *builtin_attr[] = { NULL, }; +static void handle_attr_line(struct attr_stack *res, + const char *line, + const char *src, + int lineno, + int macro_ok) +{ + struct match_attr *a; + + a = parse_attr_line(line, src, lineno, macro_ok); + if (!a) + return; + if (res->alloc <= res->num_matches) { + res->alloc = alloc_nr(res->num_matches); + res->attrs = xrealloc(res->attrs, + sizeof(struct match_attr *) * + res->alloc); + } + res->attrs[res->num_matches++] = a; +} + static struct attr_stack *read_attr_from_array(const char **list) { struct attr_stack *res; @@ -294,45 +315,37 @@ static struct attr_stack *read_attr_from_array(const char **list) int lineno = 0; res = xcalloc(1, sizeof(*res)); - while ((line = *(list++)) != NULL) { - struct match_attr *a; - - a = parse_attr_line(line, "[builtin]", ++lineno, 1); - if (!a) - continue; - res->attrs = xrealloc(res->attrs, - sizeof(struct match_attr *) * (res->num_matches + 1)); - res->attrs[res->num_matches++] = a; - } + while ((line = *(list++)) != NULL) + handle_attr_line(res, line, "[builtin]", ++lineno, 1); return res; } static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) { - FILE *fp; + FILE *fp = fopen(path, "r"); struct attr_stack *res; char buf[2048]; int lineno = 0; - res = xcalloc(1, sizeof(*res)); - fp = fopen(path, "r"); if (!fp) - return res; - - while (fgets(buf, sizeof(buf), fp)) { - struct match_attr *a; - - a = parse_attr_line(buf, path, ++lineno, macro_ok); - if (!a) - continue; - res->attrs = xrealloc(res->attrs, - sizeof(struct match_attr *) * (res->num_matches + 1)); - res->attrs[res->num_matches++] = a; - } + return NULL; + res = xcalloc(1, sizeof(*res)); + while (fgets(buf, sizeof(buf), fp)) + handle_attr_line(res, buf, path, ++lineno, macro_ok); fclose(fp); return res; } +static struct attr_stack *read_attr(const char *path, int macro_ok) +{ + struct attr_stack *res; + + res = read_attr_from_file(path, macro_ok); + if (!res) + res = xcalloc(1, sizeof(*res)); + return res; +} + #if DEBUG_ATTR static void debug_info(const char *what, struct attr_stack *elem) { @@ -370,13 +383,15 @@ static void bootstrap_attr_stack(void) elem->prev = attr_stack; attr_stack = elem; - elem = read_attr_from_file(GITATTRIBUTES_FILE, 1); + elem = read_attr(GITATTRIBUTES_FILE, 1); elem->origin = strdup(""); elem->prev = attr_stack; attr_stack = elem; debug_push(elem); elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1); + if (!elem) + elem = xcalloc(1, sizeof(*elem)); elem->origin = NULL; elem->prev = attr_stack; attr_stack = elem; @@ -441,7 +456,7 @@ static void prepare_attr_stack(const char *path, int dirlen) memcpy(pathbuf + dirlen, "/", 2); cp = strchr(pathbuf + len + 1, '/'); strcpy(cp + 1, GITATTRIBUTES_FILE); - elem = read_attr_from_file(pathbuf, 0); + elem = read_attr(pathbuf, 0); *cp = '\0'; elem->origin = strdup(pathbuf); elem->prev = attr_stack; From 1a9d7e9b484e77436edc7f5cacd39c24ec605e6d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 14 Aug 2007 01:41:02 -0700 Subject: [PATCH 092/107] attr.c: read .gitattributes from index as well. This makes .gitattributes files to be read from the index when they are not checked out to the work tree. This is in line with the way we always allowed low-level tools to operate in sparsely checked out work tree in a reasonable way. It swaps the order of new file creation and converting the blob to work tree representation; otherwise when we are in the middle of checking out .gitattributes we would notice an empty but unwritten .gitattributes file in the work tree and will ignore the copy in the index. Signed-off-by: Junio C Hamano --- attr.c | 61 +++++++++++++++++++++++++++++++++++-- entry.c | 19 ++++++------ t/t0020-crlf.sh | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 11 deletions(-) diff --git a/attr.c b/attr.c index 8d778f1d26..129399310a 100644 --- a/attr.c +++ b/attr.c @@ -336,13 +336,70 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) return res; } +static void *read_index_data(const char *path) +{ + int pos, len; + unsigned long sz; + enum object_type type; + void *data; + + len = strlen(path); + pos = cache_name_pos(path, len); + if (pos < 0) { + /* + * We might be in the middle of a merge, in which + * case we would read stage #2 (ours). + */ + int i; + for (i = -pos - 1; + (pos < 0 && i < active_nr && + !strcmp(active_cache[i]->name, path)); + i++) + if (ce_stage(active_cache[i]) == 2) + pos = i; + } + if (pos < 0) + return NULL; + data = read_sha1_file(active_cache[pos]->sha1, &type, &sz); + if (!data || type != OBJ_BLOB) { + free(data); + return NULL; + } + return data; +} + static struct attr_stack *read_attr(const char *path, int macro_ok) { struct attr_stack *res; + char *buf, *sp; + int lineno = 0; res = read_attr_from_file(path, macro_ok); - if (!res) - res = xcalloc(1, sizeof(*res)); + if (res) + return res; + + res = xcalloc(1, sizeof(*res)); + + /* + * There is no checked out .gitattributes file there, but + * we might have it in the index. We allow operation in a + * sparsely checked out work tree, so read from it. + */ + buf = read_index_data(path); + if (!buf) + return res; + + for (sp = buf; *sp; ) { + char *ep; + int more; + for (ep = sp; *ep && *ep != '\n'; ep++) + ; + more = (*ep == '\n'); + *ep = '\0'; + handle_attr_line(res, sp, path, ++lineno, macro_ok); + sp = ep + more; + } + free(buf); return res; } diff --git a/entry.c b/entry.c index 0625112339..fc3a506ece 100644 --- a/entry.c +++ b/entry.c @@ -112,6 +112,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout if (!new) return error("git-checkout-index: unable to read sha1 file of %s (%s)", path, sha1_to_hex(ce->sha1)); + + /* + * Convert from git internal format to working tree format + */ + buf = convert_to_working_tree(ce->name, new, &size); + if (buf) { + free(new); + new = buf; + } + if (to_tempfile) { strcpy(path, ".merge_file_XXXXXX"); fd = mkstemp(path); @@ -123,15 +133,6 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout path, strerror(errno)); } - /* - * Convert from git internal format to working tree format - */ - buf = convert_to_working_tree(ce->name, new, &size); - if (buf) { - free(new); - new = buf; - } - wrote = write_in_full(fd, new, size); close(fd); free(new); diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh index fe1dfd08a0..0807d9f01a 100755 --- a/t/t0020-crlf.sh +++ b/t/t0020-crlf.sh @@ -290,4 +290,85 @@ test_expect_success '.gitattributes says two and three are text' ' fi ' +test_expect_success 'in-tree .gitattributes (1)' ' + + echo "one -crlf" >>.gitattributes && + git add .gitattributes && + git commit -m "Add .gitattributes" && + + rm -rf tmp one dir .gitattributes patch.file three && + git read-tree --reset -u HEAD && + + if remove_cr one >/dev/null + then + echo "Eh? one should not have CRLF" + false + else + : happy + fi && + remove_cr three >/dev/null || { + echo "Eh? three should still have CRLF" + false + } +' + +test_expect_success 'in-tree .gitattributes (2)' ' + + rm -rf tmp one dir .gitattributes patch.file three && + git read-tree --reset HEAD && + git checkout-index -f -q -u -a && + + if remove_cr one >/dev/null + then + echo "Eh? one should not have CRLF" + false + else + : happy + fi && + remove_cr three >/dev/null || { + echo "Eh? three should still have CRLF" + false + } +' + +test_expect_success 'in-tree .gitattributes (3)' ' + + rm -rf tmp one dir .gitattributes patch.file three && + git read-tree --reset HEAD && + git checkout-index -u .gitattributes && + git checkout-index -u one dir/two three && + + if remove_cr one >/dev/null + then + echo "Eh? one should not have CRLF" + false + else + : happy + fi && + remove_cr three >/dev/null || { + echo "Eh? three should still have CRLF" + false + } +' + +test_expect_success 'in-tree .gitattributes (4)' ' + + rm -rf tmp one dir .gitattributes patch.file three && + git read-tree --reset HEAD && + git checkout-index -u one dir/two three && + git checkout-index -u .gitattributes && + + if remove_cr one >/dev/null + then + echo "Eh? one should not have CRLF" + false + else + : happy + fi && + remove_cr three >/dev/null || { + echo "Eh? three should still have CRLF" + false + } +' + test_done From 1467b5fec376b6c43a5fa54bffd356e458a8d57c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 14 Aug 2007 23:52:36 -0700 Subject: [PATCH 093/107] GIT 1.5.3-rc5 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.txt | 81 ++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt index 21bb1fc6f2..9c36e8baeb 100644 --- a/Documentation/RelNotes-1.5.3.txt +++ b/Documentation/RelNotes-1.5.3.txt @@ -9,6 +9,19 @@ Updates since v1.5.2 * The submodule support has Porcelain layer. + Note that the current submodule support is minimal and this is + deliberately so. A design decision we made is that operations + at the supermodule level do not recurse into submodules by + default. The expectation is that later we would add a + mechanism to tell git which submodules the user is interested + in, and this information might be used to determine the + recursive behaviour of certain commands (e.g. "git checkout" + and "git diff"), but currently we haven't agreed on what that + mechanism should look like. Therefore, if you use submodules, + you would probably need "git submodule update" on the + submodules you care about after running a "git checkout" at + the supermodule level. + * There are a handful pack-objects changes to help you cope better with repositories with pathologically large blobs in them. @@ -46,21 +59,21 @@ Updates since v1.5.2 - "git log" learned a new option "--follow", to follow renaming history of a single file. - - "git-filter-branch" lets you rewrite the revision history of + - "git filter-branch" lets you rewrite the revision history of specified branches. You can specify a number of filters to modify the commits, files and trees. - - "git-cvsserver" learned new options (--base-path, --export-all, - --strict-paths) inspired by git-daemon. + - "git cvsserver" learned new options (--base-path, --export-all, + --strict-paths) inspired by "git daemon". - "git daemon --base-path-relaxed" can help migrating a repository URL that did not use to use --base-path to use --base-path. - - "git-commit" can use "-t templatefile" option and commit.template + - "git commit" can use "-t templatefile" option and commit.template configuration variable to prime the commit message given to you in the editor. - - "git-submodule" command helps you manage the projects from + - "git submodule" command helps you manage the projects from the superproject that contain them. - In addition to core.compression configuration option, @@ -68,15 +81,15 @@ Updates since v1.5.2 independently tweak zlib compression levels used for loose and packed objects. - - "git-ls-tree -l" shows size of blobs pointed at by the + - "git ls-tree -l" shows size of blobs pointed at by the tree entries, similar to "/bin/ls -l". - - "git-rev-list" learned --regexp-ignore-case and + - "git rev-list" learned --regexp-ignore-case and --extended-regexp options to tweak its matching logic used for --grep fitering. - - "git-describe --contains" is a handier way to call more - obscure command "git-name-rev --tags". + - "git describe --contains" is a handier way to call more + obscure command "git name-rev --tags". - "git gc --aggressive" tells the command to spend more cycles to optimize the repository harder. @@ -112,6 +125,9 @@ Updates since v1.5.2 - "git config" learned NUL terminated output format via -z to help scripts. + - "git add" learned "--refresh ..." option to selectively refresh + the cached stat information. + - "git init -q" makes the command quieter. * Updated behavior of existing commands. @@ -126,9 +142,19 @@ Updates since v1.5.2 of the format ('tgz', 'tbz2' or 'zip'). Please update the your configuration file accordingly. + - "git clone" uses -l (hardlink files under .git) by default when + cloning locally. + + - "git bundle create" can now create a bundle without negative refs, + i.e. "everything since the beginning up to certain points". + - "git diff" (but not the plumbing level "git diff-tree") now recursively descends into trees by default. + - "git diff" does not show differences that come only from + stat-dirtiness in the form of "diff --git" header anymore. When + generating a textual diff, it shows a warning message at the end. + - The editor to use with many interactive commands can be overridden with GIT_EDITOR environment variable, or if it does not exist, with core.editor configuration variable. As @@ -143,8 +169,16 @@ Updates since v1.5.2 given strings now have shorter abbreviations. -i is for ignore case, and -E is for extended regexp. + - "git log" learned --log-size to show the number of bytes in + the log message part of the output to help qgit. + - "git svn dcommit" retains local merge information. + - "git svnimport" allows an empty string to be specified as the + trunk/ directory. This is necessary to suck data from a SVN + repository that doe not have trunk/ branches/ and tags/ organization + at all. + - "git config" to set values also honors type flags like --bool and --int. @@ -167,7 +201,7 @@ Updates since v1.5.2 and the handcrafted ones the old code created was not properly formed anyway. - - "git-push" pretends that you immediately fetched back from + - "git push" pretends that you immediately fetched back from the remote by updating corresponding remote tracking branches if you have any. @@ -177,10 +211,10 @@ Updates since v1.5.2 - "git commit --amend" is now compatible with various message source options such as -m/-C/-c/-F. - - "git-apply --whitespace=strip" removes blank lines added at + - "git apply --whitespace=strip" removes blank lines added at the end of the file. - - "git-fetch" over git native protocols with "-v" option shows + - "git fetch" over git native protocols with "-v" option shows connection status, and the IP address of the other end, to help diagnosing problems. @@ -195,10 +229,10 @@ Updates since v1.5.2 - "--find-copies-harder" option to diff family can now be spelled as "-C -C" for brevity. - - "git-mailsplit" (hence "git-am") can read from Maildir + - "git mailsplit" (hence "git am") can read from Maildir formatted mailboxes. - - "git-cvsserver" does not barf upon seeing "cvs login" + - "git cvsserver" does not barf upon seeing "cvs login" request. - "pack-objects" honors "delta" attribute set in @@ -208,7 +242,7 @@ Updates since v1.5.2 - "new-workdir" script (in contrib) can now be used with a bare repository. - - "git-mergetool" learned to use gvimdiff. + - "git mergetool" learned to use gvimdiff. - "gitview" (in contrib) has a better blame interface. @@ -223,8 +257,8 @@ Updates since v1.5.2 "oneline". - "git p4import" has been demoted to contrib status. For - a superior option, checkout the git-p4 front end to - git-fast-import (also in contrib). The man page and p4 + a superior option, checkout the "git p4" front end to + "git fast-import" (also in contrib). The man page and p4 rpm have been removed as well. - "git mailinfo" (hence "am") now tries to see if the message @@ -237,13 +271,15 @@ Updates since v1.5.2 without parameter defined with "func()", not "func(void)") have been eradicated. + - "git tag" and "git verify-tag" have been rewritten in C. + * Performance Tweaks - - git-pack-objects avoids re-deltification cost by caching + - "git pack-objects" avoids re-deltification cost by caching small enough delta results it creates while looking for the best delta candidates. - - git-pack-objects learned a new heuristcs to prefer delta + - "git pack-objects" learned a new heuristcs to prefer delta that is shallower in depth over the smallest delta possible. This improves both overall packfile access performance and packfile density. @@ -260,6 +296,13 @@ Updates since v1.5.2 - verifying pack contents done by "git fsck --full" got boost by carefully choosing the order to verify objects in them. + - "git read-tree -m" to read into an already populated index + has been optimized vastly. The effect of this can be seen + when switching branches that have differences in only a + handful paths. + + - "git commit paths..." has also been optimized. + Fixes since v1.5.2 ------------------ From 6ed77266c6e85130920ef30cd290d36ad4464695 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 15 Aug 2007 09:55:18 -0700 Subject: [PATCH 094/107] git-svn: fix log with single revision against a non-HEAD branch Running git-svn log -r against a other than the current HEAD did not work if the was exclusive to the other branch. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 24 +++++++++++++-------- t/t9116-git-svn-log.sh | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) create mode 100755 t/t9116-git-svn-log.sh diff --git a/git-svn.perl b/git-svn.perl index ee7ef693fa..d162114e26 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3501,11 +3501,17 @@ sub log_use_color { sub git_svn_log_cmd { my ($r_min, $r_max, @args) = @_; my $head = 'HEAD'; + my (@files, @log_opts); foreach my $x (@args) { - last if $x eq '--'; - next unless ::verify_ref("$x^0"); - $head = $x; - last; + if ($x eq '--' || @files) { + push @files, $x; + } else { + if (::verify_ref("$x^0")) { + $head = $x; + } else { + push @log_opts, $x; + } + } } my ($url, $rev, $uuid, $gs) = ::working_head_info($head); @@ -3515,13 +3521,13 @@ sub git_svn_log_cmd { push @cmd, '-r' unless $non_recursive; push @cmd, qw/--raw --name-status/ if $verbose; push @cmd, '--color' if log_use_color(); - return @cmd unless defined $r_max; - if ($r_max == $r_min) { + push @cmd, @log_opts; + if (defined $r_max && $r_max == $r_min) { push @cmd, '--max-count=1'; if (my $c = $gs->rev_db_get($r_max)) { push @cmd, $c; } - } else { + } elsif (defined $r_max) { my ($c_min, $c_max); $c_max = $gs->rev_db_get($r_max); $c_min = $gs->rev_db_get($r_min); @@ -3537,7 +3543,7 @@ sub git_svn_log_cmd { push @cmd, $c_min; } } - return @cmd; + return (@cmd, @files); } # adapted from pager.c @@ -3702,7 +3708,7 @@ sub cmd_show_log { } config_pager(); - @args = (git_svn_log_cmd($r_min, $r_max, @args), @args); + @args = git_svn_log_cmd($r_min, $r_max, @args); my $log = command_output_pipe(@args); run_pager(); my (@k, $c, $d, $stat); diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh new file mode 100755 index 0000000000..0d4e6b3f04 --- /dev/null +++ b/t/t9116-git-svn-log.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +# + +test_description='git-svn log tests' +. ./lib-git-svn.sh + +test_expect_success 'setup repository and import' " + mkdir import && + cd import && + for i in trunk branches/a branches/b \ + tags/0.1 tags/0.2 tags/0.3; do + mkdir -p \$i && \ + echo hello >> \$i/README || exit 1 + done && \ + svn import -m test . $svnrepo + cd .. && + git-svn init $svnrepo -T trunk -b branches -t tags && + git-svn fetch && + git reset --hard trunk && + echo bye >> README && + git commit -a -m bye && + git svn dcommit && + git reset --hard a && + echo why >> FEEDME && + git update-index --add FEEDME && + git commit -m feedme && + git svn dcommit && + git reset --hard trunk && + echo aye >> README && + git commit -a -m aye && + git svn dcommit + " + +test_expect_success 'run log' " + git reset --hard a && + git svn log -r2 trunk | grep ^r2 && + git svn log -r4 trunk | grep ^r4 && + git svn log -r3 | grep ^r3 + " + +test_expect_success 'run log against a from trunk' " + git reset --hard trunk && + git svn log -r3 a | grep ^r3 + " + +test_done From 79d722224dbad49a50072f92f823d8b12c2e5707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Wed, 15 Aug 2007 17:59:24 +0200 Subject: [PATCH 095/107] path-list.c: always free strdup'ed paths Always free .paths if .strdup_paths is set, no matter if the parameter free_items is set or not, plugging a minor memory leak. And to clarify the meaning of the flag, rename it to free_util, since it now only affects the freeing of the .util field. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- path-list.c | 14 ++++++++------ path-list.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/path-list.c b/path-list.c index dcb4b3ac13..3d83b7ba9e 100644 --- a/path-list.c +++ b/path-list.c @@ -76,16 +76,18 @@ struct path_list_item *path_list_lookup(const char *path, struct path_list *list return list->items + i; } -void path_list_clear(struct path_list *list, int free_items) +void path_list_clear(struct path_list *list, int free_util) { if (list->items) { int i; - if (free_items) - for (i = 0; i < list->nr; i++) { - if (list->strdup_paths) - free(list->items[i].path); + if (list->strdup_paths) { + for (i = 0; i < list->nr; i++) + free(list->items[i].path); + } + if (free_util) { + for (i = 0; i < list->nr; i++) free(list->items[i].util); - } + } free(list->items); } list->items = NULL; diff --git a/path-list.h b/path-list.h index ce5ffabcce..5931e2cc0c 100644 --- a/path-list.h +++ b/path-list.h @@ -15,7 +15,7 @@ struct path_list void print_path_list(const char *text, const struct path_list *p); int path_list_has_path(const struct path_list *list, const char *path); -void path_list_clear(struct path_list *list, int free_items); +void path_list_clear(struct path_list *list, int free_util); struct path_list_item *path_list_insert(const char *path, struct path_list *list); struct path_list_item *path_list_lookup(const char *path, struct path_list *list); From a4882c27f8b3793d94b03fd503a0c67ad9772cf6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 15 Aug 2007 14:12:14 -0700 Subject: [PATCH 096/107] Fix "git add -u" data corruption. This applies to 'maint' to fix a rather serious data corruption issue. When "git add -u" affects a subdirectory in such a way that the only changes to its contents are path removals, the next tree object written out of that index was bogus, as the remove codepath forgot to invalidate the cache-tree entry. Reported by Salikh Zakirov. Signed-off-by: Junio C Hamano --- builtin-add.c | 1 + t/t2200-add-update.sh | 59 +++++++++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 159117106a..a5fae7ca17 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -115,6 +115,7 @@ static void update_callback(struct diff_queue_struct *q, break; case DIFF_STATUS_DELETED: remove_file_from_cache(path); + cache_tree_invalidate_path(active_cache_tree, path); if (verbose) printf("remove '%s'\n", path); break; diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index 83005e70d0..4c7c6af432 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -13,26 +13,53 @@ only the updates to dir/sub.' . ./test-lib.sh -test_expect_success 'setup' ' -echo initial >top && -mkdir dir && -echo initial >dir/sub && -git-add dir/sub top && -git-commit -m initial && -echo changed >top && -echo changed >dir/sub && -echo other >dir/other +test_expect_success setup ' + echo initial >check && + echo initial >top && + mkdir dir1 dir2 && + echo initial >dir1/sub1 && + echo initial >dir1/sub2 && + echo initial >dir2/sub3 && + git add check dir1 dir2 top && + test_tick + git-commit -m initial && + + echo changed >check && + echo changed >top && + echo changed >dir2/sub3 && + rm -f dir1/sub1 && + echo other >dir2/other ' -test_expect_success 'update' 'git-add -u dir' +test_expect_success update ' + git add -u dir1 dir2 +' -test_expect_success 'update touched correct path' \ - 'test "`git-diff-files --name-status dir/sub`" = ""' +test_expect_success 'update noticed a removal' ' + test "$(git-ls-files dir1/sub1)" = "" +' -test_expect_success 'update did not touch other tracked files' \ - 'test "`git-diff-files --name-status top`" = "M top"' +test_expect_success 'update touched correct path' ' + test "$(git-diff-files --name-status dir2/sub3)" = "" +' -test_expect_success 'update did not touch untracked files' \ - 'test "`git-diff-files --name-status dir/other`" = ""' +test_expect_success 'update did not touch other tracked files' ' + test "$(git-diff-files --name-status check)" = "M check" && + test "$(git-diff-files --name-status top)" = "M top" +' + +test_expect_success 'update did not touch untracked files' ' + test "$(git-ls-files dir2/other)" = "" +' + +test_expect_success 'cache tree has not been corrupted' ' + + git ls-files -s | + sed -e "s/ 0 / /" >expect && + git ls-tree -r $(git write-tree) | + sed -e "s/ blob / /" >current && + diff -u expect current + +' test_done From 2ed2c222dfe372385dc562fb5dc246d5595c1eae Mon Sep 17 00:00:00 2001 From: Salikh Zakirov Date: Thu, 16 Aug 2007 02:01:43 +0900 Subject: [PATCH 097/107] git-add -u paths... now works from subdirectory git-add -u also takes the path limiters, but unlike the command without the -u option, the code forgot that it could be invoked from a subdirectory, and did not correctly handle the prefix. Signed-off-by: Salikh Zakirov Signed-off-by: Junio C Hamano --- builtin-add.c | 8 ++++---- t/t2200-add-update.sh | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index a5fae7ca17..07e3ddfd0a 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -123,12 +123,12 @@ static void update_callback(struct diff_queue_struct *q, } } -static void update(int verbose, const char **files) +static void update(int verbose, const char *prefix, const char **files) { struct rev_info rev; - init_revisions(&rev, ""); + init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); - rev.prune_data = get_pathspec(rev.prefix, files); + rev.prune_data = get_pathspec(prefix, files); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; rev.diffopt.format_callback_data = &verbose; @@ -209,7 +209,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) } if (take_worktree_changes) { - update(verbose, argv + i); + update(verbose, prefix, argv + i); goto finish; } diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index 4c7c6af432..58cd7f31be 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -62,4 +62,18 @@ test_expect_success 'cache tree has not been corrupted' ' ' +test_expect_success 'update from a subdirectory' ' + ( + cd dir1 && + echo more >sub2 && + git add -u sub2 + ) +' + +test_expect_success 'change gets noticed' ' + + test "$(git diff-files --name-status dir1)" = "" + +' + test_done From b13ef4916ac5a25cc5897f85ba0b4c5953cff609 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 15 Aug 2007 15:01:20 -0700 Subject: [PATCH 098/107] GIT 1.5.2.5 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.2.5.txt | 30 ++++++++++++++++++++++++++++++ GIT-VERSION-GEN | 2 +- RelNotes | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 Documentation/RelNotes-1.5.2.5.txt diff --git a/Documentation/RelNotes-1.5.2.5.txt b/Documentation/RelNotes-1.5.2.5.txt new file mode 100644 index 0000000000..e8281c72a0 --- /dev/null +++ b/Documentation/RelNotes-1.5.2.5.txt @@ -0,0 +1,30 @@ +GIT v1.5.2.5 Release Notes +========================== + +Fixes since v1.5.2.4 +-------------------- + + * Bugfixes + + - "git add -u" had a serious data corruption problem in one + special case (when the changes to a subdirectory's files + consist only deletion of files). + + - "git add -u " did not work from a subdirectory. + + - "git apply" left an empty directory after all its files are + renamed away. + + - "git $anycmd foo/bar", when there is a file 'foo' in the + working tree, complained that "git $anycmd foo/bar --" form + should be used to disambiguate between revs and files, + which was completely bogus. + + - "git checkout-index" and other commands that checks out + files to the work tree tried unlink(2) on directories, + which is a sane thing to do on sane systems, but not on + Solaris when you are root. + + * Documentation Fixes and Updates + + - A handful documentation fixes. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index a318baa798..a62248deb1 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.2.4.GIT +DEF_VER=v1.5.2.5.GIT LF=' ' diff --git a/RelNotes b/RelNotes index 1f6c16e1dc..215243c7cd 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.2.4.txt \ No newline at end of file +Documentation/RelNotes-1.5.2.5.txt \ No newline at end of file From f34f2b0b384b42f7a0d0d92966c145b05fe55217 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 15 Aug 2007 15:45:10 -0700 Subject: [PATCH 099/107] Fix read-tree merging more than 3 trees using 3-way merge For multi-base merges, we allowed read-tree -m to take more than three trees (the last two are our and their branches, and all the earlier ones, typically one but potentially more, are used as the merge base). Unfortunately, the conversion done by commit 933bf40 broke this. Signed-off-by: Junio C Hamano --- builtin-read-tree.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 1967d100f2..f6764b9739 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -13,8 +13,9 @@ #include "dir.h" #include "builtin.h" +#define MAX_TREES 4 static int nr_trees; -static struct tree *trees[4]; +static struct tree *trees[MAX_TREES]; static int list_tree(unsigned char *sha1) { @@ -96,7 +97,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) { int i, newfd, stage = 0; unsigned char sha1[20]; - struct tree_desc t[3]; + struct tree_desc t[MAX_TREES]; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); @@ -263,6 +264,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) opts.head_idx = 1; } + if (MAX_TREES < nr_trees) + die("I cannot read more than %d trees", MAX_TREES); + for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; parse_tree(tree); From da899deb24ff66ec2166389516a915c01bf0a387 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 15 Aug 2007 16:59:27 -0700 Subject: [PATCH 100/107] Update documentation links for older releases. Signed-off-by: Junio C Hamano --- Documentation/git.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index 18f8b6a0a1..8017997fb9 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -42,9 +42,10 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.5.2.4/git.html[documentation for release 1.5.2.4] +* link:v1.5.2.5/git.html[documentation for release 1.5.2.5] * release notes for + link:RelNotes-1.5.2.5.txt[1.5.2.5], link:RelNotes-1.5.2.4.txt[1.5.2.4], link:RelNotes-1.5.2.3.txt[1.5.2.3], link:RelNotes-1.5.2.2.txt[1.5.2.2], From e06c5a6c7bdaa8c96b72e29f7fb49a331f1e0cc2 Mon Sep 17 00:00:00 2001 From: Sven Verdoolaege Date: Wed, 15 Aug 2007 19:22:09 +0200 Subject: [PATCH 101/107] git-apply: apply submodule changes Apply "Subproject commit HEX" changes produced by git-diff. As usual in the current git, only the superproject itself is actually modified (possibly creating empty directories for new submodules). Any checked-out submodule is left untouched and is not required to be up-to-date. With clean-ups from Junio C Hamano. Signed-off-by: Sven Verdoolaege Signed-off-by: Junio C Hamano --- Documentation/git-apply.txt | 14 +++++ builtin-apply.c | 111 ++++++++++++++++++++++++++++-------- t/t7400-submodule-basic.sh | 17 ++++++ 3 files changed, 117 insertions(+), 25 deletions(-) diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index f03f661652..4c7e3a2f7f 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -171,6 +171,20 @@ apply.whitespace:: When no `--whitespace` flag is given from the command line, this configuration item is used as the default. +Submodules +---------- +If the patch contains any changes to submodules then gitlink:git-apply[1] +treats these changes as follows. + +If --index is specified (explicitly or implicitly), then the submodule +commits must match the index exactly for the patch to apply. If any +of the submodules are checked-out, then these check-outs are completely +ignored, i.e., they are not required to be up-to-date or clean and they +are not updated. + +If --index is not specified, then the submodule commits in the patch +are ignored and only the absence of presence of the corresponding +subdirectory is checked and (if possible) updated. Author ------ diff --git a/builtin-apply.c b/builtin-apply.c index da270755a7..25b1447901 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1984,6 +1984,25 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch) return 0; } +static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p, + unsigned long *size_p) +{ + if (!ce) + return 0; + + if (S_ISGITLINK(ntohl(ce->ce_mode))) { + *buf_p = xmalloc(100); + *size_p = snprintf(*buf_p, 100, + "Subproject commit %s\n", sha1_to_hex(ce->sha1)); + } else { + enum object_type type; + *buf_p = read_sha1_file(ce->sha1, &type, size_p); + if (!*buf_p) + return -1; + } + return 0; +} + static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { char *buf; @@ -1994,22 +2013,32 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * alloc = 0; buf = NULL; if (cached) { - if (ce) { - enum object_type type; - buf = read_sha1_file(ce->sha1, &type, &size); - if (!buf) + if (read_file_or_gitlink(ce, &buf, &size)) + return error("read of %s failed", patch->old_name); + alloc = size; + } else if (patch->old_name) { + if (S_ISGITLINK(patch->old_mode)) { + if (ce) + read_file_or_gitlink(ce, &buf, &size); + else { + /* + * There is no way to apply subproject + * patch without looking at the index. + */ + patch->fragments = NULL; + size = 0; + } + } + else { + size = xsize_t(st->st_size); + alloc = size + 8192; + buf = xmalloc(alloc); + if (read_old_data(st, patch->old_name, + &buf, &alloc, &size)) return error("read of %s failed", patch->old_name); - alloc = size; } } - else if (patch->old_name) { - size = xsize_t(st->st_size); - alloc = size + 8192; - buf = xmalloc(alloc); - if (read_old_data(st, patch->old_name, &buf, &alloc, &size)) - return error("read of %s failed", patch->old_name); - } desc.size = size; desc.alloc = alloc; @@ -2055,6 +2084,16 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists) return 0; } +static int verify_index_match(struct cache_entry *ce, struct stat *st) +{ + if (S_ISGITLINK(ntohl(ce->ce_mode))) { + if (!S_ISDIR(st->st_mode)) + return -1; + return 0; + } + return ce_match_stat(ce, st, 1); +} + static int check_patch(struct patch *patch, struct patch *prev_patch) { struct stat st; @@ -2065,8 +2104,14 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) int ok_if_exists; patch->rejected = 1; /* we will drop this after we succeed */ + + /* + * Make sure that we do not have local modifications from the + * index when we are looking at the index. Also make sure + * we have the preimage file to be patched in the work tree, + * unless --cached, which tells git to apply only in the index. + */ if (old_name) { - int changed = 0; int stat_ret = 0; unsigned st_mode = 0; @@ -2096,15 +2141,12 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) lstat(old_name, &st)) return -1; } - if (!cached) - changed = ce_match_stat(ce, &st, 1); - if (changed) + if (!cached && verify_index_match(ce, &st)) return error("%s: does not match index", old_name); if (cached) st_mode = ntohl(ce->ce_mode); - } - else if (stat_ret < 0) + } else if (stat_ret < 0) return error("%s: %s", old_name, strerror(errno)); if (!cached) @@ -2354,7 +2396,11 @@ static void remove_file(struct patch *patch, int rmdir_empty) cache_tree_invalidate_path(active_cache_tree, patch->old_name); } if (!cached) { - if (!unlink(patch->old_name) && rmdir_empty) { + if (S_ISGITLINK(patch->old_mode)) { + if (rmdir(patch->old_name)) + warning("unable to remove submodule %s", + patch->old_name); + } else if (!unlink(patch->old_name) && rmdir_empty) { char *name = xstrdup(patch->old_name); char *end = strrchr(name, '/'); while (end) { @@ -2382,13 +2428,21 @@ 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 (!cached) { - if (lstat(path, &st) < 0) - die("unable to stat newly created file %s", path); - fill_stat_cache_info(ce, &st); + if (S_ISGITLINK(mode)) { + const char *s = buf; + + if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1)) + die("corrupt patch for subproject %s", path); + } else { + 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 (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) die("unable to add cache entry for %s", path); } @@ -2398,6 +2452,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, int fd; char *nbuf; + if (S_ISGITLINK(mode)) { + struct stat st; + if (!lstat(path, &st) && S_ISDIR(st.st_mode)) + return 0; + return mkdir(path, 0777); + } + if (has_symlinks && S_ISLNK(mode)) /* Although buf:size is counted string, it also is NUL * terminated. diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index e8ce7cdb83..9d142ed649 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -175,4 +175,21 @@ test_expect_success 'checkout superproject with subproject already present' ' git-checkout master ' +test_expect_success 'apply submodule diff' ' + git branch second && + ( + cd lib && + echo s >s && + git add s && + git commit -m "change subproject" + ) && + git update-index --add lib && + git-commit -m "change lib" && + git-format-patch -1 --stdout >P.diff && + git checkout second && + git apply --index P.diff && + D=$(git diff --cached master) && + test -z "$D" +' + test_done From d250626cbbfc1d047b234681d2f71f56e3ada07a Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 15 Aug 2007 22:46:01 -0400 Subject: [PATCH 102/107] pack-objects: remove bogus arguments to delta_cacheable() Not only are they unused, but the order in the function declaration and the actual usage don't match. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 51a850e111..24926db27a 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1273,9 +1273,8 @@ struct unpacked { unsigned depth; }; -static int delta_cacheable(struct unpacked *trg, struct unpacked *src, - unsigned long src_size, unsigned long trg_size, - unsigned long delta_size) +static int delta_cacheable(unsigned long src_size, unsigned long trg_size, + unsigned long delta_size) { if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size) return 0; @@ -1397,7 +1396,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, trg_entry->delta_size = delta_size; trg->depth = src->depth + 1; - if (delta_cacheable(src, trg, src_size, trg_size, delta_size)) { + if (delta_cacheable(src_size, trg_size, delta_size)) { trg_entry->delta_data = xrealloc(delta_buf, delta_size); delta_cache_size += trg_entry->delta_size; } else From 312efe9b58edb428132f0bf0fdc7f3d6a41f1183 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 15 Aug 2007 20:55:44 -0700 Subject: [PATCH 103/107] git-clone: allow --bare clone This is a stop-gap to work around problem with git-init without intrusive changes. Signed-off-by: Junio C Hamano --- git-clone.sh | 7 ++++++- t/t5701-clone-local.sh | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/git-clone.sh b/git-clone.sh index 4c9b1c9710..e4a9ac4bab 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -215,7 +215,12 @@ else GIT_DIR="$D/.git" fi && export GIT_DIR && -git-init $quiet ${template+"$template"} || usage +GIT_CONFIG="$GIT_DIR/config" git-init $quiet ${template+"$template"} || usage + +if test -n "$bare" +then + GIT_CONFIG="$GIT_DIR/config" git config core.bare true +fi if test -n "$reference" then diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh index 56f9d8ae73..822ac8c28e 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5701-clone-local.sh @@ -8,13 +8,16 @@ D=`pwd` test_expect_success 'preparing origin repository' ' : >file && git add . && git commit -m1 && git clone --bare . a.git && - git clone --bare . x + git clone --bare . x && + test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true && + test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true ' test_expect_success 'local clone without .git suffix' ' cd "$D" && git clone -l -s a b && cd b && + test "$(GIT_CONFIG=.git/config git config --bool core.bare)" = false && git fetch ' From 13cc4c81df8f6b45540bd50ddffd3c11f98829bf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 16 Aug 2007 12:20:25 -0700 Subject: [PATCH 104/107] Documentation/git-rebase: fix an example The example miscounted the commit to rebase from. Noticed by Cliff Brake. Signed-off-by: Junio C Hamano --- Documentation/git-rebase.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 96907d4863..a1b6dce73d 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -130,7 +130,7 @@ the following situation: then the command - git-rebase --onto topicA~5 topicA~2 topicA + git-rebase --onto topicA~5 topicA~3 topicA would result in the removal of commits F and G: From 19b28bf5454380f9f65063e8bc3cdee669e731a9 Mon Sep 17 00:00:00 2001 From: Arjen Laarhoven Date: Fri, 17 Aug 2007 00:02:17 +0200 Subject: [PATCH 105/107] t1301-shared-repo.sh: fix 'stat' portability issue The t1301-shared-repo.sh testscript uses /usr/bin/stat to get the file mode, which isn't portable. Implement the test in shell using 'ls' as shown by Junio. Signed-off-by: Arjen Laarhoven Signed-off-by: Junio C Hamano --- t/t1301-shared-repo.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index bb5f30220a..6bfe19a4e5 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -21,7 +21,16 @@ test_expect_success 'update-server-info honors core.sharedRepository' ' git commit -m a1 && umask 0277 && git update-server-info && - test 444 = $(stat -c %a .git/info/refs) + actual="$(ls -l .git/info/refs)" && + case "$actual" in + -r--r--r--*) + : happy + ;; + *) + echo Oops, .git/info/refs is not 0444 + false + ;; + esac ' test_done From d1d028ea16af27158f5d77154cfa60dbd9143314 Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Thu, 16 Aug 2007 17:56:08 -0500 Subject: [PATCH 106/107] Clarify actual behavior of 'git add' and ignored files Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- Documentation/git-add.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index dee38f8250..3383aca9af 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -28,10 +28,12 @@ you must run 'git add' again to add the new content to the index. The 'git status' command can be used to obtain a summary of which files have changes that are staged for the next commit. -The 'add' command can be used to add ignored files with `-f` (force) -option, but they have to be explicitly and exactly specified from the -command line. File globbing and recursive behaviour do not add ignored -files. +The 'git add' command will not add ignored files by default. If any +ignored files were explicitly specified on the command line, 'git add' +will fail with a list of ignored files. Ignored files reached by +directory recursion or filename globbing will be silently ignored. +The 'add' command can be used to add ignored files with the `-f` +(force) option. Please see gitlink:git-commit[1] for alternative ways to add content to a commit. From a9ab2009dbbf769aadd52957950c1bad60a0c8fd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 16 Aug 2007 19:24:08 -0700 Subject: [PATCH 107/107] Clean-up read-tree error condition. This is a follow-up to f34f2b0b; list_tree() function is where it first notices that the command line fed too many trees for us to handle, so move the error exit message to there, and raise the MAX_TREES to 8 (not that it matters very much in practice). Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-read-tree.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/builtin-read-tree.c b/builtin-read-tree.c index f6764b9739..43cd56a3b5 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -13,7 +13,7 @@ #include "dir.h" #include "builtin.h" -#define MAX_TREES 4 +#define MAX_TREES 8 static int nr_trees; static struct tree *trees[MAX_TREES]; @@ -21,8 +21,8 @@ static int list_tree(unsigned char *sha1) { struct tree *tree; - if (nr_trees >= 4) - return -1; + if (nr_trees >= MAX_TREES) + die("I cannot read more than %d trees", MAX_TREES); tree = parse_tree_indirect(sha1); if (!tree) return -1; @@ -264,9 +264,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) opts.head_idx = 1; } - if (MAX_TREES < nr_trees) - die("I cannot read more than %d trees", MAX_TREES); - for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; parse_tree(tree);