From 9e30dd7c0ecc9f10372f31539d0122db97418353 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 May 2005 15:10:49 -0700 Subject: [PATCH 1/5] Make git-prune-script executable again. I do not know why the executable bit was lost since the change went in as GIT pull, not via e-mail patch, but here is a fix. Signed-off-by: Junio C Hamano --- git-prune-script | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 git-prune-script diff --git a/git-prune-script b/git-prune-script old mode 100644 new mode 100755 From c4b83e618f1df7d8ecc9392fa40e5bebccbe6b5a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 May 2005 15:29:06 -0700 Subject: [PATCH 2/5] Do not write out new index if nothing has changed. The git-update-cache command, especially with --refresh, may not change anything. In such a case, writing 1.6MB of the same thing is a waste. Signed-off-by: Junio C Hamano --- update-cache.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/update-cache.c b/update-cache.c index 05d584ed48..893ba8679d 100644 --- a/update-cache.c +++ b/update-cache.c @@ -15,6 +15,12 @@ */ static int allow_add = 0, allow_remove = 0, not_new = 0; +/* + * update-cache --refresh may not touch anything at all, in which case + * writing 1.6MB of the same thing is a waste. + */ +static int cache_changed = 0; + /* Three functions to allow overloaded pointer return; see linux/err.h */ static inline void *ERR_PTR(long error) { @@ -51,7 +57,7 @@ static void fill_stat_cache_info(struct cache_entry *ce, struct stat *st) ce->ce_size = htonl(st->st_size); } -static int add_file_to_cache(char *path) +static int add_file_to_cache_1(char *path) { int size, namelen; struct cache_entry *ce; @@ -93,9 +99,35 @@ static int add_file_to_cache(char *path) default: return -1; } + if (!cache_changed) { + /* If we have not smudged the cache, be careful + * to keep it clean. Find out if we have a matching + * cache entry that add_cache_entry would replace with, + * and if it matches then do not bother calling it. + */ + int pos = cache_name_pos(ce->name, namelen); + if ((0 <= pos) && + !memcmp(active_cache[pos], ce, sizeof(*ce))) { + free(ce); + /* magic to tell add_file_to_cache that + * we have not updated anything. + */ + return 999; + } + } return add_cache_entry(ce, allow_add); } +static int add_file_to_cache(char *path) +{ + int ret = add_file_to_cache_1(path); + if (ret == 0) + cache_changed = 1; + else if (ret == 999) + ret = 0; + return ret; +} + static int match_data(int fd, void *buffer, unsigned long size) { while (size) { @@ -165,6 +197,7 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce) if (compare_data(ce, st.st_size)) return ERR_PTR(-EINVAL); + cache_changed = 1; size = ce_size(ce); updated = xmalloc(size); memcpy(updated, ce, size); @@ -245,6 +278,7 @@ static int add_cacheinfo(char *arg1, char *arg2, char *arg3) if (!verify_path(arg3)) return -1; + cache_changed = 1; len = strlen(arg3); size = cache_entry_size(len); ce = xmalloc(size); @@ -339,9 +373,13 @@ int main(int argc, char **argv) if (add_file_to_cache(path)) die("Unable to add %s to database", path); } - if (write_cache(newfd, active_cache, active_nr) || rename(lockfile, indexfile)) + + if (!cache_changed) + unlink(lockfile); + else if (write_cache(newfd, active_cache, active_nr) || + rename(lockfile, indexfile)) die("Unable to write new cachefile"); lockfile_name = NULL; - return has_errors; + return has_errors ? 1 : 0; } From 660265909fc178581ef327076716dfd3550e6e7b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 May 2005 15:35:49 -0700 Subject: [PATCH 3/5] diff-cache shows differences for unmerged paths without --cache. While manually resolving a merge conflict, being able to run diff-cache without --cache option between files in the work tree and either of the ancestor trees is helpful to verify the hand merged result. However, diff-cache refuses to handle unmerged paths, even when run without --cache option. This changes the behaviour so that the above use case will report the differences between the compared tree and the magic 0{40} SHA1 (i.e. "look at the work tree"). When there is no corresponding file in the work tree, or when the command is run with "--cache" option, it continues to report "unmerged". Signed-off-by: Junio C Hamano --- diff-cache.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/diff-cache.c b/diff-cache.c index ef522cd233..7e87d28f3a 100644 --- a/diff-cache.c +++ b/diff-cache.c @@ -57,14 +57,17 @@ static void show_new_file(struct cache_entry *new) show_file("+", new, sha1, mode); } -static int show_modified(struct cache_entry *old, struct cache_entry *new) +static int show_modified(struct cache_entry *old, + struct cache_entry *new, + int report_missing) { unsigned int mode, oldmode; unsigned char *sha1; unsigned char old_sha1_hex[60]; if (get_stat_data(new, &sha1, &mode) < 0) { - show_file("-", old, old->sha1, old->ce_mode); + if (report_missing) + show_file("-", old, old->sha1, old->ce_mode); return -1; } @@ -101,7 +104,7 @@ static int diff_cache(struct cache_entry **ac, int entries) break; } /* Show difference between old and new */ - show_modified(ac[1], ce); + show_modified(ac[1], ce, 1); break; case 1: /* No stage 3 (merge) entry? That means it's been deleted */ @@ -109,7 +112,19 @@ static int diff_cache(struct cache_entry **ac, int entries) show_file("-", ce, ce->sha1, ce->ce_mode); break; } - /* Otherwise we fall through to the "unmerged" case */ + /* We come here with ce pointing at stage 1 + * (original tree) and ac[1] pointing at stage + * 3 (unmerged). show-modified with + * report-mising set to false does not say the + * file is deleted but reports true if work + * tree does not have it, in which case we + * fall through to report the unmerged state. + * Otherwise, we show the differences between + * the original tree and the work tree. + */ + if (!cached_only && !show_modified(ce, ac[1], 0)) + break; + /* fallthru */ case 3: if (generate_patch) diff_unmerge(ce->name); From b28858bf65d4fd6d8bb070865518ec43817fe7f3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 May 2005 16:10:21 -0700 Subject: [PATCH 4/5] Update diff engine for symlinks stored in the cache. This patch updates the external diff interface engine for the change to store the symbolic links in the cache, recently done by Kay Sievers. The main thing it does is when comparing with the work tree, it prepares the counterpart to the blob being compared by doing a readlink followed by sending that result to a temporary file to be diffed. Signed-off-by: Junio C Hamano --- diff.c | 78 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/diff.c b/diff.c index 8dfa624432..95488cdd9f 100644 --- a/diff.c +++ b/diff.c @@ -125,9 +125,16 @@ static void builtin_diff(const char *name, printf("Created: %s (mode:%s)\n", name, temp[1].mode); else if (!path1[1][0]) printf("Deleted: %s\n", name); - else if (strcmp(temp[0].mode, temp[1].mode)) + else if (strcmp(temp[0].mode, temp[1].mode)) { printf("Mode changed: %s (%s->%s)\n", name, temp[0].mode, temp[1].mode); + /* Be careful. We do not want to diff between + * symlink and a file. + */ + if (strncmp(temp[0].mode, "120", 3) != + strncmp(temp[1].mode, "120", 3)) + exit(0); + } fflush(NULL); execlp("/bin/sh","sh", "-c", cmd, NULL); } @@ -163,13 +170,35 @@ static int work_tree_matches(const char *name, const unsigned char *sha1) if (pos < 0) return 0; ce = active_cache[pos]; - if ((stat(name, &st) < 0) || + if ((lstat(name, &st) < 0) || + !S_ISREG(st.st_mode) || cache_match_stat(ce, &st) || memcmp(sha1, ce->sha1, 20)) return 0; return 1; } +static void prep_temp_blob(struct diff_tempfile *temp, + void *blob, + unsigned long size, + unsigned char *sha1, + int mode) +{ + int fd; + + strcpy(temp->tmp_path, ".diff_XXXXXX"); + fd = mkstemp(temp->tmp_path); + if (fd < 0) + die("unable to create temp-file"); + if (write(fd, blob, size) != size) + die("unable to write temp-file"); + close(fd); + temp->name = temp->tmp_path; + strcpy(temp->hex, sha1_to_hex(sha1)); + temp->hex[40] = 0; + sprintf(temp->mode, "%06o", mode); +} + static void prepare_temp_file(const char *name, struct diff_tempfile *temp, struct diff_spec *one) @@ -196,20 +225,36 @@ static void prepare_temp_file(const char *name, if (!one->sha1_valid || use_work_tree) { struct stat st; temp->name = name; - if (stat(temp->name, &st) < 0) { + if (lstat(temp->name, &st) < 0) { if (errno == ENOENT) goto not_a_valid_file; die("stat(%s): %s", temp->name, strerror(errno)); } - if (!one->sha1_valid) - strcpy(temp->hex, sha1_to_hex(null_sha1)); - else - strcpy(temp->hex, sha1_to_hex(one->blob_sha1)); - sprintf(temp->mode, "%06o", - S_IFREG |ce_permissions(st.st_mode)); + if (S_ISLNK(st.st_mode)) { + int ret; + char *buf, buf_[1024]; + buf = ((sizeof(buf_) < st.st_size) ? + xmalloc(st.st_size) : buf_); + ret = readlink(name, buf, st.st_size); + if (ret < 0) + die("readlink(%s)", name); + prep_temp_blob(temp, buf, st.st_size, + (one->sha1_valid ? + one->blob_sha1 : null_sha1), + (one->sha1_valid ? + one->mode : S_IFLNK)); + } + else { + if (!one->sha1_valid) + strcpy(temp->hex, sha1_to_hex(null_sha1)); + else + strcpy(temp->hex, sha1_to_hex(one->blob_sha1)); + sprintf(temp->mode, "%06o", + S_IFREG |ce_permissions(st.st_mode)); + } + return; } else { - int fd; void *blob; char type[20]; unsigned long size; @@ -218,19 +263,8 @@ static void prepare_temp_file(const char *name, if (!blob || strcmp(type, "blob")) die("unable to read blob object for %s (%s)", name, sha1_to_hex(one->blob_sha1)); - - strcpy(temp->tmp_path, ".diff_XXXXXX"); - fd = mkstemp(temp->tmp_path); - if (fd < 0) - die("unable to create temp-file"); - if (write(fd, blob, size) != size) - die("unable to write temp-file"); - close(fd); + prep_temp_blob(temp, blob, size, one->blob_sha1, one->mode); free(blob); - temp->name = temp->tmp_path; - strcpy(temp->hex, sha1_to_hex(one->blob_sha1)); - temp->hex[40] = 0; - sprintf(temp->mode, "%06o", one->mode); } } From fc54a9c30ccad3fde5890d2c0ca2e2acc0848fbc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 May 2005 16:14:01 -0700 Subject: [PATCH 5/5] Update git-apply-patch-script for symbolic links. This patch updates the git-apply-patch-script for the symbolic links in the cache, recently added by Kay Sievers. It currently is very anal about symbolic link changes. It refuses to change between a regular file and a symbolic link, and only allows symbolic link changes if the patch is based on the same original. Signed-off-by: Junio C Hamano --- git-apply-patch-script | 139 ++++++++++++++++++++++++++++++----------- 1 file changed, 101 insertions(+), 38 deletions(-) diff --git a/git-apply-patch-script b/git-apply-patch-script index dccad27061..13ec1c4490 100755 --- a/git-apply-patch-script +++ b/git-apply-patch-script @@ -8,71 +8,134 @@ # case "$#" in -2) exit 1 ;; # do not feed unmerged diff to me! +2) + echo >&2 "cannot handle unmerged diff on path $1." + exit 1 ;; esac name="$1" tmp1="$2" hex1="$3" mode1="$4" tmp2="$5" hex2="$6" mode2="$7" -case "$mode1" in *7??) mode1=+x ;; *6??) mode1=-x ;; esac -case "$mode2" in *7??) mode2=+x ;; *6??) mode2=-x ;; esac -if test -f "$name.orig" || test -f "$name.rej" -then - echo >&2 "Unresolved patch conflicts in the previous run found." - exit 1 -fi +type1=f +case "$mode1" in +*120???) type1=l ;; +*1007??) mode1=+x ;; +*1006??) mode1=-x ;; +.) type1=- ;; +esac -case "$mode1,$mode2" in -.,?x) - # newly created +type2=f +case "$mode2" in +*120???) type2=l ;; +*1007??) mode2=+x ;; +*1006??) mode2=-x ;; +.) type2=- ;; +esac + +case "$type1,$type2" in + +-,?) dir=$(dirname "$name") - case "$dir" in '' | .) ;; *) mkdir -p "$dir" esac || { + case "$dir" in '' | .) ;; *) mkdir -p "$dir" ;; esac || { echo >&2 "cannot create leading path for $name." exit 1 } - if test -f "$name" + if test -e "$name" then - echo >&2 "file $name to be created already exists." + echo >&2 "path $name to be created already exists." exit 1 fi - cat "$tmp2" >"$name" || { - echo >&2 "cannot create $name." + case "$type2" in + f) + # creating a regular file + cat "$tmp2" >"$name" || { + echo >&2 "cannot create a regular file $name." + exit 1 + } + case "$mode2" in + +x) + echo >&2 "created a regular file $name with mode +x." + chmod "$mode2" "$name" + ;; + -x) + echo >&2 "created a regular file $name." + ;; + esac + ;; + l) + # creating a symlink + ln -s "$(cat "$tmp2")" "$name" || { + echo >&2 "cannot create a symbolic link $name." + exit 1 + } + echo >&2 "created a symbolic link $name." + ;; + *) + echo >&2 "do not know how to create $name of type $type2." exit 1 - } - case "$mode2" in - +x) - echo >&2 "created $name with mode +x." - chmod "$mode2" "$name" - ;; - -x) - echo >&2 "created $name." - ;; esac - git-update-cache --add -- "$name" - ;; -?x,.) - # deleted - echo >&2 "deleted $name." + git-update-cache --add -- "$name" ;; + +?,-) rm -f "$name" || { - echo >&2 "cannot remove $name"; + echo >&2 "cannot remove $name" exit 1 } - git-update-cache --remove -- "$name" - ;; -*) + echo >&2 "deleted $name." + git-update-cache --remove -- "$name" ;; + +l,f|f,l) + echo >&2 "cannot change a regular file $name and a symbolic link $name." + exit 1 ;; + +l,l) + # symlink to symlink + current=$(readlink "$name") || { + echo >&2 "cannot read the target of the symbolic link $name." + exit 1 + } + original=$(cat "$tmp1") + next=$(cat "$tmp2") + test "$original" != "$current" || { + echo >&2 "cannot apply symbolic link target change ($original->$next) to $name which points to $current." + exit 1 + } + if test "$next" != "$current" + then + rm -f "$name" && ln -s "$next" "$name" || { + echo >&2 "cannot create symbolic link $name." + exit 1 + } + echo >&2 "changed symbolic target of $name." + git-update-cache -- "$name" + fi ;; + +f,f) # changed + test -e "$name" || { + echo >&2 "regular file $name to be patched does not exist." + exit 1 + } dir=$(dirname "$name") - case "$dir" in '' | .) ;; *) mkdir -p "$dir" esac || { + case "$dir" in '' | .) ;; *) mkdir -p "$dir";; esac || { echo >&2 "cannot create leading path for $name." exit 1 } - # This will say "patching ..." so we do not say anything outselves. - diff -u -L "a/$name" -L "b/$name" "$tmp1" "$tmp2" | patch -p1 || exit + tmp=.git-apply-patch-$$ + trap "rm -f $tmp-*" 0 1 2 3 15 + # Be careful, in case "$tmp2" is borrowed path from the work tree + # we are looking at... + diff -u -L "a/$name" -L "b/$name" "$tmp1" "$tmp2" >$tmp-patch + + # This will say "patching ..." so we do not say anything outselves. + patch -p1 <$tmp-patch || exit + rm -f $tmp-patch case "$mode1,$mode2" in "$mode2,$mode1") ;; *) - echo >&2 "changing mode from $mode1 to $mode2." chmod "$mode2" "$name" + echo >&2 "changed mode from $mode1 to $mode2." ;; esac git-update-cache -- "$name" + esac