*.html
*.[1-8]
*.made
+*.texi
git.info
+gitman.info
howto-index.txt
doc.dep
cmds-*.txt
* "git init" now autodetects the case sensitivity of the filesystem and
sets core.ignorecase accordingly.
+* cpio is no longer used; neither "curl" binary (libcurl is still used).
+
(documentation)
* Many freestanding documentation pages have been converted and made
- available to "git help" (aka "man git-<command>") as section 7 of the
- manual pages.
+ available to "git help" (aka "man git<something>") as section 7 of
+ the manual pages. This means bookmarks to some HTML documentation
+ files may need to be updated (eg "tutorial.html" became
+ "gittutorial.html").
(performance)
* "git init --bare" is a synonym for "git --bare init" now.
-* "git gc --auto" honors a new pre-aut-gc hook to temporarily disable it.
+* "git gc --auto" honors a new pre-auto-gc hook to temporarily disable it.
* "git log --pretty=tformat:<custom format>" gives a LF after each entry,
instead of giving a LF between each pair of entries which is how
All of the fixes in v1.5.5 maintenance series are included in
this release, unless otherwise noted.
-
---
-exec >/var/tmp/1
-O=v1.5.6-rc1
-echo O=`git describe refs/heads/master`
-git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
+And there are too numerous small fixes to otherwise note here ;-)
mail.identity.default.compose_html => false
mail.identity.id?.compose_html => false
+(Lukas Sandström)
+
+There is a script in contrib/thunderbird-patch-inline which can help
+you include patches with Thunderbird in an easy way. To use it, do the
+steps above and then use the script as the external editor.
Gnus
----
if (s/^\@top (.*)/\@node $1,,,Top/) {
push @menu, $1;
}
- s/\(\@pxref{\[URLS\]}\)//;
+ s/\(\@pxref{\[(URLS|REMOTES)\]}\)//;
print TMP;
}
close TMP;
SYNOPSIS
--------
[verse]
-'git-add' [-n] [-v] [-f] [--interactive | -i] [--patch | -p] [-u] [--refresh]
- [--ignore-errors] [--] <filepattern>...
+'git-add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
+ [--update | -u] [--refresh] [--ignore-errors] [--]
+ <filepattern>...
DESCRIPTION
-----------
Be verbose.
-f::
+--force::
Allow adding otherwise ignored files.
-i::
the specified filepatterns before exiting.
-u::
+--update::
Update only files that git already knows about, staging modified
content for commit and marking deleted files for removal. This
is similar
$ git bisect reset
------------------------------------------------
-to get back to the original branch, instead of being in one of the
-bisection branches ("git bisect start" will do that for you too,
-actually: it will reset the bisection state, and before it does that
-it checks that you're not using some old bisection branch).
+to get back to the original branch, instead of being on the bisection
+commit ("git bisect start" will do that for you too, actually: it will
+reset the bisection state).
Bisect visualize
~~~~~~~~~~~~~~~~
SYNOPSIS
--------
+[verse]
'git-cat-file' [-t | -s | -e | -p | <type>] <object>
'git-cat-file' [--batch | --batch-check] < <list-of-objects>
Sends missing objects to remote repository, and updates the
remote branch.
-*NOTE*: This command is temporarily disabled if your cURL
-library is older than 7.16, as the combination has been reported
+*NOTE*: This command is temporarily disabled if your libcurl
+is older than 7.16, as the combination has been reported
not to work and sometimes corrupts repository.
OPTIONS
for branch `<name>`, set configuration `branch.<name>.rebase`
to `true`.
+
-*NOTE:* This is a potentially _dangerous_ mode of operation.
+[NOTE]
+This is a potentially _dangerous_ mode of operation.
It rewrites history, which does not bode well when you
published that history already. Do *not* use this option
unless you have read linkgit:git-rebase[1] carefully.
--mirror::
Instead of naming each ref to push, specifies that all
- refs under `$GIT_DIR/refs/heads/` and `$GIT_DIR/refs/tags/`
+ refs under `$GIT_DIR/refs/` (which includes but is not
+ limited to `refs/heads/`, `refs/remotes/`, and `refs/tags/`)
be mirrored to the remote repository. Newly created local
refs will be pushed to the remote end, locally updated refs
will be force updated on the remote end, and deleted refs
trees that are not directly related to the current
working tree status into a temporary index file.
+-v::
+ Show the progress of checking files out.
+
--trivial::
Restrict three-way merge by `git-read-tree` to happen
only if there is no file-level merging required, instead
'git-remote' [-v | --verbose]
'git-remote' add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
'git-remote' rm <name>
-'git-remote' show <name>
-'git-remote' prune <name>
+'git-remote' show [-n] <name>
+'git-remote' prune [-n | --dry-run] <name>
'git-remote' update [group]
DESCRIPTION
referenced by <name>, but are still locally available in
"remotes/<name>".
+
-With `-n` option, the remote heads are not confirmed first with `git
-ls-remote <name>`; cached information is used instead. Use with
-caution.
+With `--dry-run` option, report what branches will be pruned, but do no
+actually prune them.
'update'::
the repository with a public http:// or svn:// URL in the
metadata so users of it will see the public URL.
+--
+
Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps
options all affect the metadata generated and used by git-svn; they
*must* be set in the configuration file before any history is imported
Additionally, only one of these four options can be used per-svn-remote
section because they affect the 'git-svn-id:' metadata line.
---
BASIC EXAMPLES
--------------
branch of the `git.git` repository.
Documentation for older releases are available here:
+* link:v1.5.6/git.html[documentation for release 1.5.6]
+
+* release notes for
+ link:RelNotes-1.5.6.txt[1.5.6],
+
* link:v1.5.5/git.html[documentation for release 1.5.5]
* release notes for
$ git commit
------------------------------------------------
-This will again prompt your for a message describing the change, and then
+This will again prompt you for a message describing the change, and then
record a new version of the project.
Alternatively, instead of running `git add` beforehand, you can use
------------------------
Make sure that you have HTTP support, i.e. your git was built with
-curl (version more recent than 7.10). The command 'git http-push' with
+libcurl (version more recent than 7.10). The command 'git http-push' with
no argument should display a usage message.
Then, add the following to your $HOME/.netrc (you can do without, but will be
- '%m': left, right or boundary mark
- '%n': newline
- '%x00': print a byte from a hex code
+
+* 'tformat:'
++
+The 'tformat:' format works exactly like 'format:', except that it
+provides "terminator" semantics instead of "separator" semantics. In
+other words, each commit has the message terminator character (usually a
+newline) appended, rather than a separator placed between entries.
+This means that the final entry of a single-line format will be properly
+terminated with a new line, just as the "oneline" format does.
+For example:
++
+---------------------
+$ git log -2 --pretty=format:%h 4da45bef \
+ | perl -pe '$_ .= " -- NO NEWLINE\n" unless /\n/'
+4da45be
+7134973 -- NO NEWLINE
+
+$ git log -2 --pretty=tformat:%h 4da45bef \
+ | perl -pe '$_ .= " -- NO NEWLINE\n" unless /\n/'
+4da45be
+7134973
+---------------------
path-list API
=============
-Talk about <path-list.h>, things like
+The path_list API offers a data structure and functions to handle sorted
+and unsorted string lists.
-* it is not just paths but strings in general;
-* the calling sequence.
+The name is a bit misleading, a path_list may store not only paths but
+strings in general.
-(Dscho)
+The caller:
+
+. Allocates and clears a `struct path_list` variable.
+
+. Initializes the members. You might want to set the flag `strdup_paths`
+ if the strings should be strdup()ed. For example, this is necessary
+ when you add something like git_path("..."), since that function returns
+ a static buffer that will change with the next call to git_path().
++
+If you need something advanced, you can manually malloc() the `items`
+member (you need this if you add things later) and you should set the
+`nr` and `alloc` members in that case, too.
+
+. Adds new items to the list, using `path_list_append` or `path_list_insert`.
+
+. Can check if a string is in the list using `path_list_has_path` or
+ `unsorted_path_list_has_path` and get it from the list using
+ `path_list_lookup` for sorted lists.
+
+. Can sort an unsorted list using `sort_path_list`.
+
+. Finally it should free the list using `path_list_clear`.
+
+Example:
+
+----
+struct path_list list;
+int i;
+
+memset(&list, 0, sizeof(struct path_list));
+path_list_append("foo", &list);
+path_list_append("bar", &list);
+for (i = 0; i < list.nr; i++)
+ printf("%s\n", list.items[i].path)
+----
+
+NOTE: It is more efficient to build an unsorted list and sort it
+afterwards, instead of building a sorted list (`O(n log n)` instead of
+`O(n^2)`).
++
+However, if you use the list to check if a certain string was added
+already, you should not do that (using unsorted_path_list_has_path()),
+because the complexity would be quadratic again (but with a worse factor).
+
+Functions
+---------
+
+* General ones (works with sorted and unsorted lists as well)
+
+`print_path_list`::
+
+ Dump a path_list to stdout, useful mainly for debugging purposes. It
+ can take an optional header argument and it writes out the
+ string-pointer pairs of the path_list, each one in its own line.
+
+`path_list_clear`::
+
+ Free a path_list. The `path` pointer of the items will be freed in case
+ the `strdup_paths` member of the path_list is set. The second parameter
+ controls if the `util` pointer of the items should be freed or not.
+
+* Functions for sorted lists only
+
+`path_list_has_path`::
+
+ Determine if the path_list has a given string or not.
+
+`path_list_insert`::
+
+ Insert a new element to the path_list. The returned pointer can be handy
+ if you want to write something to the `util` pointer of the
+ path_list_item containing the just added string.
++
+Since this function uses xrealloc() (which die()s if it fails) if the
+list needs to grow, it is safe not to check the pointer. I.e. you may
+write `path_list_insert(...)->util = ...;`.
+
+`path_list_lookup`::
+
+ Look up a given string in the path_list, returning the containing
+ path_list_item. If the string is not found, NULL is returned.
+
+* Functions for unsorted lists only
+
+`path_list_append`::
+
+ Append a new string to the end of the path_list.
+
+`sort_path_list`::
+
+ Make an unsorted list sorted.
+
+`unsorted_path_list_has_path`::
+
+ It's like `path_list_has_path()` but for unsorted lists.
++
+This function needs to look through all items, as opposed to its
+counterpart for sorted lists, which performs a binary search.
+
+Data structures
+---------------
+
+* `struct path_list_item`
+
+Represents an item of the list. The `path` member is a pointer to the
+string, and you may use the `util` member for any purpose, if you want.
+
+* `struct path_list`
+
+Represents the list itself.
+
+. The array of items are available via the `items` member.
+. The `nr` member contains the number of items stored in the list.
+. The `alloc` member is used to avoid reallocating at every insertion.
+ You should not tamper with it.
+. Setting the `strdup_paths` member to 1 will strdup() the strings
+ before adding them, see above.
The caller:
-1. allocates and clears (memset(&chld, '0', sizeof(chld));) a
+1. allocates and clears (memset(&chld, 0, sizeof(chld));) a
struct child_process variable;
2. initializes the members;
3. calls start_command();
The caller:
-1. allocates and clears (memset(&asy, '0', sizeof(asy));) a
+1. allocates and clears (memset(&asy, 0, sizeof(asy));) a
struct async variable;
2. initializes .proc and .data;
3. calls start_async();
-------------------------------------------------
$ git show :1:file.txt # the file in a common ancestor of both branches
-$ git show :2:file.txt # the version from HEAD, but including any
- # nonconflicting changes from MERGE_HEAD
-$ git show :3:file.txt # the version from MERGE_HEAD, but including any
- # nonconflicting changes from HEAD.
+$ git show :2:file.txt # the version from HEAD.
+$ git show :3:file.txt # the version from MERGE_HEAD.
-------------------------------------------------
-Since the stage 2 and stage 3 versions have already been updated with
-nonconflicting changes, the only remaining differences between them are
-the important ones; thus linkgit:git-diff[1] can use the information in
-the index to show only those conflicts.
+When you ask linkgit:git-diff[1] to show the conflicts, it runs a
+three-way diff between the conflicted merge results in the work tree with
+stages 2 and 3 to show only hunks whose contents come from both sides,
+mixed (in other words, when a hunk's merge results come only from stage 2,
+that part is not conflicting and is not shown. Same for stage 3).
The diff above shows the differences between the working-tree version of
file.txt and the stage 2 and stage 3 versions. So instead of preceding
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.5.GIT
+DEF_VER=v1.5.6.GIT
LF='
'
that come with git (git includes the one from Mozilla, and has
its own PowerPC and ARM optimized ones too - see the Makefile).
- - "libcurl" and "curl" executable. git-http-fetch and
- git-fetch use them. If you do not use http
- transfer, you are probably OK if you do not have
- them.
+ - libcurl library; git-http-fetch and git-fetch use them. You
+ might also want the "curl" executable for debugging purposes.
+ If you do not use http transfer, you are probably OK if you
+ do not have them.
- expat library; git-http-push uses it for remote lock
management over DAV. Similar to "curl" above, this is optional.
- "perl" and POSIX-compliant shells are needed to use most of
the barebone Porcelainish scripts.
- - "cpio" is used by git-clone when doing a local (possibly
- hardlinked) clone.
-
- Some platform specific issues are dealt with Makefile rules,
but depending on your specific installation, you may not
have all the libraries/tools needed, or you may have
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
# This also implies MOZILLA_SHA1.
#
-# Define NO_CURL if you do not have curl installed. git-http-pull and
+# Define NO_CURL if you do not have libcurl installed. git-http-pull and
# git-http-push are not built, and you cannot use http:// and https://
# transports.
#
install-doc:
$(MAKE) -C Documentation install
+install-html:
+ $(MAKE) -C Documentation install-html
+
install-info:
$(MAKE) -C Documentation install-info
documented,gitmodules | \
documented,gitcli | \
documented,git-tools | \
+ documented,gitcore-tutorial | \
+ documented,gitcvs-migration | \
+ documented,gitdiffcore | \
+ documented,gitglossary | \
+ documented,githooks | \
+ documented,gitrepository-layout | \
+ documented,gittutorial | \
+ documented,gittutorial-2 | \
sentinel,not,matching,is,ok ) continue ;; \
esac; \
case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \
elem->prev = attr_stack;
attr_stack = elem;
- elem = read_attr(GITATTRIBUTES_FILE, 1);
- elem->origin = strdup("");
- elem->prev = attr_stack;
- attr_stack = elem;
- debug_push(elem);
+ if (!is_bare_repository()) {
+ elem = read_attr(GITATTRIBUTES_FILE, 1);
+ elem->origin = strdup("");
+ elem->prev = attr_stack;
+ attr_stack = elem;
+ debug_push(elem);
+ }
elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
if (!elem)
/*
* Read from parent directories and push them down
*/
- while (1) {
- char *cp;
-
- len = strlen(attr_stack->origin);
- if (dirlen <= len)
- break;
- memcpy(pathbuf, path, dirlen);
- memcpy(pathbuf + dirlen, "/", 2);
- cp = strchr(pathbuf + len + 1, '/');
- strcpy(cp + 1, GITATTRIBUTES_FILE);
- elem = read_attr(pathbuf, 0);
- *cp = '\0';
- elem->origin = strdup(pathbuf);
- elem->prev = attr_stack;
- attr_stack = elem;
- debug_push(elem);
+ if (!is_bare_repository()) {
+ while (1) {
+ char *cp;
+
+ len = strlen(attr_stack->origin);
+ if (dirlen <= len)
+ break;
+ memcpy(pathbuf, path, dirlen);
+ memcpy(pathbuf + dirlen, "/", 2);
+ cp = strchr(pathbuf + len + 1, '/');
+ strcpy(cp + 1, GITATTRIBUTES_FILE);
+ elem = read_attr(pathbuf, 0);
+ *cp = '\0';
+ elem->origin = strdup(pathbuf);
+ elem->prev = attr_stack;
+ attr_stack = elem;
+ debug_push(elem);
+ }
}
/*
OPT_GROUP(""),
OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
- OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
- OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
+ OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
+ OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
OPT_END(),
static int batch_one_object(const char *obj_name, int print_contents)
{
unsigned char sha1[20];
- enum object_type type;
+ enum object_type type = 0;
unsigned long size;
void *contents = contents;
else
type = sha1_object_info(sha1, &size);
- if (type <= 0)
- return 1;
+ if (type <= 0) {
+ printf("%s missing\n", obj_name);
+ fflush(stdout);
+ return 0;
+ }
printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size);
fflush(stdout);
fp = fopen(git_path(commit_editmsg), "w");
if (fp == NULL)
- die("could not open %s", git_path(commit_editmsg));
+ die("could not open %s: %s",
+ git_path(commit_editmsg), strerror(errno));
if (cleanup_mode != CLEANUP_NONE)
stripspace(&sb, 0);
mark_object(&commit->object);
if (!is_encoding_utf8(encoding))
reencoded = reencode_string(message, "UTF-8", encoding);
+ if (!commit->parents)
+ printf("reset %s\n", (const char*)commit->util);
printf("commit %s\nmark :%d\n%.*s\n%.*s\ndata %u\n%s",
(const char *)commit->util, last_idnum,
(int)(author_end - author), author,
die("cannot read object %s '%s'", sha1_to_hex(sha), path);
if (type != OBJ_BLOB)
die("blob expected for %s '%s'", sha1_to_hex(sha), path);
+ if (S_ISREG(mode)) {
+ struct strbuf strbuf;
+ strbuf_init(&strbuf, 0);
+ if (convert_to_working_tree(path, buf, size, &strbuf)) {
+ free(buf);
+ size = strbuf.len;
+ buf = strbuf_detach(&strbuf, NULL);
+ }
+ }
if (make_room_for_path(path) < 0) {
update_wd = 0;
+ free(buf);
goto update_index;
}
if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
} else
die("do not know what to do with %06o %s '%s'",
mode, sha1_to_hex(sha), path);
+ free(buf);
}
update_index:
if (update_cache)
unlink(newlog_path);
} else if (cmd->updateref && commit_ref(lock)) {
status |= error("Couldn't set %s", lock->ref_name);
+ } else {
+ adjust_shared_perm(log_file);
}
}
free(newlog_path);
printf("\n");
}
-static int show_or_prune(int argc, const char **argv, int prune)
+static int get_remote_ref_states(const char *name,
+ struct ref_states *states,
+ int query)
{
- int dry_run = 0, result = 0;
+ struct transport *transport;
+ const struct ref *ref;
+
+ states->remote = remote_get(name);
+ if (!states->remote)
+ return error("No such remote: %s", name);
+
+ read_branches();
+
+ if (query) {
+ transport = transport_get(NULL, states->remote->url_nr > 0 ?
+ states->remote->url[0] : NULL);
+ ref = transport_get_remote_refs(transport);
+ transport_disconnect(transport);
+
+ get_ref_states(ref, states);
+ }
+
+ return 0;
+}
+
+static int append_ref_to_tracked_list(const char *refname,
+ const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct ref_states *states = cb_data;
+ struct refspec refspec;
+
+ memset(&refspec, 0, sizeof(refspec));
+ refspec.dst = (char *)refname;
+ if (!remote_find_tracking(states->remote, &refspec)) {
+ path_list_append(skip_prefix(refspec.src, "refs/heads/"),
+ &states->tracked);
+ }
+
+ return 0;
+}
+
+static int show(int argc, const char **argv)
+{
+ int no_query = 0, result = 0;
struct option options[] = {
OPT_GROUP("show specific options"),
- OPT__DRY_RUN(&dry_run),
+ OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
OPT_END()
};
struct ref_states states;
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
- if (argc < 1) {
- if (!prune)
- return show_all();
- usage_with_options(builtin_remote_usage, options);
- }
+ if (argc < 1)
+ return show_all();
memset(&states, 0, sizeof(states));
for (; argc; argc--, argv++) {
- struct transport *transport;
- const struct ref *ref;
struct strbuf buf;
- int i, got_states;
-
- states.remote = remote_get(*argv);
- if (!states.remote)
- return error("No such remote: %s", *argv);
- transport = transport_get(NULL, states.remote->url_nr > 0 ?
- states.remote->url[0] : NULL);
- ref = transport_get_remote_refs(transport);
- transport_disconnect(transport);
-
- read_branches();
- got_states = get_ref_states(ref, &states);
- if (got_states)
- result = error("Error getting local info for '%s'",
- states.remote->name);
+ int i;
- if (prune) {
- for (i = 0; i < states.stale.nr; i++) {
- const char *refname = states.stale.items[i].util;
- result |= delete_ref(refname, NULL);
- }
- goto cleanup_states;
- }
+ get_remote_ref_states(*argv, &states, !no_query);
printf("* remote %s\n URL: %s\n", *argv,
states.remote->url_nr > 0 ?
printf("\n");
}
- if (got_states)
- continue;
- strbuf_init(&buf, 0);
- strbuf_addf(&buf, " New remote branch%%s (next fetch will "
- "store in remotes/%s)", states.remote->name);
- show_list(buf.buf, &states.new);
- strbuf_release(&buf);
- show_list(" Stale tracking branch%s (use 'git remote prune')",
- &states.stale);
- show_list(" Tracked remote branch%s",
- &states.tracked);
+ if (!no_query) {
+ strbuf_init(&buf, 0);
+ strbuf_addf(&buf, " New remote branch%%s (next fetch "
+ "will store in remotes/%s)", states.remote->name);
+ show_list(buf.buf, &states.new);
+ strbuf_release(&buf);
+ show_list(" Stale tracking branch%s (use 'git remote "
+ "prune')", &states.stale);
+ }
+
+ if (no_query)
+ for_each_ref(append_ref_to_tracked_list, &states);
+ show_list(" Tracked remote branch%s", &states.tracked);
if (states.remote->push_refspec_nr) {
printf(" Local branch%s pushed with 'git push'\n ",
}
printf("\n");
}
-cleanup_states:
+
+ /* NEEDSWORK: free remote */
+ path_list_clear(&states.new, 0);
+ path_list_clear(&states.stale, 0);
+ path_list_clear(&states.tracked, 0);
+ }
+
+ return result;
+}
+
+static int prune(int argc, const char **argv)
+{
+ int dry_run = 0, result = 0;
+ struct option options[] = {
+ OPT_GROUP("prune specific options"),
+ OPT__DRY_RUN(&dry_run),
+ OPT_END()
+ };
+ struct ref_states states;
+
+ argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+
+ if (argc < 1)
+ usage_with_options(builtin_remote_usage, options);
+
+ memset(&states, 0, sizeof(states));
+ for (; argc; argc--, argv++) {
+ int i;
+
+ get_remote_ref_states(*argv, &states, 1);
+
+ if (states.stale.nr) {
+ printf("Pruning %s\n", *argv);
+ printf("URL: %s\n",
+ states.remote->url_nr
+ ? states.remote->url[0]
+ : "(no URL)");
+ }
+
+ for (i = 0; i < states.stale.nr; i++) {
+ const char *refname = states.stale.items[i].util;
+
+ if (!dry_run)
+ result |= delete_ref(refname, NULL);
+
+ printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
+ skip_prefix(refname, "refs/remotes/"));
+ }
+
/* NEEDSWORK: free remote */
path_list_clear(&states.new, 0);
path_list_clear(&states.stale, 0);
else if (!strcmp(argv[0], "rm"))
result = rm(argc, argv);
else if (!strcmp(argv[0], "show"))
- result = show_or_prune(argc, argv, 0);
+ result = show(argc, argv);
else if (!strcmp(argv[0], "prune"))
- result = show_or_prune(argc, argv, 1);
+ result = prune(argc, argv);
else if (!strcmp(argv[0], "update"))
result = update(argc, argv);
else {
; /* do nothing */
if (i == sizeof(buf))
die("filename too long");
- path_list_insert(buf, rr)->util = xstrdup(name);
+ path_list_insert(buf, rr)->util = name;
}
fclose(in);
}
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
-extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
- size_t bufsize, size_t *bufposn);
-extern int write_sha1_to_fd(int fd, const unsigned char *sha1);
extern int move_temp_to_file(const char *tmpfile, const char *filename);
extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
extern int server_supports(const char *feature);
extern struct packed_git *parse_pack_index(unsigned char *sha1);
-extern struct packed_git *parse_pack_index_file(const unsigned char *sha1,
- const char *idx_path);
extern void prepare_packed_git(void);
extern void reprepare_packed_git(void);
AC_SUBST(NEEDS_SSL_WITH_CRYPTO)
AC_SUBST(NO_OPENSSL)
#
-# Define NO_CURL if you do not have curl installed. git-http-pull and
+# Define NO_CURL if you do not have libcurl installed. git-http-pull and
# git-http-push are not built, and you cannot use http:// and https://
# transports.
AC_CHECK_LIB([curl], [curl_global_init],
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
- __gitcomp "--interactive --refresh"
+ __gitcomp "
+ --interactive --refresh --patch --update --dry-run
+ --ignore-errors
+ "
return
esac
COMPREPLY=()
--pretty= --name-status --name-only --raw
--not --all
--left-right --cherry-pick
+ --graph
+ --stat --numstat --shortstat
+ --decorate --diff-filter=
+ --color-words --walk-reflogs
"
return
;;
--- /dev/null
+appp.sh is a script that is supposed to be used together with ExternalEditor
+for Mozilla Thundebird. It will let you include patches inline in e-mails
+in an easy way.
+
+Usage:
+- Generate the patch with git format-patch.
+- Start writing a new e-mail in Thunderbird.
+- Press the external editor button (or Ctrl-E) to run appp.sh
+- Select the previosly generated patch file.
+- Finish editing the e-mail.
+
+Any text that is entered into the message editor before appp.sh is called
+will be moved to the section between the --- and the diffstat.
+
+All S-O-B:s and Cc:s in the patch will be added to the CC list.
+
+To set it up, just install External Editor and tell it to use appp.sh as the
+editor.
+
+Zenity is a required dependency.
--- /dev/null
+#!/bin/bash
+# Copyright 2008 Lukas Sandström <luksan@gmail.com>
+#
+# AppendPatch - A script to be used together with ExternalEditor
+# for Mozilla Thunderbird to properly include pathes inline i e-mails.
+
+# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2
+
+CONFFILE=~/.appprc
+
+SEP="-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-"
+if [ -e "$CONFFILE" ] ; then
+ LAST_DIR=`grep -m 1 "^LAST_DIR=" "${CONFFILE}"|sed -e 's/^LAST_DIR=//'`
+ cd "${LAST_DIR}"
+else
+ cd > /dev/null
+fi
+
+PATCH=$(zenity --file-selection)
+
+if [ "$?" != "0" ] ; then
+ #zenity --error --text "No patchfile given."
+ exit 1
+fi
+
+cd - > /dev/null
+
+SUBJECT=`sed -n -e '/^Subject: /p' "${PATCH}"`
+HEADERS=`sed -e '/^'"${SEP}"'$/,$d' $1`
+BODY=`sed -e "1,/${SEP}/d" $1`
+CMT_MSG=`sed -e '1,/^$/d' -e '/^---$/,$d' "${PATCH}"`
+DIFF=`sed -e '1,/^---$/d' "${PATCH}"`
+
+CCS=`echo -e "$CMT_MSG\n$HEADERS" | sed -n -e 's/^Cc: \(.*\)$/\1,/gp' \
+ -e 's/^Signed-off-by: \(.*\)/\1,/gp'`
+
+echo "$SUBJECT" > $1
+echo "Cc: $CCS" >> $1
+echo "$HEADERS" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1
+echo "$SEP" >> $1
+
+echo "$CMT_MSG" >> $1
+echo "---" >> $1
+if [ "x${BODY}x" != "xx" ] ; then
+ echo >> $1
+ echo "$BODY" >> $1
+ echo >> $1
+fi
+echo "$DIFF" >> $1
+
+LAST_DIR=`dirname "${PATCH}"`
+
+grep -v "^LAST_DIR=" "${CONFFILE}" > "${CONFFILE}_"
+echo "LAST_DIR=${LAST_DIR}" >> "${CONFFILE}_"
+mv "${CONFFILE}_" "${CONFFILE}"
static void date_never(struct tm *tm, int *num)
{
- tm->tm_mon = tm->tm_wday = tm->tm_yday
- = tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
- tm->tm_year = 70;
- tm->tm_mday = 1;
+ time_t n = 0;
+ localtime_r(&n, tm);
}
static const struct special {
static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
{
+ int has_trailing_newline = (len > 0 && line[len-1] == '\n');
+ if (has_trailing_newline)
+ len--;
+
fputs(set, file);
fwrite(line, len, 1, file);
fputs(reset, file);
+ if (has_trailing_newline)
+ fputc('\n', file);
}
static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
else
action=yes
fi
- FIRSTLINE=$(head -1 "$dotest/final-commit")
+ FIRSTLINE=$(sed 1q "$dotest/final-commit")
if test $action = skip
then
$proxyport = $1;
}
}
+ $repo ||= '/';
# if username is not explicit in CVSROOT, then use current user, as cvs would
$user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
. git-sh-setup
fqgitdir="$GIT_DIR"
-local="`git config --bool --get instaweb.local`"
-httpd="`git config --get instaweb.httpd`"
-port=`git config --get instaweb.port`
-module_path="`git config --get instaweb.modulepath`"
+local="$(git config --bool --get instaweb.local)"
+httpd="$(git config --get instaweb.httpd)"
+port=$(git config --get instaweb.port)
+module_path="$(git config --get instaweb.modulepath)"
conf="$GIT_DIR/gitweb/httpd.conf"
# any untaken local port will do...
test -z "$port" && port=1234
-start_httpd () {
- httpd_only="`echo $httpd | cut -f1 -d' '`"
+resolve_full_httpd () {
+ case "$httpd" in
+ *apache2*|*lighttpd*)
+ # ensure that the apache2/lighttpd command ends with "-f"
+ if ! echo "$httpd" | grep -- '-f *$' >/dev/null 2>&1
+ then
+ httpd="$httpd -f"
+ fi
+ ;;
+ esac
+
+ httpd_only="$(echo $httpd | cut -f1 -d' ')"
if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
then
- $httpd "$fqgitdir/gitweb/httpd.conf"
+ full_httpd=$httpd
else
# many httpds are installed in /usr/sbin or /usr/local/sbin
# these days and those are not in most users $PATHs
do
if test -x "$i/$httpd_only"
then
- # don't quote $httpd, there can be
- # arguments to it (-f)
- $i/$httpd "$fqgitdir/gitweb/httpd.conf"
+ full_httpd=$i/$httpd
return
fi
done
- echo "$httpd_only not found. Install $httpd_only or use" \
- "--httpd to specify another http daemon."
+
+ echo >&2 "$httpd_only not found. Install $httpd_only or use" \
+ "--httpd to specify another httpd daemon."
exit 1
fi
+}
+
+start_httpd () {
+ # here $httpd should have a meaningful value
+ resolve_full_httpd
+
+ # don't quote $full_httpd, there can be arguments to it (-f)
+ $full_httpd "$fqgitdir/gitweb/httpd.conf"
if test $? != 0; then
echo "Could not execute http daemon $httpd."
exit 1
}
stop_httpd () {
- test -f "$fqgitdir/pid" && kill `cat "$fqgitdir/pid"`
+ test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid")
}
while test $# != 0
done
mkdir -p "$GIT_DIR/gitweb/tmp"
-GIT_EXEC_PATH="`git --exec-path`"
+GIT_EXEC_PATH="$(git --exec-path)"
GIT_DIR="$fqgitdir"
export GIT_EXEC_PATH GIT_DIR
EOF
else
# plain-old CGI
- list_mods=`echo "$httpd" | sed "s/-f$/-l/"`
+ resolve_full_httpd
+ list_mods=$(echo "$full_httpd" | sed "s/-f$/-l/")
$list_mods | grep 'mod_cgi\.c' >/dev/null 2>&1 || \
echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
cat >> "$conf" <<EOF
summary (synonym to --stat)
log add list of one-line log to merge commit message
squash create a single commit instead of doing a merge
-commit perform a commit if the merge sucesses (default)
+commit perform a commit if the merge succeeds (default)
ff allow fast forward (default)
s,strategy= merge strategy to use
m,message= message to be used for the merge commit (if any)
branch="$(git symbolic-ref HEAD 2>/dev/null)"
remote="$(git config branch.${branch#refs/heads/}.remote)"
remote="${remote:-origin}"
- remoteurl="$(git config remote.$remote.url)" ||
- die "remote ($remote) does not have a url in .git/config"
+ remoteurl=$(git config "remote.$remote.url") ||
+ die "remote ($remote) does not have a url defined in .git/config"
url="$1"
while test -n "$url"
do
module_name()
{
# Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
- re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
+ re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
test -z "$name" &&
case "$repo" in
./*|../*)
# dereference source url relative to parent's url
- realrepo="$(resolve_relative_url $repo)" ;;
+ realrepo=$(resolve_relative_url "$repo") || exit
+ ;;
*)
# Turn the source into an absolute path if
# it is local
# Possibly a url relative to parent
case "$url" in
./*|../*)
- url="$(resolve_relative_url "$url")"
+ url=$(resolve_relative_url "$url") || exit
;;
esac
my $in_msg = 0;
my $author;
my $saw_from = 0;
+ my $msgbuf = "";
while (<$msg_fh>) {
if (!$in_msg) {
$in_msg = 1 if (/^\s*$/);
if (/^From:/ || /^Signed-off-by:/) {
$saw_from = 1;
}
- print $log_fh $_ or croak $!;
+ $msgbuf .= $_;
}
}
+ $msgbuf =~ s/\s+$//s;
if ($Git::SVN::_add_author_from && defined($author)
&& !$saw_from) {
- print $log_fh "\nFrom: $author\n"
- or croak $!;
+ $msgbuf .= "\n\nFrom: $author";
}
+ print $log_fh $msgbuf or croak $!;
command_close_pipe($msg_fh, $ctx);
}
close $log_fh or croak $!;
{ "checkout-index", cmd_checkout_index,
RUN_SETUP | NEED_WORK_TREE},
{ "check-ref-format", cmd_check_ref_format },
- { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
+ { "check-attr", cmd_check_attr, RUN_SETUP },
{ "cherry", cmd_cherry, RUN_SETUP },
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
{ "clone", cmd_clone },
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: perl-Git = %{version}-%{release}
-Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat
+Requires: zlib >= 1.2, rsync, less, openssh-clients, expat
Provides: git-core = %{version}-%{release}
Obsoletes: git-core <= 1.5.4.2
Obsoletes: git-p4
# No files for you!
%changelog
+* Sun Jun 15 2008 Junio C Hamano <gitster@pobox.com>
+- Remove curl from Requires list.
+
* Fri Feb 15 2008 Kristian Høgsberg <krh@redhat.com>
- Rename git-core to just git and rename meta package from git to git-all.
global startmsecs commitidx viewcomplete curview
global commfd leftover tclencoding
global viewargs viewargscmd viewfiles vfilelimit
- global showlocalchanges commitinterest mainheadid
+ global showlocalchanges commitinterest
global viewactive loginstance viewinstances vmergeonly
global pending_select mainheadid
global vcanopt vflags vrevs vorigargs
set viewinstances($view) [list $i]
set commfd($i) $fd
set leftover($i) {}
- if {$showlocalchanges} {
+ if {$showlocalchanges && $mainheadid ne {}} {
lappend commitinterest($mainheadid) {dodiffindex}
}
fconfigure $fd -blocking 0 -translation lf -eofchar {}
proc updatecommits {} {
global curview vcanopt vorigargs vfilelimit viewinstances
- global viewactive viewcomplete loginstance tclencoding mainheadid
+ global viewactive viewcomplete loginstance tclencoding
global startmsecs commfd showneartags showlocalchanges leftover
global mainheadid pending_select
global isworktree
if {$viewcomplete($curview)} {
global commitidx varctok
global numcommits startmsecs
- global mainheadid nullid
if {[info exists pending_select]} {
set row [first_real_row]
set mainhead {}
set mainheadid {}
catch {
+ set mainheadid [exec git rev-parse HEAD]
set thehead [exec git symbolic-ref HEAD]
if {[string match "refs/heads/*" $thehead]} {
set mainhead [string range $thehead 11 end]
- if {[info exists headids($mainhead)]} {
- set mainheadid $headids($mainhead)
- }
}
}
}
proc doshowlocalchanges {} {
global curview mainheadid
+ if {$mainheadid eq {}} return
if {[commitinview $mainheadid $curview]} {
dodiffindex
} else {
global cmitlisted commitinfo rowidlist parentlist
global rowtextx idpos idtags idheads idotherrefs
global linehtag linentag linedtag selectedline
- global canvxmax boldrows boldnamerows fgcolor nullid nullid2
+ global canvxmax boldrows boldnamerows fgcolor
+ global mainheadid nullid nullid2 circleitem circlecolors
# listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
set listed $cmitlisted($curview,$id)
set ofill red
} elseif {$id eq $nullid2} {
set ofill green
+ } elseif {$id eq $mainheadid} {
+ set ofill yellow
} else {
- set ofill [expr {$listed != 0 ? $listed == 2 ? "gray" : "blue" : "white"}]
+ set ofill [lindex $circlecolors $listed]
}
set x [xc $row $col]
set y [yc $row]
[expr {$x - $orad}] [expr {$y + $orad - 1}] \
-fill $ofill -outline $fgcolor -width 1 -tags circle]
}
+ set circleitem($row) $t
$canv raise $t
$canv bind $t <1> {selcanvline {} %x %y}
set rmx [llength [lindex $rowidlist $row]]
}
proc redrawtags {id} {
- global canv linehtag idpos currentid curview
- global canvxmax iddrawn
+ global canv linehtag idpos currentid curview cmitlisted
+ global canvxmax iddrawn circleitem mainheadid circlecolors
if {![commitinview $id $curview]} return
if {![info exists iddrawn($id)]} return
set row [rowofcommit $id]
+ if {$id eq $mainheadid} {
+ set ofill yellow
+ } else {
+ set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
+ }
+ $canv itemconf $circleitem($row) -fill $ofill
$canv delete tag.$id
set xt [eval drawtags $id $idpos($id)]
$canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
if {$mainhead ne {}} {
movehead $newhead $mainhead
movedhead $newhead $mainhead
- set mainheadid $newhead
}
+ set mainheadid $newhead
redrawtags $oldhead
redrawtags $newhead
selbyid $newhead
}
proc cobranch {} {
- global headmenuid headmenuhead mainhead headids
+ global headmenuid headmenuhead headids
global showlocalchanges mainheadid
# check the tree is clean first??
if {[catch {close $fd} err]} {
error_popup $err
}
- set oldmainhead $mainhead
+ set oldmainid $mainheadid
set mainhead $newhead
set mainheadid $newheadid
- if {[info exists headids($oldmainhead)]} {
- redrawtags $headids($oldmainhead)
- }
+ redrawtags $oldmainid
redrawtags $newheadid
selbyid $newheadid
if {$showlocalchanges} {
[array names idheads] [array names idotherrefs]]]
foreach id $refids {
set v [listrefs $id]
- if {![info exists ref($id)] || $ref($id) != $v ||
- ($id eq $oldmainhead && $id ne $mainheadid) ||
- ($id eq $mainheadid && $id ne $oldmainhead)} {
+ if {![info exists ref($id)] || $ref($id) != $v} {
redrawtags $id
}
}
+ if {$oldmainhead ne $mainheadid} {
+ redrawtags $oldmainhead
+ redrawtags $mainheadid
+ }
run refill_reflist
}
set ignorespace 0
set selectbgcolor gray85
+set circlecolors {white blue gray blue blue}
+
## For msgcat loading, first locate the installation location.
if { [info exists ::env(GITK_MSGSDIR)] } {
## Msgsdir was manually set in the environment.
}
# version of the core git binary
-our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
$projects_list ||= $projectroot;
return $GIT, '--git-dir='.$git_dir;
}
-# returns path to the core git executable and the --git-dir parameter as string
-sub git_cmd_str {
- return join(' ', git_cmd());
+# quote the given arguments for passing them to the shell
+# quote_command("command", "arg 1", "arg with ' and ! characters")
+# => "'command' 'arg 1' 'arg with '\'' and '\!' characters'"
+# Try to avoid using this function wherever possible.
+sub quote_command {
+ return join(' ',
+ map( { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ ));
}
# get HEAD ref of given project as hash
return wantarray ? @cos : \@cos;
}
-# parse ref from ref_file, given by ref_id, with given type
-sub parse_ref {
- my $ref_file = shift;
- my $ref_id = shift;
- my $type = shift || git_get_type($ref_id);
- my %ref_item;
-
- $ref_item{'type'} = $type;
- $ref_item{'id'} = $ref_id;
- $ref_item{'epoch'} = 0;
- $ref_item{'age'} = "unknown";
- if ($type eq "tag") {
- my %tag = parse_tag($ref_id);
- $ref_item{'comment'} = $tag{'comment'};
- if ($tag{'type'} eq "commit") {
- my %co = parse_commit($tag{'object'});
- $ref_item{'epoch'} = $co{'committer_epoch'};
- $ref_item{'age'} = $co{'age_string'};
- } elsif (defined($tag{'epoch'})) {
- my $age = time - $tag{'epoch'};
- $ref_item{'epoch'} = $tag{'epoch'};
- $ref_item{'age'} = age_string($age);
- }
- $ref_item{'reftype'} = $tag{'type'};
- $ref_item{'name'} = $tag{'name'};
- $ref_item{'refid'} = $tag{'object'};
- } elsif ($type eq "commit"){
- my %co = parse_commit($ref_id);
- $ref_item{'reftype'} = "commit";
- $ref_item{'name'} = $ref_file;
- $ref_item{'title'} = $co{'title'};
- $ref_item{'refid'} = $ref_id;
- $ref_item{'epoch'} = $co{'committer_epoch'};
- $ref_item{'age'} = $co{'age_string'};
- } else {
- $ref_item{'reftype'} = $type;
- $ref_item{'name'} = $ref_file;
- $ref_item{'refid'} = $ref_id;
- }
-
- return %ref_item;
-}
-
# parse line of git-diff-tree "raw" output
sub parse_difftree_raw_line {
my $line = shift;
$hash = git_get_head_hash($project);
}
- my $git_command = git_cmd_str();
my $name = $project;
$name =~ s,([^/])/*\.git$,$1,;
$name = basename($name);
$name =~ s/\047/\047\\\047\047/g;
my $cmd;
$filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}";
- $cmd = "$git_command archive " .
- "--format=$known_snapshot_formats{$format}{'format'} " .
- "--prefix=\'$name\'/ $hash";
+ $cmd = quote_command(
+ git_cmd(), 'archive',
+ "--format=$known_snapshot_formats{$format}{'format'}",
+ "--prefix=$name/", $hash);
if (exists $known_snapshot_formats{$format}{'compressor'}) {
- $cmd .= ' | ' . join ' ', @{$known_snapshot_formats{$format}{'compressor'}};
+ $cmd .= ' | ' . quote_command(@{$known_snapshot_formats{$format}{'compressor'}});
}
print $cgi->header(
if ($hash || ($hash_base && !defined $file_name)) {
my $object_id = $hash || $hash_base;
- my $git_command = git_cmd_str();
- open my $fd, "-|", "$git_command cat-file -t $object_id 2>/dev/null"
+ open my $fd, "-|", quote_command(
+ git_cmd(), 'cat-file', '-t', $object_id) . ' 2> /dev/null'
or die_error('404 Not Found', "Object does not exist");
$type = <$fd>;
chomp $type;
int len, baselen, ch;
baselen = strlen(base);
- len = baselen + 7; /* "/refs/" + NUL */
+ len = baselen + 2; /* '/' after base and terminating NUL */
for (cp = ref; (ch = *cp) != 0; cp++, len++)
if (needs_quote(ch))
len += 2; /* extra two hex plus replacement % */
static int match_explicit(struct ref *src, struct ref *dst,
struct ref ***dst_tail,
- struct refspec *rs,
- int errs)
+ struct refspec *rs)
{
struct ref *matched_src, *matched_dst;
char *dst_guess;
if (rs->pattern || rs->matching)
- return errs;
+ return 0;
matched_src = matched_dst = NULL;
switch (count_refspec_match(rs->src, src, &matched_src)) {
*/
matched_src = try_explicit_object_name(rs->src);
if (!matched_src)
- error("src refspec %s does not match any.", rs->src);
+ return error("src refspec %s does not match any.", rs->src);
break;
default:
- matched_src = NULL;
- error("src refspec %s matches more than one.", rs->src);
- break;
+ return error("src refspec %s matches more than one.", rs->src);
}
- if (!matched_src)
- errs = 1;
-
if (!dst_value) {
unsigned char sha1[20];
int flag;
- if (!matched_src)
- return errs;
dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
dst_value);
break;
}
- if (errs || !matched_dst)
- return 1;
- if (matched_dst->peer_ref) {
- errs = 1;
- error("dst ref %s receives from more than one src.",
+ if (!matched_dst)
+ return -1;
+ if (matched_dst->peer_ref)
+ return error("dst ref %s receives from more than one src.",
matched_dst->name);
- }
else {
matched_dst->peer_ref = matched_src;
matched_dst->force = rs->force;
}
- return errs;
+ return 0;
}
static int match_explicit_refs(struct ref *src, struct ref *dst,
{
int i, errs;
for (i = errs = 0; i < rs_nr; i++)
- errs |= match_explicit(src, dst, dst_tail, &rs[i], errs);
- return -errs;
+ errs += match_explicit(src, dst, dst_tail, &rs[i]);
+ return errs;
}
static const struct refspec *check_pattern_match(const struct refspec *rs,
const unsigned char null_sha1[20];
-static unsigned int sha1_file_open_flag = O_NOATIME;
-
const signed char hexval_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */
return 0;
}
-char * sha1_to_hex(const unsigned char *sha1)
+char *sha1_to_hex(const unsigned char *sha1)
{
static int bufno;
static char hexbuffer[4][50];
read_info_alternates(get_object_directory(), 0);
}
-static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
+static int has_loose_object(const unsigned char *sha1)
{
char *name = sha1_file_name(sha1);
struct alternate_object_database *alt;
- if (!stat(name, st))
- return name;
+ if (!access(name, F_OK))
+ return 1;
prepare_alt_odb();
for (alt = alt_odb_list; alt; alt = alt->next) {
name = alt->name;
fill_sha1_path(name, sha1);
- if (!stat(alt->base, st))
- return alt->base;
+ if (!access(alt->base, F_OK))
+ return 1;
}
- return NULL;
+ return 0;
}
static unsigned int pack_used_ctr;
struct packed_git *parse_pack_index(unsigned char *sha1)
{
- char *path = sha1_pack_index_name(sha1);
- return parse_pack_index_file(sha1, path);
-}
-
-struct packed_git *parse_pack_index_file(const unsigned char *sha1,
- const char *idx_path)
-{
+ const char *idx_path = sha1_pack_index_name(sha1);
const char *path = sha1_pack_name(sha1);
struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2);
return hashcmp(sha1, real_sha1) ? -1 : 0;
}
+static int git_open_noatime(const char *name)
+{
+ static int sha1_file_open_flag = O_NOATIME;
+ int fd = open(name, O_RDONLY | sha1_file_open_flag);
+
+ /* Might the failure be due to O_NOATIME? */
+ if (fd < 0 && errno != ENOENT && sha1_file_open_flag) {
+ fd = open(name, O_RDONLY);
+ if (fd >= 0)
+ sha1_file_open_flag = 0;
+ }
+ return fd;
+}
+
+static int open_sha1_file(const unsigned char *sha1)
+{
+ int fd;
+ char *name = sha1_file_name(sha1);
+ struct alternate_object_database *alt;
+
+ fd = git_open_noatime(name);
+ if (fd >= 0)
+ return fd;
+
+ prepare_alt_odb();
+ errno = ENOENT;
+ for (alt = alt_odb_list; alt; alt = alt->next) {
+ name = alt->name;
+ fill_sha1_path(name, sha1);
+ fd = git_open_noatime(alt->base);
+ if (fd >= 0)
+ return fd;
+ }
+ return -1;
+}
+
static void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
{
- struct stat st;
void *map;
int fd;
- char *filename = find_sha1_file(sha1, &st);
- if (!filename) {
- return NULL;
- }
+ fd = open_sha1_file(sha1);
+ map = NULL;
+ if (fd >= 0) {
+ struct stat st;
- fd = open(filename, O_RDONLY | sha1_file_open_flag);
- if (fd < 0) {
- /* See if it works without O_NOATIME */
- switch (sha1_file_open_flag) {
- default:
- fd = open(filename, O_RDONLY);
- if (fd >= 0)
- break;
- /* Fallthrough */
- case 0:
- return NULL;
+ if (!fstat(fd, &st)) {
+ *size = xsize_t(st.st_size);
+ map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
}
-
- /* If it failed once, it will probably fail again.
- * Stop using O_NOATIME
- */
- sha1_file_open_flag = 0;
+ close(fd);
}
- *size = xsize_t(st.st_size);
- map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
- close(fd);
return map;
}
SHA1_Final(sha1, &c);
}
-/*
- * Link the tempfile to the final place, possibly creating the
- * last directory level as you do so.
- *
- * Returns the errno on failure, 0 on success.
- */
-static int link_temp_to_file(const char *tmpfile, const char *filename)
-{
- int ret;
- char *dir;
-
- if (!link(tmpfile, filename))
- return 0;
-
- /*
- * Try to mkdir the last path component if that failed.
- *
- * Re-try the "link()" regardless of whether the mkdir
- * succeeds, since a race might mean that somebody
- * else succeeded.
- */
- ret = errno;
- dir = strrchr(filename, '/');
- if (dir) {
- *dir = 0;
- if (!mkdir(filename, 0777) && adjust_shared_perm(filename)) {
- *dir = '/';
- return -2;
- }
- *dir = '/';
- if (!link(tmpfile, filename))
- return 0;
- ret = errno;
- }
- return ret;
-}
-
/*
* Move the just written object into its final resting place
*/
int move_temp_to_file(const char *tmpfile, const char *filename)
{
- int ret = link_temp_to_file(tmpfile, filename);
+ int ret = link(tmpfile, filename);
/*
* Coda hack - coda doesn't like cross-directory links,
return 0;
}
+/* Finalize a file on disk, and close it. */
+static void close_sha1_file(int fd)
+{
+ /* For safe-mode, we could fsync_or_die(fd, "sha1 file") here */
+ fchmod(fd, 0444);
+ if (close(fd) != 0)
+ die("unable to write sha1 file");
+}
+
+/* Size of directory component, including the ending '/' */
+static inline int directory_size(const char *filename)
+{
+ const char *s = strrchr(filename, '/');
+ if (!s)
+ return 0;
+ return s - filename + 1;
+}
+
+/*
+ * This creates a temporary file in the same directory as the final
+ * 'filename'
+ *
+ * We want to avoid cross-directory filename renames, because those
+ * can have problems on various filesystems (FAT, NFS, Coda).
+ */
+static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
+{
+ int fd, dirlen = directory_size(filename);
+
+ if (dirlen + 20 > bufsiz) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ memcpy(buffer, filename, dirlen);
+ strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
+ fd = mkstemp(buffer);
+ if (fd < 0 && dirlen) {
+ /* Make sure the directory exists */
+ buffer[dirlen-1] = 0;
+ if (mkdir(buffer, 0777) || adjust_shared_perm(buffer))
+ return -1;
+
+ /* Try again */
+ strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
+ fd = mkstemp(buffer);
+ }
+ return fd;
+}
+
static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
void *buf, unsigned long len, time_t mtime)
{
static char tmpfile[PATH_MAX];
filename = sha1_file_name(sha1);
- fd = open(filename, O_RDONLY);
- if (fd >= 0) {
- /*
- * FIXME!!! We might do collision checking here, but we'd
- * need to uncompress the old file and check it. Later.
- */
- close(fd);
- return 0;
- }
-
- if (errno != ENOENT) {
- return error("sha1 file %s: %s\n", filename, strerror(errno));
- }
-
- snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
-
- fd = mkstemp(tmpfile);
+ fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
if (fd < 0) {
if (errno == EPERM)
return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
if (write_buffer(fd, compressed, size) < 0)
die("unable to write sha1 file");
- fchmod(fd, 0444);
- if (close(fd))
- die("unable to write sha1 file");
+ close_sha1_file(fd);
free(compressed);
if (mtime) {
int force_object_loose(const unsigned char *sha1, time_t mtime)
{
- struct stat st;
void *buf;
unsigned long len;
enum object_type type;
char hdr[32];
int hdrlen;
- if (find_sha1_file(sha1, &st))
+ if (has_loose_object(sha1))
return 0;
buf = read_packed_sha1(sha1, &type, &len);
if (!buf)
return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
}
-/*
- * We need to unpack and recompress the object for writing
- * it out to a different file.
- */
-static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
-{
- size_t size;
- z_stream stream;
- unsigned char *unpacked;
- unsigned long len;
- enum object_type type;
- char hdr[32];
- int hdrlen;
- void *buf;
-
- /* need to unpack and recompress it by itself */
- unpacked = read_packed_sha1(sha1, &type, &len);
- if (!unpacked)
- error("cannot read sha1_file for %s", sha1_to_hex(sha1));
-
- hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
-
- /* Set it up */
- memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, zlib_compression_level);
- size = deflateBound(&stream, len + hdrlen);
- buf = xmalloc(size);
-
- /* Compress it */
- stream.next_out = buf;
- stream.avail_out = size;
-
- /* First header.. */
- stream.next_in = (void *)hdr;
- stream.avail_in = hdrlen;
- while (deflate(&stream, 0) == Z_OK)
- /* nothing */;
-
- /* Then the data itself.. */
- stream.next_in = unpacked;
- stream.avail_in = len;
- while (deflate(&stream, Z_FINISH) == Z_OK)
- /* nothing */;
- deflateEnd(&stream);
- free(unpacked);
-
- *objsize = stream.total_out;
- return buf;
-}
-
-int write_sha1_to_fd(int fd, const unsigned char *sha1)
-{
- int retval;
- unsigned long objsize;
- void *buf = map_sha1_file(sha1, &objsize);
-
- if (buf) {
- retval = write_buffer(fd, buf, objsize);
- munmap(buf, objsize);
- return retval;
- }
-
- buf = repack_object(sha1, &objsize);
- retval = write_buffer(fd, buf, objsize);
- free(buf);
- return retval;
-}
-
-int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
- size_t bufsize, size_t *bufposn)
-{
- char tmpfile[PATH_MAX];
- int local;
- z_stream stream;
- unsigned char real_sha1[20];
- unsigned char discard[4096];
- int ret;
- SHA_CTX c;
-
- snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
-
- local = mkstemp(tmpfile);
- if (local < 0) {
- if (errno == EPERM)
- return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
- else
- return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno));
- }
-
- memset(&stream, 0, sizeof(stream));
-
- inflateInit(&stream);
-
- SHA1_Init(&c);
-
- do {
- ssize_t size;
- if (*bufposn) {
- stream.avail_in = *bufposn;
- stream.next_in = (unsigned char *) buffer;
- do {
- stream.next_out = discard;
- stream.avail_out = sizeof(discard);
- ret = inflate(&stream, Z_SYNC_FLUSH);
- SHA1_Update(&c, discard, sizeof(discard) -
- stream.avail_out);
- } while (stream.avail_in && ret == Z_OK);
- if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0)
- die("unable to write sha1 file");
- memmove(buffer, buffer + *bufposn - stream.avail_in,
- stream.avail_in);
- *bufposn = stream.avail_in;
- if (ret != Z_OK)
- break;
- }
- size = xread(fd, buffer + *bufposn, bufsize - *bufposn);
- if (size <= 0) {
- close(local);
- unlink(tmpfile);
- if (!size)
- return error("Connection closed?");
- perror("Reading from connection");
- return -1;
- }
- *bufposn += size;
- } while (1);
- inflateEnd(&stream);
-
- fchmod(local, 0444);
- if (close(local) != 0)
- die("unable to write sha1 file");
- SHA1_Final(real_sha1, &c);
- if (ret != Z_STREAM_END) {
- unlink(tmpfile);
- return error("File %s corrupted", sha1_to_hex(sha1));
- }
- if (hashcmp(sha1, real_sha1)) {
- unlink(tmpfile);
- return error("File %s has bad hash", sha1_to_hex(sha1));
- }
-
- return move_temp_to_file(tmpfile, sha1_file_name(sha1));
-}
-
int has_pack_index(const unsigned char *sha1)
{
struct stat st;
int has_sha1_file(const unsigned char *sha1)
{
- struct stat st;
struct pack_entry e;
if (find_pack_entry(sha1, &e, NULL))
return 1;
- return find_sha1_file(sha1, &st) ? 1 : 0;
+ return has_loose_object(sha1);
}
int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
-* -whitespace
+t[0-9][0-9][0-9][0-9]-*.sh -whitespace
+t[0-9][0-9][0-9][0-9]/* -whitespace
This causes the test to immediately exit upon the first
failed test.
+--long-tests::
+ This causes additional long-running tests to be run (where
+ available), for more exhaustive testing.
+
+
+Skipping Tests
+--------------
+
+In some environments, certain tests have no way of succeeding
+due to platform limitation, such as lack of 'unzip' program, or
+filesystem that do not allow arbitrary sequence of non-NUL bytes
+as pathnames.
+
+You should be able to say something like
+
+ $ GIT_SKIP_TESTS=t9200.8 sh ./t9200-git-cvsexport-commit.sh
+
+and even:
+
+ $ GIT_SKIP_TESTS='t[0-4]??? t91?? t9200.8' make
+
+to omit such tests. The value of the environment variable is a
+SP separated list of patterns that tells which tests to skip,
+and either can match the "t[0-9]{4}" part to skip the whole
+test, or t[0-9]{4} followed by ".$number" to say which
+particular test to skip.
+
+Note that some tests in the existing test suite rely on previous
+test item, so you cannot arbitrarily disable one and expect the
+remainder of test to check what the test originally was intended
+to check.
+
Naming Tests
------------
'
+test_expect_success 'setup bare' '
+
+ git clone --bare . bare.git &&
+ cd bare.git
+
+'
+
+test_expect_success 'bare repository: check that .gitattribute is ignored' '
+
+ (
+ echo "f test=f"
+ echo "a/i test=a/i"
+ ) >.gitattributes &&
+ attr_check f unspecified &&
+ attr_check a/f unspecified &&
+ attr_check a/c/f unspecified &&
+ attr_check a/i unspecified &&
+ attr_check subdir/a/i unspecified
+
+'
+
+test_expect_success 'bare repository: test info/attributes' '
+
+ (
+ echo "f test=f"
+ echo "a/i test=a/i"
+ ) >info/attributes &&
+ attr_check f f &&
+ attr_check a/f f &&
+ attr_check a/c/f f &&
+ attr_check a/i a/i &&
+ attr_check subdir/a/i unspecified
+
+'
+
test_done
test -z "$content" ||
test_expect_success "--batch output of $type is correct" '
expect="$(maybe_remove_timestamp "$batch_output" $no_ts)"
- actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" no_ts)"
+ actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts)"
if test "z$expect" = "z$actual"
then
: happy
'
done
-test_expect_success "--batch-check for a non-existent object" '
- test "deadbeef missing" = \
- "$(echo_without_newline deadbeef | git cat-file --batch-check)"
+test_expect_success "--batch-check for a non-existent named object" '
+ test "foobar42 missing
+foobar84 missing" = \
+ "$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)"
+'
+
+test_expect_success "--batch-check for a non-existent hash" '
+ test "0000000000000000000000000000000000000042 missing
+0000000000000000000000000000000000000084 missing" = \
+ "$( ( echo 0000000000000000000000000000000000000042;
+ echo_without_newline 0000000000000000000000000000000000000084; ) \
+ | git cat-file --batch-check)"
+'
+
+test_expect_success "--batch for an existent and a non-existent hash" '
+ test "$tag_sha1 tag $tag_size
+$tag_content
+0000000000000000000000000000000000000000 missing" = \
+ "$( ( echo $tag_sha1;
+ echo_without_newline 0000000000000000000000000000000000000000; ) \
+ | git cat-file --batch)"
'
test_expect_success "--batch-check for an emtpy line" '
done
+test_expect_success 'git reflog expire honors core.sharedRepository' '
+ git config core.sharedRepository group &&
+ git reflog expire --all &&
+ actual="$(ls -l .git/logs/refs/heads/master)" &&
+ case "$actual" in
+ -rw-rw-*)
+ : happy
+ ;;
+ *)
+ echo Ooops, .git/logs/refs/heads/master is not 0662 [$actual]
+ false
+ ;;
+ esac
+'
+
test_done
test_expect_success 'apply empty' '
git reset --hard &&
- >empty &&
rm -f missing &&
git apply patch0 &&
test_cmp expect empty
test_expect_success 'apply --index empty' '
git reset --hard &&
- >empty &&
rm -f missing &&
git apply --index patch0 &&
test_cmp expect empty &&
test_expect_success 'apply create' '
git reset --hard &&
- >empty &&
rm -f missing &&
git apply patch1 &&
test_cmp expect missing
test_expect_success 'apply --index create' '
git reset --hard &&
- >empty &&
rm -f missing &&
git apply --index patch1 &&
test_cmp expect missing &&
test_cmp expect output)
'
+cat > test/expect << EOF
+* remote origin
+ URL: $(pwd)/one/.git
+ Remote branch merged with 'git pull' while on branch master
+ master
+ Tracked remote branches
+ master side
+ Local branches pushed with 'git push'
+ master:upstream +refs/tags/lastbackup
+EOF
+
+test_expect_success 'show -n' '
+ (mv one one.unreachable &&
+ cd test &&
+ git remote show -n origin > output &&
+ mv ../one.unreachable ../one &&
+ test_cmp expect output)
+'
+
test_expect_success 'prune' '
(cd one &&
git branch -m side side2) &&
! git rev-parse refs/remotes/origin/side)
'
+cat > test/expect << EOF
+Pruning origin
+URL: $(pwd)/one/.git
+ * [would prune] origin/side2
+EOF
+
+test_expect_success 'prune --dry-run' '
+ (cd one &&
+ git branch -m side2 side) &&
+ (cd test &&
+ git remote prune --dry-run origin > output &&
+ git rev-parse refs/remotes/origin/side2 &&
+ ! git rev-parse refs/remotes/origin/side &&
+ (cd ../one &&
+ git branch -m side side2) &&
+ test_cmp expect output)
+'
+
test_expect_success 'add --mirror && prune' '
(mkdir mirror &&
cd mirror &&
echo "Pull: refs/heads/one:refs/heads/one"
} >.git/remotes/two &&
cd .. &&
- git clone . bundle
+ git clone . bundle &&
+ git clone . seven
'
test_expect_success "fetch test" '
)
'
+test_expect_success 'pushing nonexistent branch by mistake should not segv' '
+
+ cd "$D" &&
+ test_must_fail git push seven no:no
+
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+append_cr () {
+ sed -e 's/$/Q/' | tr Q '\015'
+}
+
+remove_cr () {
+ tr '\015' Q | sed -e 's/Q$//'
+}
+
+test_description='merge conflict in crlf repo
+
+ b---M
+ / /
+ initial---a
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ git config core.autocrlf true &&
+ echo foo | append_cr >file &&
+ git add file &&
+ git commit -m "Initial" &&
+ git tag initial &&
+ git branch side &&
+ echo line from a | append_cr >file &&
+ git commit -m "add line from a" file &&
+ git tag a &&
+ git checkout side &&
+ echo line from b | append_cr >file &&
+ git commit -m "add line from b" file &&
+ git tag b &&
+ git checkout master
+'
+
+test_expect_success 'Check "ours" is CRLF' '
+ git reset --hard initial &&
+ git merge side -s ours &&
+ cat file | remove_cr | append_cr >file.temp &&
+ test_cmp file file.temp
+'
+
+test_expect_success 'Check that conflict file is CRLF' '
+ git reset --hard a &&
+ test_must_fail git merge side &&
+ cat file | remove_cr | append_cr >file.temp &&
+ test_cmp file file.temp
+'
+
+test_done
# Must fail due to conflict
test_must_fail git cherry-pick -n master &&
echo "editor not started" >.git/result &&
- test_must_fail GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" git commit &&
+ (
+ GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" &&
+ export GIT_EDITOR &&
+ test_must_fail git commit
+ ) &&
test "$(cat .git/result)" = "editor not started"
'
# Make sure --add-author-from with --use-log-author affected
# the authorship information
- grep "^Author: A U Thor " actual.4
+ grep "^Author: A U Thor " actual.4 &&
+
+ # Make sure there are no commit messages with excess blank lines
+ test $(grep "^ " actual.2 | wc -l) = 3 &&
+ test $(grep "^ " actual.3 | wc -l) = 5 &&
+ test $(grep "^ " actual.4 | wc -l) = 5 &&
+
+ # Make sure there are no svn commit messages with excess blank lines
+ (
+ cd work.svn &&
+ svn up &&
+
+ test $(svn log -r2:2 | wc -l) = 5 &&
+ test $(svn log -r4:4 | wc -l) = 7
+ )
'
test_done
debug=t; shift ;;
-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
immediate=t; shift ;;
+ -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
+ export GIT_TEST_LONG=t; shift ;;
-h|--h|--he|--hel|--help)
help=t; shift ;;
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
# environment variables to work around this.
#
# In particular, quoting isn't enough, as the path may contain the same quote
-# that we're using.
+# that we're using.
test_set_editor () {
FAKE_EDITOR="$1"
export FAKE_EDITOR
}
# Test the binaries we have just built. The tests are kept in
-# t/ subdirectory and are run in trash subdirectory.
+# t/ subdirectory and are run in 'trash directory' subdirectory.
PATH=$(pwd)/..:$PATH
GIT_EXEC_PATH=$(pwd)/..
GIT_TEMPLATE_DIR=$(pwd)/../templates/blt