diff options
-rw-r--r-- | Documentation/git-init-db.txt | 91 | ||||
-rw-r--r-- | Documentation/git-init.txt | 114 | ||||
-rw-r--r-- | builtin-branch.c | 55 | ||||
-rw-r--r-- | builtin-rm.c | 6 | ||||
-rw-r--r-- | cache.h | 2 | ||||
-rw-r--r-- | config.c | 43 | ||||
-rw-r--r-- | fsck-objects.c | 2 | ||||
-rwxr-xr-x | git-checkout.sh | 74 | ||||
-rw-r--r-- | git-compat-util.h | 2 | ||||
-rw-r--r-- | index-pack.c | 2 | ||||
-rw-r--r-- | path.c | 26 | ||||
-rw-r--r-- | read-cache.c | 2 | ||||
-rw-r--r-- | setup.c | 5 | ||||
-rw-r--r-- | sha1_file.c | 2 | ||||
-rwxr-xr-x | t/t3200-branch.sh | 2 | ||||
-rw-r--r-- | write_or_die.c | 21 | ||||
-rw-r--r-- | wt-status.c | 32 |
17 files changed, 316 insertions, 165 deletions
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt index bc3ba1493..5412135d7 100644 --- a/Documentation/git-init-db.txt +++ b/Documentation/git-init-db.txt @@ -11,96 +11,9 @@ SYNOPSIS 'git-init-db' [--template=<template_directory>] [--shared[=<permissions>]] -OPTIONS -------- - --- - ---template=<template_directory>:: - -Provide the directory from which templates will be used. The default template -directory is `/usr/share/git-core/templates`. - -When specified, `<template_directory>` is used as the source of the template -files rather than the default. The template files include some directory -structure, some suggested "exclude patterns", and copies of non-executing -"hook" files. The suggested patterns and hook files are all modifiable and -extensible. - ---shared[={false|true|umask|group|all|world|everybody}]:: - -Specify that the git repository is to be shared amongst several users. This -allows users belonging to the same group to push into that -repository. When specified, the config variable "core.sharedRepository" is -set so that files and directories under `$GIT_DIR` are created with the -requested permissions. When not specified, git will use permissions reported -by umask(2). - -The option can have the following values, defaulting to 'group' if no value -is given: - - - 'umask' (or 'false'): Use permissions reported by umask(2). The default, - when `--shared` is not specified. - - - 'group' (or 'true'): Make the repository group-writable, (and g+sx, since - the git group may be not the primary group of all users). - - - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository - readable by all users. - -By default, the configuration flag receive.denyNonFastforward is enabled -in shared repositories, so that you cannot force a non fast-forwarding push -into it. - --- - - DESCRIPTION ----------- -This command creates an empty git repository - basically a `.git` directory -with subdirectories for `objects`, `refs/heads`, `refs/tags`, and -template files. -An initial `HEAD` file that references the HEAD of the master branch -is also created. - -If the `$GIT_DIR` environment variable is set then it specifies a path -to use instead of `./.git` for the base of the repository. - -If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY` -environment variable then the sha1 directories are created underneath - -otherwise the default `$GIT_DIR/objects` directory is used. - -Running `git-init-db` in an existing repository is safe. It will not overwrite -things that are already there. The primary reason for rerunning `git-init-db` -is to pick up newly added templates. - -Note that `git-init` is the same as `git-init-db`. - - -EXAMPLES --------- - -Start a new git repository for an existing code base:: -+ ----------------- -$ cd /path/to/my/codebase -$ git-init-db <1> -$ git-add . <2> ----------------- -+ -<1> prepare /path/to/my/codebase/.git directory -<2> add all existing file to the index - - -Author ------- -Written by Linus Torvalds <torvalds@osdl.org> - -Documentation --------------- -Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. -GIT ---- -Part of the gitlink:git[7] suite +This is a synonym for gitlink:git-init[1]. Please refer to the +documentation of that command. diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index 36838c753..e1fd6884f 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -1 +1,113 @@ -include::git-init-db.txt[] +git-init(1) +=========== + +NAME +---- +git-init - Creates an empty git repository + + +SYNOPSIS +-------- +'git-init' [--template=<template_directory>] [--shared[=<permissions>]] + + +OPTIONS +------- + +-- + +--template=<template_directory>:: + +Provide the directory from which templates will be used. The default template +directory is `/usr/share/git-core/templates`. + +When specified, `<template_directory>` is used as the source of the template +files rather than the default. The template files include some directory +structure, some suggested "exclude patterns", and copies of non-executing +"hook" files. The suggested patterns and hook files are all modifiable and +extensible. + +--shared[={false|true|umask|group|all|world|everybody}]:: + +Specify that the git repository is to be shared amongst several users. This +allows users belonging to the same group to push into that +repository. When specified, the config variable "core.sharedRepository" is +set so that files and directories under `$GIT_DIR` are created with the +requested permissions. When not specified, git will use permissions reported +by umask(2). + +The option can have the following values, defaulting to 'group' if no value +is given: + + - 'umask' (or 'false'): Use permissions reported by umask(2). The default, + when `--shared` is not specified. + + - 'group' (or 'true'): Make the repository group-writable, (and g+sx, since + the git group may be not the primary group of all users). + + - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository + readable by all users. + +By default, the configuration flag receive.denyNonFastforward is enabled +in shared repositories, so that you cannot force a non fast-forwarding push +into it. + +-- + + +DESCRIPTION +----------- +This command creates an empty git repository - basically a `.git` directory +with subdirectories for `objects`, `refs/heads`, `refs/tags`, and +template files. +An initial `HEAD` file that references the HEAD of the master branch +is also created. + +If the `$GIT_DIR` environment variable is set then it specifies a path +to use instead of `./.git` for the base of the repository. + +If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY` +environment variable then the sha1 directories are created underneath - +otherwise the default `$GIT_DIR/objects` directory is used. + +Running `git-init` in an existing repository is safe. It will not overwrite +things that are already there. The primary reason for rerunning `git-init` +is to pick up newly added templates. + +Note that `git-init` is the same as `git-init-db`. The command +was primarily meant to initialize the object database, but over +time it has become responsible for setting up the other aspects +of the repository, such as installing the default hooks and +setting the configuration variables. The old name is retained +because people are so used to it and many existing documents +refer to it that way, and this will not change for some time to +come. + + +EXAMPLES +-------- + +Start a new git repository for an existing code base:: ++ +---------------- +$ cd /path/to/my/codebase +$ git-init <1> +$ git-add . <2> +---------------- ++ +<1> prepare /path/to/my/codebase/.git directory +<2> add all existing file to the index + + +Author +------ +Written by Linus Torvalds <torvalds@osdl.org> + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. + +GIT +--- +Part of the gitlink:git[7] suite + diff --git a/builtin-branch.c b/builtin-branch.c index d3df5a57f..c760e188e 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -275,7 +275,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } } -static void print_ref_list(int kinds, int verbose, int abbrev) +static void print_ref_list(int kinds, int detached, int verbose, int abbrev) { int i; struct ref_list ref_list; @@ -286,8 +286,20 @@ static void print_ref_list(int kinds, int verbose, int abbrev) qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); + detached = (detached && (kinds & REF_LOCAL_BRANCH)); + if (detached) { + struct ref_item item; + item.name = "(no branch)"; + item.kind = REF_LOCAL_BRANCH; + hashcpy(item.sha1, head_sha1); + if (strlen(item.name) > ref_list.maxwidth) + ref_list.maxwidth = strlen(item.name); + print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1); + } + for (i = 0; i < ref_list.index; i++) { - int current = (ref_list.list[i].kind == REF_LOCAL_BRANCH) && + int current = !detached && + (ref_list.list[i].kind == REF_LOCAL_BRANCH) && !strcmp(ref_list.list[i].name, head); print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose, abbrev, current); @@ -296,7 +308,8 @@ static void print_ref_list(int kinds, int verbose, int abbrev) free_ref_list(&ref_list); } -static void create_branch(const char *name, const char *start, +static void create_branch(const char *name, const char *start_name, + unsigned char *start_sha1, int force, int reflog) { struct ref_lock *lock; @@ -315,9 +328,14 @@ static void create_branch(const char *name, const char *start, die("Cannot force update the current branch."); } - if (get_sha1(start, sha1) || - (commit = lookup_commit_reference(sha1)) == NULL) - die("Not a valid branch point: '%s'.", start); + if (start_sha1) + /* detached HEAD */ + hashcpy(sha1, start_sha1); + else if (get_sha1(start_name, sha1)) + die("Not a valid object name: '%s'.", start_name); + + if ((commit = lookup_commit_reference(sha1)) == NULL) + die("Not a valid branch point: '%s'.", start_name); hashcpy(sha1, commit->object.sha1); lock = lock_any_ref_for_update(ref, NULL); @@ -326,7 +344,8 @@ static void create_branch(const char *name, const char *start, if (reflog) { log_all_ref_updates = 1; - snprintf(msg, sizeof msg, "branch: Created from %s", start); + snprintf(msg, sizeof msg, "branch: Created from %s", + start_name); } if (write_ref_sha1(lock, sha1, msg) < 0) @@ -338,6 +357,9 @@ static void rename_branch(const char *oldname, const char *newname, int force) char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100]; unsigned char sha1[20]; + if (!oldname) + die("cannot rename the curren branch while not on any."); + if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref)) die("Old branchname too long"); @@ -367,7 +389,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) { int delete = 0, force_delete = 0, force_create = 0; int rename = 0, force_rename = 0; - int verbose = 0, abbrev = DEFAULT_ABBREV; + int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0; int reflog = 0; int kinds = REF_LOCAL_BRANCH; int i; @@ -444,22 +466,27 @@ int cmd_branch(int argc, const char **argv, const char *prefix) head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL)); if (!head) die("Failed to resolve HEAD as a valid ref."); - if (strncmp(head, "refs/heads/", 11)) - die("HEAD not found below refs/heads!"); - head += 11; + if (!strcmp(head, "HEAD")) { + detached = 1; + } + else { + if (strncmp(head, "refs/heads/", 11)) + die("HEAD not found below refs/heads!"); + head += 11; + } if (delete) return delete_branches(argc - i, argv + i, force_delete, kinds); else if (i == argc) - print_ref_list(kinds, verbose, abbrev); + print_ref_list(kinds, detached, verbose, abbrev); else if (rename && (i == argc - 1)) rename_branch(head, argv[i], force_rename); else if (rename && (i == argc - 2)) rename_branch(argv[i], argv[i + 1], force_rename); else if (i == argc - 1) - create_branch(argv[i], head, force_create, reflog); + create_branch(argv[i], head, head_sha1, force_create, reflog); else if (i == argc - 2) - create_branch(argv[i], argv[i + 1], force_create, reflog); + create_branch(argv[i], argv[i+1], NULL, force_create, reflog); else usage(builtin_branch_usage); diff --git a/builtin-rm.c b/builtin-rm.c index 5b078c419..d81f289c3 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -32,6 +32,10 @@ static int remove_file(const char *name) char *slash; ret = unlink(name); + if (ret && errno == ENOENT) + /* The user has removed it from the filesystem by hand */ + ret = errno = 0; + if (!ret && (slash = strrchr(name, '/'))) { char *n = xstrdup(name); do { @@ -204,7 +208,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) return 0; /* - * Then, unless we used "--cache", remove the filenames from + * Then, unless we used "--cached", remove the filenames from * the workspace. If we fail to remove the first one, we * abort the "git rm" (but once we've successfully removed * any file at all, we'll go ahead and commit to it all: @@ -299,7 +299,7 @@ extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern int read_ref(const char *filename, unsigned char *sha1); extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); extern int create_symref(const char *ref, const char *refs_heads_master); -extern int validate_symref(const char *ref); +extern int validate_headref(const char *ref); extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2); extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2); @@ -694,11 +694,9 @@ int git_config_set_multivar(const char* key, const char* value, store.key = (char*)key; if (!store_write_section(fd, key) || - !store_write_pair(fd, key, value)) { - ret = write_error(); - goto out_free; - } - } else{ + !store_write_pair(fd, key, value)) + goto write_err_out; + } else { struct stat st; char* contents; int i, copy_begin, copy_end, new_line = 0; @@ -777,31 +775,33 @@ int git_config_set_multivar(const char* key, const char* value, /* write the first part of the config */ if (copy_end > copy_begin) { - write_in_full(fd, contents + copy_begin, - copy_end - copy_begin); - if (new_line) - write_in_full(fd, "\n", 1); + if (write_in_full(fd, contents + copy_begin, + copy_end - copy_begin) < + copy_end - copy_begin) + goto write_err_out; + if (new_line && + write_in_full(fd, "\n", 1) != 1) + goto write_err_out; } copy_begin = store.offset[i]; } /* write the pair (value == NULL means unset) */ if (value != NULL) { - if (store.state == START) - if (!store_write_section(fd, key)) { - ret = write_error(); - goto out_free; - } - if (!store_write_pair(fd, key, value)) { - ret = write_error(); - goto out_free; + if (store.state == START) { + if (!store_write_section(fd, key)) + goto write_err_out; } + if (!store_write_pair(fd, key, value)) + goto write_err_out; } /* write the rest of the config */ if (copy_begin < st.st_size) - write_in_full(fd, contents + copy_begin, - st.st_size - copy_begin); + if (write_in_full(fd, contents + copy_begin, + st.st_size - copy_begin) < + st.st_size - copy_begin) + goto write_err_out; munmap(contents, st.st_size); unlink(config_filename); @@ -824,6 +824,11 @@ out_free: free(lock_file); } return ret; + +write_err_out: + ret = write_error(); + goto out_free; + } int git_config_rename_section(const char *old_name, const char *new_name) diff --git a/fsck-objects.c b/fsck-objects.c index 0d8a8ebb4..81f00db90 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -290,7 +290,7 @@ static int fsck_sha1(unsigned char *sha1) { struct object *obj = parse_object(sha1); if (!obj) - return error("%s: object not found", sha1_to_hex(sha1)); + return error("%s: object corrupt or missing", sha1_to_hex(sha1)); if (obj->flags & SEEN) return 0; obj->flags |= SEEN; diff --git a/git-checkout.sh b/git-checkout.sh index 92ec069a3..8f4356d49 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -6,6 +6,7 @@ SUBDIRECTORY_OK=Sometimes old_name=HEAD old=$(git-rev-parse --verify $old_name 2>/dev/null) +oldbranch=$(git-symbolic-ref $old_name 2>/dev/null) new= new_name= force= @@ -13,6 +14,8 @@ branch= newbranch= newbranch_log= merge= +LF=' +' while [ "$#" != "0" ]; do arg="$1" shift @@ -50,7 +53,7 @@ while [ "$#" != "0" ]; do exit 1 fi new="$rev" - new_name="$arg^0" + new_name="$arg" if git-show-ref --verify --quiet -- "refs/heads/$arg" then branch="$arg" @@ -139,23 +142,49 @@ fi [ -z "$new" ] && new=$old && new_name="$old_name" -# If we don't have an old branch that we're switching to, +# If we don't have an existing branch that we're switching to, # and we don't have a new branch name for the target we -# are switching to, then we'd better just be checking out -# what we already had +# are switching to, then we are detaching our HEAD from any +# branch. However, if "git checkout HEAD" detaches the HEAD +# from the current branch, even though that may be logically +# correct, it feels somewhat funny. More importantly, we do not +# want "git checkout" nor "git checkout -f" to detach HEAD. -[ -z "$branch$newbranch" ] && - [ "$new" != "$old" ] && - die "git checkout: provided reference cannot be checked out directly +detached= +detach_warn= - You need -b to associate a new branch with the wanted checkout. Example: - git checkout -b <new_branch_name> $arg -" +if test -z "$branch$newbranch" && test "$new" != "$old" +then + detached="$new" + if test -n "$oldbranch" + then + detach_warn="warning: you are not on ANY branch anymore. +If you meant to create a new branch from the commit, you need -b to +associate a new branch with the wanted checkout. Example: + git checkout -b <new_branch_name> $arg" + fi +elif test -z "$oldbranch" && test -n "$branch" +then + # Coming back... + if test -z "$force" + then + git show-ref -d -s | grep "$old" >/dev/null || { + echo >&2 \ +"You are not on any branch and switching to branch '$new_name' +may lose your changes. At this point, you can do one of two things: + (1) Decide it is Ok and say 'git checkout -f $new_name'; + (2) Start a new branch from the current commit, by saying + 'git checkout -b <branch-name>'. +Leaving your HEAD detached; not switching to branch '$new_name'." + exit 1; + } + fi +fi if [ "X$old" = X ] then - echo "warning: You do not appear to currently be on a branch." >&2 - echo "warning: Forcing checkout of $new_name." >&2 + echo >&2 "warning: You appear to be on a branch yet to be born." + echo >&2 "warning: Forcing checkout of $new_name." force=1 fi @@ -226,8 +255,25 @@ if [ "$?" -eq 0 ]; then git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit branch="$newbranch" fi - [ "$branch" ] && - GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch" + if test -n "$branch" + then + GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch" + elif test -n "$detached" + then + # NEEDSWORK: we would want a command to detach the HEAD + # atomically, instead of this handcrafted command sequence. + # Perhaps: + # git update-ref --detach HEAD $new + # or something like that... + # + echo "$detached" >"$GIT_DIR/HEAD.new" && + mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" || + die "Cannot detach HEAD" + if test -n "$detach_warn" + then + echo >&2 "$detach_warn" + fi + fi rm -f "$GIT_DIR/MERGE_HEAD" else exit 1 diff --git a/git-compat-util.h b/git-compat-util.h index f8d46d587..8781e8e22 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -202,6 +202,8 @@ static inline void *xmmap(void *start, size_t length, { void *ret = mmap(start, length, prot, flags, fd, offset); if (ret == MAP_FAILED) { + if (!length) + return NULL; release_pack_memory(length); ret = mmap(start, length, prot, flags, fd, offset); if (ret == MAP_FAILED) diff --git a/index-pack.c b/index-pack.c index 8d10d6ba2..72e096241 100644 --- a/index-pack.c +++ b/index-pack.c @@ -814,7 +814,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, char buf[48]; int len = snprintf(buf, sizeof(buf), "%s\t%s\n", report, sha1_to_hex(sha1)); - write_in_full(1, buf, len); + write_or_die(1, buf, len); /* * Let's just mimic git-unpack-objects here and write @@ -90,10 +90,11 @@ int git_mkstemp(char *path, size_t len, const char *template) } -int validate_symref(const char *path) +int validate_headref(const char *path) { struct stat st; char *buf, buffer[256]; + unsigned char sha1[20]; int len, fd; if (lstat(path, &st) < 0) @@ -119,14 +120,23 @@ int validate_symref(const char *path) /* * Is it a symbolic ref? */ - if (len < 4 || memcmp("ref:", buffer, 4)) + if (len < 4) return -1; - buf = buffer + 4; - len -= 4; - while (len && isspace(*buf)) - buf++, len--; - if (len >= 5 && !memcmp("refs/", buf, 5)) + if (!memcmp("ref:", buffer, 4)) { + buf = buffer + 4; + len -= 4; + while (len && isspace(*buf)) + buf++, len--; + if (len >= 5 && !memcmp("refs/", buf, 5)) + return 0; + } + + /* + * Is this a detached HEAD? + */ + if (!get_sha1_hex(buffer, sha1)) return 0; + return -1; } @@ -241,7 +251,7 @@ char *enter_repo(char *path, int strict) return NULL; if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 && - validate_symref("HEAD") == 0) { + validate_headref("HEAD") == 0) { putenv("GIT_DIR=."); check_repository_format(); return path; diff --git a/read-cache.c b/read-cache.c index 8ecd826e9..c54a61187 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1010,7 +1010,7 @@ int write_cache(int newfd, struct cache_entry **cache, int entries) if (data && !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) && !ce_write(&c, newfd, data, sz)) - ; + free(data); else { free(data); return -1; @@ -138,7 +138,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec) * GIT_OBJECT_DIRECTORY environment variable * - a refs/ directory * - either a HEAD symlink or a HEAD file that is formatted as - * a proper "ref:". + * a proper "ref:", or a regular file HEAD that has a properly + * formatted sha1 object name. */ static int is_git_directory(const char *suspect) { @@ -161,7 +162,7 @@ static int is_git_directory(const char *suspect) return 0; strcpy(path + len, "/HEAD"); - if (validate_symref(path)) + if (validate_headref(path)) return 0; return 1; diff --git a/sha1_file.c b/sha1_file.c index 53e25f278..18dd89b50 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1620,6 +1620,8 @@ static int write_buffer(int fd, const void *buf, size_t len) { ssize_t size; + if (!len) + return 0; size = write_in_full(fd, buf, len); if (!size) return error("file write: disk full"); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index a6ea0f6a1..bb80e4286 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -48,7 +48,7 @@ test_expect_success \ test ! -f .git/logs/refs/heads/d/e/f' cat >expect <<EOF -0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 checkout: Created from master^0 +0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 checkout: Created from master EOF test_expect_success \ 'git checkout -b g/h/i -l should create a branch and a log' \ diff --git a/write_or_die.c b/write_or_die.c index a119e1d20..488de721d 100644 --- a/write_or_die.c +++ b/write_or_die.c @@ -26,6 +26,8 @@ void read_or_die(int fd, void *buf, size_t count) { ssize_t loaded; + if (!count) + return; loaded = read_in_full(fd, buf, count); if (loaded == 0) die("unexpected end of file"); @@ -37,15 +39,14 @@ int write_in_full(int fd, const void *buf, size_t count) { const char *p = buf; ssize_t total = 0; - ssize_t written = 0; while (count > 0) { - written = xwrite(fd, p, count); - if (written <= 0) { - if (total) - return total; - else - return written; + size_t written = xwrite(fd, p, count); + if (written < 0) + return -1; + if (!written) { + errno = ENOSPC; + return -1; } count -= written; p += written; @@ -59,6 +60,8 @@ void write_or_die(int fd, const void *buf, size_t count) { ssize_t written; + if (!count) + return; written = write_in_full(fd, buf, count); if (written == 0) die("disk full?"); @@ -73,6 +76,8 @@ int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg) { ssize_t written; + if (!count) + return 1; written = write_in_full(fd, buf, count); if (written == 0) { fprintf(stderr, "%s: disk full?\n", msg); @@ -93,6 +98,8 @@ int write_or_whine(int fd, const void *buf, size_t count, const char *msg) { ssize_t written; + if (!count) + return 1; written = write_in_full(fd, buf, count); if (written == 0) { fprintf(stderr, "%s: disk full?\n", msg); diff --git a/wt-status.c b/wt-status.c index 1dc2fdc34..daba9a610 100644 --- a/wt-status.c +++ b/wt-status.c @@ -15,7 +15,13 @@ static char wt_status_colors[][COLOR_MAXLEN] = { "\033[31m", /* WT_STATUS_CHANGED: red */ "\033[31m", /* WT_STATUS_UNTRACKED: red */ }; -static const char* use_add_msg = "use \"git add <file>...\" to incrementally add content to commit"; + +static const char use_add_msg[] = +"use \"git add <file>...\" to update what will be committed"; +static const char use_add_rm_msg[] = +"use \"git add/rm <file>...\" to update what will be committed"; +static const char use_add_to_include_msg[] = +"use \"git add <file>...\" to include in what will be committed"; static int parse_status_slot(const char *var, int offset) { @@ -177,8 +183,14 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q, struct wt_status *s = data; int i; if (q->nr) { + const char *msg = use_add_msg; s->workdir_dirty = 1; - wt_status_print_header("Changed but not added", use_add_msg); + for (i = 0; i < q->nr; i++) + if (q->queue[i]->status == DIFF_STATUS_DELETED) { + msg = use_add_rm_msg; + break; + } + wt_status_print_header("Changed but not updated", msg); } for (i = 0; i < q->nr; i++) wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]); @@ -265,7 +277,8 @@ static void wt_status_print_untracked(struct wt_status *s) } if (!shown_header) { s->workdir_untracked = 1; - wt_status_print_header("Untracked files", use_add_msg); + wt_status_print_header("Untracked files", + use_add_to_include_msg); shown_header = 1; } color_printf(color(WT_STATUS_HEADER), "#\t"); @@ -289,9 +302,18 @@ void wt_status_print(struct wt_status *s) unsigned char sha1[20]; s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; - if (s->branch) + if (s->branch) { + const char *on_what = "On branch "; + const char *branch_name = s->branch; + if (!strncmp(branch_name, "refs/heads/", 11)) + branch_name += 11; + else if (!strcmp(branch_name, "HEAD")) { + branch_name = ""; + on_what = "Not currently on any branch."; + } color_printf_ln(color(WT_STATUS_HEADER), - "# On branch %s", s->branch); + "# %s%s", on_what, branch_name); + } if (s->is_initial) { color_printf_ln(color(WT_STATUS_HEADER), "#"); |