diff options
48 files changed, 464 insertions, 134 deletions
diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes-1.6.0.4.txt new file mode 100644 index 0000000000..db1002e8c7 --- /dev/null +++ b/Documentation/RelNotes-1.6.0.4.txt @@ -0,0 +1,40 @@ +GIT v1.6.0.4 Release Notes +========================== + +Fixes since v1.6.0.3 +-------------------- + +* 'git add -p' said "No changes" when only binary files were changed. + +* 'git archive' did not work correctly in bare repositories. + +* 'git checkout -t -b newbranch' when you are on detached HEAD was broken. + +* when we refuse to detect renames because there are too many new or + deleted files, 'git diff' did not say how many there are. + +* 'git push --mirror' tried and failed to push the stash; there is no + point in sending it to begin with. + +* 'git pull $there $branch:$current_branch' did not work when you were on + a branch yet to be born. + +* when giving up resolving a conflicted merge, 'git reset --hard' failed + to remove new paths from the working tree. + +* 'git send-email' had a small fd leak while scanning directory. + +* 'git status' incorrectly reported a submodule directory as an untracked + directory. + +* 'git svn' used deprecated 'git-foo' form of subcommand invocaition. + +* Plugged small memleaks here and there. + +* Also contains many documentation updates. + +-- +exec >/var/tmp/1 +O=v1.6.0.3-34-gf6276b7 +echo O=$(git describe maint) +git shortlog --no-merges $O..maint diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 841bead9db..c59edfa6a9 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -456,3 +456,30 @@ This should help you to submit patches inline using KMail. 5) Back in the compose window: add whatever other text you wish to the message, complete the addressing and subject fields, and press send. + + +Gmail +----- + +Submitting properly formatted patches via Gmail is simple now that +IMAP support is available. First, edit your ~/.gitconfig to specify your +account settings: + +[imap] + folder = "[Gmail]/Drafts" + host = imaps://imap.gmail.com + user = user@gmail.com + pass = p4ssw0rd + port = 993 + sslverify = false + +Next, ensure that your Gmail settings are correct. In "Settings" the +"Use Unicode (UTF-8) encoding for outgoing messages" should be checked. + +Once your commits are ready to send to the mailing list, run the following +command to send the patch emails to your Gmail Drafts folder. + + $ git format-patch -M --stdout origin/master | git imap-send + +Go to your Gmail account, open the Drafts folder, find the patch email, fill +in the To: and CC: fields and send away! diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index 40d43b78ee..2da867d2f8 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -40,6 +40,26 @@ endif::doctype-manpage[] </literallayout> {title#}</example> endif::docbook-xsl-172[] + +ifdef::docbook-xsl-172[] +ifdef::doctype-manpage[] +# The following two small workarounds insert a simple paragraph after screen +[listingblock] +<example><title>{title}</title> +<screen> +| +</screen><simpara></simpara> +{title#}</example> + +[verseblock] +<formalpara{id? id="{id}"}><title>{title}</title><para> +{title%}<literallayout{id? id="{id}"}> +{title#}<literallayout> +| +</literallayout><simpara></simpara> +{title#}</para></formalpara> +endif::doctype-manpage[] +endif::docbook-xsl-172[] endif::backend-docbook[] ifdef::doctype-manpage[] diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt index 400cbb3b1c..aafd3a3941 100644 --- a/Documentation/diff-format.txt +++ b/Documentation/diff-format.txt @@ -46,6 +46,22 @@ That is, from the left to the right: . path for "dst"; only exists for C or R. . an LF or a NUL when '-z' option is used, to terminate the record. +Possible status letters are: + +- A: addition of a file +- C: copy of a file into a new one +- D: deletion of a file +- M: modification of the contents or mode of a file +- R: renaming of a file +- T: change in the type of the file +- U: file is unmerged (you must complete the merge before it can +be committed) +- X: "unknown" change type (most probably a bug, please report it) + +Status letters C and M are always followed by a score (denoting the +percentage of similarity between the source and target of the move or +copy), and are the only ones to be so. + <sha1> is shown as all 0's if a file is new on the filesystem and it is out of sync with the index. diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index adb4ea7b1b..7426109f62 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -46,7 +46,8 @@ applies to that command line and you do not get "everything since the beginning of the time". If you want to format everything since project inception to one commit, say "git format-patch \--root <commit>" to make it clear that it is the -latter case. +latter case. If you want to format a single commit, you can do +this with "git format-patch -1 <commit>". By default, each output file is numbered sequentially from 1, and uses the first line of the commit message (massaged for pathname safety) as diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 53da9b4f6b..37fff208ff 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -163,8 +163,8 @@ few exceptions. Even though... `ident` ^^^^^^^ -When the attribute `ident` is set to a path, git replaces -`$Id$` in the blob object with `$Id:`, followed by +When the attribute `ident` is set for a path, git replaces +`$Id$` in the blob object with `$Id:`, followed by the 40-character hexadecimal blob object name, followed by a dollar sign `$` upon checkout. Any byte sequence that begins with `$Id:` and ends with `$` in the worktree file is replaced @@ -213,6 +213,9 @@ with `crlf`, and then `ident` and fed to `filter`. Generating diff text ~~~~~~~~~~~~~~~~~~~~ +`diff` +^^^^^^ + The attribute `diff` affects if 'git-diff' generates textual patch for the path or just says `Binary files differ`. It also can affect what line is shown on the hunk header `@@ -k,l +n,m @@` @@ -323,6 +326,9 @@ patterns are available: Performing a three-way merge ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`merge` +^^^^^^^ + The attribute `merge` affects how three versions of a file is merged when a file-level merge is necessary during `git merge`, and other programs such as `git revert` and `git cherry-pick`. diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 735cf07b20..5df35ce519 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -174,6 +174,10 @@ endif::git-rev-list[] Limit the commits output to ones with log message that matches the specified pattern (regular expression). +--all-match:: + Limit the commits output to ones that match all given --grep, + --author and --committer instead of ones that match at least one. + -i:: --regexp-ignore-case:: @@ -1355,7 +1355,7 @@ install: all $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' - $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install ifndef NO_TCLTK @@ -1 +1 @@ -Documentation/RelNotes-1.6.0.3.txt
\ No newline at end of file +Documentation/RelNotes-1.6.0.4.txt
\ No newline at end of file @@ -338,5 +338,7 @@ int write_archive(int argc, const char **argv, const char *prefix, parse_treeish_arg(argv, &args, prefix); parse_pathspec_arg(argv + 1, &args); + git_config(git_default_config, NULL); + return ar->write_archive(&args); } @@ -129,7 +129,9 @@ void create_branch(const char *head, die("Cannot setup tracking information; starting point is not a branch."); break; case 1: - /* Unique completion -- good */ + /* Unique completion -- good, only if it is a real ref */ + if (track == BRANCH_TRACK_EXPLICIT && !strcmp(real_ref, "HEAD")) + die("Cannot setup tracking information; starting point is not a branch."); break; default: die("Ambiguous object name: '%s'.", start_name); diff --git a/builtin-archive.c b/builtin-archive.c index 432ce2acc6..5ceec433fd 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -111,8 +111,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix) { const char *remote = NULL; - git_config(git_default_config, NULL); - remote = extract_remote_arg(&argc, argv); if (remote) return run_remote_archiver(remote, argc, argv); diff --git a/builtin-commit.c b/builtin-commit.c index e2a7e48b1c..b563a0d67c 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -320,7 +320,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) die("unable to write new_index file"); fd = hold_lock_file_for_update(&false_lock, - git_path("next-index-%d", getpid()), 1); + git_path("next-index-%d", getpid()), + LOCK_DIE_ON_ERROR); create_base_index(); add_remove_files(&partial); diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 85509f5ee5..21ce3e0163 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -813,7 +813,8 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, ) die("shallow file was changed during fetch"); - fd = hold_lock_file_for_update(&lock, shallow, 1); + fd = hold_lock_file_for_update(&lock, shallow, + LOCK_DIE_ON_ERROR); if (!write_shallow_commits(fd, 0)) { unlink(shallow); rollback_lock_file(&lock); diff --git a/builtin-fetch.c b/builtin-fetch.c index ee93d3a93d..57c161d35b 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -534,6 +534,19 @@ static void find_non_local_tags(struct transport *transport, string_list_clear(&new_refs, 0); } +static void check_not_current_branch(struct ref *ref_map) +{ + struct branch *current_branch = branch_get(NULL); + + if (is_bare_repository() || !current_branch) + return; + + for (; ref_map; ref_map = ref_map->next) + if (ref_map->peer_ref && !strcmp(current_branch->refname, + ref_map->peer_ref->name)) + die("Refusing to fetch into current branch"); +} + static int do_fetch(struct transport *transport, struct refspec *refs, int ref_count) { @@ -558,6 +571,8 @@ static int do_fetch(struct transport *transport, } ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags); + if (!update_head_ok) + check_not_current_branch(ref_map); for (rm = ref_map; rm; rm = rm->next) { if (rm->peer_ref) diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 068f424696..b48327db95 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -91,39 +91,10 @@ static void show_other_files(struct dir_struct *dir) { int i; - - /* - * Skip matching and unmerged entries for the paths, - * since we want just "others". - * - * (Matching entries are normally pruned during - * the directory tree walk, but will show up for - * gitlinks because we don't necessarily have - * dir->show_other_directories set to suppress - * them). - */ for (i = 0; i < dir->nr; i++) { struct dir_entry *ent = dir->entries[i]; - int len, pos; - struct cache_entry *ce; - - /* - * Remove the '/' at the end that directory - * walking adds for directory entries. - */ - len = ent->len; - if (len && ent->name[len-1] == '/') - len--; - pos = cache_name_pos(ent->name, len); - if (0 <= pos) - continue; /* exact match */ - pos = -pos - 1; - if (pos < active_nr) { - ce = active_cache[pos]; - if (ce_namelen(ce) == len && - !memcmp(ce->name, ent->name, len)) - continue; /* Yup, this one exists unmerged */ - } + if (!cache_name_is_other(ent->name, ent->len)) + continue; show_dir_entry(tag_other, ent); } } diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 4004e73e40..b0dddbee4f 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1377,7 +1377,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, memset(array, 0, array_size); for (;;) { - struct object_entry *entry = *list++; + struct object_entry *entry; struct unpacked *n = array + idx; int j, max_depth, best_base = -1; @@ -1386,6 +1386,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, progress_unlock(); break; } + entry = *list++; (*list_size)--; if (!entry->preferred_base) { (*processed)++; diff --git a/builtin-revert.c b/builtin-revert.c index 27881e9493..786a956f26 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -269,7 +269,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) int i; char *oneline, *reencoded_message = NULL; const char *message, *encoding; - const char *defmsg = xstrdup(git_path("MERGE_MSG")); + char *defmsg = xstrdup(git_path("MERGE_MSG")); git_config(git_default_config, NULL); me = action == REVERT ? "revert" : "cherry-pick"; @@ -338,7 +338,8 @@ static int revert_or_cherry_pick(int argc, const char **argv) * reverse of it if we are revert. */ - msg_fd = hold_lock_file_for_update(&msg_file, defmsg, 1); + msg_fd = hold_lock_file_for_update(&msg_file, defmsg, + LOCK_DIE_ON_ERROR); encoding = get_encoding(message); if (!encoding) @@ -426,6 +427,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) return execv_git_cmd(args); } free(reencoded_message); + free(defmsg); return 0; } diff --git a/builtin-send-pack.c b/builtin-send-pack.c index ea1ad6e35f..ed94f9a460 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -132,7 +132,13 @@ static struct ref *remote_refs, **remote_tail; static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct ref *ref; - int len = strlen(refname) + 1; + int len; + + /* we already know it starts with refs/ to get here */ + if (check_ref_format(refname + 5)) + return 0; + + len = strlen(refname) + 1; ref = xcalloc(1, sizeof(*ref) + len); hashcpy(ref->new_sha1, sha1); memcpy(ref->name, refname, len); @@ -186,7 +186,8 @@ int create_bundle(struct bundle_header *header, const char *path, if (bundle_to_stdout) bundle_fd = 1; else - bundle_fd = hold_lock_file_for_update(&lock, path, 1); + bundle_fd = hold_lock_file_for_update(&lock, path, + LOCK_DIE_ON_ERROR); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); @@ -270,6 +270,7 @@ static inline void remove_name_hash(struct cache_entry *ce) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase)) +#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen)) #endif enum object_type { @@ -382,6 +383,7 @@ extern int add_to_index(struct index_state *, const char *path, struct stat *, i extern int add_file_to_index(struct index_state *, const char *path, int flags); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); +extern int index_name_is_other(const struct index_state *, const char *, int); /* do stat comparison even if CE_VALID is true */ #define CE_MATCH_IGNORE_VALID 01 @@ -411,6 +413,8 @@ struct lock_file { char on_list; char filename[PATH_MAX]; }; +#define LOCK_DIE_ON_ERROR 1 +#define LOCK_NODEREF 2 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); extern int hold_lock_file_for_append(struct lock_file *, const char *path, int); extern int commit_lock_file(struct lock_file *); diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 751e273e1a..39a1ce5a39 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1410,6 +1410,8 @@ _git_shortlog () _git_show () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) diff --git a/diffcore-rename.c b/diffcore-rename.c index 1b2ebb4001..168a95b541 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -493,7 +493,7 @@ void diffcore_rename(struct diff_options *options) if ((num_create > rename_limit && num_src > rename_limit) || (num_create * num_src > rename_limit * rename_limit)) { if (options->warn_on_too_large_rename) - warning("too many files, skipping inexact rename detection"); + warning("too many files (created: %d deleted: %d), skipping inexact rename detection", num_create, num_src); goto cleanup; } diff --git a/diffcore.h b/diffcore.h index 8ae35785fd..1ebfdae8b8 100644 --- a/diffcore.h +++ b/diffcore.h @@ -58,7 +58,7 @@ struct diff_filepair { struct diff_filespec *one; struct diff_filespec *two; unsigned short int score; - char status; /* M C R N D U (see Documentation/diff-format.txt) */ + char status; /* M C R A D U etc. (see Documentation/diff-format.txt or DIFF_STATUS_* in diff.h) */ unsigned broken_pair : 1; unsigned renamed_pair : 1; unsigned is_unmerged : 1; diff --git a/git-add--interactive.perl b/git-add--interactive.perl index da768ee7ac..b0223c3419 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -811,11 +811,16 @@ EOF } sub patch_update_cmd { - my @mods = grep { !($_->{BINARY}) } list_modified('file-only'); + my @all_mods = list_modified('file-only'); + my @mods = grep { !($_->{BINARY}) } @all_mods; my @them; if (!@mods) { - print STDERR "No changes.\n"; + if (@all_mods) { + print STDERR "Only binary files changed.\n"; + } else { + print STDERR "No changes.\n"; + } return 0; } if ($patch_mode) { diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl index 78f344f08f..a479b2f285 100644 --- a/git-gui/lib/spellcheck.tcl +++ b/git-gui/lib/spellcheck.tcl @@ -80,7 +80,7 @@ method _connect {pipe_fd} { error_popup [strcat [mc "Unrecognized spell checker"] ":\n\n$s_version"] return } - set s_version [string range $s_version 5 end] + set s_version [string range [string trim $s_version] 5 end] regexp \ {International Ispell Version .* \(but really (Aspell .*?)\)$} \ $s_version _junk s_version diff --git a/git-pull.sh b/git-pull.sh index 75c36100a2..664fe34419 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -124,7 +124,7 @@ orig_head=$(git rev-parse --verify HEAD 2>/dev/null) git fetch --update-head-ok "$@" || exit 1 curr_head=$(git rev-parse --verify HEAD 2>/dev/null) -if test "$curr_head" != "$orig_head" +if test -n "$orig_head" && test "$curr_head" != "$orig_head" then # The fetch involved updating the current branch. @@ -172,7 +172,7 @@ esac if test -z "$orig_head" then - git update-ref -m "initial pull" HEAD $merge_head "" && + git update-ref -m "initial pull" HEAD $merge_head "$curr_head" && git read-tree --reset -u HEAD || exit 1 exit fi diff --git a/git-send-email.perl b/git-send-email.perl index d2fd899076..18529c76e6 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -407,10 +407,9 @@ for my $f (@ARGV) { push @files, grep { -f $_ } map { +$f . "/" . $_ } sort readdir(DH); - + closedir(DH); } elsif (-f $f or -p $f) { push @files, $f; - } else { print STDERR "Skipping $f - not found.\n"; } diff --git a/git-svn.perl b/git-svn.perl index 33e1b503c4..56238dad08 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1126,7 +1126,7 @@ sub read_repo_config { my $v = $opts->{$o}; my ($key) = ($o =~ /^([a-zA-Z\-]+)/); $key =~ s/-//g; - my $arg = 'git-config'; + my $arg = 'git config'; $arg .= ' --int' if ($o =~ /[:=]i$/); $arg .= ' --bool' if ($o !~ /[:=][sfi]$/); if (ref $v eq 'ARRAY') { @@ -2202,7 +2202,7 @@ sub do_git_commit { } die "Tree is not a valid sha1: $tree\n" if $tree !~ /^$::sha1$/o; - my @exec = ('git-commit-tree', $tree); + my @exec = ('git', 'commit-tree', $tree); foreach ($self->get_commit_parents($log_entry)) { push @exec, '-p', $_; } diff --git a/lockfile.c b/lockfile.c index 4023797b00..6d75608693 100644 --- a/lockfile.c +++ b/lockfile.c @@ -121,15 +121,17 @@ static char *resolve_symlink(char *p, size_t s) } -static int lock_file(struct lock_file *lk, const char *path) +static int lock_file(struct lock_file *lk, const char *path, int flags) { - if (strlen(path) >= sizeof(lk->filename)) return -1; + if (strlen(path) >= sizeof(lk->filename)) + return -1; strcpy(lk->filename, path); /* * subtract 5 from size to make sure there's room for adding * ".lock" for the lock file name */ - resolve_symlink(lk->filename, sizeof(lk->filename)-5); + if (!(flags & LOCK_NODEREF)) + resolve_symlink(lk->filename, sizeof(lk->filename)-5); strcat(lk->filename, ".lock"); lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= lk->fd) { @@ -155,21 +157,21 @@ static int lock_file(struct lock_file *lk, const char *path) return lk->fd; } -int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error) +int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags) { - int fd = lock_file(lk, path); - if (fd < 0 && die_on_error) + int fd = lock_file(lk, path, flags); + if (fd < 0 && (flags & LOCK_DIE_ON_ERROR)) die("unable to create '%s.lock': %s", path, strerror(errno)); return fd; } -int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on_error) +int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags) { int fd, orig_fd; - fd = lock_file(lk, path); + fd = lock_file(lk, path, flags); if (fd < 0) { - if (die_on_error) + if (flags & LOCK_DIE_ON_ERROR) die("unable to create '%s.lock': %s", path, strerror(errno)); return fd; } @@ -177,13 +179,13 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on orig_fd = open(path, O_RDONLY); if (orig_fd < 0) { if (errno != ENOENT) { - if (die_on_error) + if (flags & LOCK_DIE_ON_ERROR) die("cannot open '%s' for copying", path); close(fd); return error("cannot open '%s' for copying", path); } } else if (copy_fd(orig_fd, fd)) { - if (die_on_error) + if (flags & LOCK_DIE_ON_ERROR) exit(128); close(fd); return -1; @@ -215,7 +217,10 @@ int commit_lock_file(struct lock_file *lk) int hold_locked_index(struct lock_file *lk, int die_on_error) { - return hold_lock_file_for_update(lk, get_index_file(), die_on_error); + return hold_lock_file_for_update(lk, get_index_file(), + die_on_error + ? LOCK_DIE_ON_ERROR + : 0); } void set_alternate_index_output(const char *name) diff --git a/pack-refs.c b/pack-refs.c index 848d311c2b..2c76fb181f 100644 --- a/pack-refs.c +++ b/pack-refs.c @@ -89,7 +89,8 @@ int pack_refs(unsigned int flags) memset(&cbdata, 0, sizeof(cbdata)); cbdata.flags = flags; - fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1); + fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), + LOCK_DIE_ON_ERROR); cbdata.refs_file = fdopen(fd, "w"); if (!cbdata.refs_file) die("unable to create ref-pack file structure (%s)", @@ -348,7 +348,7 @@ int normalize_absolute_path(char *buf, const char *path) goto next; } - memcpy(dst, comp_start, comp_len); + memmove(dst, comp_start, comp_len); dst += comp_len; next: comp_start = comp_end; diff --git a/read-cache.c b/read-cache.c index 8f96fd1722..967f483f78 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1460,23 +1460,56 @@ int write_index(const struct index_state *istate, int newfd) int read_index_unmerged(struct index_state *istate) { int i; - struct cache_entry **dst; - struct cache_entry *last = NULL; + int unmerged = 0; read_index(istate); - dst = istate->cache; for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i]; - if (ce_stage(ce)) { - remove_name_hash(ce); - if (last && !strcmp(ce->name, last->name)) - continue; - cache_tree_invalidate_path(istate->cache_tree, ce->name); - last = ce; + struct cache_entry *new_ce; + int size, len; + + if (!ce_stage(ce)) continue; - } - *dst++ = ce; + unmerged = 1; + len = strlen(ce->name); + size = cache_entry_size(len); + new_ce = xcalloc(1, size); + hashcpy(new_ce->sha1, ce->sha1); + memcpy(new_ce->name, ce->name, len); + new_ce->ce_flags = create_ce_flags(len, 0); + new_ce->ce_mode = ce->ce_mode; + if (add_index_entry(istate, new_ce, 0)) + return error("%s: cannot drop to stage #0", + ce->name); + i = index_name_pos(istate, new_ce->name, len); + } + return unmerged; +} + +/* + * Returns 1 if the path is an "other" path with respect to + * the index; that is, the path is not mentioned in the index at all, + * either as a file, a directory with some files in the index, + * or as an unmerged entry. + * + * We helpfully remove a trailing "/" from directories so that + * the output of read_directory can be used as-is. + */ +int index_name_is_other(const struct index_state *istate, const char *name, + int namelen) +{ + int pos; + if (namelen && name[namelen - 1] == '/') + namelen--; + pos = index_name_pos(istate, name, namelen); + if (0 <= pos) + return 0; /* exact match */ + pos = -pos - 1; + if (pos < istate->cache_nr) { + struct cache_entry *ce = istate->cache[pos]; + if (ce_namelen(ce) == namelen && + !memcmp(ce->name, name, namelen)) + return 0; /* Yup, this one exists unmerged */ } - istate->cache_nr = dst - istate->cache; - return !!last; + return 1; } @@ -790,7 +790,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char struct ref_lock *lock; struct stat st; int last_errno = 0; - int type; + int type, lflags; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); lock = xcalloc(1, sizeof(struct ref_lock)); @@ -830,8 +830,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char lock->lk = xcalloc(1, sizeof(struct lock_file)); - if (flags & REF_NODEREF) + lflags = LOCK_DIE_ON_ERROR; + if (flags & REF_NODEREF) { ref = orig_ref; + lflags |= LOCK_NODEREF; + } lock->ref_name = xstrdup(ref); lock->orig_ref_name = xstrdup(orig_ref); ref_file = git_path("%s", ref); @@ -845,8 +848,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char error("unable to create directory for %s", ref_file); goto error_return; } - lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1); + lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags); return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; error_return: @@ -346,7 +346,8 @@ int setup_rerere(struct string_list *merge_rr) return -1; merge_rr_path = xstrdup(git_path("MERGE_RR")); - fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1); + fd = hold_lock_file_for_update(&write_lock, merge_rr_path, + LOCK_DIE_ON_ERROR); read_rr(merge_rr); return fd; } diff --git a/sha1_file.c b/sha1_file.c index c78507152f..12fc767ee5 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -385,7 +385,7 @@ static void read_info_alternates(const char * relative_base, int depth) void add_to_alternates_file(const char *reference) { struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1); + int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR); char *alt = mkpath("%s/objects\n", reference); write_or_die(fd, alt, strlen(alt)); if (commit_lock_file(lock)) diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh index b0d31f5a9b..849911683a 100755 --- a/t/t1005-read-tree-reset.sh +++ b/t/t1005-read-tree-reset.sh @@ -27,4 +27,64 @@ test_expect_success 'reset should work' ' test_cmp expect actual ' +test_expect_success 'reset should remove remnants from a failed merge' ' + git read-tree --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git read-tree --reset -u HEAD && + git ls-files -s >actual && + ! test -f old +' + +test_expect_success 'Porcelain reset should remove remnants too' ' + git read-tree --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git reset --hard && + git ls-files -s >actual && + ! test -f old +' + +test_expect_success 'Porcelain checkout -f should remove remnants too' ' + git read-tree --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git checkout -f && + git ls-files -s >actual && + ! test -f old +' + +test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' ' + git read-tree --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git checkout -f HEAD && + git ls-files -s >actual && + ! test -f old +' + test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index e395ff4e34..c942c8be85 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -58,6 +58,11 @@ test_expect_success \ git commit-tree $treeid </dev/null)' test_expect_success \ + 'create bare clone' \ + 'git clone --bare . bare.git && + cp .gitattributes bare.git/info/attributes' + +test_expect_success \ 'remove ignored file' \ 'rm a/ignored' @@ -74,10 +79,18 @@ test_expect_success \ 'diff b.tar b2.tar' test_expect_success \ + 'git archive in a bare repo' \ + '(cd bare.git && git archive HEAD) >b3.tar' + +test_expect_success \ + 'git archive vs. the same in a bare repo' \ + 'test_cmp b.tar b3.tar' + +test_expect_success \ 'validate file modification time' \ 'mkdir extract && "$TAR" xf b.tar -C extract a/a && - perl -e '\''print((stat("extract/a/a"))[9], "\n")'\'' >b.mtime && + test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime && echo "1117231200" >expected.mtime && diff expected.mtime b.mtime' @@ -151,6 +164,14 @@ test_expect_success \ 'git archive --format=zip' \ 'git archive --format=zip HEAD >d.zip' +test_expect_success \ + 'git archive --format=zip in a bare repo' \ + '(cd bare.git && git archive --format=zip HEAD) >d1.zip' + +test_expect_success \ + 'git archive --format=zip vs. the same in a bare repo' \ + 'test_cmp d.zip d1.zip' + $UNZIP -v >/dev/null 2>&1 if [ $? -eq 127 ]; then echo "Skipping ZIP tests, because unzip was not found" diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh index 86abc62271..cb9aacc7bc 100755 --- a/t/t5405-send-pack-rewind.sh +++ b/t/t5405-send-pack-rewind.sh @@ -12,7 +12,7 @@ test_expect_success setup ' mkdir another && ( cd another && git init && - git fetch .. master:master + git fetch --update-head-ok .. master:master ) && >file2 && git add file2 && test_tick && diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index c4496635df..0103e1a180 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -188,7 +188,7 @@ test_expect_success 'prune --dry-run' ' test_expect_success 'add --mirror && prune' ' (mkdir mirror && cd mirror && - git init && + git init --bare && git remote add --mirror -f origin ../one) && (cd one && git branch -m side2 side) && diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index de26c20327..52094e78dc 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -303,4 +303,16 @@ test_expect_success 'pushing nonexistent branch by mistake should not segv' ' ' +test_expect_success 'refuse to fetch into the current branch' ' + + test_must_fail git fetch . side:master + +' + +test_expect_success 'fetch into the current branch with --update-head-ok' ' + + git fetch --update-head-ok . side:master + +' + test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 997b2db827..725771fac1 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -29,6 +29,18 @@ test_expect_success 'checking the results' ' diff file cloned/file ' +test_expect_success 'pulling into void using master:master' ' + mkdir cloned-uho && + ( + cd cloned-uho && + git init && + git pull .. master:master + ) && + test -f file && + test -f cloned-uho/file && + test_cmp file cloned-uho/file +' + test_expect_success 'test . as a remote' ' git branch copy master && diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 0679abd29d..c9abed6a2b 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -330,12 +330,26 @@ test_expect_success \ test "$(git config branch.track2.merge)" git config branch.autosetupmerge false' -test_expect_success \ - 'checkout w/--track from non-branch HEAD fails' ' - git checkout -b delete-me master && - rm .git/refs/heads/delete-me && - test refs/heads/delete-me = "$(git symbolic-ref HEAD)" && - test_must_fail git checkout --track -b track' +test_expect_success 'checkout w/--track from non-branch HEAD fails' ' + git checkout master^0 && + test_must_fail git symbolic-ref HEAD && + test_must_fail git checkout --track -b track && + test_must_fail git rev-parse --verify track && + test_must_fail git symbolic-ref HEAD && + test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)" +' + +test_expect_success 'detach a symbolic link HEAD' ' + git checkout master && + git config --bool core.prefersymlinkrefs yes && + git checkout side && + git checkout master && + it=$(git symbolic-ref HEAD) && + test "z$it" = zrefs/heads/master && + here=$(git rev-parse --verify refs/heads/master) && + git checkout side^ && + test "z$(git rev-parse --verify refs/heads/master)" = "z$here" +' test_expect_success 'checkout an unmerged path should fail' ' rm -f .git/index && diff --git a/t/t7502-status.sh b/t/t7502-status.sh index c8e4c2e7b4..187a13e64f 100755 --- a/t/t7502-status.sh +++ b/t/t7502-status.sh @@ -285,6 +285,12 @@ test_expect_success 'status submodule summary is disabled by default' ' test_cmp expect output ' +# we expect the same as the previous test +test_expect_success 'status --untracked-files=all does not show submodule' ' + git status --untracked-files=all >output && + test_cmp expect output +' + head=$(cd sm && git rev-parse --short=7 --verify HEAD) cat >expect <<EOF diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index c6bc0a607f..dba3a1b48f 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -983,7 +983,7 @@ test_expect_success \ git checkout subuse1 && rm -rf sub && mkdir sub && cd sub && git init && - git fetch .. refs/heads/sub:refs/heads/master && + git fetch --update-head-ok .. refs/heads/sub:refs/heads/master && git checkout master && cd .. && git submodule init && diff --git a/test-chmtime.c b/test-chmtime.c index 90da448ebe..d5358cbaac 100644 --- a/test-chmtime.c +++ b/test-chmtime.c @@ -1,39 +1,83 @@ +/* + * This program can either change modification time of the given + * file(s) or just print it. The program does not change atime nor + * ctime (their values are explicitely preserved). + * + * The mtime can be changed to an absolute value: + * + * test-chmtime =<seconds> file... + * + * Relative to the current time as returned by time(3): + * + * test-chmtime =+<seconds> (or =-<seconds>) file... + * + * Or relative to the current mtime of the file: + * + * test-chmtime <seconds> file... + * test-chmtime +<seconds> (or -<seconds>) file... + * + * Examples: + * + * To just print the mtime use --verbose and set the file mtime offset to 0: + * + * test-chmtime -v +0 file + * + * To set the mtime to current time: + * + * test-chmtime =+0 file + * + */ #include "git-compat-util.h" #include <utime.h> -static const char usage_str[] = "(+|=|=+|=-|-)<seconds> <file>..."; +static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-)<seconds> <file>..."; -int main(int argc, const char *argv[]) +static int timespec_arg(const char *arg, long int *set_time, int *set_eq) { - int i; - int set_eq; - long int set_time; char *test; - const char *timespec; - - if (argc < 3) - goto usage; - - timespec = argv[1]; - set_eq = (*timespec == '=') ? 1 : 0; - if (set_eq) { + const char *timespec = arg; + *set_eq = (*timespec == '=') ? 1 : 0; + if (*set_eq) { timespec++; if (*timespec == '+') { - set_eq = 2; /* relative "in the future" */ + *set_eq = 2; /* relative "in the future" */ timespec++; } } - set_time = strtol(timespec, &test, 10); + *set_time = strtol(timespec, &test, 10); if (*test) { - fprintf(stderr, "Not a base-10 integer: %s\n", argv[1] + 1); - goto usage; + fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1); + return 0; } - if ((set_eq && set_time < 0) || set_eq == 2) { + if ((*set_eq && *set_time < 0) || *set_eq == 2) { time_t now = time(NULL); - set_time += now; + *set_time += now; } + return 1; +} + +int main(int argc, const char *argv[]) +{ + static int verbose; - for (i = 2; i < argc; i++) { + int i = 1; + /* no mtime change by default */ + int set_eq = 0; + long int set_time = 0; + + if (argc < 3) + goto usage; + + if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) { + verbose = 1; + ++i; + } + if (timespec_arg(argv[i], &set_time, &set_eq)) + ++i; + else + goto usage; + + for (; i < argc; i++) { struct stat sb; struct utimbuf utb; @@ -46,7 +90,12 @@ int main(int argc, const char *argv[]) utb.actime = sb.st_atime; utb.modtime = set_eq ? set_time : sb.st_mtime + set_time; - if (utime(argv[i], &utb) < 0) { + if (verbose) { + uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime; + printf("%"PRIuMAX"\t%s\n", mtime, argv[i]); + } + + if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) { fprintf(stderr, "Failed to modify time on %s: %s\n", argv[i], strerror(errno)); return -1; diff --git a/unpack-trees.c b/unpack-trees.c index e59d144d28..e5749ef638 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -382,7 +382,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->merge_size = len; if (!dfc) - dfc = xcalloc(1, sizeof(struct cache_entry) + 1); + dfc = xcalloc(1, cache_entry_size(0)); o->df_conflict_entry = dfc; if (len) { diff --git a/wt-status.c b/wt-status.c index 889e50f89f..64cedfcbe1 100644 --- a/wt-status.c +++ b/wt-status.c @@ -275,20 +275,9 @@ static void wt_status_print_untracked(struct wt_status *s) read_directory(&dir, ".", "", 0, NULL); for(i = 0; i < dir.nr; i++) { - /* check for matching entry, which is unmerged; lifted from - * builtin-ls-files:show_other_files */ struct dir_entry *ent = dir.entries[i]; - int pos = cache_name_pos(ent->name, ent->len); - struct cache_entry *ce; - if (0 <= pos) - die("bug in wt_status_print_untracked"); - pos = -pos - 1; - if (pos < active_nr) { - ce = active_cache[pos]; - if (ce_namelen(ce) == ent->len && - !memcmp(ce->name, ent->name, ent->len)) - continue; - } + if (!cache_name_is_other(ent->name, ent->len)) + continue; if (!shown_header) { s->workdir_untracked = 1; wt_status_print_header(s, "Untracked files", |