From: Junio C Hamano Date: Fri, 10 Feb 2006 14:51:28 +0000 (-0800) Subject: Merge branch 'lt/diff-tree' X-Git-Tag: v1.3.0-rc1~54^2~86 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=f732d0b8576303403db2b1824cfd8b5a2fe632ef;hp=d416df8869d803282fb254a18505eccceee9ba3e;p=git.git Merge branch 'lt/diff-tree' * lt/diff-tree: combine-diff: Record diff status a bit more faithfully find_unique_abbrev() simplification. --- diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 5c85167f..2a0275ee 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -35,6 +35,17 @@ -C:: Detect copies as well as renames. +--diff-filter=[ACDMRTUXB*]:: + Select only files that are Added (`A`), Copied (`C`), + Deleted (`D`), Modified (`M`), Renamed (`R`), have their + type (mode) changed (`T`), are Unmerged (`U`), are + Unknown (`X`), or have had their pairing Broken (`B`). + Any combination of the filter characters may be used. + When `*` (All-or-none) is added to the combination, all + paths are selected if there is any file that matches + other criteria in the comparison; if there is no file + that matches other criteria, nothing is selected. + --find-copies-harder:: For performance reasons, by default, -C option finds copies only if the original file of the copy was modified in the same diff --git a/Makefile b/Makefile index 5c329346..f240e452 100644 --- a/Makefile +++ b/Makefile @@ -107,7 +107,7 @@ SCRIPT_SH = \ git-merge-one-file.sh git-parse-remote.sh \ git-prune.sh git-pull.sh git-push.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ - git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \ + git-resolve.sh git-revert.sh git-sh-setup.sh \ git-tag.sh git-verify-tag.sh git-whatchanged.sh \ git-applymbox.sh git-applypatch.sh git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ @@ -125,7 +125,7 @@ SCRIPT_PYTHON = \ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ - git-cherry-pick git-show + git-cherry-pick git-show git-status # The ones that do not have to link with lcrypto nor lz. SIMPLE_PROGRAMS = \ @@ -443,6 +443,9 @@ git-cherry-pick: git-revert git-show: git-whatchanged cp $< $@ +git-status: git-commit + cp $< $@ + # These can record GIT_VERSION git$X git.spec \ $(patsubst %.sh,%,$(SCRIPT_SH)) \ diff --git a/apply.c b/apply.c index 2ad47fbb..35ae48ee 100644 --- a/apply.c +++ b/apply.c @@ -1309,7 +1309,7 @@ static int check_patch(struct patch *patch) return -1; } - changed = ce_match_stat(active_cache[pos], &st); + changed = ce_match_stat(active_cache[pos], &st, 1); if (changed) return error("%s: does not match index", old_name); diff --git a/cache.h b/cache.h index bdbe2d68..cd58fada 100644 --- a/cache.h +++ b/cache.h @@ -91,6 +91,7 @@ struct cache_entry { #define CE_NAMEMASK (0x0fff) #define CE_STAGEMASK (0x3000) #define CE_UPDATE (0x4000) +#define CE_VALID (0x8000) #define CE_STAGESHIFT 12 #define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT)) @@ -144,8 +145,8 @@ extern int add_cache_entry(struct cache_entry *ce, int option); extern int remove_cache_entry_at(int pos); extern int remove_file_from_cache(const char *path); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); -extern int ce_match_stat(struct cache_entry *ce, struct stat *st); -extern int ce_modified(struct cache_entry *ce, struct stat *st); +extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int); +extern int ce_modified(struct cache_entry *ce, struct stat *st, int); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type); extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object); @@ -161,6 +162,7 @@ extern int commit_index_file(struct cache_file *); extern void rollback_index_file(struct cache_file *); extern int trust_executable_bit; +extern int assume_unchanged; extern int only_use_symrefs; extern int diff_rename_limit_default; extern int shared_repository; diff --git a/checkout-index.c b/checkout-index.c index 53dd8cba..957b4a86 100644 --- a/checkout-index.c +++ b/checkout-index.c @@ -116,6 +116,7 @@ int main(int argc, char **argv) int all = 0; prefix = setup_git_directory(); + git_config(git_default_config); prefix_length = prefix ? strlen(prefix) : 0; if (read_cache() < 0) { diff --git a/commit-tree.c b/commit-tree.c index 4634b50e..b1c8dca4 100644 --- a/commit-tree.c +++ b/commit-tree.c @@ -86,13 +86,13 @@ int main(int argc, char **argv) unsigned int size; setup_ident(); + setup_git_directory(); + git_config(git_default_config); if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0) usage(commit_tree_usage); - setup_git_directory(); - check_valid(tree_sha1, "tree"); for (i = 2; i < argc; i += 2) { char *a, *b; diff --git a/config.c b/config.c index 8355224b..7dbdce19 100644 --- a/config.c +++ b/config.c @@ -222,6 +222,11 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.ignorestat")) { + assume_unchanged = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "core.symrefsonly")) { only_use_symrefs = git_config_bool(var, value); return 0; diff --git a/count-delta.c b/count-delta.c index 7559ff68..978a60ca 100644 --- a/count-delta.c +++ b/count-delta.c @@ -50,13 +50,10 @@ int count_delta(void *delta_buf, unsigned long delta_size, if (cmd & 0x08) cp_off |= (*data++ << 24); if (cmd & 0x10) cp_size = *data++; if (cmd & 0x20) cp_size |= (*data++ << 8); + if (cmd & 0x40) cp_size |= (*data++ << 16); if (cp_size == 0) cp_size = 0x10000; - if (cmd & 0x40) - /* copy from dst */ - ; - else - copied_from_source += cp_size; + copied_from_source += cp_size; out += cp_size; } else { /* write literal into dst */ diff --git a/diff-files.c b/diff-files.c index 7db5ce64..574294f3 100644 --- a/diff-files.c +++ b/diff-files.c @@ -191,7 +191,7 @@ int main(int argc, const char **argv) show_file('-', ce); continue; } - changed = ce_match_stat(ce, &st); + changed = ce_match_stat(ce, &st, 0); if (!changed && !diff_options.find_copies_harder) continue; oldmode = ntohl(ce->ce_mode); diff --git a/diff-index.c b/diff-index.c index f8a102ec..12a9418d 100644 --- a/diff-index.c +++ b/diff-index.c @@ -33,7 +33,7 @@ static int get_stat_data(struct cache_entry *ce, } return -1; } - changed = ce_match_stat(ce, &st); + changed = ce_match_stat(ce, &st, 0); if (changed) { mode = create_ce_mode(st.st_mode); if (!trust_executable_bit && diff --git a/diff.c b/diff.c index 890bdaa6..804c08c2 100644 --- a/diff.c +++ b/diff.c @@ -311,7 +311,7 @@ static int work_tree_matches(const char *name, const unsigned char *sha1) ce = active_cache[pos]; if ((lstat(name, &st) < 0) || !S_ISREG(st.st_mode) || /* careful! */ - ce_match_stat(ce, &st) || + ce_match_stat(ce, &st, 0) || memcmp(sha1, ce->sha1, 20)) return 0; /* we return 1 only when we can stat, it is a regular file, diff --git a/entry.c b/entry.c index 6c47c3a3..8fb99bc8 100644 --- a/entry.c +++ b/entry.c @@ -123,7 +123,7 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state) strcpy(path + len, ce->name); if (!lstat(path, &st)) { - unsigned changed = ce_match_stat(ce, &st); + unsigned changed = ce_match_stat(ce, &st, 1); if (!changed) return 0; if (!state->force) { diff --git a/environment.c b/environment.c index 0596fc64..251e53ca 100644 --- a/environment.c +++ b/environment.c @@ -12,6 +12,7 @@ char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; int trust_executable_bit = 1; +int assume_unchanged = 0; int only_use_symrefs = 0; int repository_format_version = 0; char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8"; diff --git a/git-commit.sh b/git-commit.sh index 25389240..e6793bd6 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -3,13 +3,21 @@ # Copyright (c) 2005 Linus Torvalds # Copyright (c) 2006 Junio C Hamano -USAGE='[-a] [-i] [-s] [-v | --no-verify] [-m | -F | (-C|-c) ] [-e] [--author ] [...]' - +USAGE='[-a] [-i] [-s] [-v] [--no-verify] [-m | -F | (-C|-c) ] [-e] [--author ] [...]' SUBDIRECTORY_OK=Yes . git-sh-setup -git-rev-parse --verify HEAD >/dev/null 2>&1 || -initial_commit=t +git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t +branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) + +case "$0" in +*status) + status_only=t + unmerged_ok_if_status=--unmerged ;; +*commit) + status_only= + unmerged_ok_if_status= ;; +esac refuse_partial () { echo >&2 "$1" @@ -17,23 +25,155 @@ refuse_partial () { exit 1 } -SAVE_INDEX="$GIT_DIR/save-index$$" +THIS_INDEX="$GIT_DIR/index" +NEXT_INDEX="$GIT_DIR/next-index$$" +rm -f "$NEXT_INDEX" save_index () { - cp "$GIT_DIR/index" "$SAVE_INDEX" + cp "$THIS_INDEX" "$NEXT_INDEX" +} + +report () { + header="# +# $1: +# ($2) +# +" + trailer="" + while read status name newname + do + printf '%s' "$header" + header="" + trailer="# +" + case "$status" in + M ) echo "# modified: $name";; + D*) echo "# deleted: $name";; + T ) echo "# typechange: $name";; + C*) echo "# copied: $name -> $newname";; + R*) echo "# renamed: $name -> $newname";; + A*) echo "# new file: $name";; + U ) echo "# unmerged: $name";; + esac + done + printf '%s' "$trailer" + [ "$header" ] } run_status () { - ( - cd "$TOP" - if test '' != "$TMP_INDEX" + ( + # We always show status for the whole tree. + cd "$TOP" + + # If TMP_INDEX is defined, that means we are doing + # "--only" partial commit, and that index file is used + # to build the tree for the commit. Otherwise, if + # NEXT_INDEX exists, that is the index file used to + # make the commit. Otherwise we are using as-is commit + # so the regular index file is what we use to compare. + if test '' != "$TMP_INDEX" + then + GIT_INDEX_FILE="$TMP_INDEX" + export GIT_INDEX_FILE + elif test -f "$NEXT_INDEX" + then + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE + fi + + case "$branch" in + refs/heads/master) ;; + *) echo "# On branch $branch" ;; + esac + + if test -z "$initial_commit" + then + if test -z "$verbose" + then + git-diff-index -M --cached --name-status \ + --diff-filter=MDTCRA HEAD | + sed -e ' + s/\\/\\\\/g + s/ /\\ /g + ' | + report "Updated but not checked in" "will commit" + else + if git-diff-index --cached -M -p --diff-filter=MDTCRA HEAD | + grep . then - GIT_INDEX_FILE="$TMP_INDEX" git-status + false else - git-status + true fi - ) + fi + committable="$?" + else + echo '# +# Initial commit +#' + git-ls-files | + sed -e ' + s/\\/\\\\/g + s/ /\\ /g + s/^/A / + ' | + report "Updated but not checked in" "will commit" + + committable="$?" + fi + + git-diff-files --name-status | + sed -e ' + s/\\/\\\\/g + s/ /\\ /g + ' | + report "Changed but not updated" \ + "use git-update-index to mark for commit" + + if test -f "$GIT_DIR/info/exclude" + then + git-ls-files -z --others --directory \ + --exclude-from="$GIT_DIR/info/exclude" \ + --exclude-per-directory=.gitignore + else + git-ls-files -z --others --directory \ + --exclude-per-directory=.gitignore + fi | + perl -e '$/ = "\0"; + my $shown = 0; + while (<>) { + chomp; + s|\\|\\\\|g; + s|\t|\\t|g; + s|\n|\\n|g; + s/^/# /; + if (!$shown) { + print "#\n# Untracked files:\n"; + print "# (use \"git add\" to add to commit)\n"; + print "#\n"; + $shown = 1; + } + print "$_\n"; + } + ' + case "$committable" in + 0) + echo "nothing to commit" + exit 1 + esac + exit 0 + ) } +trap ' + test -z "$TMP_INDEX" || { + test -f "$TMP_INDEX" && rm -f "$TMP_INDEX" + } + rm -f "$NEXT_INDEX" +' 0 + +################################################################ +# Command line argument parsing and sanity checking + all= also= only= @@ -43,6 +183,7 @@ no_edit= log_given= log_message= verify=t +verbose= signoff= force_author= while case "$#" in 0) break;; esac @@ -172,9 +313,9 @@ do signoff=t shift ;; - -v|--v|--ve|--ver|--veri|--verif|--verify) - verify=t - shift + -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) + verbose=t + shift ;; --) shift @@ -189,6 +330,9 @@ do esac done +################################################################ +# Sanity check options + case "$log_given" in tt*) die "Only one of -c/-C/-F/-m can be used." ;; @@ -207,9 +351,24 @@ case "$#,$also$only" in # Later when switch the defaults, we will replace them with these: # echo >&2 "assuming --only paths..." # also= + + # If we are going to launch an editor, the message won't be + # shown without this... + test -z "$log_given$status_only" && sleep 1 ;; esac unset only +case "$all,$also,$#" in +t,t,*) + die "Cannot use -a and -i at the same time." ;; +t,,[1-9]*) + die "Paths with -a does not make sense." ;; +,t,0) + die "No paths with -i does not make sense." ;; +esac + +################################################################ +# Prepare index to have a tree to be committed TOP=`git-rev-parse --show-cdup` if test -z "$TOP" @@ -218,29 +377,25 @@ then fi case "$all,$also" in -t,t) - die "Cannot use -a and -i at the same time." ;; t,) - case "$#" in - 0) ;; - *) die "Paths with -a does not make sense." ;; - esac - save_index && ( cd "$TOP" + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE git-diff-files --name-only -z | git-update-index --remove -z --stdin ) ;; ,t) - case "$#" in - 0) die "No paths with -i does not make sense." ;; - esac - save_index && git-diff-files --name-only -z -- "$@" | - (cd "$TOP" && git-update-index --remove -z --stdin) + ( + cd "$TOP" + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE + git-update-index --remove -z --stdin + ) ;; ,) case "$#" in @@ -262,43 +417,68 @@ t,) refuse_partial "Different in index and the last commit: $dirty_in_index" fi - commit_only=`git-ls-files -- "$@"` ;; + commit_only=`git-ls-files -- "$@"` + + # Build the temporary index and update the real index + # the same way. + if test -z "$initial_commit" + then + cp "$THIS_INDEX" "$TMP_INDEX" + GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD + else + rm -f "$TMP_INDEX" + fi || exit + + echo "$commit_only" | + GIT_INDEX_FILE="$TMP_INDEX" \ + git-update-index --add --remove --stdin && + + save_index && + echo "$commit_only" | + ( + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE + git-update-index --remove --stdin + ) || exit + ;; esac ;; esac -git-update-index -q --refresh || exit 1 - -trap ' - test -z "$TMP_INDEX" || { - test -f "$TMP_INDEX" && rm -f "$TMP_INDEX" - } - test -f "$SAVE_INDEX" && mv -f "$SAVE_INDEX" "$GIT_DIR/index" -' 0 +################################################################ +# If we do as-is commit, the index file will be THIS_INDEX, +# otherwise NEXT_INDEX after we make this commit. We leave +# the index as is if we abort. -if test "$TMP_INDEX" +if test -f "$NEXT_INDEX" then - if test -z "$initial_commit" - then - GIT_INDEX_FILE="$TMP_INDEX" git-read-tree HEAD - else - rm -f "$TMP_INDEX" - fi || exit - echo "$commit_only" | - GIT_INDEX_FILE="$TMP_INDEX" git-update-index --add --remove --stdin && - save_index && - echo "$commit_only" | - git-update-index --remove --stdin || - exit + USE_INDEX="$NEXT_INDEX" +else + USE_INDEX="$THIS_INDEX" fi +GIT_INDEX_FILE="$USE_INDEX" \ + git-update-index -q $unmerged_ok_if_status --refresh || exit + +################################################################ +# If the request is status, just show it and exit. + +case "$0" in +*status) + run_status + exit $? +esac + +################################################################ +# Grab commit message, write out tree and make commit. + if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit then if test "$TMP_INDEX" then GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit else - "$GIT_DIR"/hooks/pre-commit + GIT_INDEX_FILE="$USE_INDEX" "$GIT_DIR"/hooks/pre-commit fi || exit fi @@ -398,8 +578,10 @@ else PARENTS="" fi - -run_status >>"$GIT_DIR"/COMMIT_EDITMSG +{ + test -z "$verbose" || echo '---' + run_status +} >>"$GIT_DIR"/COMMIT_EDITMSG if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ] then rm -f "$GIT_DIR/COMMIT_EDITMSG" @@ -429,8 +611,14 @@ t) fi esac -grep -v '^#' < "$GIT_DIR"/COMMIT_EDITMSG | -git-stripspace > "$GIT_DIR"/COMMIT_MSG +sed -e ' + /^---$/{ + s/// + q + } + /^#/d +' "$GIT_DIR"/COMMIT_EDITMSG | +git-stripspace >"$GIT_DIR"/COMMIT_MSG if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG | git-stripspace | @@ -439,14 +627,20 @@ if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG | then if test -z "$TMP_INDEX" then - tree=$(git-write-tree) + tree=$(GIT_INDEX_FILE="$USE_INDEX" git-write-tree) else tree=$(GIT_INDEX_FILE="$TMP_INDEX" git-write-tree) && rm -f "$TMP_INDEX" fi && commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) && git-update-ref HEAD $commit $current && - rm -f -- "$GIT_DIR/MERGE_HEAD" + rm -f -- "$GIT_DIR/MERGE_HEAD" && + if test -f "$NEXT_INDEX" + then + mv "$NEXT_INDEX" "$THIS_INDEX" + else + : ;# happy + fi else echo >&2 "* no commit message? aborting commit." false @@ -459,8 +653,4 @@ if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0 then "$GIT_DIR"/hooks/post-commit fi -if test 0 -eq "$ret" -then - rm -f "$SAVE_INDEX" -fi exit "$ret" diff --git a/git-status.sh b/git-status.sh deleted file mode 100755 index 10d781c9..00000000 --- a/git-status.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Linus Torvalds -# - -USAGE='' -SUBDIRECTORY_OK='Yes' - -. git-sh-setup - -if [ "$#" != "0" ] -then - usage -fi - -report () { - header="# -# $1: -# ($2) -# -" - trailer="" - while read status name newname - do - printf '%s' "$header" - header="" - trailer="# -" - case "$status" in - M ) echo "# modified: $name";; - D*) echo "# deleted: $name";; - T ) echo "# typechange: $name";; - C*) echo "# copied: $name -> $newname";; - R*) echo "# renamed: $name -> $newname";; - A*) echo "# new file: $name";; - U ) echo "# unmerged: $name";; - esac - done - printf '%s' "$trailer" - [ "$header" ] -} - -branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) -case "$branch" in -refs/heads/master) ;; -*) echo "# On branch $branch" ;; -esac - -git-update-index -q --unmerged --refresh || exit - -if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1 -then - git-diff-index -M --cached --name-status --diff-filter=MDTCRA HEAD | - sed -e ' - s/\\/\\\\/g - s/ /\\ /g - ' | - report "Updated but not checked in" "will commit" - - committable="$?" -else - echo '# -# Initial commit -#' - git-ls-files | - sed -e ' - s/\\/\\\\/g - s/ /\\ /g - s/^/A / - ' | - report "Updated but not checked in" "will commit" - - committable="$?" -fi - -git-diff-files --name-status | -sed -e ' - s/\\/\\\\/g - s/ /\\ /g -' | -report "Changed but not updated" "use git-update-index to mark for commit" - - -if test -f "$GIT_DIR/info/exclude" -then - git-ls-files -z --others --directory \ - --exclude-from="$GIT_DIR/info/exclude" \ - --exclude-per-directory=.gitignore -else - git-ls-files -z --others --directory \ - --exclude-per-directory=.gitignore -fi | -perl -e '$/ = "\0"; - my $shown = 0; - while (<>) { - chomp; - s|\\|\\\\|g; - s|\t|\\t|g; - s|\n|\\n|g; - s/^/# /; - if (!$shown) { - print "#\n# Untracked files:\n"; - print "# (use \"git add\" to add to commit)\n#\n"; - $shown = 1; - } - print "$_\n"; - } -' - -case "$committable" in -0) - echo "nothing to commit" - exit 1 -esac -exit 0 diff --git a/ident.c b/ident.c index 0461b8b2..23b8cfc6 100644 --- a/ident.c +++ b/ident.c @@ -167,6 +167,11 @@ static const char *get_ident(const char *name, const char *email, name = git_default_name; if (!email) email = git_default_email; + + if (!*name || !*email) + die("empty ident %s <%s> not allowed", + name, email); + strcpy(date, git_default_date); if (date_str) parse_date(date_str, date, sizeof(date)); diff --git a/index-pack.c b/index-pack.c index 541d7bc1..babe34b2 100644 --- a/index-pack.c +++ b/index-pack.c @@ -68,9 +68,9 @@ static void parse_pack_header(void) hdr = (void *)pack_base; if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) die("packfile '%s' signature mismatch", pack_name); - if (hdr->hdr_version != htonl(PACK_VERSION)) - die("packfile '%s' version %d different from ours %d", - pack_name, ntohl(hdr->hdr_version), PACK_VERSION); + if (!pack_version_ok(hdr->hdr_version)) + die("packfile '%s' version %d unsupported", + pack_name, ntohl(hdr->hdr_version)); nr_objects = ntohl(hdr->hdr_entries); diff --git a/ls-files.c b/ls-files.c index 6af3b091..1c8ab739 100644 --- a/ls-files.c +++ b/ls-files.c @@ -447,6 +447,22 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) if (pathspec && !match(pathspec, ce->name, len)) return; + if (tag && *tag && (ce->ce_flags & htons(CE_VALID))) { + static char alttag[4]; + memcpy(alttag, tag, 3); + if (isalpha(tag[0])) + alttag[0] = tolower(tag[0]); + else if (tag[0] == '?') + alttag[0] = '!'; + else { + alttag[0] = 'v'; + alttag[1] = tag[0]; + alttag[2] = ' '; + alttag[3] = 0; + } + tag = alttag; + } + if (!show_stage) { fputs(tag, stdout); write_name_quoted("", 0, ce->name + offset, @@ -474,8 +490,28 @@ static void show_files(void) const char *path = ".", *base = ""; int baselen = prefix_len; - if (baselen) + if (baselen) { path = base = prefix; + if (exclude_per_dir) { + char *p, *pp = xmalloc(baselen+1); + memcpy(pp, prefix, baselen+1); + p = pp; + while (1) { + char save = *p; + *p = 0; + push_exclude_per_directory(pp, p-pp); + *p++ = save; + if (!save) + break; + p = strchr(p, '/'); + if (p) + p++; + else + p = pp + baselen; + } + free(pp); + } + } read_directory(path, base, baselen); qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name); if (show_others) @@ -503,7 +539,7 @@ static void show_files(void) err = lstat(ce->name, &st); if (show_deleted && err) show_ce_entry(tag_removed, ce); - if (show_modified && ce_modified(ce, &st)) + if (show_modified && ce_modified(ce, &st, 0)) show_ce_entry(tag_modified, ce); } } diff --git a/pack-check.c b/pack-check.c index 511f2942..67a7ecdf 100644 --- a/pack-check.c +++ b/pack-check.c @@ -16,9 +16,9 @@ static int verify_packfile(struct packed_git *p) hdr = p->pack_base; if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) return error("Packfile %s signature mismatch", p->pack_name); - if (hdr->hdr_version != htonl(PACK_VERSION)) - return error("Packfile version %d different from ours %d", - ntohl(hdr->hdr_version), PACK_VERSION); + if (!pack_version_ok(hdr->hdr_version)) + return error("Packfile version %d unsupported", + ntohl(hdr->hdr_version)); nr_objects = ntohl(hdr->hdr_entries); if (num_packed_objects(p) != nr_objects) return error("Packfile claims to have %d objects, " diff --git a/pack.h b/pack.h index 657deaa3..9dafa2b6 100644 --- a/pack.h +++ b/pack.h @@ -21,6 +21,7 @@ enum object_type { */ #define PACK_SIGNATURE 0x5041434b /* "PACK" */ #define PACK_VERSION 2 +#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) struct pack_header { unsigned int hdr_signature; unsigned int hdr_version; diff --git a/patch-delta.c b/patch-delta.c index 98c27beb..c0e13114 100644 --- a/patch-delta.c +++ b/patch-delta.c @@ -44,16 +44,15 @@ void *patch_delta(void *src_buf, unsigned long src_size, cmd = *data++; if (cmd & 0x80) { unsigned long cp_off = 0, cp_size = 0; - const unsigned char *buf; if (cmd & 0x01) cp_off = *data++; if (cmd & 0x02) cp_off |= (*data++ << 8); if (cmd & 0x04) cp_off |= (*data++ << 16); if (cmd & 0x08) cp_off |= (*data++ << 24); if (cmd & 0x10) cp_size = *data++; if (cmd & 0x20) cp_size |= (*data++ << 8); + if (cmd & 0x40) cp_size |= (*data++ << 16); if (cp_size == 0) cp_size = 0x10000; - buf = (cmd & 0x40) ? dst_buf : src_buf; - memcpy(out, buf + cp_off, cp_size); + memcpy(out, src_buf + cp_off, cp_size); out += cp_size; } else { memcpy(out, data, cmd); diff --git a/read-cache.c b/read-cache.c index c5474d49..efbb1be8 100644 --- a/read-cache.c +++ b/read-cache.c @@ -27,6 +27,9 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st) ce->ce_uid = htonl(st->st_uid); ce->ce_gid = htonl(st->st_gid); ce->ce_size = htonl(st->st_size); + + if (assume_unchanged) + ce->ce_flags |= htons(CE_VALID); } static int ce_compare_data(struct cache_entry *ce, struct stat *st) @@ -146,9 +149,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) return changed; } -int ce_match_stat(struct cache_entry *ce, struct stat *st) +int ce_match_stat(struct cache_entry *ce, struct stat *st, int ignore_valid) { - unsigned int changed = ce_match_stat_basic(ce, st); + unsigned int changed; + + /* + * If it's marked as always valid in the index, it's + * valid whatever the checked-out copy says. + */ + if (!ignore_valid && (ce->ce_flags & htons(CE_VALID))) + return 0; + + changed = ce_match_stat_basic(ce, st); /* * Within 1 second of this sequence: @@ -164,7 +176,7 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st) * effectively mean we can make at most one commit per second, * which is not acceptable. Instead, we check cache entries * whose mtime are the same as the index file timestamp more - * careful than others. + * carefully than others. */ if (!changed && index_file_timestamp && @@ -174,10 +186,10 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st) return changed; } -int ce_modified(struct cache_entry *ce, struct stat *st) +int ce_modified(struct cache_entry *ce, struct stat *st, int really) { int changed, changed_fs; - changed = ce_match_stat(ce, st); + changed = ce_match_stat(ce, st, really); if (!changed) return 0; /* @@ -233,6 +245,11 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla return -1; if (len1 > len2) return 1; + + /* Differences between "assume up-to-date" should not matter. */ + flags1 &= ~CE_VALID; + flags2 &= ~CE_VALID; + if (flags1 < flags2) return -1; if (flags1 > flags2) @@ -430,6 +447,7 @@ int add_cache_entry(struct cache_entry *ce, int option) int ok_to_add = option & ADD_CACHE_OK_TO_ADD; int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE; int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK; + pos = cache_name_pos(ce->name, ntohs(ce->ce_flags)); /* existing match? Just replace it. */ diff --git a/read-tree.c b/read-tree.c index 5580f15b..52f06e31 100644 --- a/read-tree.c +++ b/read-tree.c @@ -349,7 +349,7 @@ static void verify_uptodate(struct cache_entry *ce) return; if (!lstat(ce->name, &st)) { - unsigned changed = ce_match_stat(ce, &st); + unsigned changed = ce_match_stat(ce, &st, 1); if (!changed) return; errno = 0; diff --git a/sha1_file.c b/sha1_file.c index 20f6419b..3d11a9bf 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -6,8 +6,6 @@ * This handles basic git sha1 object files - packing, unpacking, * creation etc. */ -#include -#include #include "cache.h" #include "delta.h" #include "pack.h" @@ -74,6 +72,8 @@ int adjust_shared_perm(const char *path) int safe_create_leading_directories(char *path) { char *pos = path; + struct stat st; + if (*pos == '/') pos++; @@ -82,12 +82,17 @@ int safe_create_leading_directories(char *path) if (!pos) break; *pos = 0; - if (mkdir(path, 0777) < 0) { - if (errno != EEXIST) { + if (!stat(path, &st)) { + /* path exists */ + if (!S_ISDIR(st.st_mode)) { *pos = '/'; - return -1; + return -3; } } + else if (mkdir(path, 0777)) { + *pos = '/'; + return -1; + } else if (adjust_shared_perm(path)) { *pos = '/'; return -2; diff --git a/show-branch.c b/show-branch.c index ffe7456a..511fd3b6 100644 --- a/show-branch.c +++ b/show-branch.c @@ -548,8 +548,8 @@ int main(int ac, char **av) int with_current_branch = 0; int head_at = -1; - git_config(git_show_branch_config); setup_git_directory(); + git_config(git_show_branch_config); /* If nothing is specified, try the default first */ if (ac == 1 && default_num) { diff --git a/t/t6000lib.sh b/t/t6000lib.sh index 01f796e9..c6752af4 100755 --- a/t/t6000lib.sh +++ b/t/t6000lib.sh @@ -51,7 +51,12 @@ as_author() export GIT_AUTHOR_EMAIL="$_author" "$@" - export GIT_AUTHOR_EMAIL="$_save" + if test -z "$_save" + then + unset GIT_AUTHOR_EMAIL + else + export GIT_AUTHOR_EMAIL="$_save" + fi } commit_date() diff --git a/unpack-objects.c b/unpack-objects.c index 4b5b5cb3..815a1b38 100644 --- a/unpack-objects.c +++ b/unpack-objects.c @@ -246,13 +246,12 @@ static void unpack_all(void) { int i; struct pack_header *hdr = fill(sizeof(struct pack_header)); - unsigned version = ntohl(hdr->hdr_version); unsigned nr_objects = ntohl(hdr->hdr_entries); if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE) die("bad pack file"); - if (version != PACK_VERSION) - die("unable to handle pack file version %d", version); + if (!pack_version_ok(hdr->hdr_version)) + die("unknown pack file version %d", ntohl(hdr->hdr_version)); fprintf(stderr, "Unpacking %d objects\n", nr_objects); use(sizeof(struct pack_header)); diff --git a/update-index.c b/update-index.c index afec98dd..ce1db38d 100644 --- a/update-index.c +++ b/update-index.c @@ -23,6 +23,10 @@ static int quiet; /* --refresh needing update is not error */ static int info_only; static int force_remove; static int verbose; +static int mark_valid_only = 0; +#define MARK_VALID 1 +#define UNMARK_VALID 2 + /* Three functions to allow overloaded pointer return; see linux/err.h */ static inline void *ERR_PTR(long error) @@ -53,6 +57,25 @@ static void report(const char *fmt, ...) va_end(vp); } +static int mark_valid(const char *path) +{ + int namelen = strlen(path); + int pos = cache_name_pos(path, namelen); + if (0 <= pos) { + switch (mark_valid_only) { + case MARK_VALID: + active_cache[pos]->ce_flags |= htons(CE_VALID); + break; + case UNMARK_VALID: + active_cache[pos]->ce_flags &= ~htons(CE_VALID); + break; + } + active_cache_changed = 1; + return 0; + } + return -1; +} + static int add_file_to_cache(const char *path) { int size, namelen, option, status; @@ -94,6 +117,7 @@ static int add_file_to_cache(const char *path) ce = xmalloc(size); memset(ce, 0, size); memcpy(ce->name, path, namelen); + ce->ce_flags = htons(namelen); fill_stat_cache_info(ce, &st); ce->ce_mode = create_ce_mode(st.st_mode); @@ -105,7 +129,6 @@ static int add_file_to_cache(const char *path) if (0 <= pos) ce->ce_mode = active_cache[pos]->ce_mode; } - ce->ce_flags = htons(namelen); if (index_path(ce->sha1, path, &st, !info_only)) return -1; @@ -128,7 +151,7 @@ static int add_file_to_cache(const char *path) * For example, you'd want to do this after doing a "git-read-tree", * to link up the stat cache details with the proper files. */ -static struct cache_entry *refresh_entry(struct cache_entry *ce) +static struct cache_entry *refresh_entry(struct cache_entry *ce, int really) { struct stat st; struct cache_entry *updated; @@ -137,21 +160,36 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce) if (lstat(ce->name, &st) < 0) return ERR_PTR(-errno); - changed = ce_match_stat(ce, &st); - if (!changed) - return NULL; + changed = ce_match_stat(ce, &st, really); + if (!changed) { + if (really && assume_unchanged && + !(ce->ce_flags & htons(CE_VALID))) + ; /* mark this one VALID again */ + else + return NULL; + } - if (ce_modified(ce, &st)) + if (ce_modified(ce, &st, really)) return ERR_PTR(-EINVAL); size = ce_size(ce); updated = xmalloc(size); memcpy(updated, ce, size); fill_stat_cache_info(updated, &st); + + /* In this case, if really is not set, we should leave + * CE_VALID bit alone. Otherwise, paths marked with + * --no-assume-unchanged (i.e. things to be edited) will + * reacquire CE_VALID bit automatically, which is not + * really what we want. + */ + if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) + updated->ce_flags &= ~htons(CE_VALID); + return updated; } -static int refresh_cache(void) +static int refresh_cache(int really) { int i; int has_errors = 0; @@ -171,12 +209,19 @@ static int refresh_cache(void) continue; } - new = refresh_entry(ce); + new = refresh_entry(ce, really); if (!new) continue; if (IS_ERR(new)) { if (not_new && PTR_ERR(new) == -ENOENT) continue; + if (really && PTR_ERR(new) == -EINVAL) { + /* If we are doing --really-refresh that + * means the index is not valid anymore. + */ + ce->ce_flags &= ~htons(CE_VALID); + active_cache_changed = 1; + } if (quiet) continue; printf("%s: needs update\n", ce->name); @@ -274,6 +319,8 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, memcpy(ce->name, path, len); ce->ce_flags = create_ce_flags(len, stage); ce->ce_mode = create_ce_mode(mode); + if (assume_unchanged) + ce->ce_flags |= htons(CE_VALID); option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; if (add_cache_entry(ce, option)) @@ -317,6 +364,12 @@ static void update_one(const char *path, const char *prefix, int prefix_length) fprintf(stderr, "Ignoring path %s\n", path); return; } + if (mark_valid_only) { + if (mark_valid(p)) + die("Unable to mark file %s", path); + return; + } + if (force_remove) { if (remove_file_from_cache(p)) die("git-update-index: unable to remove %s", path); @@ -467,7 +520,11 @@ int main(int argc, const char **argv) continue; } if (!strcmp(path, "--refresh")) { - has_errors |= refresh_cache(); + has_errors |= refresh_cache(0); + continue; + } + if (!strcmp(path, "--really-refresh")) { + has_errors |= refresh_cache(1); continue; } if (!strcmp(path, "--cacheinfo")) { @@ -493,6 +550,14 @@ int main(int argc, const char **argv) die("git-update-index: %s cannot chmod %s", path, argv[i]); continue; } + if (!strcmp(path, "--assume-unchanged")) { + mark_valid_only = MARK_VALID; + continue; + } + if (!strcmp(path, "--no-assume-unchanged")) { + mark_valid_only = UNMARK_VALID; + continue; + } if (!strcmp(path, "--info-only")) { info_only = 1; continue; diff --git a/write-tree.c b/write-tree.c index f866059f..addb5de3 100644 --- a/write-tree.c +++ b/write-tree.c @@ -111,7 +111,7 @@ int main(int argc, char **argv) funny = 0; for (i = 0; i < entries; i++) { struct cache_entry *ce = active_cache[i]; - if (ntohs(ce->ce_flags) & ~CE_NAMEMASK) { + if (ce_stage(ce)) { if (10 < ++funny) { fprintf(stderr, "...\n"); break;