From: Junio C Hamano Date: Wed, 27 Feb 2008 21:02:57 +0000 (-0800) Subject: Merge branch 'js/branch-track' X-Git-Tag: v1.5.5-rc0~132 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/60b188a9844cdcf865174c685a38acc053a9d43b?ds=inline;hp=-c Merge branch 'js/branch-track' * js/branch-track: doc: documentation update for the branch track changes branch: optionally setup branch.*.merge from upstream local branches Conflicts: Documentation/config.txt Documentation/git-branch.txt Documentation/git-checkout.txt builtin-branch.c cache.h t/t7201-co.sh --- 60b188a9844cdcf865174c685a38acc053a9d43b diff --combined Documentation/config.txt index 8e361a1e77,f5994bd4a7..4027726f2e --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -139,51 -139,6 +139,51 @@@ core.autocrlf: "text" (i.e. be subjected to the autocrlf mechanism) is decided purely based on the contents. +core.safecrlf:: + If true, makes git check if converting `CRLF` as controlled by + `core.autocrlf` is reversible. Git will verify if a command + modifies a file in the work tree either directly or indirectly. + For example, committing a file followed by checking out the + same file should yield the original file in the work tree. If + this is not the case for the current setting of + `core.autocrlf`, git will reject the file. The variable can + be set to "warn", in which case git will only warn about an + irreversible conversion but continue the operation. ++ +CRLF conversion bears a slight chance of corrupting data. +autocrlf=true will convert CRLF to LF during commit and LF to +CRLF during checkout. A file that contains a mixture of LF and +CRLF before the commit cannot be recreated by git. For text +files this is the right thing to do: it corrects line endings +such that we have only LF line endings in the repository. +But for binary files that are accidentally classified as text the +conversion can corrupt data. ++ +If you recognize such corruption early you can easily fix it by +setting the conversion type explicitly in .gitattributes. Right +after committing you still have the original file in your work +tree and this file is not yet corrupted. You can explicitly tell +git that this file is binary and git will handle the file +appropriately. ++ +Unfortunately, the desired effect of cleaning up text files with +mixed line endings and the undesired effect of corrupting binary +files cannot be distinguished. In both cases CRLFs are removed +in an irreversible way. For text files this is the right thing +to do because CRLFs are line endings, while for binary files +converting CRLFs corrupts data. ++ +Note, this safety check does not mean that a checkout will generate a +file identical to the original file for a different setting of +`core.autocrlf`, but only for the current one. For example, a text +file with `LF` would be accepted with `core.autocrlf=input` and could +later be checked out with `core.autocrlf=true`, in which case the +resulting file would contain `CRLF`, although the original file +contained `LF`. However, in both work trees the line endings would be +consistent, that is either all `LF` or all `CRLF`, but never mixed. A +file with mixed line endings would be reported by the `core.safecrlf` +mechanism. + core.symlinks:: If false, symbolic links are checked out as small plain files that contain the link text. linkgit:git-update-index[1] and @@@ -353,10 -308,6 +353,10 @@@ core.whitespace: error (enabled by default). * `indent-with-non-tab` treats a line that is indented with 8 or more space characters as an error (not enabled by default). +* `cr-at-eol` treats a carriage-return at the end of line as + part of the line terminator, i.e. with it, `trailing-space` + does not trigger if the character before such a carriage-return + is not a whitespace (not enabled by default). alias.*:: Command aliases for the linkgit:git[1] command wrapper - e.g. @@@ -379,10 -330,14 +379,14 @@@ apply.whitespace: branch.autosetupmerge:: Tells `git-branch` and `git-checkout` to setup new branches - so that linkgit:git-pull[1] will appropriately merge from that - remote branch. Note that even if this option is not set, + so that linkgit:git-pull[1] will appropriately merge from the + starting point branch. Note that even if this option is not set, this behavior can be chosen per-branch using the `--track` - and `--no-track` options. This option defaults to true. + and `--no-track` options. The valid settings are: `false` -- no + automatic setup is done; `true` -- automatic setup is done when the + starting point is a remote branch; `always` -- automatic setup is + done when the starting point is either a local branch or remote + branch. This option defaults to true. branch..remote:: When in branch , it tells `git fetch` which remote to fetch. @@@ -416,11 -371,6 +420,11 @@@ branch..rebase: it unless you understand the implications (see linkgit:git-rebase[1] for details). +browser..path:: + Override the path for the given tool that may be used to + browse HTML help (see '-w' option in linkgit:git-help[1]) or a + working repository in gitweb (see linkgit:git-instaweb[1]). + clean.requireForce:: A boolean to make git-clean do nothing unless given -f or -n. Defaults to true. @@@ -493,13 -443,6 +497,13 @@@ color.status.: commit.template:: Specify a file to use as the template for new commit messages. +color.ui:: + When set to `always`, always use colors in all git commands which + are capable of colored output. When false (or `never`), never. When + set to `true` or `auto`, use colors only when the output is to the + terminal. When more specific variables of color.* are set, they always + take precedence over this setting. Defaults to false. + diff.autorefreshindex:: When using `git diff` to compare with work tree files, do not consider stat-only change as changed. @@@ -812,8 -755,6 +816,8 @@@ pack.threads: warning. This is meant to reduce packing time on multiprocessor machines. The required amount of memory for the delta search window is however multiplied by the number of threads. + Specifying 0 will cause git to auto-detect the number of CPU's + and set the number of threads accordingly. pack.indexVersion:: Specify the default pack index version. Valid values are 1 for @@@ -824,12 -765,6 +828,12 @@@ whenever the corresponding pack is larger than 2 GB. Otherwise the default is 1. +pack.packSizeLimit: + The default maximum size of a pack. This setting only affects + packing to a file, i.e. the git:// protocol is unaffected. It + can be overridden by the `\--max-pack-size` option of + linkgit:git-repack[1]. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. @@@ -899,17 -834,6 +903,17 @@@ tar.umask: archiving user's umask will be used instead. See umask(2) and linkgit:git-archive[1]. +url..insteadOf:: + Any URL that starts with this value will be rewritten to + start, instead, with . In cases where some site serves a + large number of repositories, and serves them with multiple + access methods, and some users need to use different access + methods, this feature allows people to specify any of the + equivalent URLs and have git automatically rewrite the URL to + the best alternative for the particular user, even for a + never-before-seen repository on the site. When more than one + insteadOf strings match a given URL, the longest match is used. + user.email:: Your email address to be recorded in any newly created commits. Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and diff --combined builtin-branch.c index 7e991030ca,32eaf0d1e4..a7844454a2 --- a/builtin-branch.c +++ b/builtin-branch.c @@@ -30,9 -30,7 +30,7 @@@ static const char * const builtin_branc static const char *head; static unsigned char head_sha1[20]; - static int branch_track = 1; - -static int branch_use_color; +static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { "\033[m", /* reset */ "", /* PLAIN (normal) */ @@@ -71,21 -69,15 +69,17 @@@ static int git_branch_config(const cha } if (!prefixcmp(var, "color.branch.")) { int slot = parse_branch_color_slot(var, 13); + if (!value) + return config_error_nonbool(var); color_parse(value, var, branch_colors[slot]); return 0; } - if (!strcmp(var, "branch.autosetupmerge")) { - branch_track = git_config_bool(var, value); - return 0; - } - return git_default_config(var, value); + return git_color_default_config(var, value); } static const char *branch_get_color(enum color_branch ix) { - if (branch_use_color) + if (branch_use_color > 0) return branch_colors[ix]; return ""; } @@@ -420,14 -412,16 +414,16 @@@ int cmd_branch(int argc, const char **a { int delete = 0, rename = 0, force_create = 0; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0; - int reflog = 0, track; + int reflog = 0; + enum branch_track track; int kinds = REF_LOCAL_BRANCH; struct commit_list *with_commit = NULL; struct option options[] = { OPT_GROUP("Generic options"), OPT__VERBOSE(&verbose), - OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"), + OPT_SET_INT( 0 , "track", &track, "set up tracking mode (see git-pull(1))", + BRANCH_TRACK_EXPLICIT), OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches", REF_REMOTE_BRANCH), @@@ -454,11 -448,7 +450,11 @@@ }; git_config(git_branch_config); + + if (branch_use_color == -1) + branch_use_color = git_use_color_default; + - track = branch_track; + track = git_branch_track; argc = parse_options(argc, argv, options, builtin_branch_usage, 0); if (!!delete + !!rename + !!force_create > 1) usage_with_options(builtin_branch_usage, options); diff --combined builtin-checkout.c index d5f093094c,b1820a4462..4a4bb8b77d --- a/builtin-checkout.c +++ b/builtin-checkout.c @@@ -186,7 -186,7 +186,7 @@@ struct checkout_opts char *new_branch; int new_branch_log; - int track; + enum branch_track track; }; struct branch_info { @@@ -205,7 -205,8 +205,7 @@@ static void setup_branch_path(struct br } static int merge_working_tree(struct checkout_opts *opts, - struct branch_info *old, struct branch_info *new, - const char *prefix) + struct branch_info *old, struct branch_info *new) { int ret; struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); @@@ -226,25 -227,24 +226,25 @@@ refresh_cache(REFRESH_QUIET); if (unmerged_cache()) { - ret = opts->merge ? -1 : - error("you need to resolve your current index first"); - } else { - topts.update = 1; - topts.merge = 1; - topts.gently = opts->merge; - topts.fn = twoway_merge; - topts.dir = xcalloc(1, sizeof(*topts.dir)); - topts.dir->show_ignored = 1; - topts.dir->exclude_per_dir = ".gitignore"; - topts.prefix = prefix; - tree = parse_tree_indirect(old->commit->object.sha1); - init_tree_desc(&trees[0], tree->buffer, tree->size); - tree = parse_tree_indirect(new->commit->object.sha1); - init_tree_desc(&trees[1], tree->buffer, tree->size); - ret = unpack_trees(2, trees, &topts); + error("you need to resolve your current index first"); + return 1; } - if (ret) { + + /* 2-way merge to the new branch */ + topts.update = 1; + topts.merge = 1; + topts.gently = opts->merge; + topts.verbose_update = !opts->quiet; + topts.fn = twoway_merge; + topts.dir = xcalloc(1, sizeof(*topts.dir)); + topts.dir->show_ignored = 1; + topts.dir->exclude_per_dir = ".gitignore"; + tree = parse_tree_indirect(old->commit->object.sha1); + init_tree_desc(&trees[0], tree->buffer, tree->size); + tree = parse_tree_indirect(new->commit->object.sha1); + init_tree_desc(&trees[1], tree->buffer, tree->size); + + if (unpack_trees(2, trees, &topts)) { /* * Unpack couldn't do a trivial merge; either * give up or do a real merge, depending on @@@ -291,7 -291,7 +291,7 @@@ return 0; } -static void adjust_to_tracking(struct branch_info *new, struct checkout_opts *opts) +static void report_tracking(struct branch_info *new, struct checkout_opts *opts) { /* * We have switched to a new branch; is it building on @@@ -301,88 -301,64 +301,88 @@@ char *base; unsigned char sha1[20]; struct commit *ours, *theirs; - const char *msgfmt; char symmetric[84]; - int show_log; - struct branch *branch = branch_get(NULL); + struct rev_info revs; + const char *rev_argv[10]; + int rev_argc; + int num_ours, num_theirs; + const char *remote_msg; + struct branch *branch = branch_get(new->name); - if (!branch || !branch->merge) + /* + * Nothing to report unless we are marked to build on top of + * somebody else. + */ + if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst) return; - base = branch->merge[0]->dst; - - ours = new->commit; - - sprintf(symmetric, "%s", sha1_to_hex(ours->object.sha1)); - /* - * Ok, it is tracking base; is it ahead of us? + * If what we used to build on no longer exists, there is + * nothing to report. */ + base = branch->merge[0]->dst; if (!resolve_ref(base, sha1, 1, NULL)) return; - theirs = lookup_commit(sha1); - - sprintf(symmetric + 40, "...%s", sha1_to_hex(sha1)); + theirs = lookup_commit(sha1); + ours = new->commit; if (!hashcmp(sha1, ours->object.sha1)) return; /* we are the same */ - show_log = 1; - if (in_merge_bases(theirs, &ours, 1)) { - msgfmt = "You are ahead of the tracked branch '%s'\n"; - show_log = 0; + /* Run "rev-list --left-right ours...theirs" internally... */ + rev_argc = 0; + rev_argv[rev_argc++] = NULL; + rev_argv[rev_argc++] = "--left-right"; + rev_argv[rev_argc++] = symmetric; + rev_argv[rev_argc++] = "--"; + rev_argv[rev_argc] = NULL; + + strcpy(symmetric, sha1_to_hex(ours->object.sha1)); + strcpy(symmetric + 40, "..."); + strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1)); + + init_revisions(&revs, NULL); + setup_revisions(rev_argc, rev_argv, &revs, NULL); + prepare_revision_walk(&revs); + + /* ... and count the commits on each side. */ + num_ours = 0; + num_theirs = 0; + while (1) { + struct commit *c = get_revision(&revs); + if (!c) + break; + if (c->object.flags & SYMMETRIC_LEFT) + num_ours++; + else + num_theirs++; } - else if (in_merge_bases(ours, &theirs, 1)) - msgfmt = "Your branch can be fast-forwarded to the tracked branch '%s'\n"; - else - msgfmt = "Both your branch and the tracked branch '%s' have own changes, you would eventually need to merge\n"; - if (!prefixcmp(base, "refs/remotes/")) + if (!prefixcmp(base, "refs/remotes/")) { + remote_msg = " remote"; base += strlen("refs/remotes/"); - fprintf(stderr, msgfmt, base); - - if (show_log) { - const char *args[32]; - int ac; - - ac = 0; - args[ac++] = "log"; - args[ac++] = "--pretty=oneline"; - args[ac++] = "--abbrev-commit"; - args[ac++] = "--left-right"; - args[ac++] = "--boundary"; - args[ac++] = symmetric; - args[ac++] = "--"; - args[ac] = NULL; - - run_command_v_opt(args, RUN_GIT_CMD); + } else { + remote_msg = ""; } -} + if (!num_theirs) + printf("Your branch is ahead of the tracked%s branch '%s' " + "by %d commit%s.\n", + remote_msg, base, + num_ours, (num_ours == 1) ? "" : "s"); + else if (!num_ours) + printf("Your branch is behind the tracked%s branch '%s' " + "by %d commit%s,\n" + "and can be fast-forwarded.\n", + remote_msg, base, + num_theirs, (num_theirs == 1) ? "" : "s"); + else + printf("Your branch and the tracked%s branch '%s' " + "have diverged,\nand respectively " + "have %d and %d different commit(s) each.\n", + remote_msg, base, + num_ours, num_theirs); +} static void update_refs_for_switch(struct checkout_opts *opts, struct branch_info *old, @@@ -426,11 -402,12 +426,11 @@@ } remove_branch_state(); strbuf_release(&msg); - if (new->path || !strcmp(new->name, "HEAD")) - adjust_to_tracking(new, opts); + if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD"))) + report_tracking(new, opts); } -static int switch_branches(struct checkout_opts *opts, - struct branch_info *new, const char *prefix) +static int switch_branches(struct checkout_opts *opts, struct branch_info *new) { int ret = 0; struct branch_info old; @@@ -471,7 -448,7 +471,7 @@@ opts->force = 1; } - ret = merge_working_tree(opts, &old, new, prefix); + ret = merge_working_tree(opts, &old, new); if (ret) return ret; @@@ -480,13 -457,8 +480,8 @@@ return post_checkout_hook(old.commit, new->commit, 1); } - static int branch_track = 0; - static int git_checkout_config(const char *var, const char *value) { - if (!strcmp(var, "branch.autosetupmerge")) - branch_track = git_config_bool(var, value); - return git_default_config(var, value); } @@@ -501,7 -473,8 +496,8 @@@ int cmd_checkout(int argc, const char * OPT__QUIET(&opts.quiet), OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"), OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"), - OPT_BOOLEAN( 0 , "track", &opts.track, "track"), + OPT_SET_INT( 0 , "track", &opts.track, "track", + BRANCH_TRACK_EXPLICIT), OPT_BOOLEAN('f', NULL, &opts.force, "force"), OPT_BOOLEAN('m', NULL, &opts.merge, "merge"), OPT_END(), @@@ -512,7 -485,7 +508,7 @@@ git_config(git_checkout_config); - opts.track = branch_track; + opts.track = git_branch_track; argc = parse_options(argc, argv, options, checkout_usage, 0); if (argc) { @@@ -541,7 -514,7 +537,7 @@@ argc--; } - if (!opts.new_branch && (opts.track != branch_track)) + if (!opts.new_branch && (opts.track != git_branch_track)) die("git checkout: --track and --no-track require -b"); if (opts.force && opts.merge) @@@ -569,5 -542,5 +565,5 @@@ die("Cannot switch branch to a non-commit."); } - return switch_branches(&opts, &new, prefix); + return switch_branches(&opts, &new); } diff --combined cache.h index 9ebe7913b0,0cd1368aef..f16d341f52 --- a/cache.h +++ b/cache.h @@@ -110,6 -110,7 +110,6 @@@ struct ondisk_cache_entry }; struct cache_entry { - struct cache_entry *next; unsigned int ce_ctime; unsigned int ce_mtime; unsigned int ce_dev; @@@ -120,7 -121,6 +120,7 @@@ unsigned int ce_size; unsigned int ce_flags; unsigned char sha1[20]; + struct cache_entry *next; char name[FLEX_ARRAY]; /* more */ }; @@@ -133,39 -133,7 +133,39 @@@ #define CE_UPDATE (0x10000) #define CE_REMOVE (0x20000) #define CE_UPTODATE (0x40000) -#define CE_UNHASHED (0x80000) + +#define CE_HASHED (0x100000) +#define CE_UNHASHED (0x200000) + +/* + * Copy the sha1 and stat state of a cache entry from one to + * another. But we never change the name, or the hash state! + */ +#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED) +static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src) +{ + unsigned int state = dst->ce_flags & CE_STATE_MASK; + + /* Don't copy hash chain and name */ + memcpy(dst, src, offsetof(struct cache_entry, next)); + + /* Restore the hash state */ + dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state; +} + +/* + * We don't actually *remove* it, we can just mark it invalid so that + * we won't find it in lookups. + * + * Not only would we have to search the lists (simple enough), but + * we'd also have to rehash other hash buckets in case this makes the + * hash bucket empty (common). So it's much better to just mark + * it. + */ +static inline void remove_index_entry(struct cache_entry *ce) +{ + ce->ce_flags |= CE_UNHASHED; +} static inline unsigned create_ce_flags(size_t len, unsigned stage) { @@@ -210,18 -178,6 +210,18 @@@ static inline unsigned int ce_mode_from } return create_ce_mode(mode); } +static inline int ce_to_dtype(const struct cache_entry *ce) +{ + unsigned ce_mode = ntohl(ce->ce_mode); + if (S_ISREG(ce_mode)) + return DT_REG; + else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode)) + return DT_DIR; + else if (S_ISLNK(ce_mode)) + return DT_LNK; + else + return DT_UNKNOWN; +} #define canon_mode(mode) \ (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \ S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK) @@@ -417,14 -373,15 +417,23 @@@ extern size_t packed_git_limit extern size_t delta_base_cache_limit; extern int auto_crlf; +enum safe_crlf { + SAFE_CRLF_FALSE = 0, + SAFE_CRLF_FAIL = 1, + SAFE_CRLF_WARN = 2, +}; + +extern enum safe_crlf safe_crlf; + + enum branch_track { + BRANCH_TRACK_NEVER = 0, + BRANCH_TRACK_REMOTE, + BRANCH_TRACK_ALWAYS, + BRANCH_TRACK_EXPLICIT, + }; + + extern enum branch_track git_branch_track; + #define GIT_REPO_VERSION 0 extern int repository_format_version; extern int check_repository_format(void); @@@ -679,16 -636,11 +688,16 @@@ extern int git_parse_ulong(const char * extern int git_config_int(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); extern int git_config_bool(const char *, const char *); +extern int git_config_string(const char **, const char *, const char *); extern int git_config_set(const char *, const char *); extern int git_config_set_multivar(const char *, const char *, const char *, int); extern int git_config_rename_section(const char *, const char *); extern const char *git_etc_gitconfig(void); extern int check_repository_format_version(const char *var, const char *value); +extern int git_env_bool(const char *, int); +extern int git_config_system(void); +extern int git_config_global(void); +extern int config_error_nonbool(const char *); #define MAX_GITNAME (1000) extern char git_default_email[MAX_GITNAME]; @@@ -700,7 -652,6 +709,7 @@@ extern const char *git_log_output_encod /* IO helper functions */ extern void maybe_flush_or_die(FILE *, const char *); extern int copy_fd(int ifd, int ofd); +extern int copy_file(const char *dst, const char *src, int mode); extern int read_in_full(int fd, void *buf, size_t count); extern int write_in_full(int fd, const void *buf, size_t count); extern void write_or_die(int fd, const void *buf, size_t count); @@@ -709,12 -660,12 +718,12 @@@ extern int write_or_whine_pipe(int fd, /* pager.c */ extern void setup_pager(void); -extern char *pager_program; +extern const char *pager_program; extern int pager_in_use(void); extern int pager_use_color; -extern char *editor_program; -extern char *excludes_file; +extern const char *editor_program; +extern const char *excludes_file; /* base85 */ int decode_85(char *dst, const char *line, int linelen); @@@ -734,8 -685,7 +743,8 @@@ extern void trace_argv_printf(const cha /* convert.c */ /* returns 1 if *dst was used */ -extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst); +extern int convert_to_git(const char *path, const char *src, size_t len, + struct strbuf *dst, enum safe_crlf checksafe); extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); /* add */ @@@ -754,7 -704,6 +763,7 @@@ void shift_tree(const unsigned char *, #define WS_TRAILING_SPACE 01 #define WS_SPACE_BEFORE_TAB 02 #define WS_INDENT_WITH_NON_TAB 04 +#define WS_CR_AT_EOL 010 #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB) extern unsigned whitespace_rule_cfg; extern unsigned whitespace_rule(const char *); @@@ -763,13 -712,10 +772,13 @@@ extern unsigned check_and_emit_line(con FILE *stream, const char *set, const char *reset, const char *ws); extern char *whitespace_error_string(unsigned ws); +extern int ws_fix_copy(char *, const char *, int, unsigned, int *); /* ls-files */ int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen); int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset); void overlay_tree_on_cache(const char *tree_name, const char *prefix); +char *alias_lookup(const char *alias); + #endif /* CACHE_H */ diff --combined config.c index cba2bcfb67,97d75051a4..062449459e --- a/config.c +++ b/config.c @@@ -280,18 -280,11 +280,18 @@@ int git_parse_ulong(const char *value, return 0; } +static void die_bad_config(const char *name) +{ + if (config_file_name) + die("bad config value for '%s' in %s", name, config_file_name); + die("bad config value for '%s'", name); +} + int git_config_int(const char *name, const char *value) { long ret; if (!git_parse_long(value, &ret)) - die("bad config value for '%s' in %s", name, config_file_name); + die_bad_config(name); return ret; } @@@ -299,7 -292,7 +299,7 @@@ unsigned long git_config_ulong(const ch { unsigned long ret; if (!git_parse_ulong(value, &ret)) - die("bad config value for '%s' in %s", name, config_file_name); + die_bad_config(name); return ret; } @@@ -316,14 -309,6 +316,14 @@@ int git_config_bool(const char *name, c return git_config_int(name, value) != 0; } +int git_config_string(const char **dest, const char *var, const char *value) +{ + if (!value) + return config_error_nonbool(var); + *dest = xstrdup(value); + return 0; +} + int git_default_config(const char *var, const char *value) { /* This needs a better name */ @@@ -422,55 -407,61 +422,63 @@@ return 0; } + if (!strcmp(var, "core.safecrlf")) { + if (value && !strcasecmp(value, "warn")) { + safe_crlf = SAFE_CRLF_WARN; + return 0; + } + safe_crlf = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "user.name")) { + if (!value) + return config_error_nonbool(var); strlcpy(git_default_name, value, sizeof(git_default_name)); return 0; } if (!strcmp(var, "user.email")) { + if (!value) + return config_error_nonbool(var); strlcpy(git_default_email, value, sizeof(git_default_email)); return 0; } - if (!strcmp(var, "i18n.commitencoding")) { - git_commit_encoding = xstrdup(value); - return 0; - } - - if (!strcmp(var, "i18n.logoutputencoding")) { - git_log_output_encoding = xstrdup(value); - return 0; - } + if (!strcmp(var, "i18n.commitencoding")) + return git_config_string(&git_commit_encoding, var, value); + if (!strcmp(var, "i18n.logoutputencoding")) + return git_config_string(&git_log_output_encoding, var, value); if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { pager_use_color = git_config_bool(var,value); return 0; } - if (!strcmp(var, "core.pager")) { - pager_program = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.pager")) + return git_config_string(&pager_program, var, value); - if (!strcmp(var, "core.editor")) { - editor_program = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.editor")) + return git_config_string(&editor_program, var, value); - if (!strcmp(var, "core.excludesfile")) { - if (!value) - die("core.excludesfile without value"); - excludes_file = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.excludesfile")) + return git_config_string(&excludes_file, var, value); if (!strcmp(var, "core.whitespace")) { + if (!value) + return config_error_nonbool(var); whitespace_rule_cfg = parse_whitespace_rule(value); return 0; } + if (!strcmp(var, "branch.autosetupmerge")) { + if (value && !strcasecmp(value, "always")) { + git_branch_track = BRANCH_TRACK_ALWAYS; + return 0; + } + git_branch_track = git_config_bool(var, value); + return 0; + } /* Add other config variables here and to Documentation/config.txt. */ return 0; @@@ -501,30 -492,14 +509,30 @@@ const char *git_etc_gitconfig(void system_wide = ETC_GITCONFIG; if (!is_absolute_path(system_wide)) { /* interpret path relative to exec-dir */ - const char *exec_path = git_exec_path(); - system_wide = prefix_path(exec_path, strlen(exec_path), - system_wide); + struct strbuf d = STRBUF_INIT; + strbuf_addf(&d, "%s/%s", git_exec_path(), system_wide); + system_wide = strbuf_detach(&d, NULL); } } return system_wide; } +int git_env_bool(const char *k, int def) +{ + const char *v = getenv(k); + return v ? git_config_bool(k, v) : def; +} + +int git_config_system(void) +{ + return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0); +} + +int git_config_global(void) +{ + return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0); +} + int git_config(config_fn_t fn) { int ret = 0; @@@ -537,7 -512,7 +545,7 @@@ * config file otherwise. */ filename = getenv(CONFIG_ENVIRONMENT); if (!filename) { - if (!access(git_etc_gitconfig(), R_OK)) + if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) ret += git_config_from_file(fn, git_etc_gitconfig()); home = getenv("HOME"); filename = getenv(CONFIG_LOCAL_ENVIRONMENT); @@@ -545,7 -520,7 +553,7 @@@ filename = repo_config = xstrdup(git_path("config")); } - if (home) { + if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) ret = git_config_from_file(fn, user_config); @@@ -734,17 -709,12 +742,17 @@@ static ssize_t find_beginning_of_line(c size_t equal_offset = size, bracket_offset = size; ssize_t offset; +contline: for (offset = offset_-2; offset > 0 && contents[offset] != '\n'; offset--) switch (contents[offset]) { case '=': equal_offset = offset; break; case ']': bracket_offset = offset; break; } + if (offset > 0 && contents[offset-1] == '\\') { + offset_ = offset; + goto contline; + } if (bracket_offset < equal_offset) { *found_bracket = 1; offset = bracket_offset+1; @@@ -1112,12 -1082,3 +1120,12 @@@ int git_config_rename_section(const cha free(config_filename); return ret; } + +/* + * Call this to report error for your variable that should not + * get a boolean value (i.e. "[my] var" means "true"). + */ +int config_error_nonbool(const char *var) +{ + return error("Missing value for '%s'", var); +} diff --combined environment.c index 3527f1663f,1f74b4b557..6739a3f417 --- a/environment.c +++ b/environment.c @@@ -30,13 -30,13 +30,14 @@@ int core_compression_seen size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE; size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT; size_t delta_base_cache_limit = 16 * 1024 * 1024; -char *pager_program; +const char *pager_program; int pager_use_color = 1; -char *editor_program; -char *excludes_file; +const char *editor_program; +const char *excludes_file; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ +enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; + enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; diff --combined t/t3200-branch.sh index d21081d0f1,900d814627..38a90adad6 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@@ -15,13 -15,15 +15,16 @@@ test_expect_success 'echo Hello > A && git update-index --add A && git-commit -m "Initial commit." && + echo World >> A && + git update-index --add A && + git-commit -m "Second commit." && HEAD=$(git rev-parse --verify HEAD)' -test_expect_failure \ - 'git branch --help should not have created a bogus branch' \ - 'git branch --help /dev/null 2>/dev/null || : - test -f .git/refs/heads/--help' +test_expect_success \ + 'git branch --help should not have created a bogus branch' ' + git branch --help /dev/null 2>/dev/null; + ! test -f .git/refs/heads/--help +' test_expect_success \ 'git branch abc should create a branch' \ @@@ -72,17 -74,17 +75,17 @@@ test_expect_success git branch -m n/n n test -f .git/logs/refs/heads/n' -test_expect_failure \ - 'git branch -m o/o o should fail when o/p exists' \ - 'git branch o/o && +test_expect_success 'git branch -m o/o o should fail when o/p exists' ' + git branch o/o && git branch o/p && - git branch -m o/o o' + ! git branch -m o/o o +' -test_expect_failure \ - 'git branch -m q r/q should fail when r exists' \ - 'git branch q && - git branch r && - git branch -m q r/q' +test_expect_success 'git branch -m q r/q should fail when r exists' ' + git branch q && + git branch r && + ! git branch -m q r/q +' mv .git/config .git/config-saved @@@ -109,13 -111,12 +112,13 @@@ test_expect_success 'config informatio "test $(git config branch.s.dummy) = Hello && ! git config branch.s/s/dummy" -test_expect_failure \ - 'git branch -m u v should fail when the reflog for u is a symlink' \ - 'git branch -l u && +test_expect_success \ + 'git branch -m u v should fail when the reflog for u is a symlink' ' + git branch -l u && mv .git/logs/refs/heads/u real-u && ln -s real-u .git/logs/refs/heads/u && - git branch -m u v' + ! git branch -m u v +' test_expect_success 'test tracking setup via --track' \ 'git config remote.local.url . && @@@ -171,7 -172,9 +174,9 @@@ test_expect_success 'test overriding tr ! test "$(git config branch.my2.merge)" = refs/heads/master' test_expect_success 'no tracking without .fetch entries' \ - 'git branch --track my6 s && + 'git config branch.autosetupmerge true && + git branch my6 s && + git config branch.automsetupmerge false && test -z "$(git config branch.my6.remote)" && test -z "$(git config branch.my6.merge)"' @@@ -192,6 -195,21 +197,21 @@@ test_expect_success 'test deleting bran 'git branch my7 s && test "$(git branch -d my7 2>&1)" = "Deleted branch my7."' + test_expect_success 'test --track without .fetch entries' \ + 'git branch --track my8 && + test "$(git config branch.my8.remote)" && + test "$(git config branch.my8.merge)"' + + test_expect_success \ + 'branch from non-branch HEAD w/autosetupmerge=always' \ + 'git config branch.autosetupmerge always && + git branch my9 HEAD^ && + git config branch.autosetupmerge false' + + test_expect_success \ + 'branch from non-branch HEAD w/--track causes failure' \ + '!(git branch --track my10 HEAD^)' + # Keep this test last, as it changes the current branch cat >expect < 1117150200 +0000 branch: Created from master diff --combined t/t7201-co.sh index 724adef0bf,17cff8d515..63915cd87b --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@@ -207,22 -207,6 +207,22 @@@ test_expect_success 'checkout to detac fi ' +test_expect_success 'checkout to detach HEAD with :/message' ' + + git checkout -f master && git clean -f && + git checkout ":/Initial" && + H=$(git rev-parse --verify HEAD) && + M=$(git show-ref -s --verify refs/heads/master) && + test "z$H" = "z$M" && + if git symbolic-ref HEAD >/dev/null 2>&1 + then + echo "OOPS, HEAD is still symbolic???" + false + else + : happy + fi +' + test_expect_success 'checkout to detach HEAD with HEAD^0' ' git checkout -f master && git clean -f && @@@ -279,38 -263,28 +279,62 @@@ test_expect_success 'checkout with ambi ' +test_expect_success 'switch branches while in subdirectory' ' + + git reset --hard && + git checkout master && + + mkdir subs && + ( + cd subs && + git checkout side + ) && + ! test -f subs/one && + rm -fr subs + +' + +test_expect_success 'checkout specific path while in subdirectory' ' + + git reset --hard && + git checkout side && + mkdir subs && + >subs/bero && + git add subs/bero && + git commit -m "add subs/bero" && + + git checkout master && + mkdir -p subs && + ( + cd subs && + git checkout side -- bero + ) && + test -f subs/bero + +' + + test_expect_success \ + 'checkout w/--track sets up tracking' ' + git config branch.autosetupmerge false && + git checkout master && + git checkout --track -b track1 && + test "$(git config branch.track1.remote)" && + test "$(git config branch.track1.merge)"' + + test_expect_success \ + 'checkout w/autosetupmerge=always sets up tracking' ' + git config branch.autosetupmerge always && + git checkout master && + git checkout -b track2 && + test "$(git config branch.track2.remote)" && + 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)" && + !(git checkout --track -b track)' + test_done