From fc71db39e0d75e8a403f36f3fecf4450886cd165 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 29 Apr 2009 23:21:46 +0200 Subject: [PATCH 01/53] Introduce an unlink(2) wrapper which gives warning if unlink failed This seem to be a very common pattern in the current code. The function prints a generic removal failure message, the file name which failed and readable errno presentation. The function preserves errno and always returns the value unlink(2) returned, but prints no message for ENOENT, as it was the most often filtered out in the code calling unlink. Besides, removing a file is anyway the purpose of calling unlink. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- git-compat-util.h | 6 ++++++ wrapper.c | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/git-compat-util.h b/git-compat-util.h index 1ac16bde5a..c7cf2d5d9c 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -415,4 +415,10 @@ void git_qsort(void *base, size_t nmemb, size_t size, #define fstat_is_reliable() 1 #endif +/* + * Preserves errno, prints a message, but gives no warning for ENOENT. + * Always returns the return value of unlink(2). + */ +int unlink_or_warn(const char *path); + #endif diff --git a/wrapper.c b/wrapper.c index d8efb1365a..7eb3218ee9 100644 --- a/wrapper.c +++ b/wrapper.c @@ -289,3 +289,19 @@ int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1) safe_create_leading_directories(name); return open(name, O_RDWR|O_CREAT|O_EXCL, 0600); } + +int unlink_or_warn(const char *file) +{ + int rc = unlink(file); + + if (rc < 0) { + int err = errno; + if (ENOENT != err) { + warning("unable to unlink %s: %s", + file, strerror(errno)); + errno = err; + } + } + return rc; +} + From 691f1a28bf57618d8b44a193b1d28013c858aba6 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 29 Apr 2009 23:22:56 +0200 Subject: [PATCH 02/53] replace direct calls to unlink(2) with unlink_or_warn This helps to notice when something's going wrong, especially on systems which lock open files. I used the following criteria when selecting the code for replacement: - it was already printing a warning for the unlink failures - it is in a function which already printing something or is called from such a function - it is in a static function, returning void and the function is only called from a builtin main function (cmd_) - it is in a function which handles emergency exit (signal handlers) - it is in a function which is obvously cleaning up the lockfiles Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- builtin-apply.c | 4 ++-- builtin-fetch-pack.c | 2 +- builtin-prune-packed.c | 4 ++-- builtin-prune.c | 4 ++-- builtin-receive-pack.c | 2 +- builtin-remote.c | 4 ++-- builtin-rerere.c | 2 +- builtin-tag.c | 2 +- builtin-verify-tag.c | 2 +- diff.c | 2 +- entry.c | 2 +- fast-import.c | 4 ++-- http-push.c | 12 ++++++------ http-walker.c | 14 +++++++------- ll-merge.c | 2 +- lockfile.c | 4 ++-- pack-refs.c | 2 +- refs.c | 15 +++++---------- rerere.c | 2 +- server-info.c | 2 +- sha1_file.c | 2 +- transport.c | 2 +- unpack-trees.c | 2 +- 23 files changed, 44 insertions(+), 49 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index 7b404ef660..8a3771e87e 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2781,7 +2781,7 @@ static void remove_file(struct patch *patch, int rmdir_empty) if (rmdir(patch->old_name)) warning("unable to remove submodule %s", patch->old_name); - } else if (!unlink(patch->old_name) && rmdir_empty) { + } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) { remove_path(patch->old_name); } } @@ -2891,7 +2891,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned if (!try_create_file(newpath, mode, buf, size)) { if (!rename(newpath, path)) return; - unlink(newpath); + unlink_or_warn(newpath); break; } if (errno != EEXIST) diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 5d134be47c..bd97cfd9bf 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -814,7 +814,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, fd = hold_lock_file_for_update(&lock, shallow, LOCK_DIE_ON_ERROR); if (!write_shallow_commits(fd, 0)) { - unlink(shallow); + unlink_or_warn(shallow); rollback_lock_file(&lock); } else { commit_lock_file(&lock); diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 4942892e9f..00590b1c3c 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -28,8 +28,8 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) memcpy(pathname + len, de->d_name, 38); if (opts & DRY_RUN) printf("rm -f %s\n", pathname); - else if (unlink(pathname) < 0) - error("unable to unlink %s", pathname); + else + unlink_or_warn(pathname); display_progress(progress, i + 1); } pathname[len] = 0; diff --git a/builtin-prune.c b/builtin-prune.c index 545e9c1f94..145ba83651 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -27,7 +27,7 @@ static int prune_tmp_object(const char *path, const char *filename) } printf("Removing stale temporary file %s\n", fullpath); if (!show_only) - unlink(fullpath); + unlink_or_warn(fullpath); return 0; } @@ -47,7 +47,7 @@ static int prune_object(char *path, const char *filename, const unsigned char *s (type > 0) ? typename(type) : "unknown"); } if (!show_only) - unlink(fullpath); + unlink_or_warn(fullpath); return 0; } diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index a970b39505..035b723e50 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -702,7 +702,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) unpack_status = unpack(); execute_commands(unpack_status); if (pack_lockfile) - unlink(pack_lockfile); + unlink_or_warn(pack_lockfile); if (report_status) report(unpack_status); run_receive_hook(post_receive_hook); diff --git a/builtin-remote.c b/builtin-remote.c index 2ed752cbf1..71abf68404 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -525,8 +525,8 @@ static int migrate_file(struct remote *remote) path = git_path("remotes/%s", remote->name); else if (remote->origin == REMOTE_BRANCHES) path = git_path("branches/%s", remote->name); - if (path && unlink(path)) - warning("failed to remove '%s'", path); + if (path) + unlink_or_warn(path); return 0; } diff --git a/builtin-rerere.c b/builtin-rerere.c index 020af7377b..adfb7b5f48 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -116,7 +116,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) if (!has_rerere_resolution(name)) unlink_rr_item(name); } - unlink(git_path("rr-cache/MERGE_RR")); + unlink_or_warn(git_path("rr-cache/MERGE_RR")); } else if (!strcmp(argv[1], "gc")) garbage_collect(&merge_rr); else if (!strcmp(argv[1], "status")) diff --git a/builtin-tag.c b/builtin-tag.c index 01e73747d0..e544430094 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -338,7 +338,7 @@ static void create_tag(const unsigned char *object, const char *tag, exit(128); } if (path) { - unlink(path); + unlink_or_warn(path); free(path); } } diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c index 729a1593e6..7f7fda42f9 100644 --- a/builtin-verify-tag.c +++ b/builtin-verify-tag.c @@ -55,7 +55,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) close(gpg.in); ret = finish_command(&gpg); - unlink(path); + unlink_or_warn(path); return ret; } diff --git a/diff.c b/diff.c index 3ac71686eb..6802f5ac12 100644 --- a/diff.c +++ b/diff.c @@ -189,7 +189,7 @@ static void remove_tempfile(void) int i; for (i = 0; i < ARRAY_SIZE(diff_temp); i++) { if (diff_temp[i].name == diff_temp[i].tmp_path) - unlink(diff_temp[i].name); + unlink_or_warn(diff_temp[i].name); diff_temp[i].name = NULL; } } diff --git a/entry.c b/entry.c index 915514aa5c..cc841edf90 100644 --- a/entry.c +++ b/entry.c @@ -35,7 +35,7 @@ static void create_directories(const char *path, int path_len, */ if (mkdir(buf, 0777)) { if (errno == EEXIST && state->force && - !unlink(buf) && !mkdir(buf, 0777)) + !unlink_or_warn(buf) && !mkdir(buf, 0777)) continue; die("cannot create directory at %s", buf); } diff --git a/fast-import.c b/fast-import.c index 8d959af3b2..6a618e9163 100644 --- a/fast-import.c +++ b/fast-import.c @@ -931,7 +931,7 @@ static void unkeep_all_packs(void) struct packed_git *p = all_packs[k]; snprintf(name, sizeof(name), "%s/pack/pack-%s.keep", get_object_directory(), sha1_to_hex(p->sha1)); - unlink(name); + unlink_or_warn(name); } } @@ -981,7 +981,7 @@ static void end_packfile(void) } else { close(old_p->pack_fd); - unlink(old_p->pack_name); + unlink_or_warn(old_p->pack_name); } free(old_p); diff --git a/http-push.c b/http-push.c index 5138224cc3..29e8ebfebb 100644 --- a/http-push.c +++ b/http-push.c @@ -315,9 +315,9 @@ static void start_fetch_loose(struct transfer_request *request) "%s.temp", filename); snprintf(prevfile, sizeof(prevfile), "%s.prev", request->filename); - unlink(prevfile); + unlink_or_warn(prevfile); rename(request->tmpfile, prevfile); - unlink(request->tmpfile); + unlink_or_warn(request->tmpfile); if (request->local_fileno != -1) error("fd leakage in start: %d", request->local_fileno); @@ -372,7 +372,7 @@ static void start_fetch_loose(struct transfer_request *request) } while (prev_read > 0); close(prevlocal); } - unlink(prevfile); + unlink_or_warn(prevfile); /* Reset inflate/SHA1 if there was an error reading the previous temp file; also rewind to the beginning of the local file. */ @@ -784,7 +784,7 @@ static void finish_request(struct transfer_request *request) request->http_code != 416) { if (stat(request->tmpfile, &st) == 0) { if (st.st_size == 0) - unlink(request->tmpfile); + unlink_or_warn(request->tmpfile); } } else { if (request->http_code == 416) @@ -793,9 +793,9 @@ static void finish_request(struct transfer_request *request) git_inflate_end(&request->stream); git_SHA1_Final(request->real_sha1, &request->c); if (request->zret != Z_STREAM_END) { - unlink(request->tmpfile); + unlink_or_warn(request->tmpfile); } else if (hashcmp(request->obj->sha1, request->real_sha1)) { - unlink(request->tmpfile); + unlink_or_warn(request->tmpfile); } else { request->rename = move_temp_to_file( diff --git a/http-walker.c b/http-walker.c index c5a3ea3b31..7321ccc9fe 100644 --- a/http-walker.c +++ b/http-walker.c @@ -111,9 +111,9 @@ static void start_object_request(struct walker *walker, struct walker_data *data = walker->data; snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename); - unlink(prevfile); + unlink_or_warn(prevfile); rename(obj_req->tmpfile, prevfile); - unlink(obj_req->tmpfile); + unlink_or_warn(obj_req->tmpfile); if (obj_req->local != -1) error("fd leakage in start: %d", obj_req->local); @@ -177,7 +177,7 @@ static void start_object_request(struct walker *walker, } while (prev_read > 0); close(prevlocal); } - unlink(prevfile); + unlink_or_warn(prevfile); /* Reset inflate/SHA1 if there was an error reading the previous temp file; also rewind to the beginning of the local file. */ @@ -238,18 +238,18 @@ static void finish_object_request(struct object_request *obj_req) } else if (obj_req->curl_result != CURLE_OK) { if (stat(obj_req->tmpfile, &st) == 0) if (st.st_size == 0) - unlink(obj_req->tmpfile); + unlink_or_warn(obj_req->tmpfile); return; } git_inflate_end(&obj_req->stream); git_SHA1_Final(obj_req->real_sha1, &obj_req->c); if (obj_req->zret != Z_STREAM_END) { - unlink(obj_req->tmpfile); + unlink_or_warn(obj_req->tmpfile); return; } if (hashcmp(obj_req->sha1, obj_req->real_sha1)) { - unlink(obj_req->tmpfile); + unlink_or_warn(obj_req->tmpfile); return; } obj_req->rename = @@ -809,7 +809,7 @@ static void abort_object_request(struct object_request *obj_req) close(obj_req->local); obj_req->local = -1; } - unlink(obj_req->tmpfile); + unlink_or_warn(obj_req->tmpfile); if (obj_req->slot) { release_active_slot(obj_req->slot); obj_req->slot = NULL; diff --git a/ll-merge.c b/ll-merge.c index fa2ca5250c..81c02ad053 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -219,7 +219,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, close(fd); bad: for (i = 0; i < 3; i++) - unlink(temp[i]); + unlink_or_warn(temp[i]); strbuf_release(&cmd); return status; } diff --git a/lockfile.c b/lockfile.c index 3dbb2d1ff9..984eb320fc 100644 --- a/lockfile.c +++ b/lockfile.c @@ -16,7 +16,7 @@ static void remove_lock_file(void) lock_file_list->filename[0]) { if (lock_file_list->fd >= 0) close(lock_file_list->fd); - unlink(lock_file_list->filename); + unlink_or_warn(lock_file_list->filename); } lock_file_list = lock_file_list->next; } @@ -259,7 +259,7 @@ void rollback_lock_file(struct lock_file *lk) if (lk->filename[0]) { if (lk->fd >= 0) close(lk->fd); - unlink(lk->filename); + unlink_or_warn(lk->filename); } lk->filename[0] = 0; } diff --git a/pack-refs.c b/pack-refs.c index 2c76fb181f..301fc60eae 100644 --- a/pack-refs.c +++ b/pack-refs.c @@ -66,7 +66,7 @@ static void prune_ref(struct ref_to_prune *r) struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1); if (lock) { - unlink(git_path("%s", r->name)); + unlink_or_warn(git_path("%s", r->name)); unlock_ref(lock); } } diff --git a/refs.c b/refs.c index e65a3b4c4e..2b1f0f0e6e 100644 --- a/refs.c +++ b/refs.c @@ -1002,12 +1002,10 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt) } else { path = git_path("%s", refname); } - err = unlink(path); - if (err && errno != ENOENT) { + err = unlink_or_warn(path); + if (err && errno != ENOENT) ret = 1; - error("unlink(%s) failed: %s", - path, strerror(errno)); - } + if (!(delopt & REF_NODEREF)) lock->lk->filename[i] = '.'; } @@ -1017,10 +1015,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt) */ ret |= repack_without_ref(refname); - err = unlink(git_path("logs/%s", lock->ref_name)); - if (err && errno != ENOENT) - warning("unlink(%s) failed: %s", - git_path("logs/%s", lock->ref_name), strerror(errno)); + unlink_or_warn(git_path("logs/%s", lock->ref_name)); invalidate_cached_refs(); unlock_ref(lock); return ret; @@ -1381,7 +1376,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master, if (adjust_shared_perm(git_HEAD)) { error("Unable to fix permissions on %s", lockpath); error_unlink_return: - unlink(lockpath); + unlink_or_warn(lockpath); error_free_return: free(git_HEAD); return -1; diff --git a/rerere.c b/rerere.c index 713c6e16ac..87360dc23e 100644 --- a/rerere.c +++ b/rerere.c @@ -173,7 +173,7 @@ static int handle_file(const char *path, git_SHA1_Final(sha1, &ctx); if (hunk != RR_CONTEXT) { if (output) - unlink(output); + unlink_or_warn(output); return error("Could not parse conflict hunks in %s", path); } if (wrerror) diff --git a/server-info.c b/server-info.c index 66b0d9d878..d096dc7718 100644 --- a/server-info.c +++ b/server-info.c @@ -246,7 +246,7 @@ int update_server_info(int force) errs = errs | update_info_packs(force); /* remove leftover rev-cache file if there is any */ - unlink(git_path("info/rev-cache")); + unlink_or_warn(git_path("info/rev-cache")); return errs; } diff --git a/sha1_file.c b/sha1_file.c index f708cf4f67..dd474116a8 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2247,7 +2247,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename) goto out; ret = errno; } - unlink(tmpfile); + unlink_or_warn(tmpfile); if (ret) { if (ret != EEXIST) { return error("unable to write sha1 filename %s: %s\n", filename, strerror(ret)); diff --git a/transport.c b/transport.c index 3dfb03c06e..efecb65258 100644 --- a/transport.c +++ b/transport.c @@ -1069,7 +1069,7 @@ int transport_fetch_refs(struct transport *transport, const struct ref *refs) void transport_unlock_pack(struct transport *transport) { if (transport->pack_lockfile) { - unlink(transport->pack_lockfile); + unlink_or_warn(transport->pack_lockfile); free(transport->pack_lockfile); transport->pack_lockfile = NULL; } diff --git a/unpack-trees.c b/unpack-trees.c index e4eb8fa3af..aaacaf1015 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -61,7 +61,7 @@ static void unlink_entry(struct cache_entry *ce) { if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) return; - if (unlink(ce->name)) + if (unlink_or_warn(ce->name)) return; schedule_dir_for_removal(ce->name, ce_namelen(ce)); } From f6a5f1bb509a3af182fe568135398f1f2be15e5d Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 29 Apr 2009 23:24:52 +0200 Subject: [PATCH 03/53] print unlink(2) errno in copy_or_link_directory Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- builtin-clone.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin-clone.c b/builtin-clone.c index 880373f279..ba286e0160 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -228,7 +228,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) } if (unlink(dest->buf) && errno != ENOENT) - die("failed to unlink %s", dest->buf); + die("failed to unlink %s: %s", + dest->buf, strerror(errno)); if (!option_no_hardlinks) { if (!link(src->buf, dest->buf)) continue; From adfd55d51bff3bfcf17c3cd40d0689fb018c14cf Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 29 Apr 2009 23:40:50 +0200 Subject: [PATCH 04/53] Clarify kind of conflict in merge-one-file helper Not as verbose as the recursive merge driver, but better still. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- git-merge-one-file.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index e1eb963266..9c2c1b7202 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -113,6 +113,10 @@ case "${1:-.}${2:-.}${3:-.}" in src1=`git-unpack-file $2` git merge-file "$src1" "$orig" "$src2" ret=$? + msg= + if [ $ret -ne 0 ]; then + msg='content conflict' + fi # Create the working tree file, using "our tree" version from the # index, and then store the result of the merge. @@ -120,7 +124,10 @@ case "${1:-.}${2:-.}${3:-.}" in rm -f -- "$orig" "$src1" "$src2" if [ "$6" != "$7" ]; then - echo "ERROR: Permissions conflict: $5->$6,$7." + if [ -n "$msg" ]; then + msg="$msg, " + fi + msg="${msg}permissions conflict: $5->$6,$7" ret=1 fi if [ "$1" = '' ]; then @@ -128,7 +135,7 @@ case "${1:-.}${2:-.}${3:-.}" in fi if [ $ret -ne 0 ]; then - echo "ERROR: Merge conflict in $4" + echo "ERROR: $msg in $4" exit 1 fi exec git update-index -- "$4" From b74fce16fa51362d4a3875d46e488006c3ad5371 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 1 May 2009 16:56:47 -0400 Subject: [PATCH 05/53] allow OFS_DELTA objects during a push The fetching of OFS_DELTA objects has been negotiated between both peers since git version 1.4.4. However, this was missing from the push side where every OFS_DELTA objects were always converted to REF_DELTA objects causing an increase in transferred data. To fix this, both the client and the server processes have to be modified: the former to invoke pack-objects with --delta-base-offset when the server provides the ofs-delta capability, and the later to send that capability when OFS_DELTA objects are allowed as already indicated by the repack.usedeltabaseoffset config variable which is TRUE by default since git v1.6.0. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-receive-pack.c | 22 +++++++++++++++------- builtin-send-pack.c | 8 +++++++- send-pack.h | 1 + 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index a970b39505..4b9d9218d1 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -27,10 +27,9 @@ static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; static int unpack_limit = 100; static int report_status; +static int prefer_ofs_delta = 1; static const char *head_name; - -static char capabilities[] = " report-status delete-refs "; -static int capabilities_sent; +static char *capabilities_to_send; static enum deny_action parse_deny_action(const char *var, const char *value) { @@ -84,24 +83,29 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "repack.usedeltabaseoffset") == 0) { + prefer_ofs_delta = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value, cb); } static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { - if (capabilities_sent) + if (!capabilities_to_send) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); else packet_write(1, "%s %s%c%s\n", - sha1_to_hex(sha1), path, 0, capabilities); - capabilities_sent = 1; + sha1_to_hex(sha1), path, 0, capabilities_to_send); + capabilities_to_send = NULL; return 0; } static void write_head_info(void) { for_each_ref(show_ref, NULL); - if (!capabilities_sent) + if (capabilities_to_send) show_ref("capabilities^{}", null_sha1, 0, NULL); } @@ -687,6 +691,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; + capabilities_to_send = (prefer_ofs_delta) ? + " report-status delete-refs ofs-delta " : + " report-status delete-refs "; + add_alternate_refs(); write_head_info(); clear_extra_refs(); diff --git a/builtin-send-pack.c b/builtin-send-pack.c index d5a1c48d0e..473a3de40c 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -43,12 +43,16 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext "--stdout", NULL, NULL, + NULL, }; struct child_process po; int i; + i = 4; if (args->use_thin_pack) - argv[4] = "--thin"; + argv[i++] = "--thin"; + if (args->use_ofs_delta) + argv[i++] = "--delta-base-offset"; memset(&po, 0, sizeof(po)); po.argv = argv; po.in = -1; @@ -315,6 +319,8 @@ int send_pack(struct send_pack_args *args, ask_for_status_report = 1; if (server_supports("delete-refs")) allow_deleting_refs = 1; + if (server_supports("ofs-delta")) + args->use_ofs_delta = 1; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" diff --git a/send-pack.h b/send-pack.h index 83d76c7e35..1d7b1b3b4f 100644 --- a/send-pack.h +++ b/send-pack.h @@ -6,6 +6,7 @@ struct send_pack_args { send_mirror:1, force_update:1, use_thin_pack:1, + use_ofs_delta:1, dry_run:1; }; From 27d5438d9f4eb2cefc2a989c68f9b42b529b2a12 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 8 May 2009 05:06:15 -0400 Subject: [PATCH 06/53] fix GIT_TRACE segfault with shell-quoted aliases The alias argv comes from the split_cmdline function, which splits the config text for the alias into an array of strings. It returns the number of elements in the array, but does not actually put a NULL at the end of the array. Later, the trace function tries to print this argv and assumes that it has the trailing NULL. The split_cmdline function is probably at fault, since argv lists almost always end with a NULL signal. This patch adds one, in addition to the returned count; this doesn't hurt the other callers at all, since they were presumably using the count already (and will never look at the NULL). While we're there and using ALLOC_GROW, let's clean up the other manual grow. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- alias.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/alias.c b/alias.c index ccb1108c94..4f4d0141cd 100644 --- a/alias.c +++ b/alias.c @@ -38,10 +38,7 @@ int split_cmdline(char *cmdline, const char ***argv) while (cmdline[++src] && isspace(cmdline[src])) ; /* skip */ - if (count >= size) { - size += 16; - *argv = xrealloc(*argv, sizeof(char*) * size); - } + ALLOC_GROW(*argv, count+1, size); (*argv)[count++] = cmdline + dst; } else if (!quoted && (c == '\'' || c == '"')) { quoted = c; @@ -72,6 +69,9 @@ int split_cmdline(char *cmdline, const char ***argv) return error("unclosed quote"); } + ALLOC_GROW(*argv, count+1, size); + (*argv)[count] = NULL; + return count; } From ca6b91d29b7ea937b71f62bf00ba7750f3e594ce Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 9 May 2009 10:12:01 +0200 Subject: [PATCH 07/53] format-patch let -k override a config-specified format.numbered Let a command-line --keep-subject (-k) override a config-specified format.numbered (--numbered (-n)), rather than provoking the "-n and -k are mutually exclusive" failure. * t4021-format-patch-numbered.sh: Test for the above Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- builtin-log.c | 14 +++++++++++++- t/t4021-format-patch-numbered.sh | 7 +++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/builtin-log.c b/builtin-log.c index 5eaec5d24e..f10cfebdbb 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -755,6 +755,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int cover_letter = 0; int boundary_count = 0; int no_binary_diff = 0; + int numbered_cmdline_opt = 0; struct commit *origin = NULL, *head = NULL; const char *in_reply_to = NULL; struct patch_ids ids; @@ -786,8 +787,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!strcmp(argv[i], "--stdout")) use_stdout = 1; else if (!strcmp(argv[i], "-n") || - !strcmp(argv[i], "--numbered")) + !strcmp(argv[i], "--numbered")) { numbered = 1; + numbered_cmdline_opt = 1; + } else if (!strcmp(argv[i], "-N") || !strcmp(argv[i], "--no-numbered")) { numbered = 0; @@ -918,6 +921,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (start_number < 0) start_number = 1; + + /* + * If numbered is set solely due to format.numbered in config, + * and it would conflict with --keep-subject (-k) from the + * command line, reset "numbered". + */ + if (numbered && keep_subject && !numbered_cmdline_opt) + numbered = 0; + if (numbered && keep_subject) die ("-n and -k are mutually exclusive."); if (keep_subject && subject_prefix) diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh index 390af2389f..9b6e1be8b2 100755 --- a/t/t4021-format-patch-numbered.sh +++ b/t/t4021-format-patch-numbered.sh @@ -86,6 +86,13 @@ test_expect_success 'format.numbered && --no-numbered' ' ' +test_expect_success 'format.numbered && --keep-subject' ' + + git format-patch --keep-subject --stdout HEAD^ >patch4a && + grep "^Subject: Third" patch4a + +' + test_expect_success 'format.numbered = auto' ' git config format.numbered auto From 07d7bedda8d18ffbfe5960ce27b73a24c01cac1a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 28 Apr 2009 23:21:01 -0400 Subject: [PATCH 08/53] add: don't complain when adding empty project root We try to warn the user if one of their pathspecs caused no matches, as it may have been a typo. However, we disable the warning if the pathspec points to an existing file, since that means it is not a typo but simply an empty directory. Unfortunately, the file_exists() test was broken for one special case: the pathspec of the project root is just "". This patch detects this special case and acts as if the file exists (which it must, since it is the project root). The user-visible effect is that this: $ mkdir repo && cd repo && git init && git add . used to complain like: fatal: pathspec '' did not match any files but now is a silent no-op. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-add.c b/builtin-add.c index cb67d2c17e..ad889aac5b 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -61,7 +61,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p fill_pathspec_matches(pathspec, seen, specs); for (i = 0; i < specs; i++) { - if (!seen[i] && !file_exists(pathspec[i])) + if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i])) die("pathspec '%s' did not match any files", pathspec[i]); } From 658dd48c8572d0db49719cbef6605d384621d87c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 9 May 2009 14:57:30 -0700 Subject: [PATCH 09/53] Avoid unnecessary 'lstat()' calls in 'get_stat_data()' When we ask get_stat_data() to get the mode and size of an index entry, we can avoid the lstat() call if we have marked the index entry as being uptodate due to earlier lstat() calls. This avoids a lot of unnecessary lstat() calls in eg 'git checkout', where the last phase shows the differences to the working tree (requiring a diff), but earlier phases have already verified the index. On the kernel repo (with a fast machine and everything cached), this changes timings of a nul 'git checkout' from - Before (best of ten): 0.14user 0.05system 0:00.19elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+13237minor)pagefaults 0swaps - After 0.11user 0.03system 0:00.15elapsed 98%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+13235minor)pagefaults 0swaps so it can obviously be noticeable, although equally obviously it's not a show-stopper on this particular machine. The difference is likely larger on slower machines, or with operating systems that don't do as good a job of name caching. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff-lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff-lib.c b/diff-lib.c index ae96c64ca2..d230efc146 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -222,7 +222,7 @@ static int get_stat_data(struct cache_entry *ce, const unsigned char *sha1 = ce->sha1; unsigned int mode = ce->ce_mode; - if (!cached) { + if (!cached && !ce_uptodate(ce)) { int changed; struct stat st; changed = check_removed(ce, &st); From 53996fe5397ff37c5934bb5e9b23ef5985b3d152 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 9 May 2009 15:11:17 -0700 Subject: [PATCH 10/53] Teach 'git checkout' to preload the index contents This makes git checkout know to use the threaded index preloading if it is enabled in the config file. You need to have [core] preloadindex = true in your config file to see it, and for that feature to make sense your filesystem needs to be able to do concurrent 'lstat()' lookups, but when that is the case (especially NFS over a high-latency network), this can be a noticeable performance win. But with a low-latency network and at least older Linux NFS clients, this will clearly potentially cause a lot of lock contention. It may still speed up the uncached case, but the threading and locking overhead will result in the cached case likely slowing down. That was almost certainly fixed by Linux commit fc0f684c2 ("NFS: Remove BKL from NFS lookup code"), but that one got merged into 2.6.27-rc1, so older kernel versions than 2.6.27 will not scale very well. But regardless, it's the right thing to do. If your filesystem doesn't scale, don't enable index preloading. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-checkout.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index b5dd9c07b4..f1342abd02 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -228,7 +228,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); newfd = hold_locked_index(lock_file, 1); - if (read_cache() < 0) + if (read_cache_preload(pathspec) < 0) return error("corrupt index file"); if (source_tree) @@ -373,7 +373,7 @@ static int merge_working_tree(struct checkout_opts *opts, struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); int newfd = hold_locked_index(lock_file, 1); - if (read_cache() < 0) + if (read_cache_preload(NULL) < 0) return error("corrupt index file"); if (opts->force) { From bf74106a5b4577fd695d15a28ad51537ae7470d8 Mon Sep 17 00:00:00 2001 From: Dave Olszewski Date: Sat, 9 May 2009 14:49:59 -0700 Subject: [PATCH 11/53] merge-recursive: never leave index unmerged while recursing When you are trying to come up with the final result (i.e. depth=0), you want to record how the conflict arose by registering the state of the common ancestor, your branch and the other branch in the index, hence you want to do update_stages(). When you are merging with positive depth, that is because of a criss-cross merge situation. In such a case, you would need to record the tentative result, with conflict markers and all, as if the merge went cleanly, even if there are conflicts, in order to write it out as a tree object later to be used as a common ancestor tree. update_file() calls update_file_flags() with update_cache=1 to signal that the result needs to be written to the index at stage #0 (i.e. merged), and the code should not clobber the index further by calling update_stages(). The codepath to deal with rename/delete conflict in a recursive merge however left the index unmerged. Signed-off-by: Dave Olszewski Signed-off-by: Junio C Hamano --- merge-recursive.c | 11 +++-- t/t3031-merge-criscross.sh | 95 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 5 deletions(-) create mode 100755 t/t3031-merge-criscross.sh diff --git a/merge-recursive.c b/merge-recursive.c index 9bf5cc7175..2f1025c2aa 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -934,11 +934,12 @@ static int process_renames(struct merge_options *o, ren1_src, ren1_dst, branch1, branch2); update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); - update_stages(ren1_dst, NULL, - branch1 == o->branch1 ? - ren1->pair->two : NULL, - branch1 == o->branch1 ? - NULL : ren1->pair->two, 1); + if (!o->call_depth) + update_stages(ren1_dst, NULL, + branch1 == o->branch1 ? + ren1->pair->two : NULL, + branch1 == o->branch1 ? + NULL : ren1->pair->two, 1); } else if (!sha_eq(dst_other.sha1, null_sha1)) { const char *new_path; clean_merge = 0; diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh new file mode 100755 index 0000000000..7f41607c56 --- /dev/null +++ b/t/t3031-merge-criscross.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +test_description='merge-recursive backend test' + +. ./test-lib.sh + +# A <- create some files +# / \ +# B C <- cause rename/delete conflicts between B and C +# / \ +# |\ /| +# | D E | +# | \ / | +# | X | +# | / \ | +# | / \ | +# |/ \| +# F G <- merge E into B, D into C +# \ / +# \ / +# \ / +# H <- recursive merge crashes +# + +# initialize +test_expect_success 'setup repo with criss-cross history' ' + mkdir data && + + # create a bunch of files + n=1 && + while test $n -le 10 + do + echo $n > data/$n && + n=$(($n+1)) || + break + done && + + # check them in + git add data && + git commit -m A && + git branch A && + + # a file in one branch + git checkout -b B A && + git rm data/9 && + git add data && + git commit -m B && + + # with a branch off of it + git branch D && + + # put some commits on D + git checkout D && + echo testD > data/testD && + git add data && + git commit -m D && + + # back up to the top, create another branch and cause + # a rename conflict with the file we deleted earlier + git checkout -b C A && + git mv data/9 data/new-9 && + git add data && + git commit -m C && + + # with a branch off of it + git branch E && + + # put a commit on E + git checkout E && + echo testE > data/testE && + git add data && + git commit -m E && + + # now, merge E into B + git checkout B && + test_must_fail git merge E && + # force-resolve + git add data && + git commit -m F && + git branch F && + + # and merge D into C + git checkout C && + test_must_fail git merge D && + # force-resolve + git add data && + git commit -m G && + git branch G +' + +test_expect_success 'recursive merge between F and G, causes segfault' ' + git merge F +' + +test_done From 713697b34f1a1f716df97e331fd82782338c2083 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 10 May 2009 18:13:45 +0200 Subject: [PATCH 12/53] ls-tree manpage: use "unless" instead of "when ... is not" Delayed negation in a statement is harder to spot and keep in mind. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/git-ls-tree.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt index 4c7262f1cd..69c7dee1b9 100644 --- a/Documentation/git-ls-tree.txt +++ b/Documentation/git-ls-tree.txt @@ -76,7 +76,7 @@ Output Format ------------- SP SP TAB -When the `-z` option is not used, TAB, LF, and backslash characters +Unless the `-z` option is used, TAB, LF, and backslash characters in pathnames are represented as `\t`, `\n`, and `\\`, respectively. When the `-l` option is used, format changes to From c98a95eea825ffe66ed690064c80733656506c66 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 10 May 2009 18:14:49 +0200 Subject: [PATCH 13/53] ls-tree manpage: output of ls-tree is compatible with update-index Such format relationships are very useful things to remember for script writers. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/git-ls-tree.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt index 69c7dee1b9..3f87d7266b 100644 --- a/Documentation/git-ls-tree.txt +++ b/Documentation/git-ls-tree.txt @@ -78,6 +78,8 @@ Output Format Unless the `-z` option is used, TAB, LF, and backslash characters in pathnames are represented as `\t`, `\n`, and `\\`, respectively. +This output format is compatible with what '--index-info --stdin' of +'git update-index' expects. When the `-l` option is used, format changes to From 3426e34feddf97085615e619d39f8173ff3f9fb4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 11 May 2009 13:02:18 +0200 Subject: [PATCH 14/53] Add NO_CROSS_DIRECTORY_HARDLINKS support to the Makefile When the installed programs are tar'ed up and installed on a system where bin/ and libexec/git-core/ live on different file systems, we do not want libexec/git-core/git-* to be hardlinks to bin/git. Noticed by Cedric Staniewski. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 0675c43e73..2b8ce48c0c 100644 --- a/Makefile +++ b/Makefile @@ -159,6 +159,9 @@ all:: # Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call # your external grep (e.g., if your system lacks grep, if its grep is # broken, or spawning external process is slower than built-in grep git has). +# +# Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed +# programs as a tar, where bin/ and libexec/ might be on different file systems. GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @$(SHELL_PATH) ./GIT-VERSION-GEN @@ -1468,6 +1471,7 @@ endif bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \ { $(RM) "$$execdir/git-add$X" && \ + test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \ ln "$$bindir/git$X" "$$execdir/git-add$X" 2>/dev/null || \ cp "$$bindir/git$X" "$$execdir/git-add$X"; } && \ { for p in $(filter-out git-add$X,$(BUILT_INS)); do \ From 90f2e6526b3b800af4d10b900167a508b3774b98 Mon Sep 17 00:00:00 2001 From: Tony Kemp Date: Thu, 14 May 2009 16:47:41 +1000 Subject: [PATCH 15/53] Turn on USE_ST_TIMESPEC for OpenBSD Like Darwin, OpenBSD's stat struct uses st_ctimespec and st_mtimestruct rather than st_ctim and st_mtim. Signed-off-by: Tony Kemp Signed-off-by: Junio C Hamano --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 6e216436c3..26d180cc54 100644 --- a/Makefile +++ b/Makefile @@ -749,6 +749,7 @@ endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease + USE_ST_TIMESPEC = YesPlease NEEDS_LIBICONV = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib From ca156cfcc2dee5a1cee4f96023bb2f8c15a2c48c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 16 May 2009 02:24:44 -0700 Subject: [PATCH 16/53] api-parse-options.txt: use 'func' instead of 'funct' Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- Documentation/technical/api-parse-options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index e66ca9f70c..e30c602f47 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -198,7 +198,7 @@ The function must be defined in this form: The callback mechanism is as follows: -* Inside `funct`, the only interesting member of the structure +* Inside `func`, the only interesting member of the structure given by `opt` is the void pointer `opt->value`. `\*opt->value` will be the value that is saved into `var`, if you use `OPT_CALLBACK()`. From f044fe2de6f7bbace158853c075a4065f3722265 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 16 May 2009 02:24:45 -0700 Subject: [PATCH 17/53] tests: Add tests for missing format-patch long options Exercise format-patch's --signoff, --in-reply-to and --start-number long options. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- t/t4014-format-patch.sh | 11 +++++++++++ t/t4021-format-patch-numbered.sh | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 11061ddd5b..922a8941ed 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -505,4 +505,15 @@ test_expect_success 'format-patch from a subdirectory (3)' ' test -f "$basename" ' +test_expect_success 'format-patch --in-reply-to' ' + git format-patch -1 --stdout --in-reply-to "baz@foo.bar" > patch8 && + grep "^In-Reply-To: " patch8 && + grep "^References: " patch8 +' + +test_expect_success 'format-patch --signoff' ' + git format-patch -1 --signoff --stdout | + grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" +' + test_done diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh index 390af2389f..3c27f0dc19 100755 --- a/t/t4021-format-patch-numbered.sh +++ b/t/t4021-format-patch-numbered.sh @@ -108,4 +108,10 @@ test_expect_success 'format.numbered = auto && --no-numbered' ' ' +test_expect_success '--start-number && --numbered' ' + + git format-patch --start-number 3 --numbered --stdout HEAD~1 > patch8 && + grep "^Subject: \[PATCH 3/3\]" patch8 +' + test_done From c646217e1366b0397552fad8c32acb47fbe8977d Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 16 May 2009 12:21:50 +0200 Subject: [PATCH 18/53] pre-commit.sample: don't print incidental SHA1 Make the sample pre-commit hook script discard all git-rev-parse output, not just stderr. Otherwise, it would print an SHA1. Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- templates/hooks--pre-commit.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample index 0e49279c7f..0ba62076fb 100755 --- a/templates/hooks--pre-commit.sample +++ b/templates/hooks--pre-commit.sample @@ -7,7 +7,7 @@ # # To enable this hook, rename this file to "pre-commit". -if git-rev-parse --verify HEAD 2>/dev/null +if git-rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else From 77ebd56dc3d2efaeac87edc990cc1b99f331527c Mon Sep 17 00:00:00 2001 From: Daniel Cordero Date: Sat, 16 May 2009 10:54:45 -0700 Subject: [PATCH 19/53] builtin-checkout: Don't tell user that HEAD has moved before it has Previously, checkout would tell the user this message before moving HEAD, without regard to whether the upcoming move will result in success. If the move failed, this causes confusion. Show the message after the move, unless the move failed. Signed-off-by: Daniel Cordero Signed-off-by: Junio C Hamano --- builtin-checkout.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index dc4bfb5fc0..f2d7ef01b0 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -541,14 +541,6 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) parse_commit(new->commit); } - /* - * If we were on a detached HEAD, but we are now moving to - * a new commit, we want to mention the old commit once more - * to remind the user that it might be lost. - */ - if (!opts->quiet && !old.path && old.commit && new->commit != old.commit) - describe_detached_head("Previous HEAD position was", old.commit); - if (!old.commit && !opts->force) { if (!opts->quiet) { warning("You appear to be on a branch yet to be born."); @@ -561,6 +553,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) if (ret) return ret; + /* + * If we were on a detached HEAD, but have now moved to + * a new commit, we want to mention the old commit once more + * to remind the user that it might be lost. + */ + if (!opts->quiet && !old.path && old.commit && new->commit != old.commit) + describe_detached_head("Previous HEAD position was", old.commit); + update_refs_for_switch(opts, &old, new); ret = post_checkout_hook(old.commit, new->commit, 1); From f67182bf65b01290a9544e9599ea8255e657e567 Mon Sep 17 00:00:00 2001 From: Matt Graham Date: Fri, 15 May 2009 23:10:19 -0400 Subject: [PATCH 20/53] Splitting a hunk that adds a line at the top fails in "add -p" Splitting a hunk into two in add -p doesn't work for a diff that adds a new line at the top of the file with other add in the same hunk. Signed-off-by: Matthew Graham Signed-off-by: Junio C Hamano --- t/t3701-add-interactive.sh | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index dfc65601aa..9999fcd4b6 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -165,4 +165,36 @@ test_expect_success FILEMODE 'stage mode but not hunk' ' # end of tests disabled when filemode is not usable +# Write the patch file with a new line at the top and bottom +cat >patch <expected < diff && + test_cmp expected diff +' + test_done From 7a26e653929025e11e689bc7d98365c9a0107dc9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 May 2009 10:48:23 -0700 Subject: [PATCH 21/53] Revert "git-add--interactive: remove hunk coalescing" This reverts commit 0beee4c6dec15292415e3d56075c16a76a22af54 but with a bit of twist, as we have added "edit hunk manually" hack and we cannot rely on the original line numbers of the hunks that were manually edited. Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 96 +++++++++++++++++++++++++++++++++++++- t/t3701-add-interactive.sh | 2 +- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index f6e536ece3..a06172c69f 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -767,6 +767,96 @@ sub split_hunk { return @split; } +sub find_last_o_ctx { + my ($it) = @_; + my $text = $it->{TEXT}; + my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]); + my $i = @{$text}; + my $last_o_ctx = $o_ofs + $o_cnt; + while (0 < --$i) { + my $line = $text->[$i]; + if ($line =~ /^ /) { + $last_o_ctx--; + next; + } + last; + } + return $last_o_ctx; +} + +sub merge_hunk { + my ($prev, $this) = @_; + my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) = + parse_hunk_header($prev->{TEXT}[0]); + my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) = + parse_hunk_header($this->{TEXT}[0]); + + my (@line, $i, $ofs, $o_cnt, $n_cnt); + $ofs = $o0_ofs; + $o_cnt = $n_cnt = 0; + for ($i = 1; $i < @{$prev->{TEXT}}; $i++) { + my $line = $prev->{TEXT}[$i]; + if ($line =~ /^\+/) { + $n_cnt++; + push @line, $line; + next; + } + + last if ($o1_ofs <= $ofs); + + $o_cnt++; + $ofs++; + if ($line =~ /^ /) { + $n_cnt++; + } + push @line, $line; + } + + for ($i = 1; $i < @{$this->{TEXT}}; $i++) { + my $line = $this->{TEXT}[$i]; + if ($line =~ /^\+/) { + $n_cnt++; + push @line, $line; + next; + } + $ofs++; + $o_cnt++; + if ($line =~ /^ /) { + $n_cnt++; + } + push @line, $line; + } + my $head = ("@@ -$o0_ofs" . + (($o_cnt != 1) ? ",$o_cnt" : '') . + " +$n0_ofs" . + (($n_cnt != 1) ? ",$n_cnt" : '') . + " @@\n"); + @{$prev->{TEXT}} = ($head, @line); +} + +sub coalesce_overlapping_hunks { + my (@in) = @_; + my @out = (); + + my ($last_o_ctx, $last_was_dirty); + + for (grep { $_->{USE} } @in) { + my $text = $_->{TEXT}; + my ($o_ofs) = parse_hunk_header($text->[0]); + if (defined $last_o_ctx && + $o_ofs <= $last_o_ctx && + !$_->{DIRTY} && + !$last_was_dirty) { + merge_hunk($out[-1], $_); + } + else { + push @out, $_; + } + $last_o_ctx = find_last_o_ctx($out[-1]); + $last_was_dirty = $_->{DIRTY}; + } + return @out; +} sub color_diff { return map { @@ -878,7 +968,8 @@ sub edit_hunk_loop { my $newhunk = { TEXT => $text, TYPE => $hunk->[$ix]->{TYPE}, - USE => 1 + USE => 1, + DIRTY => 1, }; if (diff_applies($head, @{$hunk}[0..$ix-1], @@ -1210,6 +1301,8 @@ sub patch_update_file { } } + @hunk = coalesce_overlapping_hunks(@hunk); + my $n_lofs = 0; my @result = (); for (@hunk) { @@ -1224,6 +1317,7 @@ sub patch_update_file { open $fh, '| git apply --cached --recount'; for (@{$head->{TEXT}}, @result) { print $fh $_; + print STDERR $_; } if (!close $fh) { for (@{$head->{TEXT}}, @result) { diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 9999fcd4b6..5cdb83d28c 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -189,7 +189,7 @@ index b6f2c08..61b9053 100755 +lastline EOF # Test splitting the first patch, then adding both -test_expect_failure 'add first line works' ' +test_expect_success 'add first line works' ' git commit -am "clear local changes" && git apply patch && (echo s; echo y; echo y) | git add -p file && From 8763dbb1b24c260243f69130c734c13563a16db6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 May 2009 11:46:22 -0700 Subject: [PATCH 22/53] completion: fix PS1 display during a merge on detached HEAD If your merge stops in a conflict while on a detached HEAD, recent completion code fails to show anything. This was because various cases added to support the operation-in-progress markers (e.g. REBASE, MERGING) forgot that they need to set the variable "b" to something for the result they computed to be displayed at all. Probably not many people make trial merges on a detached HEAD (which is tremendously useful feature of git, by the way), and that may be why this was not noticed for a long time. Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1683e6d7b8..c2f8ea3444 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -99,10 +99,10 @@ __git_ps1 () elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" - elif [ -f "$g/MERGE_HEAD" ]; then - r="|MERGING" - b="$(git symbolic-ref HEAD 2>/dev/null)" else + if [ -f "$g/MERGE_HEAD" ]; then + r="|MERGING" + fi if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi From ff790b6a4bb7fa3bbccd5ea23cefd89da900aa2e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 May 2009 01:53:19 -0700 Subject: [PATCH 23/53] completion: simplify "current branch" in __git_ps1() As I very often work on a detached HEAD, I found it pretty confusing when __git_ps1() said 'some-name'. Did I create a branch with that name by mistake, or do I happen to be on a commit with that exact tag? This patch fixes the issue by enclosing non branch names in a pair of parentheses when used to substitute %s token in __git_ps1() argument. It also fixes a small bug where the branch part is left empty when .git/HEAD is unreadable for whatever reason. The output now says "(unknown)". Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c2f8ea3444..be591468db 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -106,13 +106,14 @@ __git_ps1 () if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi - if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then - if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then - if [ -r "$g/HEAD" ]; then - b="$(cut -c1-7 "$g/HEAD")..." - fi - fi - fi + + b="$(git symbolic-ref HEAD 2>/dev/null)" || { + b="$(git describe --exact-match HEAD 2>/dev/null)" || + b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || + b="unknown" + + b="($b)" + } fi local w From dd42c2f015102626562da05bb290f47862ea06fb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 May 2009 01:56:21 -0700 Subject: [PATCH 24/53] completion: enhance "current branch" display Introduce GIT_PS1_DESCRIBE option you can set to "contains", "branch", or "describe" to tweak the way how a detached HEAD is described. The default behaviour is to describe only exact match with some tag (otherwise use the first 7 hexdigits) as before. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index be591468db..dd6cd250e3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -108,10 +108,21 @@ __git_ps1 () fi b="$(git symbolic-ref HEAD 2>/dev/null)" || { - b="$(git describe --exact-match HEAD 2>/dev/null)" || + + b="$( + case "${GIT_PS1_DESCRIBE_STYLE-}" in + (contains) + git describe --contains HEAD ;; + (branch) + git describe --contains --all HEAD ;; + (describe) + git describe HEAD ;; + (* | default) + git describe --exact-match HEAD ;; + esac 2>/dev/null)" || + b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || b="unknown" - b="($b)" } fi From e4b09dad9f65395fd4bb8ab165012a3a6698a75b Mon Sep 17 00:00:00 2001 From: Nanako Shiraishi Date: Sun, 17 May 2009 11:43:08 +0900 Subject: [PATCH 25/53] test: checkout shouldn't say that HEAD has moved if it didn't MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: しらいしななこ Signed-off-by: Junio C Hamano --- t/t7201-co.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/t/t7201-co.sh b/t/t7201-co.sh index bdb808af1a..ebfd34df36 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -534,4 +534,12 @@ test_expect_success 'failing checkout -b should not break working tree' ' ' +test_expect_success 'switch out of non-branch' ' + git reset --hard master && + git checkout master^0 && + echo modified >one && + test_must_fail git checkout renamer 2>error.log && + ! grep "^Previous HEAD" error.log +' + test_done From b867d324ceb7e5c4f14a04c6b55d69498812d24b Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 11 May 2009 20:17:38 -0500 Subject: [PATCH 26/53] Fix type-punning issues In these two places we are casting part of our unsigned char sha1 array into an unsigned int, which violates GCCs strict-aliasing rules (and probably other compilers). Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- decorate.c | 4 +++- object.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/decorate.c b/decorate.c index 82d9e221ea..e6fd8a7441 100644 --- a/decorate.c +++ b/decorate.c @@ -8,7 +8,9 @@ static unsigned int hash_obj(const struct object *obj, unsigned int n) { - unsigned int hash = *(unsigned int *)obj->sha1; + unsigned int hash; + + memcpy(&hash, obj->sha1, sizeof(unsigned int)); return hash % n; } diff --git a/object.c b/object.c index 7e6a92c88e..e1feef9c33 100644 --- a/object.c +++ b/object.c @@ -45,7 +45,8 @@ int type_from_string(const char *str) static unsigned int hash_obj(struct object *obj, unsigned int n) { - unsigned int hash = *(unsigned int *)obj->sha1; + unsigned int hash; + memcpy(&hash, obj->sha1, sizeof(unsigned int)); return hash % n; } From da4b3e8c28b1dc2b856d2555ac7bb47ab712598c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 14 May 2009 13:05:03 -0700 Subject: [PATCH 27/53] dir.c: clean up handling of 'path' parameter in read_directory_recursive() Right now we pass two different pathnames ('path' and 'base') down to read_directory_recursive(), and the only real reason for that is that we want to allow an empty 'base' parameter, but when we do so, we need the pathname to "opendir()" to be "." rather than the empty string. And rather than handle that confusion in the caller, we can just fix read_directory_recursive() to handle the case of an empty path itself, by just passing opendir() a "." ourselves if the path is empty. This would allow us to then drop one of the pathnames entirely from the calling convention, but rather than do that, we'll start separating them out as a "filesystem pathname" (the one we use for filesystem accesses) and a "git internal base name" (which is the name that we use for git internally). That will eventually allow us to do things like handle different encodings (eg the filesystem pathnames might be Latin1, while git itself would use UTF-8 for filename information). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dir.c b/dir.c index 6aae09a22e..0e6b752cd5 100644 --- a/dir.c +++ b/dir.c @@ -576,7 +576,7 @@ static int get_dtype(struct dirent *de, const char *path) */ static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify) { - DIR *fdir = opendir(path); + DIR *fdir = opendir(*path ? path : "."); int contents = 0; if (fdir) { From 076c32370d8a6ac2fb57b2a55c674942e106f8ab Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 16 May 2009 20:42:43 -0700 Subject: [PATCH 28/53] completion: add missing options to show-branch and show Add --oneline and --abbrev-commit to show and --sparse to show-branch. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index dd6cd250e3..a0c5794828 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1804,7 +1804,7 @@ _git_show () return ;; --*) - __gitcomp "--pretty= --format= + __gitcomp "--pretty= --format= --abbrev-commit --oneline $__git_diff_common_options " return @@ -1821,7 +1821,7 @@ _git_show_branch () __gitcomp " --all --remotes --topo-order --current --more= --list --independent --merge-base --no-name - --sha1-name --topics --reflog + --sha1-name --sparse --topics --reflog " return ;; From 5acb3e5012966cc11e54f50e0592b3639bade02c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 17 May 2009 03:47:02 -0700 Subject: [PATCH 29/53] show-branch: Fix die message in parse_reflog_param() Commit 76a44c5 (show-branch --reflog: show the reflog message at the top, 2007-01-19) introduced parse_reflog_param(). The die() call was incorrectly passed arg + 9, when it should have been passed arg. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-show-branch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 828e6f86de..c3afabbe91 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -576,7 +576,7 @@ static void parse_reflog_param(const char *arg, int *cnt, const char **base) if (*ep == ',') *base = ep + 1; else if (*ep) - die("unrecognized reflog param '%s'", arg + 9); + die("unrecognized reflog param '%s'", arg); else *base = NULL; if (*cnt <= 0) From e64c1b0053f2dc4fc5b434a9806b90318bac9592 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 18 May 2009 13:58:11 -0400 Subject: [PATCH 30/53] for-each-ref: fix segfault in copy_email You can trigger a segfault in git.git by doing: git for-each-ref --format='%(taggeremail)' refs/tags/v0.99 The v0.99 tag is special in that it contains no "tagger" header. The bug is obvious in copy_email, which carefully checks to make sure the result of a strchr is non-NULL, but only after already having used it to perform other work. The fix is to move the check up. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-for-each-ref.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 91e8f95fd2..d091e04af9 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -339,8 +339,11 @@ static const char *copy_name(const char *buf) static const char *copy_email(const char *buf) { const char *email = strchr(buf, '<'); - const char *eoemail = strchr(email, '>'); - if (!email || !eoemail) + const char *eoemail; + if (!email) + return ""; + eoemail = strchr(email, '>'); + if (!eoemail) return ""; return xmemdupz(email, eoemail + 1 - email); } From fd73ccf27956f24dc0db9acd4ff7d9dcd5e41bfb Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 15 May 2009 20:52:47 +0200 Subject: [PATCH 31/53] Cope better with a _lot_ of packs You might end up with a situation where you have tons of pack files, e.g. when using hg2git. In this situation, all kinds of operations may end up with a "too many files open" error. Let's recover gracefully from that. Signed-off-by: Johannes Schindelin Looks-right-to-me-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- sha1_file.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sha1_file.c b/sha1_file.c index 28bd9082fc..bd5edd8e65 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -720,6 +720,8 @@ static int open_packed_git_1(struct packed_git *p) return error("packfile %s index unavailable", p->pack_name); p->pack_fd = open(p->pack_name, O_RDONLY); + while (p->pack_fd < 0 && errno == EMFILE && unuse_one_window(p, -1)) + p->pack_fd = open(p->pack_name, O_RDONLY); if (p->pack_fd < 0 || fstat(p->pack_fd, &st)) return -1; @@ -937,6 +939,8 @@ static void prepare_packed_git_one(char *objdir, int local) sprintf(path, "%s/pack", objdir); len = strlen(path); dir = opendir(path); + while (!dir && errno == EMFILE && unuse_one_window(packed_git, -1)) + dir = opendir(path); if (!dir) { if (errno != ENOENT) error("unable to open object pack directory: %s: %s", @@ -2339,6 +2343,8 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, filename = sha1_file_name(sha1); fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename); + while (fd < 0 && errno == EMFILE && unuse_one_window(packed_git, -1)) + fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename); if (fd < 0) { if (errno == EACCES) return error("insufficient permission for adding an object to repository database %s\n", get_object_directory()); From 8dfb17e1fd7dec1d3a1978eb46743964c481cd08 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Mon, 18 May 2009 18:24:30 +0200 Subject: [PATCH 32/53] completion: use git rev-parse to detect bare repos Its check is more robust than a config check for core.bare Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a0c5794828..f44152c433 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -132,7 +132,7 @@ __git_ps1 () local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then - if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then c="BARE:" else b="GIT_DIR!" From e701fadb9e0e51a6811690d95a53bd1f5b6fad86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Wed, 20 May 2009 23:31:53 +0200 Subject: [PATCH 33/53] grep: fix word-regexp colouring As noticed by Dmitry Gryazin: When a pattern is found but it doesn't start and end at word boundaries, bol is forwarded to after the match and the pattern is searched again. When a pattern is finally found between word boundaries, the match offsets are off by the number of characters that have been skipped. This patch corrects the offsets to be relative to the value of bol as passed to match_one_pattern() by its caller. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/grep.c b/grep.c index 04c777a20c..a649f063cf 100644 --- a/grep.c +++ b/grep.c @@ -305,6 +305,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, { int hit = 0; int saved_ch = 0; + const char *start = bol; if ((p->token != GREP_PATTERN) && ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD))) @@ -365,6 +366,10 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, } if (p->token == GREP_PATTERN_HEAD && saved_ch) *eol = saved_ch; + if (hit) { + pmatch[0].rm_so += bol - start; + pmatch[0].rm_eo += bol - start; + } return hit; } From aae94ffbc138181777a942ef18daf52606df833d Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 23 May 2009 11:53:10 -0700 Subject: [PATCH 34/53] commit: -F overrides -t Commit dbd0f5c7 (Files given on the command line are relative to $cwd, 2008-08-06) introduced parse_options_fix_filename() as a quick fix for filename arguments used in the parse options API. git-commit was still broken. This means git commit -F log -t temp in a subdirectory would make git think the log message should be taken from temp instead of log. This is because parse_options_fix_filename() calls prefix_filename() which uses a single static char buffer to do its work. Making two calls with two char pointers causes the pointers to alias. To prevent aliasing, we duplicate the string returned by parse_options_fix_filename(). Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-commit.c | 4 ++++ t/t7500-commit.sh | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/builtin-commit.c b/builtin-commit.c index 81371b1d26..baaa75cf90 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -699,7 +699,11 @@ static int parse_and_validate_options(int argc, const char *argv[], argc = parse_options(argc, argv, builtin_commit_options, usage, 0); logfile = parse_options_fix_filename(prefix, logfile); + if (logfile) + logfile = xstrdup(logfile); template_file = parse_options_fix_filename(prefix, template_file); + if (template_file) + template_file = xstrdup(template_file); if (force_author && !strchr(force_author, '>')) force_author = find_author_by_nickname(force_author); diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index 5998baf27b..8eec0fa9bc 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -183,4 +183,14 @@ test_expect_success 'commit message from stdin' ' commit_msg_is "Log with foo word" ' +test_expect_success 'commit -F overrides -t' ' + ( + cd subdir && + echo "-F log" > f.log && + echo "-t template" > t.template && + git commit --allow-empty -F f.log -t t.template + ) && + commit_msg_is "-F log" +' + test_done From 4c8d4c14c6de59bc14d6118724ffee949e8654a7 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 23 May 2009 11:53:11 -0700 Subject: [PATCH 35/53] apply, fmt-merge-msg: use relative filenames Commit dbd0f5c7 (Files given on the command line are relative to $cwd, 2008-08-06) only fixed git-commit and git-tag. But, git-apply and git-fmt-merge-msg didn't get the update and exhibit the same behavior. Fix them and add tests for "apply --build-fake-ancestor" and "fmt-merge-msg -F". Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-apply.c | 4 ++++ builtin-fmt-merge-msg.c | 1 + t/t4131-apply-fake-ancestor.sh | 42 ++++++++++++++++++++++++++++++++++ t/t6200-fmt-merge-msg.sh | 32 ++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100755 t/t4131-apply-fake-ancestor.sh diff --git a/builtin-apply.c b/builtin-apply.c index c6feaf5ca8..472865b7f1 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -3316,6 +3316,10 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) argc = parse_options(argc, argv, builtin_apply_options, apply_usage, 0); + fake_ancestor = parse_options_fix_filename(prefix, fake_ancestor); + if (fake_ancestor) + fake_ancestor = xstrdup(fake_ancestor); + if (apply_with_reject) apply = apply_verbosely = 1; if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor)) diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index df18f4070f..c566c2df77 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -365,6 +365,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, options, fmt_merge_msg_usage, 0); if (argc > 0) usage_with_options(fmt_merge_msg_usage, options); + inpath = parse_options_fix_filename(prefix, inpath); if (inpath && strcmp(inpath, "-")) { in = fopen(inpath, "r"); diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh new file mode 100755 index 0000000000..94373ca9a0 --- /dev/null +++ b/t/t4131-apply-fake-ancestor.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Copyright (c) 2009 Stephen Boyd +# + +test_description='git apply --build-fake-ancestor handling.' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit 1 && + test_commit 2 && + mkdir sub && + test_commit 3 sub/3 && + test_commit 4 +' + +test_expect_success 'apply --build-fake-ancestor' ' + git checkout 2 && + echo "A" > 1.t && + git diff > 1.patch && + git reset --hard && + git checkout 1 && + git apply --build-fake-ancestor 1.ancestor 1.patch +' + +test_expect_success 'apply --build-fake-ancestor in a subdirectory' ' + git checkout 3 && + echo "C" > sub/3.t && + git diff > 3.patch && + git reset --hard && + git checkout 4 && + ( + cd sub && + git apply --build-fake-ancestor 3.ancestor ../3.patch && + test -f 3.ancestor + ) && + git apply --build-fake-ancestor 3.ancestor 3.patch && + test_cmp sub/3.ancestor 3.ancestor +' + +test_done diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 8f5a06f7dd..706d93769f 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -208,4 +208,36 @@ test_expect_success 'merge-msg test #5-2' ' test_cmp expected actual ' +test_expect_success 'merge-msg -F' ' + + git config --unset-all merge.log + git config --unset-all merge.summary + git config merge.summary yes && + + git checkout master && + setdate && + git fetch . left right && + + git fmt-merge-msg -F .git/FETCH_HEAD >actual && + test_cmp expected actual +' + +test_expect_success 'merge-msg -F in subdirectory' ' + + git config --unset-all merge.log + git config --unset-all merge.summary + git config merge.summary yes && + + git checkout master && + setdate && + git fetch . left right && + mkdir sub && + cp .git/FETCH_HEAD sub/FETCH_HEAD && + ( + cd sub && + git fmt-merge-msg -F FETCH_HEAD >../actual + ) && + test_cmp expected actual +' + test_done From dbb6a4ada6c6c1065b62313127ff032196e9d232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 23 May 2009 13:45:26 +0200 Subject: [PATCH 36/53] grep: fix word-regexp at the beginning of lines After bol is forwarded, it doesn't represent the beginning of the line any more. This means that the beginning-of-line marker (^) mustn't match, i.e. the regex flag REG_NOTBOL needs to be set. This bug was introduced by fb62eb7fab97cea880ea7fe4f341a4dfad14ab48 ("grep -w: forward to next possible position after rejected match"). Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 1 + t/t7002-grep.sh | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/grep.c b/grep.c index a649f063cf..cc6d5b04c1 100644 --- a/grep.c +++ b/grep.c @@ -360,6 +360,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, bol = pmatch[0].rm_so + bol + 1; while (word_char(bol[-1]) && bol < eol) bol++; + eflags |= REG_NOTBOL; if (bol < eol) goto again; } diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh index b81593780a..f275af8240 100755 --- a/t/t7002-grep.sh +++ b/t/t7002-grep.sh @@ -16,12 +16,13 @@ test_expect_success setup ' echo foo mmap bar_mmap echo foo_mmap bar mmap baz } >file && + echo ww w >w && echo x x xx x >x && echo y yy >y && echo zzz > z && mkdir t && echo test >t/t && - git add file x y z t/t && + git add file w x y z t/t && test_tick && git commit -m initial ' @@ -48,6 +49,12 @@ do diff expected actual ' + test_expect_success "grep -w $L (w)" ' + : >expected && + ! git grep -n -w -e "^w" >actual && + test_cmp expected actual + ' + test_expect_success "grep -w $L (x)" ' { echo ${HC}x:1:x x xx x From 6589ebf107214a9e6db31764e847301f1adebc81 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 24 May 2009 15:16:49 +0200 Subject: [PATCH 37/53] http-push.c::remove_locks(): fix use after free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed and reported by Serhat Şevki Dinçer. Signed-off-by: Alex Riesen Acked-by: Clemens Buchacher Signed-off-by: Junio C Hamano --- http-push.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http-push.c b/http-push.c index 6805288857..0696da0fec 100644 --- a/http-push.c +++ b/http-push.c @@ -1356,8 +1356,9 @@ static void remove_locks(void) fprintf(stderr, "Removing remote locks...\n"); while (lock) { + struct remote_lock *next = lock->next; unlock_remote(lock); - lock = lock->next; + lock = next; } } From 1cd12926cedb340d176db607e087495381032ce2 Mon Sep 17 00:00:00 2001 From: Charles Bailey Date: Mon, 25 May 2009 01:21:13 +0100 Subject: [PATCH 38/53] t6023: merge-file fails to output anything for a degenerate merge In the case that merge-file is passed three files with identical contents it wipes the contents of the output file instead of leaving it unchanged. Althought merge-file is porcelain and this will never happen in normal usage, it is still wrong. Signed-off-by: Charles Bailey Signed-off-by: Junio C Hamano --- t/t6023-merge-file.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh index f674c48cab..19556350b5 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6023-merge-file.sh @@ -54,6 +54,12 @@ deduxit me super semitas jusitiae, EOF printf "propter nomen suum." >> new4.txt +test_expect_failure 'merge with no changes' ' + cp orig.txt test.txt && + git merge-file test.txt orig.txt orig.txt && + test_cmp test.txt orig.txt +' + cp new1.txt test.txt test_expect_success "merge without conflict" \ "git merge-file test.txt orig.txt new2.txt" From 5719db91ce5915ee07c50f1afdc94fe34e91529f Mon Sep 17 00:00:00 2001 From: Charles Bailey Date: Mon, 25 May 2009 01:21:14 +0100 Subject: [PATCH 39/53] Change xdl_merge to generate output even for null merges xdl_merge used to have a check to ensure that there was at least some change in one or other side being merged but this suppressed output for the degenerate case when base, local and remote contents were all identical. Removing this check enables correct output in the degenerate case and xdl_free_script handles freeing NULL scripts so there is no need to have the check for these calls. Signed-off-by: Charles Bailey Signed-off-by: Junio C Hamano --- t/t6023-merge-file.sh | 2 +- xdiff/xmerge.c | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh index 19556350b5..796f2128da 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6023-merge-file.sh @@ -54,7 +54,7 @@ deduxit me super semitas jusitiae, EOF printf "propter nomen suum." >> new4.txt -test_expect_failure 'merge with no changes' ' +test_expect_success 'merge with no changes' ' cp orig.txt test.txt && git merge-file test.txt orig.txt orig.txt && test_cmp test.txt orig.txt diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c index 82b3573e7a..1ef1d358ce 100644 --- a/xdiff/xmerge.c +++ b/xdiff/xmerge.c @@ -470,23 +470,22 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1, return -1; } status = 0; - if (xscr1 || xscr2) { - if (!xscr1) { - result->ptr = xdl_malloc(mf2->size); - memcpy(result->ptr, mf2->ptr, mf2->size); - result->size = mf2->size; - } else if (!xscr2) { - result->ptr = xdl_malloc(mf1->size); - memcpy(result->ptr, mf1->ptr, mf1->size); - result->size = mf1->size; - } else { - status = xdl_do_merge(&xe1, xscr1, name1, - &xe2, xscr2, name2, - level, xpp, result); - } - xdl_free_script(xscr1); - xdl_free_script(xscr2); + if (!xscr1) { + result->ptr = xdl_malloc(mf2->size); + memcpy(result->ptr, mf2->ptr, mf2->size); + result->size = mf2->size; + } else if (!xscr2) { + result->ptr = xdl_malloc(mf1->size); + memcpy(result->ptr, mf1->ptr, mf1->size); + result->size = mf1->size; + } else { + status = xdl_do_merge(&xe1, xscr1, name1, + &xe2, xscr2, name2, + level, xpp, result); } + xdl_free_script(xscr1); + xdl_free_script(xscr2); + xdl_free_env(&xe1); xdl_free_env(&xe2); From f5d4c4d0f19084a2d48f55f2f42cac25696a34f5 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Mon, 25 May 2009 18:00:10 +0200 Subject: [PATCH 40/53] merge-options.txt: Clarify merge --squash With the --squash option, merge sets up the index just like for a real merge, but without the merge info (stages). Say so. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- Documentation/merge-options.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 637b53f898..adadf8e4bf 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -39,7 +39,8 @@ --squash:: Produce the working tree and index state as if a real - merge happened, but do not actually make a commit or + merge happened (except for the merge information), + but do not actually make a commit or move the `HEAD`, nor record `$GIT_DIR/MERGE_HEAD` to cause the next `git commit` command to create a merge commit. This allows you to create a single commit on From 32a90233d147bd697b338326bd1fabf9edc3f1fe Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 25 May 2009 14:07:55 +0200 Subject: [PATCH 41/53] t3701: ensure correctly set up repository after skipped tests There are two tests that are skipped if file modes are not obeyed by the file system. In this case, the subsequent test failed because the repository was in an unexpected state. This corrects it. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t3701-add-interactive.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 5cdb83d28c..fd2a55a5c2 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -165,6 +165,12 @@ test_expect_success FILEMODE 'stage mode but not hunk' ' # end of tests disabled when filemode is not usable +test_expect_success 'setup again' ' + git reset --hard && + test_chmod +x file && + echo content >>file +' + # Write the patch file with a new line at the top and bottom cat >patch < Date: Mon, 25 May 2009 06:37:15 -0400 Subject: [PATCH 42/53] lock_ref: inform callers of unavailable ref One of the ways that locking might fail is that there is a DF conflict between two refs (e.g., you want to lock "foo/bar" but "foo" already exists). In this case, we return an error, but there is no way for the caller to know the specific problem. This patch sets errno to ENOTDIR, which is the most sensible code. It's what we would see if the refs were stored purely in the filesystem (but these days we must check the namespace manually due to packed refs). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- refs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/refs.c b/refs.c index e65a3b4c4e..8679c17e0d 100644 --- a/refs.c +++ b/refs.c @@ -893,8 +893,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char * name is a proper prefix of our refname. */ if (missing && - !is_refname_available(ref, NULL, get_packed_refs(), 0)) + !is_refname_available(ref, NULL, get_packed_refs(), 0)) { + last_errno = ENOTDIR; goto error_return; + } lock->lk = xcalloc(1, sizeof(struct lock_file)); From fa250759794ab98e6edfbbf2f6aa2cb912e535eb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 25 May 2009 06:40:54 -0400 Subject: [PATCH 43/53] fetch: report ref storage DF errors more accurately When we fail to store a fetched ref, we recommend that the user try running "git prune" to remove up any old refs that have been deleted by the remote, which would clear up any DF conflicts. However, ref storage might fail for other reasons (e.g., permissions problems) in which case the advice is useless and misleading. This patch detects when there is an actual DF situation and only issues the advice when one is found. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-fetch.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 3c998ea740..1f7a3f1ce6 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -167,6 +167,9 @@ static struct ref *get_ref_map(struct transport *transport, return ref_map; } +#define STORE_REF_ERROR_OTHER 1 +#define STORE_REF_ERROR_DF_CONFLICT 2 + static int s_update_ref(const char *action, struct ref *ref, int check_old) @@ -181,9 +184,11 @@ static int s_update_ref(const char *action, lock = lock_any_ref_for_update(ref->name, check_old ? ref->old_sha1 : NULL, 0); if (!lock) - return 2; + return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT : + STORE_REF_ERROR_OTHER; if (write_ref_sha1(lock, ref->new_sha1, msg) < 0) - return 2; + return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT : + STORE_REF_ERROR_OTHER; return 0; } @@ -377,7 +382,7 @@ static int store_updated_refs(const char *url, const char *remote_name, } } fclose(fp); - if (rc & 2) + if (rc & STORE_REF_ERROR_DF_CONFLICT) error("some local refs could not be updated; try running\n" " 'git remote prune %s' to remove any old, conflicting " "branches", remote_name); From 0e5168fd18f3975d71d3a292b0e0df174bb884d5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 25 May 2009 06:33:15 -0400 Subject: [PATCH 44/53] fix cat-file usage message and documentation cat-file with an object on the command line requires an option to tell it what to output (type, size, pretty-print, etc). However, the square brackets in the usage imply that those options are not required. This patch switches them to parentheses to indicate "required but grouped-OR" (curly braces might also work, but this follows the convention used already by "git stash"). While we're at it, let's change the specifier in the usage to . That's what the documentation uses, and it does actually use the regular object lookup. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-cat-file.txt | 4 ++-- builtin-cat-file.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index b191276d7a..58c8d65772 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -9,8 +9,8 @@ git-cat-file - Provide content or type and size information for repository objec SYNOPSIS -------- [verse] -'git cat-file' [-t | -s | -e | -p | ] -'git cat-file' [--batch | --batch-check] < +'git cat-file' (-t | -s | -e | -p | ) +'git cat-file' (--batch | --batch-check) < DESCRIPTION ----------- diff --git a/builtin-cat-file.c b/builtin-cat-file.c index 8fad19daed..43ffe7ffae 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -201,8 +201,8 @@ static int batch_objects(int print_contents) } static const char * const cat_file_usage[] = { - "git cat-file [-t|-s|-e|-p|] ", - "git cat-file [--batch|--batch-check] < ", + "git cat-file (-t|-s|-e|-p|) ", + "git cat-file (--batch|--batch-check) < ", NULL }; From e57cb0158209ece040a4b873064504efd7d2ec0e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 May 2009 19:20:39 -0700 Subject: [PATCH 45/53] Prepare for 1.6.3.2 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.3.2.txt | 51 ++++++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.6.3.2.txt diff --git a/Documentation/RelNotes-1.6.3.2.txt b/Documentation/RelNotes-1.6.3.2.txt new file mode 100644 index 0000000000..a3fceebb11 --- /dev/null +++ b/Documentation/RelNotes-1.6.3.2.txt @@ -0,0 +1,51 @@ +GIT v1.6.3.2 Release Notes +========================== + +Fixes since v1.6.3.1 +-------------------- + + * A few codepaths picked up the first few bytes from an sha1[] by + casting the (char *) pointer to (int *); GCC 4.4 did not like this, + and aborted compilation. + + * http-push had a small use-after-free bug. + + * command completion code in bash did not reliably detect that we are + in a bare repository. + + * "git for-each-ref" had a segfaulting bug when dealing with a tag object + created by an ancient git. + + * Some unlink(2) failures went undiagnosed. + + * The "recursive" merge strategy misbehaved when faced rename/delete + conflicts while coming up with an intermediate merge base. + + * GIT_TRACE mechanism segfaulted when tracing a shell-quoted aliases. + + * "git add ." in an empty directory complained that pathspec "." did not + match anything, which may be technically correct, but not useful. We + silently make it a no-op now. + + * "git format-patch -k" still added patch numbers if format.numbered + configuration was set. + + * OpenBSD also uses st_ctimspec in "struct stat", instead of "st_ctim". + + * With NO_CROSS_DIRECTORY_HARDLINKS, "make install" can be told not to + create hardlinks between $(gitexecdir)/git-$builtin_commands and + $(bindir)/git. + + * "git push" was converting OFS_DELTA pack representation into less + efficient REF_DELTA representation unconditionally upon transfer, + making the transferred data unnecessarily larger. + +Many other general usability updates around help text, diagnostic messages +and documentation are included as well. + +--- +exec >/var/tmp/1 +O=v1.6.3.1-51-g2a1feb9 +echo O=$(git describe maint) +git shortlog --no-merges $O..maint + diff --git a/RelNotes b/RelNotes index 0f6a588f1d..a433be58b7 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.6.3.1.txt \ No newline at end of file +Documentation/RelNotes-1.6.3.2.txt \ No newline at end of file From 6a01554e6350123c78de805d820e90f1c56e5fdc Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Wed, 27 May 2009 22:13:43 +0200 Subject: [PATCH 46/53] fix segfault showing an empty remote In case of an empty list, the search for its tail caused a NULL-pointer dereference. Signed-off-by: Clemens Buchacher Reported-by: Erik Faye-Lund Acked-by: Jay Soffian Signed-off-by: Junio C Hamano --- builtin-remote.c | 8 ++++---- t/t5505-remote.sh | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 71abf68404..fda9a54a0c 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -299,11 +299,11 @@ static int get_push_ref_states(const struct ref *remote_refs, return 0; local_refs = get_local_heads(); - ref = push_map = copy_ref_list(remote_refs); - while (ref->next) - ref = ref->next; - push_tail = &ref->next; + push_map = copy_ref_list(remote_refs); + push_tail = &push_map; + while (*push_tail) + push_tail = &((*push_tail)->next); match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr, remote->push_refspec, MATCH_REFS_NONE); diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 5ec668d6d8..e70246b3fb 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -494,5 +494,15 @@ test_expect_success 'remote prune to cause a dangling symref' ' grep "dangling symref" err ' +test_expect_success 'show empty remote' ' + + test_create_repo empty && + git clone empty empty-clone && + ( + cd empty-clone && + git remote show origin + ) +' + test_done From 7b8988e113b44b9a2d806dde6161c133750c14e5 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Thu, 28 May 2009 11:40:15 +0200 Subject: [PATCH 47/53] Documentation: teach stash/pop workflow instead of stash/apply Recent discussion on the list showed some comments in favour of a stash/pop workflow: http://marc.info/?l=git&m=124234911423358&w=2 http://marc.info/?l=git&m=124235348327711&w=2 Change the stash documentation and examples to document pop in its own right (and apply in terms of pop), and use stash/pop in the examples. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- Documentation/git-stash.txt | 30 ++++++++++++++++-------------- Documentation/user-manual.txt | 4 ++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 051f94d26f..1cc24cc47e 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -75,14 +75,22 @@ show []:: it will accept any format known to 'git-diff' (e.g., `git stash show -p stash@\{1}` to view the second most recent stash in patch form). +pop []:: + + Remove a single stashed state from the stash list and apply it + on top of the current working tree state, i.e., do the inverse + operation of `git stash save`. The working directory must + match the index. ++ +Applying the state can fail with conflicts; in this case, it is not +removed from the stash list. You need to resolve the conflicts by hand +and call `git stash drop` manually afterwards. ++ +When no `` is given, `stash@\{0}` is assumed. See also `apply`. + apply [--index] []:: - Restore the changes recorded in the stash on top of the current - working tree state. When no `` is given, applies the latest - one. The working directory must match the index. -+ -This operation can fail with conflicts; you need to resolve them -by hand in the working tree. + Like `pop`, but do not remove the state from the stash list. + If the `--index` option is used, then tries to reinstate not only the working tree's changes, but also the index's ones. However, this can fail, when you @@ -112,12 +120,6 @@ drop []:: Remove a single stashed state from the stash list. When no `` is given, it removes the latest one. i.e. `stash@\{0}` -pop []:: - - Remove a single stashed state from the stash list and apply on top - of the current working tree state. When no `` is given, - `stash@\{0}` is assumed. See also `apply`. - create:: Create a stash (which is a regular commit object) and return its @@ -163,7 +165,7 @@ $ git pull file foobar not up to date, cannot merge. $ git stash $ git pull -$ git stash apply +$ git stash pop ---------------------------------------------------------------- Interrupted workflow:: @@ -192,7 +194,7 @@ You can use 'git-stash' to simplify the above, like this: $ git stash $ edit emergency fix $ git commit -a -m "Fix in a hurry" -$ git stash apply +$ git stash pop # ... continue hacking ... ---------------------------------------------------------------- diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index dbbeb7e7c7..0b88a51d0b 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1520,10 +1520,10 @@ $ git commit -a -m "blorpl: typofix" ------------------------------------------------ After that, you can go back to what you were working on with -`git stash apply`: +`git stash pop`: ------------------------------------------------ -$ git stash apply +$ git stash pop ------------------------------------------------ From 1f5b9cc40e80a8a7902236a26844be138887b1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 1 Jun 2009 23:53:05 +0200 Subject: [PATCH 48/53] grep: fix colouring of matches with zero length If a zero-length match is encountered, break out of loop and show the rest of the line uncoloured. Otherwise we'd be looping forever, trying to make progress by advancing the pointer by zero characters. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grep.c b/grep.c index cc6d5b04c1..7bf4a60ac7 100644 --- a/grep.c +++ b/grep.c @@ -500,6 +500,8 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, *eol = '\0'; while (next_match(opt, bol, eol, ctx, &match, eflags)) { + if (match.rm_so == match.rm_eo) + break; printf("%.*s%s%.*s%s", (int)match.rm_so, bol, opt->color_match, From 40bad52d7d559177e373127f50c7bb3a19d56d85 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Jun 2009 07:57:39 -0700 Subject: [PATCH 49/53] Update draft release notes for 1.6.3.2 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.3.2.txt | 46 +++++++++++++++++++----------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/Documentation/RelNotes-1.6.3.2.txt b/Documentation/RelNotes-1.6.3.2.txt index a3fceebb11..0b48d1a1df 100644 --- a/Documentation/RelNotes-1.6.3.2.txt +++ b/Documentation/RelNotes-1.6.3.2.txt @@ -8,44 +8,56 @@ Fixes since v1.6.3.1 casting the (char *) pointer to (int *); GCC 4.4 did not like this, and aborted compilation. - * http-push had a small use-after-free bug. - - * command completion code in bash did not reliably detect that we are - in a bare repository. - - * "git for-each-ref" had a segfaulting bug when dealing with a tag object - created by an ancient git. - * Some unlink(2) failures went undiagnosed. * The "recursive" merge strategy misbehaved when faced rename/delete conflicts while coming up with an intermediate merge base. + * The low-level merge algorithm did not handle a degenerate case of + merging a file with itself using itself as the common ancestor + gracefully. It should produce the file itself, but instead + produced an empty result. + * GIT_TRACE mechanism segfaulted when tracing a shell-quoted aliases. - * "git add ." in an empty directory complained that pathspec "." did not - match anything, which may be technically correct, but not useful. We - silently make it a no-op now. - - * "git format-patch -k" still added patch numbers if format.numbered - configuration was set. - * OpenBSD also uses st_ctimspec in "struct stat", instead of "st_ctim". * With NO_CROSS_DIRECTORY_HARDLINKS, "make install" can be told not to create hardlinks between $(gitexecdir)/git-$builtin_commands and $(bindir)/git. + * command completion code in bash did not reliably detect that we are + in a bare repository. + + * "git add ." in an empty directory complained that pathspec "." did not + match anything, which may be technically correct, but not useful. We + silently make it a no-op now. + + * "git add -p" (and "patch" action in "git add -i") was broken when + the first hunk that adds a line at the top was split into two and + both halves are marked to be used. + + * "git for-each-ref" had a segfaulting bug when dealing with a tag object + created by an ancient git. + + * "git format-patch -k" still added patch numbers if format.numbered + configuration was set. + + * "git grep --color ''" did not terminate. + + * http-push had a small use-after-free bug. + * "git push" was converting OFS_DELTA pack representation into less efficient REF_DELTA representation unconditionally upon transfer, making the transferred data unnecessarily larger. + * "git remote show origin" segfaulted when origin was still empty. + Many other general usability updates around help text, diagnostic messages and documentation are included as well. --- exec >/var/tmp/1 -O=v1.6.3.1-51-g2a1feb9 +O=v1.6.3.1-68-g456cb4c echo O=$(git describe maint) git shortlog --no-merges $O..maint - From 8dc3a47c3edcee923225000e93f8052c50e5a004 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Tue, 2 Jun 2009 23:34:27 +0200 Subject: [PATCH 50/53] add -i: do not dump patch during application Remove a debugging print that snuck in at 7a26e65 (Revert "git-add--interactive: remove hunk coalescing", 2009-05-16). Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 1 - 1 file changed, 1 deletion(-) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index a06172c69f..df9f231635 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -1317,7 +1317,6 @@ sub patch_update_file { open $fh, '| git apply --cached --recount'; for (@{$head->{TEXT}}, @result) { print $fh $_; - print STDERR $_; } if (!close $fh) { for (@{$head->{TEXT}}, @result) { From a9b2d42486ee0b461220bf3895114926d9ddf9be Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 3 Jun 2009 00:43:22 -0700 Subject: [PATCH 51/53] blame: correctly handle a path that used to be a directory When trying to see if the same path exists in the parent, we ran "diff-tree" with pathspec set to the path we are interested in with the parent, and expect either to have exactly one resulting filepair (either "changed from the parent", "created when there was none") or nothing (when there is no change from the parent). If the path used to be a directory, however, we will also see unbounded number of entries that talk about the files that used to exist underneath the directory in question. Correctly pick only the entry that describes the path we are interested in in such a case (namely, the creation of the path as a regular file). Noticed by Ben Willard. Signed-off-by: Junio C Hamano --- builtin-blame.c | 26 ++++++++++++++++++-------- t/t8003-blame.sh | 15 +++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index cf74a92614..0afdb16cb0 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -362,18 +362,28 @@ static struct origin *find_origin(struct scoreboard *sb, "", &diff_opts); diffcore_std(&diff_opts); - /* It is either one entry that says "modified", or "created", - * or nothing. - */ if (!diff_queued_diff.nr) { /* The path is the same as parent */ porigin = get_origin(sb, parent, origin->path); hashcpy(porigin->blob_sha1, origin->blob_sha1); - } - else if (diff_queued_diff.nr != 1) - die("internal error in blame::find_origin"); - else { - struct diff_filepair *p = diff_queued_diff.queue[0]; + } else { + /* + * Since origin->path is a pathspec, if the parent + * commit had it as a directory, we will see a whole + * bunch of deletion of files in the directory that we + * do not care about. + */ + int i; + struct diff_filepair *p = NULL; + for (i = 0; i < diff_queued_diff.nr; i++) { + const char *name; + p = diff_queued_diff.queue[i]; + name = p->one->path ? p->one->path : p->two->path; + if (!strcmp(name, origin->path)) + break; + } + if (!p) + die("internal error in blame::find_origin"); switch (p->status) { default: die("internal error in blame::find_origin (%c)", diff --git a/t/t8003-blame.sh b/t/t8003-blame.sh index 966bb0a61a..13c25f1d52 100755 --- a/t/t8003-blame.sh +++ b/t/t8003-blame.sh @@ -129,4 +129,19 @@ test_expect_success 'blame wholesale copy and more' ' ' +test_expect_success 'blame path that used to be a directory' ' + mkdir path && + echo A A A A A >path/file && + echo B B B B B >path/elif && + git add path && + test_tick && + git commit -m "path was a directory" && + rm -fr path && + echo A A A A A >path && + git add path && + test_tick && + git commit -m "path is a regular file" && + git blame HEAD^.. -- path +' + test_done From 84201eae771fbb1d774cf1356d89016053e56778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Wed, 3 Jun 2009 18:19:01 +0200 Subject: [PATCH 52/53] grep: fix empty word-regexp matches The command "git grep -w ''" dies as soon as it encounters an empty line, reporting (wrongly) that "regexp returned nonsense". The first hunk of this patch relaxes the sanity check that is responsible for that, allowing matches to start at the end. The second hunk complements it by making sure that empty matches are rejected if -w was specified, as they are not really words. GNU grep does the same: $ echo foo | grep -c '' 1 $ echo foo | grep -c -w '' 0 Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grep.c b/grep.c index 7bf4a60ac7..92a47c71e7 100644 --- a/grep.c +++ b/grep.c @@ -331,7 +331,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, if (hit && p->word_regexp) { if ((pmatch[0].rm_so < 0) || - (eol - bol) <= pmatch[0].rm_so || + (eol - bol) < pmatch[0].rm_so || (pmatch[0].rm_eo < 0) || (eol - bol) < pmatch[0].rm_eo) die("regexp returned nonsense"); @@ -350,6 +350,10 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, else hit = 0; + /* Words consist of at least one character. */ + if (pmatch->rm_so == pmatch->rm_eo) + hit = 0; + if (!hit && pmatch[0].rm_so + bol + 1 < eol) { /* There could be more than one match on the * line, and the first match might not be From 6c7f58d6f691c1091b90b0891e94c91e20fd6054 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 3 Jun 2009 22:42:15 -0700 Subject: [PATCH 53/53] GIT 1.6.3.2 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.3.2.txt | 12 +++++------- GIT-VERSION-GEN | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Documentation/RelNotes-1.6.3.2.txt b/Documentation/RelNotes-1.6.3.2.txt index 0b48d1a1df..b2f3f0293c 100644 --- a/Documentation/RelNotes-1.6.3.2.txt +++ b/Documentation/RelNotes-1.6.3.2.txt @@ -37,13 +37,17 @@ Fixes since v1.6.3.1 the first hunk that adds a line at the top was split into two and both halves are marked to be used. + * "git blame path" misbehaved at the commit where path became file + from a directory with some files in it. + * "git for-each-ref" had a segfaulting bug when dealing with a tag object created by an ancient git. * "git format-patch -k" still added patch numbers if format.numbered configuration was set. - * "git grep --color ''" did not terminate. + * "git grep --color ''" did not terminate. The command also had + subtle bugs with its -w option. * http-push had a small use-after-free bug. @@ -55,9 +59,3 @@ Fixes since v1.6.3.1 Many other general usability updates around help text, diagnostic messages and documentation are included as well. - ---- -exec >/var/tmp/1 -O=v1.6.3.1-68-g456cb4c -echo O=$(git describe maint) -git shortlog --no-merges $O..maint diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index d292e3a2d3..0673f0db9f 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.6.3.1 +DEF_VER=v1.6.3.2 LF=' '