[[Note that none of these are not merged to 'master' as of this writing
but they will be before 1.6.0 happens]]
-With default Makefile settings, most of the programs are now installed
-outside your $PATH, except for "git", "gitk", "git-gui" and some server
-side programs that needs to be accessible when connecting over ssh.
-
-When talking to remote repository over ssh, necessary server side programs
-are now invoked with "git $program" notation, not with "git-$program"
-notation. This should work with both servers running older git where you
-had all of these programs installed on $PATH, or newer git where you have
-only "git" on $PATH. However, if the remote side is running a custom
-software that restricts programs you can run over ssh, it might cause
-problems. Use --upload-pack="git-upload-pack" (when using ls-remote,
-fetch and pull on the client side), --receive-pack="git-receive-pack"
-(when using push on the client side), or --exec="git-upload-archive" (when
-using git-archive) as appropriate when talking to such a remote.
+With the default Makefile settings, most of the programs are now
+installed outside your $PATH, except for "git", "gitk", "git-gui" and
+some server side programs that need to be accessible for technical
+reasons. Invoking a git subcommand as "git-xyzzy" from the command
+line has been deprecated since early 2006 (and officially announced in
+1.5.4 release notes); use of them from your scripts after adding
+output from "git --exec-path" to the $PATH is still supported in this
+release, but users are again strongly encouraged to adjust their
+scripts to use "git xyzzy" form, as we will stop installing
+"git-xyzzy" hardlinks for built-in commands in later releases.
Source changes needed for porting to MinGW environment are now all in the
main git.git codebase.
(subsystems)
+* git-p4 in contrib learned "allowSubmit" configuration to control on
+ which branch to allow "submit" subcommand.
+
(portability)
* Sample hook scripts shipped in templates/ are now suffixed with
* Updated howto/update-hook-example
-(performance, robustness etc.)
+* Got rid of usage of "git-foo" from the tutorial.
+
+* Disambiguating "--" between revs and paths is finally documented.
+
+(performance, robustness, sanity etc.)
+
+* even more documentation pages are now accessible via "man" and "git help".
* reduced excessive inlining to shrink size of the "git" binary.
objects created will be fsync'ed (this is only useful on filesystems
that does not order data writes properly).
+* "git commit-tree" plumbing can make Octopus with more than 16 parents.
+ "git commit" has been capable of this for quite some time.
+
(usability, bells and whistles)
* git-archive can be told to omit certain paths from its output using
---
exec >/var/tmp/1
-O=v1.5.6.1-77-gf9a08f6
+O=v1.5.6.1-104-ga08b868
echo O=$(git describe refs/heads/master)
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
# the command.
[attributes]
+asterisk=*
plus=+
caret=^
startsb=[
legacy pack index used by Git versions prior to 1.5.2, and 2 for
the new pack index with capabilities for packs larger than 4 GB
as well as proper protection against the repacking of corrupted
- packs. Version 2 is selected and this config option ignored
- whenever the corresponding pack is larger than 2 GB. Otherwise
- the default is 1.
+ packs. Version 2 is the default. Note that version 2 is enforced
+ and this config option ignored whenever the corresponding pack is
+ larger than 2 GB.
++
+If you have an old git that does not understand the version 2 `{asterisk}.idx` file,
+cloning or fetching over a non native protocol (e.g. "http" and "rsync")
+that will copy both `{asterisk}.pack` file and corresponding `{asterisk}.idx` file from the
+other side may give you a repository that cannot be accessed with your
+older version of git. If the `{asterisk}.pack` file is smaller than 2 GB, however,
+you can use linkgit:git-index-pack[1] on the *.pack file to regenerate
+the `{asterisk}.idx` file.
pack.packSizeLimit::
The default maximum size of a pack. This setting only affects
<group>". See linkgit:git-remote[1].
repack.usedeltabaseoffset::
- Allow linkgit:git-repack[1] to create packs that uses
- delta-base offset. Defaults to false.
-
-show.difftree::
- The default linkgit:git-diff-tree[1] arguments to be used
- for linkgit:git-show[1].
+ By default, linkgit:git-repack[1] creates packs that use
+ delta-base offset. If you need to share your repository with
+ git older than version 1.4.4, either directly or via a dumb
+ protocol such as http, then you need to set this option to
+ "false" and repack. Access from old git versions over the
+ native protocol are unaffected by this option.
showbranch.default::
The default set of branches for linkgit:git-show-branch[1].
unchanged to gpg's --local-user parameter, so you may specify a key
using any method that gpg supports.
-whatchanged.difftree::
- The default linkgit:git-diff-tree[1] arguments to be used
- for linkgit:git-whatchanged[1].
-
imap::
The configuration variables in the 'imap' section are described
in linkgit:git-imap-send[1].
SYNOPSIS
--------
-'. git-parse-remote'
+'. "$(git --exec-path)/git-parse-remote"'
DESCRIPTION
-----------
second ago\}' or '\{1979-02-26 18:30:00\}') to specify the value
of the ref at a prior point in time. This suffix may only be
used immediately following a ref name and the ref must have an
- existing log ($GIT_DIR/logs/<ref>).
+ existing log ($GIT_DIR/logs/<ref>). Note that this looks up the state
+ of your *local* ref at a given time; e.g., what was in your local
+ `master` branch last week. If you want to look at commits made during
+ certain times, see `--since` and `--until`.
* A ref followed by the suffix '@' with an ordinal specification
enclosed in a brace pair (e.g. '\{1\}', '\{15\}') to specify
specified (with --smtp-pass or a configuration variable), then the
user is prompted for a password while the input is masked for privacy.
+--smtp-encryption::
+ Specify the encryption to use, either 'ssl' or 'tls'. Any other
+ value reverts to plain SMTP. Default is the value of
+ 'sendemail.smtpencryption'.
+
--smtp-ssl::
- If set, connects to the SMTP server using SSL.
- Default is the value of the 'sendemail.smtpssl' configuration value;
- if that is unspecified, does not use SSL.
+ Legacy alias for '--smtp-encryption=ssl'.
--subject::
Specify the initial subject of the email thread.
sendemail.smtppass::
Default SMTP-AUTH password.
+sendemail.smtpencryption::
+ Default encryption method. Use 'ssl' for SSL (and specify an
+ appropriate port), or 'tls' for TLS. Takes precedence over
+ 'smtpssl' if both are specified.
+
sendemail.smtpssl::
- Boolean value specifying the default to the '--smtp-ssl' parameter.
+ Legacy boolean that sets 'smtpencryption=ssl' if enabled.
Author
------
SYNOPSIS
--------
-'git-sh-setup'
+'. "$(git --exec-path)/git-sh-setup"'
DESCRIPTION
-----------
cd project
git-init
git remote add origin server:/pub/project
- git config --add remote.origin.fetch=+refs/remotes/*:refs/remotes/*
+ git config --add remote.origin.fetch '+refs/remotes/*:refs/remotes/*'
git fetch
# Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server)
git-svn init http://svn.foo.org/project
DESCRIPTION
-----------
-This manual describes best practice in how to use git CLI. Here are
-the rules that you should follow when you are scripting git:
+This manual describes the convention used throughout git CLI.
+
+Many commands take revisions (most often "commits", but sometimes
+"tree-ish", depending on the context and command) and paths as their
+arguments. Here are the rules:
+
+ * Revisions come first and then paths.
+ E.g. in `git diff v1.0 v2.0 arch/x86 include/asm-x86`,
+ `v1.0` and `v2.0` are revisions and `arch/x86` and `include/asm-x86`
+ are paths.
+
+ * When an argument can be misunderstood as either a revision or a path,
+ they can be disambiguated by placing `\--` between them.
+ E.g. `git diff \-- HEAD` is, "I have a file called HEAD in my work
+ tree. Please show changes between the version I staged in the index
+ and what I have in the work tree for that file". not "show difference
+ between the HEAD commit and the work tree as a whole". You can say
+ `git diff HEAD \--` to ask for the latter.
+
+ * Without disambiguating `\--`, git makes a reasonable guess, but errors
+ out and asking you to disambiguate when ambiguous. E.g. if you have a
+ file called HEAD in your work tree, `git diff HEAD` is ambiguous, and
+ you have to say either `git diff HEAD \--` or `git diff \-- HEAD` to
+ disambiguate.
+
+When writing a script that is expected to handle random user-input, it is
+a good practice to make it explicit which arguments are which by placing
+disambiguating `\--` at appropriate places.
+
+Here are the rules regarding the "flags" that you should follow when you are
+scripting git:
* it's preferred to use the non dashed form of git commands, which means that
you should prefer `"git foo"` to `"git-foo"`.
if you happen to have a file called `HEAD` in the work tree.
-ENHANCED CLI
-------------
+ENHANCED OPTION PARSER
+----------------------
From the git 1.5.4 series and further, many git commands (not all of them at the
time of the writing though) come with an enhanced option parser.
case "$VN" in
*$LF*) (exit 1) ;;
v[0-9]*)
- test -z "$(git diff-index --name-only HEAD)" ||
+ test -z "$(git diff-index --name-only HEAD --)" ||
VN="$VN-dirty" ;;
esac
then
bindir = $(prefix)/bin
mandir = $(prefix)/share/man
infodir = $(prefix)/share/info
-gitexecdir = $(bindir)
+gitexecdir = $(prefix)/libexec/git-core
sharedir = $(prefix)/share
template_dir = $(sharedir)/git-core/templates
htmldir=$(sharedir)/doc/git-doc
LIB_H += utf8.h
LIB_H += wt-status.h
+LIB_OBJS += abspath.o
LIB_OBJS += alias.o
LIB_OBJS += alloc.o
LIB_OBJS += archive.o
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
- $(INSTALL) git$X '$(DESTDIR_SQ)$(bindir_SQ)'
+ $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)'
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
ifndef NO_TCLTK
ifneq (,$X)
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';)
endif
+ ./check_bindir 'z$(bindir_SQ)' 'z$(gitexecdir_SQ)' '$(DESTDIR_SQ)$(bindir_SQ)/git-shell$X'
install-doc:
$(MAKE) -C Documentation install
--- /dev/null
+#include "cache.h"
+
+/* We allow "recursive" symbolic links. Only within reason, though. */
+#define MAXDEPTH 5
+
+const char *make_absolute_path(const char *path)
+{
+ static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
+ char cwd[1024] = "";
+ int buf_index = 1, len;
+
+ int depth = MAXDEPTH;
+ char *last_elem = NULL;
+ struct stat st;
+
+ if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
+ die ("Too long path: %.*s", 60, path);
+
+ while (depth--) {
+ if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
+ char *last_slash = strrchr(buf, '/');
+ if (last_slash) {
+ *last_slash = '\0';
+ last_elem = xstrdup(last_slash + 1);
+ } else {
+ last_elem = xstrdup(buf);
+ *buf = '\0';
+ }
+ }
+
+ if (*buf) {
+ if (!*cwd && !getcwd(cwd, sizeof(cwd)))
+ die ("Could not get current working directory");
+
+ if (chdir(buf))
+ die ("Could not switch to '%s'", buf);
+ }
+ if (!getcwd(buf, PATH_MAX))
+ die ("Could not get current working directory");
+
+ if (last_elem) {
+ int len = strlen(buf);
+ if (len + strlen(last_elem) + 2 > PATH_MAX)
+ die ("Too long path name: '%s/%s'",
+ buf, last_elem);
+ buf[len] = '/';
+ strcpy(buf + len + 1, last_elem);
+ free(last_elem);
+ last_elem = NULL;
+ }
+
+ if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
+ len = readlink(buf, next_buf, PATH_MAX);
+ if (len < 0)
+ die ("Invalid symlink: %s", buf);
+ next_buf[len] = '\0';
+ buf = next_buf;
+ buf_index = 1 - buf_index;
+ next_buf = bufs[buf_index];
+ } else
+ break;
+ }
+
+ if (*cwd && chdir(cwd))
+ die ("Could not change back to '%s'", cwd);
+
+ return buf;
+}
#include "blob.h"
#include "delta.h"
#include "builtin.h"
+#include "path-list.h"
/*
* --check turns on checking that the working tree matches the
struct line *line;
};
+/*
+ * Records filenames that have been touched, in order to handle
+ * the case where more than one patches touch the same file.
+ */
+
+static struct path_list fn_table;
+
static uint32_t hash_line(const char *cp, size_t len)
{
size_t i;
static void check_whitespace(const char *line, int len, unsigned ws_rule)
{
char *err;
- unsigned result = check_and_emit_line(line + 1, len - 1, ws_rule,
- NULL, NULL, NULL, NULL);
+ unsigned result = ws_check(line + 1, len - 1, ws_rule);
if (!result)
return;
else {
err = whitespace_error_string(result);
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
- patch_input_file, linenr, err, len - 2, line + 1);
+ patch_input_file, linenr, err, len - 2, line + 1);
free(err);
}
}
return 0;
}
+static struct patch *in_fn_table(const char *name)
+{
+ struct path_list_item *item;
+
+ if (name == NULL)
+ return NULL;
+
+ item = path_list_lookup(name, &fn_table);
+ if (item != NULL)
+ return (struct patch *)item->util;
+
+ return NULL;
+}
+
+static void add_to_fn_table(struct patch *patch)
+{
+ struct path_list_item *item;
+
+ /*
+ * Always add new_name unless patch is a deletion
+ * This should cover the cases for normal diffs,
+ * file creations and copies
+ */
+ if (patch->new_name != NULL) {
+ item = path_list_insert(patch->new_name, &fn_table);
+ item->util = patch;
+ }
+
+ /*
+ * store a failure on rename/deletion cases because
+ * later chunks shouldn't patch old names
+ */
+ if ((patch->new_name == NULL) || (patch->is_rename)) {
+ item = path_list_insert(patch->old_name, &fn_table);
+ item->util = (struct patch *) -1;
+ }
+}
+
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
{
struct strbuf buf;
struct image image;
size_t len;
char *img;
+ struct patch *tpatch;
strbuf_init(&buf, 0);
- if (cached) {
+
+ if ((tpatch = in_fn_table(patch->old_name)) != NULL) {
+ if (tpatch == (struct patch *) -1) {
+ return error("patch %s has been renamed/deleted",
+ patch->old_name);
+ }
+ /* We have a patched copy in memory use that */
+ strbuf_add(&buf, tpatch->result, tpatch->resultsize);
+ } else if (cached) {
if (read_file_or_gitlink(ce, &buf))
return error("read of %s failed", patch->old_name);
} else if (patch->old_name) {
return -1; /* note with --reject this succeeds. */
patch->result = image.buf;
patch->resultsize = image.len;
+ add_to_fn_table(patch);
free(image.line_allocated);
if (0 < patch->is_delete && patch->resultsize)
static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
{
const char *old_name = patch->old_name;
+ struct patch *tpatch;
int stat_ret = 0;
unsigned st_mode = 0;
return 0;
assert(patch->is_new <= 0);
- if (!cached) {
+ if ((tpatch = in_fn_table(old_name)) != NULL) {
+ if (tpatch == (struct patch *) -1) {
+ return error("%s: has been deleted/renamed", old_name);
+ }
+ st_mode = tpatch->new_mode;
+ } else if (!cached) {
stat_ret = lstat(old_name, st);
if (stat_ret && errno != ENOENT)
return error("%s: %s", old_name, strerror(errno));
}
- if (check_index) {
+ if (check_index && !tpatch) {
int pos = cache_name_pos(old_name, strlen(old_name));
if (pos < 0) {
if (patch->is_new < 0)
return 0;
}
-static int check_patch(struct patch *patch, struct patch *prev_patch)
+static int check_patch(struct patch *patch)
{
struct stat st;
const char *old_name = patch->old_name;
return status;
old_name = patch->old_name;
- if (new_name && prev_patch && 0 < prev_patch->is_delete &&
- !strcmp(prev_patch->old_name, new_name))
+ if (in_fn_table(new_name) == (struct patch *) -1)
/*
* A type-change diff is always split into a patch to
* delete old, immediately followed by a patch to
static int check_patch_list(struct patch *patch)
{
- struct patch *prev_patch = NULL;
int err = 0;
- for (prev_patch = NULL; patch ; patch = patch->next) {
+ while (patch) {
if (apply_verbosely)
say_patch_name(stderr,
"Checking patch ", patch, "...\n");
- err |= check_patch(patch, prev_patch);
- prev_patch = patch;
+ err |= check_patch(patch);
+ patch = patch->next;
}
return err;
}
struct patch *list = NULL, **listp = &list;
int skipped_patch = 0;
+ /* FIXME - memory leak when using multiple patch files as inputs */
+ memset(&fn_table, 0, sizeof(struct path_list));
strbuf_init(&buf, 0);
patch_input_file = filename;
read_patch_file(&buf, fd);
write_or_die(1, contents, size);
printf("\n");
fflush(stdout);
+ free(contents);
}
return 0;
fprintf(stderr, "Initialize %s\n", git_dir);
init_db(option_template, option_quiet ? INIT_DB_QUIET : 0);
+ /*
+ * At this point, the config exists, so we do not need the
+ * environment variable. We actually need to unset it, too, to
+ * re-enable parsing of the global configs.
+ */
+ unsetenv(CONFIG_ENVIRONMENT);
+
if (option_reference)
setup_reference(git_dir);
refs = clone_local(path, git_dir);
else {
struct remote *remote = remote_get(argv[0]);
- struct transport *transport = transport_get(remote, argv[0]);
+ struct transport *transport =
+ transport_get(remote, remote->url[0]);
if (!transport->get_refs_list || !transport->fetch)
die("Don't know how to clone %s", transport->url);
typename(expect));
}
-/*
- * Having more than two parents is not strange at all, and this is
- * how multi-way merges are represented.
- */
-#define MAXPARENT (16)
-static unsigned char parent_sha1[MAXPARENT][20];
-
static const char commit_tree_usage[] = "git-commit-tree <sha1> [-p <sha1>]* < changelog";
-static int new_parent(int idx)
+static void new_parent(struct commit *parent, struct commit_list **parents_p)
{
- int i;
- unsigned char *sha1 = parent_sha1[idx];
- for (i = 0; i < idx; i++) {
- if (!hashcmp(parent_sha1[i], sha1)) {
+ unsigned char *sha1 = parent->object.sha1;
+ struct commit_list *parents;
+ for (parents = *parents_p; parents; parents = parents->next) {
+ if (parents->item == parent) {
error("duplicate parent %s ignored", sha1_to_hex(sha1));
- return 0;
+ return;
}
+ parents_p = &parents->next;
}
- return 1;
+ commit_list_insert(parent, parents_p);
}
static const char commit_utf8_warn[] =
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{
int i;
- int parents = 0;
+ struct commit_list *parents = NULL;
unsigned char tree_sha1[20];
unsigned char commit_sha1[20];
struct strbuf buffer;
check_valid(tree_sha1, OBJ_TREE);
for (i = 2; i < argc; i += 2) {
+ unsigned char sha1[20];
const char *a, *b;
a = argv[i]; b = argv[i+1];
if (!b || strcmp(a, "-p"))
usage(commit_tree_usage);
- if (parents >= MAXPARENT)
- die("Too many parents (%d max)", MAXPARENT);
- if (get_sha1(b, parent_sha1[parents]))
+ if (get_sha1(b, sha1))
die("Not a valid object name %s", b);
- check_valid(parent_sha1[parents], OBJ_COMMIT);
- if (new_parent(parents))
- parents++;
+ check_valid(sha1, OBJ_COMMIT);
+ new_parent(lookup_commit(sha1), &parents);
}
/* Not having i18n.commitencoding is the same as having utf-8 */
* different order of parents will be a _different_ changeset even
* if everything else stays the same.
*/
- for (i = 0; i < parents; i++)
- strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i]));
+ while (parents) {
+ struct commit_list *next = parents->next;
+ strbuf_addf(&buffer, "parent %s\n",
+ sha1_to_hex(parents->item->object.sha1));
+ free(parents);
+ parents = next;
+ }
/* Person/date information */
strbuf_addf(&buffer, "author %s\n", git_author_info(IDENT_ERROR_ON_NO_NAME));
lock = lock_any_ref_for_update(ref->name,
check_old ? ref->old_sha1 : NULL, 0);
if (!lock)
- return 1;
+ return 2;
if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
- return 1;
+ return 2;
return 0;
}
if (!is_null_sha1(ref->old_sha1) &&
!prefixcmp(ref->name, "refs/tags/")) {
- sprintf(display, "- %-*s %-*s -> %s",
+ int r;
+ r = s_update_ref("updating tag", ref, 0);
+ sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
- pretty_ref);
- return s_update_ref("updating tag", ref, 0);
+ pretty_ref, r ? " (unable to update local ref)" : "");
+ return r;
}
current = lookup_commit_reference_gently(ref->old_sha1, 1);
if (!current || !updated) {
const char *msg;
const char *what;
+ int r;
if (!strncmp(ref->name, "refs/tags/", 10)) {
msg = "storing tag";
what = "[new tag]";
what = "[new branch]";
}
- sprintf(display, "* %-*s %-*s -> %s", SUMMARY_WIDTH, what,
- REFCOL_WIDTH, remote, pretty_ref);
- return s_update_ref(msg, ref, 0);
+ r = s_update_ref(msg, ref, 0);
+ sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
+ SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
+ r ? " (unable to update local ref)" : "");
+ return r;
}
if (in_merge_bases(current, &updated, 1)) {
char quickref[83];
+ int r;
strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
strcat(quickref, "..");
strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
- sprintf(display, " %-*s %-*s -> %s", SUMMARY_WIDTH, quickref,
- REFCOL_WIDTH, remote, pretty_ref);
- return s_update_ref("fast forward", ref, 1);
+ r = s_update_ref("fast forward", ref, 1);
+ sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
+ SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
+ pretty_ref, r ? " (unable to update local ref)" : "");
+ return r;
} else if (force || ref->force) {
char quickref[84];
+ int r;
strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
strcat(quickref, "...");
strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
- sprintf(display, "+ %-*s %-*s -> %s (forced update)",
- SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref);
- return s_update_ref("forced-update", ref, 1);
+ r = s_update_ref("forced-update", ref, 1);
+ sprintf(display, "%c %-*s %-*s -> %s (%s)", r ? '!' : '+',
+ SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
+ pretty_ref,
+ r ? "unable to update local ref" : "forced update");
+ return r;
} else {
sprintf(display, "! %-*s %-*s -> %s (non fast forward)",
SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
}
}
-static int store_updated_refs(const char *url, struct ref *ref_map)
+static int store_updated_refs(const char *url, const char *remote_name,
+ struct ref *ref_map)
{
FILE *fp;
struct commit *commit;
}
}
fclose(fp);
+ if (rc & 2)
+ error("some local refs could not be updated; try running\n"
+ " 'git remote prune %s' to remove any old, conflicting "
+ "branches", remote_name);
return rc;
}
if (ret)
ret = transport_fetch_refs(transport, ref_map);
if (!ret)
- ret |= store_updated_refs(transport->url, ref_map);
+ ret |= store_updated_refs(transport->url,
+ transport->remote->name,
+ ref_map);
transport_unlock_pack(transport);
return ret;
}
reflog_action = args_to_str(argv);
setenv("GIT_REFLOG_ACTION", reflog_action, 0);
- if (i < argc && strcmp(argv[i], "--"))
- rev = argv[i++];
+ /*
+ * Possible arguments are:
+ *
+ * git reset [-opts] <rev> <paths>...
+ * git reset [-opts] <rev> -- <paths>...
+ * git reset [-opts] -- <paths>...
+ * git reset [-opts] <paths>...
+ *
+ * At this point, argv[i] points immediately after [-opts].
+ */
+
+ if (i < argc) {
+ if (!strcmp(argv[i], "--")) {
+ i++; /* reset to HEAD, possibly with paths */
+ } else if (i + 1 < argc && !strcmp(argv[i+1], "--")) {
+ rev = argv[i];
+ i += 2;
+ }
+ /*
+ * Otherwise, argv[i] could be either <rev> or <paths> and
+ * has to be unambigous.
+ */
+ else if (!get_sha1(argv[i], sha1)) {
+ /*
+ * Ok, argv[i] looks like a rev; it should not
+ * be a filename.
+ */
+ verify_non_filename(prefix, argv[i]);
+ rev = argv[i++];
+ } else {
+ /* Otherwise we treat this as a filename */
+ verify_filename(prefix, argv[i]);
+ }
+ }
if (get_sha1(rev, sha1))
die("Failed to resolve '%s' as a valid ref.", rev);
die("Could not parse object '%s'.", rev);
hashcpy(sha1, commit->object.sha1);
- if (i < argc && !strcmp(argv[i], "--"))
- i++;
-
/* git reset tree [--] paths... can be used to
* load chosen paths from the tree into the index without
* affecting the working tree nor HEAD. */
if (argc != 2)
usage(upload_archive_usage);
- if (strlen(argv[1]) > sizeof(buf))
+ if (strlen(argv[1]) + 1 > sizeof(buf))
die("insanely long repository name");
strcpy(buf, argv[1]); /* enter-repo smudges its argument */
extern unsigned whitespace_rule_cfg;
extern unsigned whitespace_rule(const char *);
extern unsigned parse_whitespace_rule(const char *);
-extern unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
- FILE *stream, const char *set,
- const char *reset, const char *ws);
+extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
+extern void ws_check_emit(const char *line, int len, unsigned ws_rule, 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 *);
+extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
/* ls-files */
int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
--- /dev/null
+#!/bin/sh
+bindir="$1"
+gitexecdir="$2"
+gitcmd="$3"
+if test "$bindir" != "$gitexecdir" -a -x "$gitcmd"
+then
+ echo
+ echo "!! You have installed git-* commands to new gitexecdir."
+ echo "!! Old version git-* commands still remain in bindir."
+ echo "!! Mixing two versions of Git will lead to problems."
+ echo "!! Please remove old version commands in bindir now."
+ echo
+fi
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
-#gitexecdir = @libexecdir@/git-core/
+gitexecdir = @libexecdir@/git-core/
datarootdir = @datarootdir@
template_dir = @datadir@/git-core/templates/
pull.octopus
pull.twohead
repack.useDeltaBaseOffset
- show.difftree
showbranch.default
tar.umask
transfer.unpackLimit
user.name
user.email
user.signingkey
- whatchanged.difftree
branch. remote.
"
}
else {
/* Emit just the prefix, then the rest. */
emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
- (void)check_and_emit_line(line + ecbdata->nparents,
- len - ecbdata->nparents, ecbdata->ws_rule,
- ecbdata->file, set, reset, ws);
+ ws_check_emit(line + ecbdata->nparents,
+ len - ecbdata->nparents, ecbdata->ws_rule,
+ ecbdata->file, set, reset, ws);
}
}
/* Sanity: give at least 5 columns to the graph,
* but leave at least 10 columns for the name.
*/
- if (width < name_width + 15) {
- if (name_width <= 25)
- width = name_width + 15;
- else
- name_width = width - 15;
- }
+ if (width < 25)
+ width = 25;
+ if (name_width < 10)
+ name_width = 10;
+ else if (width < name_width + 15)
+ name_width = width - 15;
/* Find the longest filename and max number of changes */
reset = diff_get_color_opt(options, DIFF_RESET);
struct checkdiff_t {
struct xdiff_emit_state xm;
const char *filename;
- int lineno, color_diff;
+ int lineno;
+ struct diff_options *o;
unsigned ws_rule;
unsigned status;
- FILE *file;
+ int trailing_blanks_start;
};
+static int is_conflict_marker(const char *line, unsigned long len)
+{
+ char firstchar;
+ int cnt;
+
+ if (len < 8)
+ return 0;
+ firstchar = line[0];
+ switch (firstchar) {
+ case '=': case '>': case '<':
+ break;
+ default:
+ return 0;
+ }
+ for (cnt = 1; cnt < 7; cnt++)
+ if (line[cnt] != firstchar)
+ return 0;
+ /* line[0] thru line[6] are same as firstchar */
+ if (firstchar == '=') {
+ /* divider between ours and theirs? */
+ if (len != 8 || line[7] != '\n')
+ return 0;
+ } else if (len < 8 || !isspace(line[7])) {
+ /* not divider before ours nor after theirs */
+ return 0;
+ }
+ return 1;
+}
+
static void checkdiff_consume(void *priv, char *line, unsigned long len)
{
struct checkdiff_t *data = priv;
- const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE);
- const char *reset = diff_get_color(data->color_diff, DIFF_RESET);
- const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW);
+ int color_diff = DIFF_OPT_TST(data->o, COLOR_DIFF);
+ const char *ws = diff_get_color(color_diff, DIFF_WHITESPACE);
+ const char *reset = diff_get_color(color_diff, DIFF_RESET);
+ const char *set = diff_get_color(color_diff, DIFF_FILE_NEW);
char *err;
if (line[0] == '+') {
unsigned bad;
data->lineno++;
- bad = check_and_emit_line(line + 1, len - 1,
- data->ws_rule, NULL, NULL, NULL, NULL);
+ if (!ws_blank_line(line + 1, len - 1, data->ws_rule))
+ data->trailing_blanks_start = 0;
+ else if (!data->trailing_blanks_start)
+ data->trailing_blanks_start = data->lineno;
+ if (is_conflict_marker(line + 1, len - 1)) {
+ data->status |= 1;
+ fprintf(data->o->file,
+ "%s:%d: leftover conflict marker\n",
+ data->filename, data->lineno);
+ }
+ bad = ws_check(line + 1, len - 1, data->ws_rule);
if (!bad)
return;
data->status |= bad;
err = whitespace_error_string(bad);
- fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err);
+ fprintf(data->o->file, "%s:%d: %s.\n",
+ data->filename, data->lineno, err);
free(err);
- emit_line(data->file, set, reset, line, 1);
- (void)check_and_emit_line(line + 1, len - 1, data->ws_rule,
- data->file, set, reset, ws);
- } else if (line[0] == ' ')
+ emit_line(data->o->file, set, reset, line, 1);
+ ws_check_emit(line + 1, len - 1, data->ws_rule,
+ data->o->file, set, reset, ws);
+ } else if (line[0] == ' ') {
data->lineno++;
- else if (line[0] == '@') {
+ data->trailing_blanks_start = 0;
+ } else if (line[0] == '@') {
char *plus = strchr(line, '+');
if (plus)
data->lineno = strtol(plus, NULL, 10) - 1;
else
die("invalid diff");
+ data->trailing_blanks_start = 0;
}
}
static void builtin_checkdiff(const char *name_a, const char *name_b,
const char *attr_path,
- struct diff_filespec *one,
- struct diff_filespec *two, struct diff_options *o)
+ struct diff_filespec *one,
+ struct diff_filespec *two,
+ struct diff_options *o)
{
mmfile_t mf1, mf2;
struct checkdiff_t data;
data.xm.consume = checkdiff_consume;
data.filename = name_b ? name_b : name_a;
data.lineno = 0;
- data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
+ data.o = o;
data.ws_rule = whitespace_rule(attr_path);
- data.file = o->file;
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
+ /*
+ * All the other codepaths check both sides, but not checking
+ * the "old" side here is deliberate. We are checking the newly
+ * introduced changes, and as long as the "new" side is text, we
+ * can and should check what it introduces.
+ */
if (diff_filespec_is_binary(two))
goto free_and_return;
else {
ecb.outf = xdiff_outf;
ecb.priv = &data;
xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+
+ if (data.trailing_blanks_start) {
+ fprintf(o->file, "%s:%d: ends with blank lines.\n",
+ data.filename, data.trailing_blanks_start);
+ data.status = 1; /* report errors */
+ }
}
free_and_return:
diff_free_filespec_data(one);
char git_default_name[MAX_GITNAME];
int user_ident_explicitly_given;
int trust_executable_bit = 1;
-int quote_path_fully = 1;
int has_symlinks = 1;
int ignore_case;
int assume_unchanged;
int execv_git_cmd(const char **argv)
{
- struct strbuf cmd;
- const char *tmp;
-
- strbuf_init(&cmd, 0);
- strbuf_addf(&cmd, "git-%s", argv[0]);
+ int argc;
+ const char **nargv;
- /*
- * argv[0] must be the git command, but the argv array
- * belongs to the caller, and may be reused in
- * subsequent loop iterations. Save argv[0] and
- * restore it on error.
- */
- tmp = argv[0];
- argv[0] = cmd.buf;
+ for (argc = 0; argv[argc]; argc++)
+ ; /* just counting */
+ nargv = xmalloc(sizeof(*nargv) * (argc + 2));
- trace_argv_printf(argv, "trace: exec:");
+ nargv[0] = "git";
+ for (argc = 0; argv[argc]; argc++)
+ nargv[argc + 1] = argv[argc];
+ nargv[argc + 1] = NULL;
+ trace_argv_printf(nargv, "trace: exec:");
/* execvp() can only ever return if it fails */
- execvp(cmd.buf, (char **)argv);
+ execvp("git", (char **)nargv);
trace_printf("trace: exec failed: %s\n", strerror(errno));
- argv[0] = tmp;
-
- strbuf_release(&cmd);
-
+ free(nargv);
return -1;
}
shift
done
-# Later we will default repack.UseDeltaBaseOffset to true
-default_dbo=false
-
-case "`git config --bool repack.usedeltabaseoffset ||
- echo $default_dbo`" in
+case "`git config --bool repack.usedeltabaseoffset || echo true`" in
true)
extra="$extra --delta-base-offset" ;;
esac
--smtp-pass The password for SMTP-AUTH.
- --smtp-ssl If set, connects to the SMTP server using SSL.
+ --smtp-encryption Specify 'tls' for STARTTLS encryption, or 'ssl' for SSL.
+ Any other value disables the feature.
+
+ --smtp-ssl Synonym for '--smtp-encryption=ssl'. Deprecated.
--suppress-cc Suppress the specified category of auto-CC. The category
can be one of 'author' for the patch author, 'self' to
# Variables with corresponding config settings
my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
-my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_ssl);
+my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
my ($no_validate);
my (@suppress_cc);
"chainreplyto" => [\$chain_reply_to, 1],
"suppressfrom" => [\$suppress_from, undef],
"signedoffcc" => [\$signed_off_cc, undef],
- "smtpssl" => [\$smtp_ssl, 0],
);
my %config_settings = (
"smtp-server-port=s" => \$smtp_server_port,
"smtp-user=s" => \$smtp_authuser,
"smtp-pass:s" => \$smtp_authpass,
- "smtp-ssl!" => \$smtp_ssl,
+ "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
+ "smtp-encryption=s" => \$smtp_encryption,
"identity=s" => \$identity,
"compose" => \$compose,
"quiet" => \$quiet,
$$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target);
}
}
+
+ if (!defined $smtp_encryption) {
+ my $enc = Git::config(@repo, "$prefix.smtpencryption");
+ if (defined $enc) {
+ $smtp_encryption = $enc;
+ } elsif (Git::config_bool(@repo, "$prefix.smtpssl")) {
+ $smtp_encryption = 'ssl';
+ }
+ }
}
# read configuration from [sendemail "$identity"], fall back on [sendemail]
${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
}
+# 'default' encryption is none -- this only prevents a warning
+$smtp_encryption = '' unless (defined $smtp_encryption);
+
# Set CC suppressions
my(%suppress_cc);
if (@suppress_cc) {
push @files, grep { -f $_ } map { +$f . "/" . $_ }
sort readdir(DH);
- } elsif (-f $f) {
+ } elsif (-f $f or -p $f) {
push @files, $f;
} else {
if (!$no_validate) {
foreach my $f (@files) {
- my $error = validate_patch($f);
- $error and die "fatal: $f: $error\nwarning: no patches were sent\n";
+ unless (-p $f) {
+ my $error = validate_patch($f);
+ $error and die "fatal: $f: $error\nwarning: no patches were sent\n";
+ }
}
}
die "The required SMTP server is not properly defined."
}
- if ($smtp_ssl) {
+ if ($smtp_encryption eq 'ssl') {
$smtp_server_port ||= 465; # ssmtp
require Net::SMTP::SSL;
$smtp ||= Net::SMTP::SSL->new($smtp_server, Port => $smtp_server_port);
$smtp ||= Net::SMTP->new((defined $smtp_server_port)
? "$smtp_server:$smtp_server_port"
: $smtp_server);
+ if ($smtp_encryption eq 'tls') {
+ require Net::SMTP::SSL;
+ $smtp->command('STARTTLS');
+ $smtp->response();
+ if ($smtp->code == 220) {
+ $smtp = Net::SMTP::SSL->start_SSL($smtp)
+ or die "STARTTLS failed! ".$smtp->message;
+ } else {
+ die "Server does not support STARTTLS! ".$smtp->message;
+ }
+ }
}
if (!$smtp) {
}
}
-# we allow more chars than remotes2config.sh...
-sub sanitize_remote_name {
- my ($name) = @_;
- $name =~ tr{A-Za-z0-9:,/+-}{.}c;
- $name;
-}
-
sub find_existing_remote {
my ($url, $remotes) = @_;
return undef if $no_reuse_existing;
unless (defined $ref_id && length $ref_id) {
$_[2] = $ref_id = $Git::SVN::default_ref_id;
}
- $_[1] = $repo_id = sanitize_remote_name($repo_id);
+ $_[1] = $repo_id;
my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
$_[3] = $path = '' unless (defined $path);
mkpath(["$ENV{GIT_DIR}/svn"]);
my ($tmp_fh, $tmp_filename) = File::Temp::tempfile(UNLINK => 1);
my $result;
while ($result = sysread($fh, my $string, 1024)) {
- syswrite($tmp_fh, $string, $result);
+ my $wrote = syswrite($tmp_fh, $string, $result);
+ defined($wrote) && $wrote == $result
+ or croak("write $tmp_filename: $!\n");
}
defined $result or croak $!;
close $tmp_fh or croak $!;
close $fh or croak $!;
$hash = $::_repository->hash_and_insert_object($tmp_filename);
+ unlink($tmp_filename);
$hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
close $fb->{base} or croak $!;
} else {
# skip existing cases where we already connect to the root
if (($ra->{url} eq $ra->{repos_root}) ||
- (Git::SVN::sanitize_remote_name($ra->{repos_root}) eq
- $repo_id)) {
+ ($ra->{repos_root} eq $repo_id)) {
$root_repos->{$ra->{url}} = $repo_id;
next;
}
foreach my $url (keys %$new_urls) {
# see if we can re-use an existing [svn-remote "repo_id"]
# instead of creating a(n ugly) new section:
- my $repo_id = $root_repos->{$url} ||
- Git::SVN::sanitize_remote_name($url);
+ my $repo_id = $root_repos->{$url} || $url;
my $fetch = $new_urls->{$url};
foreach my $path (keys %$fetch) {
}
}
+static void execv_dashed_external(const char **argv)
+{
+ struct strbuf cmd;
+ const char *tmp;
+
+ strbuf_init(&cmd, 0);
+ strbuf_addf(&cmd, "git-%s", argv[0]);
+
+ /*
+ * argv[0] must be the git command, but the argv array
+ * belongs to the caller, and may be reused in
+ * subsequent loop iterations. Save argv[0] and
+ * restore it on error.
+ */
+ tmp = argv[0];
+ argv[0] = cmd.buf;
+
+ trace_argv_printf(argv, "trace: exec:");
+
+ /* execvp() can only ever return if it fails */
+ execvp(cmd.buf, (char **)argv);
+
+ trace_printf("trace: exec failed: %s\n", strerror(errno));
+
+ argv[0] = tmp;
+
+ strbuf_release(&cmd);
+}
+
+
int main(int argc, const char **argv)
{
const char *cmd = argv[0] ? argv[0] : "git-help";
handle_internal_command(argc, argv);
/* .. then try the external ones */
- execv_git_cmd(argv);
+ execv_dashed_external(argv);
/* It could be an alias -- this works around the insanity
* of overriding "git log" with "git show" by having
find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
+(find $RPM_BUILD_ROOT%{_libexecdir}/git-core -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) >> bin-man-doc-files
(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
%if %{!?_without_docs:1}0
(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
%files svn
%defattr(-,root,root)
-%{_bindir}/*svn*
+%{_libexecdir}/git-core/*svn*
%doc Documentation/*svn*.txt
%{!?_without_docs: %{_mandir}/man1/*svn*.1*}
%{!?_without_docs: %doc Documentation/*svn*.html }
%files cvs
%defattr(-,root,root)
%doc Documentation/*git-cvs*.txt
-%{_bindir}/*cvs*
+%{_libexecdir}/git-core/*cvs*
%{!?_without_docs: %{_mandir}/man1/*cvs*.1*}
%{!?_without_docs: %doc Documentation/*git-cvs*.html }
%files arch
%defattr(-,root,root)
%doc Documentation/git-archimport.txt
-%{_bindir}/git-archimport
+%{_libexecdir}/git-core/git-archimport
%{!?_without_docs: %{_mandir}/man1/git-archimport.1*}
%{!?_without_docs: %doc Documentation/git-archimport.html }
%files email
%defattr(-,root,root)
%doc Documentation/*email*.txt
-%{_bindir}/*email*
+%{_libexecdir}/git-core/*email*
%{!?_without_docs: %{_mandir}/man1/*email*.1*}
%{!?_without_docs: %doc Documentation/*email*.html }
%files gui
%defattr(-,root,root)
-%{_bindir}/git-gui
-%{_bindir}/git-citool
+%{_libexecdir}/git-core/git-gui
+%{_libexecdir}/git-core/git-citool
%{_datadir}/git-gui/
%{!?_without_docs: %{_mandir}/man1/git-gui.1*}
%{!?_without_docs: %doc Documentation/git-gui.html}
is_in_cmdlist(&other_cmds, s);
}
+static const char *prepend(const char *prefix, const char *cmd)
+{
+ size_t pre_len = strlen(prefix);
+ size_t cmd_len = strlen(cmd);
+ char *p = xmalloc(pre_len + cmd_len + 1);
+ memcpy(p, prefix, pre_len);
+ strcpy(p + pre_len, cmd);
+ return p;
+}
+
static const char *cmd_to_page(const char *git_cmd)
{
if (!git_cmd)
return "git";
else if (!prefixcmp(git_cmd, "git"))
return git_cmd;
- else {
- int page_len = strlen(git_cmd) + 4;
- char *p = xmalloc(page_len + 1);
- strcpy(p, "git-");
- strcpy(p + 4, git_cmd);
- p[page_len] = 0;
- return p;
- }
+ else if (is_git_command(git_cmd))
+ return prepend("git-", git_cmd);
+ else
+ return prepend("git", git_cmd);
}
static void setup_man_path(void)
#include "pack.h"
#include "csum-file.h"
-uint32_t pack_idx_default_version = 1;
+uint32_t pack_idx_default_version = 2;
uint32_t pack_idx_off32_limit = 0x7fffffff;
static int sha1_compare(const void *_a, const void *_b)
return buf;
}
-/* We allow "recursive" symbolic links. Only within reason, though. */
-#define MAXDEPTH 5
-
const char *make_relative_path(const char *abs, const char *base)
{
static char buf[PATH_MAX + 1];
strcpy(buf, abs + baselen);
return buf;
}
-
-const char *make_absolute_path(const char *path)
-{
- static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
- char cwd[1024] = "";
- int buf_index = 1, len;
-
- int depth = MAXDEPTH;
- char *last_elem = NULL;
- struct stat st;
-
- if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
- die ("Too long path: %.*s", 60, path);
-
- while (depth--) {
- if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
- char *last_slash = strrchr(buf, '/');
- if (last_slash) {
- *last_slash = '\0';
- last_elem = xstrdup(last_slash + 1);
- } else {
- last_elem = xstrdup(buf);
- *buf = '\0';
- }
- }
-
- if (*buf) {
- if (!*cwd && !getcwd(cwd, sizeof(cwd)))
- die ("Could not get current working directory");
-
- if (chdir(buf))
- die ("Could not switch to '%s'", buf);
- }
- if (!getcwd(buf, PATH_MAX))
- die ("Could not get current working directory");
-
- if (last_elem) {
- int len = strlen(buf);
- if (len + strlen(last_elem) + 2 > PATH_MAX)
- die ("Too long path name: '%s/%s'",
- buf, last_elem);
- buf[len] = '/';
- strcpy(buf + len + 1, last_elem);
- free(last_elem);
- last_elem = NULL;
- }
-
- if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
- len = readlink(buf, next_buf, PATH_MAX);
- if (len < 0)
- die ("Invalid symlink: %s", buf);
- next_buf[len] = '\0';
- buf = next_buf;
- buf_index = 1 - buf_index;
- next_buf = bufs[buf_index];
- } else
- break;
- }
-
- if (*cwd && chdir(cwd))
- die ("Could not change back to '%s'", cwd);
-
- return buf;
-}
#include "cache.h"
#include "quote.h"
+int quote_path_fully = 1;
+
/* Help to copy the thing properly quoted for the shell safety.
* any single quote is replaced with '\'', any exclamation point
* is replaced with '\!', and the whole thing is enclosed in a
#include "exec_cmd.h"
#include "strbuf.h"
+/* Stubs for functions that make no sense for git-shell. These stubs
+ * are provided here to avoid linking in external redundant modules.
+ */
+void release_pack_memory(size_t need, int fd){}
+void trace_argv_printf(const char **argv, const char *fmt, ...){}
+void trace_printf(const char *fmt, ...){}
+
+
static int do_generic_cmd(const char *me, char *arg)
{
const char *my_argv[4];
/trash directory
+/test-results
'
+test_expect_success 'checkdiff detects trailing blank lines' '
+ echo "foo();" >x &&
+ echo "" >>x &&
+ git diff --check | grep "ends with blank"
+'
+
test_done
'
+test_expect_success 'check detects leftover conflict markers' '
+ git reset --hard &&
+ git checkout HEAD^ &&
+ echo binary >>b &&
+ git commit -m "side" b &&
+ test_must_fail git merge master &&
+ git add b && (
+ git --no-pager diff --cached --check >test.out
+ test $? = 2
+ ) &&
+ test 3 = $(grep "conflict marker" test.out | wc -l) &&
+ git reset --hard
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='apply same filename'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in a b c d e f g h i j k l m
+ do
+ echo $i
+ done >same_fn &&
+ cp same_fn other_fn &&
+ git add same_fn other_fn &&
+ git commit -m initial
+'
+test_expect_success 'apply same filename with independent changes' '
+ sed -i -e "s/^d/z/" same_fn &&
+ git diff > patch0 &&
+ git add same_fn &&
+ sed -i -e "s/^i/y/" same_fn &&
+ git diff >> patch0 &&
+ cp same_fn same_fn2 &&
+ git reset --hard &&
+ git-apply patch0 &&
+ diff same_fn same_fn2
+'
+
+test_expect_success 'apply same filename with overlapping changes' '
+ git reset --hard
+ sed -i -e "s/^d/z/" same_fn &&
+ git diff > patch0 &&
+ git add same_fn &&
+ sed -i -e "s/^e/y/" same_fn &&
+ git diff >> patch0 &&
+ cp same_fn same_fn2 &&
+ git reset --hard &&
+ git-apply patch0 &&
+ diff same_fn same_fn2
+'
+
+test_expect_success 'apply same new filename after rename' '
+ git reset --hard
+ git mv same_fn new_fn
+ sed -i -e "s/^d/z/" new_fn &&
+ git add new_fn &&
+ git diff -M --cached > patch1 &&
+ sed -i -e "s/^e/y/" new_fn &&
+ git diff >> patch1 &&
+ cp new_fn new_fn2 &&
+ git reset --hard &&
+ git apply --index patch1 &&
+ diff new_fn new_fn2
+'
+
+test_expect_success 'apply same old filename after rename -- should fail.' '
+ git reset --hard
+ git mv same_fn new_fn
+ sed -i -e "s/^d/z/" new_fn &&
+ git add new_fn &&
+ git diff -M --cached > patch1 &&
+ git mv new_fn same_fn
+ sed -i -e "s/^e/y/" same_fn &&
+ git diff >> patch1 &&
+ git reset --hard &&
+ test_must_fail git apply patch1
+'
+
+test_expect_success 'apply A->B (rename), C->A (rename), A->A -- should pass.' '
+ git reset --hard
+ git mv same_fn new_fn
+ sed -i -e "s/^d/z/" new_fn &&
+ git add new_fn &&
+ git diff -M --cached > patch1 &&
+ git commit -m "a rename" &&
+ git mv other_fn same_fn
+ sed -i -e "s/^e/y/" same_fn &&
+ git add same_fn &&
+ git diff -M --cached >> patch1 &&
+ sed -i -e "s/^g/x/" same_fn &&
+ git diff >> patch1 &&
+ git reset --hard HEAD^ &&
+ git apply patch1
+'
+
+test_done
test_cmp expect output
'
+test_expect_success 'disambiguation (1)' '
+
+ git reset --hard &&
+ >secondfile &&
+ git add secondfile &&
+ test_must_fail git reset secondfile &&
+ test -z "$(git diff --cached --name-only)" &&
+ test -f secondfile &&
+ test ! -s secondfile
+
+'
+
+test_expect_success 'disambiguation (2)' '
+
+ git reset --hard &&
+ >secondfile &&
+ git add secondfile &&
+ rm -f secondfile &&
+ test_must_fail git reset secondfile &&
+ test -n "$(git diff --cached --name-only -- secondfile)" &&
+ test ! -f secondfile
+
+'
+
+test_expect_success 'disambiguation (3)' '
+
+ git reset --hard &&
+ >secondfile &&
+ git add secondfile &&
+ rm -f secondfile &&
+ test_must_fail git reset HEAD secondfile &&
+ test -z "$(git diff --cached --name-only)" &&
+ test ! -f secondfile
+
+'
+
+test_expect_success 'disambiguation (4)' '
+
+ git reset --hard &&
+ >secondfile &&
+ git add secondfile &&
+ rm -f secondfile &&
+ test_must_fail git reset -- secondfile &&
+ test -z "$(git diff --cached --name-only)" &&
+ test ! -f secondfile
+'
+
test_done
test_description='perl interface (Git.pm)'
. ./test-lib.sh
+perl -MTest::More -e 0 2>/dev/null || {
+ say_color skip "Perl Test::More unavailable, skipping test"
+ test_done
+}
+
# set up test repository
test_expect_success \
#
# To enable this hook, rename this file to "pre-commit".
-# This is slightly modified from Andrew Morton's Perfect Patch.
-# Lines you introduce should not have trailing whitespace.
-# Also check for an indentation that has SP before a TAB.
-
if git-rev-parse --verify HEAD 2>/dev/null
then
- git-diff-index -p -M --cached HEAD --
+ against=HEAD
else
- # NEEDSWORK: we should produce a diff with an empty tree here
- # if we want to do the same verification for the initial import.
- :
-fi |
-perl -e '
- my $found_bad = 0;
- my $filename;
- my $reported_filename = "";
- my $lineno;
- sub bad_line {
- my ($why, $line) = @_;
- if (!$found_bad) {
- print STDERR "*\n";
- print STDERR "* You have some suspicious patch lines:\n";
- print STDERR "*\n";
- $found_bad = 1;
- }
- if ($reported_filename ne $filename) {
- print STDERR "* In $filename\n";
- $reported_filename = $filename;
- }
- print STDERR "* $why (line $lineno)\n";
- print STDERR "$filename:$lineno:$line\n";
- }
- while (<>) {
- if (m|^diff --git a/(.*) b/\1$|) {
- $filename = $1;
- next;
- }
- if (/^@@ -\S+ \+(\d+)/) {
- $lineno = $1 - 1;
- next;
- }
- if (/^ /) {
- $lineno++;
- next;
- }
- if (s/^\+//) {
- $lineno++;
- chomp;
- if (/\s$/) {
- bad_line("trailing whitespace", $_);
- }
- if (/^\s* \t/) {
- bad_line("indent SP followed by a TAB", $_);
- }
- if (/^([<>])\1{6} |^={7}$/) {
- bad_line("unresolved merge conflict", $_);
- }
- }
- }
- exit($found_bad);
-'
+ # Initial commit: diff against an empty tree object
+ against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
+exec git diff-index --check --cached $against --
}
/* If stream is non-NULL, emits the line after checking. */
-unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
- FILE *stream, const char *set,
- const char *reset, const char *ws)
+static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
+ FILE *stream, const char *set,
+ const char *reset, const char *ws)
{
unsigned result = 0;
int written = 0;
return result;
}
+void ws_check_emit(const char *line, int len, unsigned ws_rule,
+ FILE *stream, const char *set,
+ const char *reset, const char *ws)
+{
+ (void)ws_check_emit_1(line, len, ws_rule, stream, set, reset, ws);
+}
+
+unsigned ws_check(const char *line, int len, unsigned ws_rule)
+{
+ return ws_check_emit_1(line, len, ws_rule, NULL, NULL, NULL, NULL);
+}
+
+int ws_blank_line(const char *line, int len, unsigned ws_rule)
+{
+ /*
+ * We _might_ want to treat CR differently from other
+ * whitespace characters when ws_rule has WS_CR_AT_EOL, but
+ * for now we just use this stupid definition.
+ */
+ while (len-- > 0) {
+ if (!isspace(*line))
+ return 0;
+ line++;
+ }
+ return 1;
+}
+
/* Copy the line to the buffer while fixing whitespaces */
int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *error_count)
{