diff options
50 files changed, 984 insertions, 232 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index 179cb177d1..3d8f03dfe5 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -280,6 +280,13 @@ apply.whitespace:: Tells `git-apply` how to handle whitespaces, in the same way as the '--whitespace' option. See gitlink:git-apply[1]. +branch.autosetupmerge:: + Tells `git-branch` and `git-checkout` to setup new branches + so that gitlink:git-pull[1] will appropriately merge from that + remote branch. Note that even if this option is not set, + this behavior can be chosen per-branch using the `--track` + and `--no-track` options. This option defaults to false. + branch.<name>.remote:: When in branch <name>, it tells `git fetch` which remote to fetch. If this option is not given, `git fetch` defaults to remote "origin". diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index ba79773f79..25cf84a0c7 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -12,7 +12,8 @@ SYNOPSIS 'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8] [--3way] [--interactive] [--binary] [--whitespace=<option>] [-C<n>] [-p<n>] - <mbox>... + <mbox>|<Maildir>... + 'git-am' [--skip | --resolved] DESCRIPTION @@ -23,9 +24,10 @@ current branch. OPTIONS ------- -<mbox>...:: +<mbox>|<Maildir>...:: The list of mailbox files to read patches from. If you do not - supply this argument, reads from the standard input. + supply this argument, reads from the standard input. If you supply + directories, they'll be treated as Maildirs. -s, --signoff:: Add `Signed-off-by:` line to the commit message, using diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index fd7f54093f..da5c242241 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout SYNOPSIS -------- -'git-cvsexportcommit' [-h] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID +'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID DESCRIPTION @@ -58,6 +58,9 @@ OPTIONS Prepend the commit message with the provided prefix. Useful for patch series and the like. +-u:: + Update affected files from cvs repository before attempting export. + -v:: Verbose. diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index d22844ba49..e5005f02f9 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -46,16 +46,28 @@ INSTALLATION cvspserver stream tcp nowait nobody git-cvsserver pserver ------ -Note: In some cases, you need to pass the 'pserver' argument twice for -git-cvsserver to see it. So the line would look like +Note: Some inetd servers let you specify the name of the executable +independently of the value of argv[0] (i.e. the name the program assumes +it was executed with). In this case the correct line in /etc/inetd.conf +looks like ------ - cvspserver stream tcp nowait nobody git-cvsserver pserver pserver + cvspserver stream tcp nowait nobody /usr/bin/git-cvsserver git-cvsserver pserver ------ No special setup is needed for SSH access, other than having GIT tools in the PATH. If you have clients that do not accept the CVS_SERVER -env variable, you can rename git-cvsserver to cvs. +environment variable, you can rename git-cvsserver to cvs. + +Note: Newer cvs versions (>= 1.12.11) also support specifying +CVS_SERVER directly in CVSROOT like + +------ +cvs -d ":ext;CVS_SERVER=git-cvsserver:user@server/path/repo.git" co <HEAD_name> +------ +This has the advantage that it will be saved in your 'CVS/Root' files and +you don't need to worry about always setting the correct environment +variable. -- 2. For each repo that you want accessible from CVS you need to edit config in the repo and add the following section. @@ -74,7 +86,7 @@ write access to the log file and to the database (see SSH, the users of course also need write access to the git repository itself. [[configaccessmethod]] -All configuration variables can also be overriden for a specific method of +All configuration variables can also be overridden for a specific method of access. Valid method names are "ext" (for SSH access) and "pserver". The following example configuration would disable pserver access while still allowing access over SSH. @@ -116,7 +128,7 @@ Database Backend git-cvsserver uses one database per git head (i.e. CVS module) to store information about the repository for faster access. The -database doesn't contain any persitent data and can be completly +database doesn't contain any persistent data and can be completely regenerated from the git repository at any time. The database needs to be updated (i.e. written to) after every commit. diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 076cebca17..43e0d2266c 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -124,7 +124,7 @@ which case it outputs: detailed information on unmerged paths. For an unmerged path, instead of recording a single mode/SHA1 pair, -the dircache records up to three such pairs; one from tree O in stage +the index records up to three such pairs; one from tree O in stage 1, A in stage 2, and B in stage 3. This information can be used by the user (or the porcelain) to see what should eventually be recorded at the path. (see git-read-tree for more information on state) diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt index 7899394081..ad7f1b9202 100644 --- a/Documentation/git-ls-tree.txt +++ b/Documentation/git-ls-tree.txt @@ -9,7 +9,7 @@ git-ls-tree - List the contents of a tree object SYNOPSIS -------- [verse] -'git-ls-tree' [-d] [-r] [-t] [-z] +'git-ls-tree' [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev=[<n>]] <tree-ish> [paths...] @@ -36,6 +36,10 @@ OPTIONS Show tree entries even when going to recurse them. Has no effect if '-r' was not passed. '-d' implies '-t'. +-l:: +--long:: + Show object size of blob (file) entries. + -z:: \0 line termination on output. @@ -65,6 +69,14 @@ Output Format When the `-z` option is not used, TAB, LF, and backslash characters in pathnames are represented as `\t`, `\n`, and `\\`, respectively. +When the `-l` option is used, format changes to + + <mode> SP <type> SP <object> SP <object size> TAB <file> + +Object size identified by <object> is given in bytes, and right-justified +with minimum width of 7 characters. Object size is given only for blobs +(file) entries; for other entries `-` character is used in place of size. + Author ------ diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt index c11d6a530f..abb0903696 100644 --- a/Documentation/git-mailsplit.txt +++ b/Documentation/git-mailsplit.txt @@ -7,12 +7,15 @@ git-mailsplit - Simple UNIX mbox splitter program SYNOPSIS -------- -'git-mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>...] +'git-mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>|<Maildir>...] DESCRIPTION ----------- -Splits a mbox file into a list of files: "0001" "0002" .. in the specified -directory so you can process them further from there. +Splits a mbox file or a Maildir into a list of files: "0001" "0002" .. in the +specified directory so you can process them further from there. + +IMPORTANT: Maildir splitting relies upon filenames being sorted to output +patches in the correct order. OPTIONS ------- @@ -20,6 +23,10 @@ OPTIONS Mbox file to split. If not given, the mbox is read from the standard input. +<Maildir>:: + Root of the Maildir to split. This directory should contain the cur, tmp + and new subdirectories. + <directory>:: Directory in which to place the individual messages. diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 9c08efa53a..912ef29efc 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -9,7 +9,7 @@ git-merge - Join two or more development histories together SYNOPSIS -------- [verse] -'git-merge' [-n] [--no-commit] [--squash] [-s <strategy>]... +'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]... [-m <msg>] <remote> <remote>... DESCRIPTION diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt index 489c3e9d5b..e903abfeb8 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary.txt @@ -10,7 +10,7 @@ GIT Glossary A bare repository is normally an appropriately named <<def_directory,directory>> with a `.git` suffix that does not have a locally checked-out copy of any of the files under - <<def_revision,revision>> control. That is, all of the `git` + revision control. That is, all of the `git` administrative and control files that would normally be present in the hidden `.git` sub-directory are directly present in the `repository.git` directory instead, @@ -37,7 +37,7 @@ GIT Glossary [[def_chain]]chain:: A list of objects, where each <<def_object,object>> in the list contains a reference to its successor (for example, the successor of a - <<def_commit,commit>> could be one of its parents). + <<def_commit,commit>> could be one of its <<def_parent,parents>>). [[def_changeset]]changeset:: BitKeeper/cvsps speak for "<<def_commit,commit>>". Since git does not @@ -77,10 +77,10 @@ to point at the new commit. [[def_commit_object]]commit object:: An <<def_object,object>> which contains the information about a - particular <<def_revision,revision>>, such as parents, committer, + particular <<def_revision,revision>>, such as <<def_parent,parents>>, committer, author, date and the <<def_tree_object,tree object>> which corresponds to the top <<def_directory,directory>> of the stored - <<def_revision,revision>>. + revision. [[def_core_git]]core git:: Fundamental data structures and utilities of git. Exposes only limited @@ -101,19 +101,19 @@ to point at the new commit. [[def_detached_HEAD]]detached HEAD:: Normally the <<def_HEAD,HEAD>> stores the name of a - <<def_branch,branch>>. However, git also allows you to check - out an arbitrary commit that isn't necessarily the tip of any + <<def_branch,branch>>. However, git also allows you to <<def_checkout,check out>> + an arbitrary <<def_commit,commit>> that isn't necessarily the tip of any particular branch. In this case HEAD is said to be "detached". [[def_dircache]]dircache:: - You are *waaaaay* behind. + You are *waaaaay* behind. See <<def_index,index>>. [[def_directory]]directory:: The list you get with "ls" :-) [[def_dirty]]dirty:: A <<def_working_tree,working tree>> is said to be "dirty" if - it contains modifications which have not been committed to the current + it contains modifications which have not been <<def_commit,committed>> to the current <<def_branch,branch>>. [[def_ent]]ent:: @@ -121,6 +121,10 @@ to point at the new commit. `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth explanation. Avoid this term, not to confuse people. +[[def_evil_merge]]evil merge:: + An evil merge is a <<def_merge,merge>> that introduces changes that + do not appear in any <<def_parent,parent>>. + [[def_fast_forward]]fast forward:: A fast-forward is a special type of <<def_merge,merge>> where you have a <<def_revision,revision>> and you are "merging" another @@ -149,7 +153,7 @@ to point at the new commit. [[def_grafts]]grafts:: Grafts enables two otherwise different lines of development to be joined together by recording fake ancestry information for commits. This way - you can make git pretend the set of parents a <<def_commit,commit>> has + you can make git pretend the set of <<def_parent,parents>> a <<def_commit,commit>> has is different from what was recorded when the commit was created. Configured via the `.git/info/grafts` file. @@ -157,13 +161,13 @@ to point at the new commit. In git's context, synonym to <<def_object_name,object name>>. [[def_head]]head:: - A named reference to the <<def_commit,commit>> at the tip of a + A <<def_ref,named reference>> to the <<def_commit,commit>> at the tip of a <<def_branch,branch>>. Heads are stored in `$GIT_DIR/refs/heads/`, except when using packed refs. (See gitlink:git-pack-refs[1].) [[def_HEAD]]HEAD:: - The current branch. In more detail: Your <<def_working_tree, + The current <<def_branch,branch>>. In more detail: Your <<def_working_tree, working tree>> is normally derived from the state of the tree referred to by HEAD. HEAD is a reference to one of the <<def_head,heads>> in your repository, except when using a @@ -179,15 +183,15 @@ to point at the new commit. checking. Typically, the hooks allow for a command to be pre-verified and potentially aborted, and allow for a post-notification after the operation is done. The hook scripts are found in the - `$GIT_DIR/hooks/` <<def_directory,directory>>, and are enabled by simply + `$GIT_DIR/hooks/` directory, and are enabled by simply making them executable. [[def_index]]index:: A collection of files with stat information, whose contents are stored - as objects. The index is a stored version of your working - <<def_tree,tree>>. Truth be told, it can also contain a second, and even - a third version of a <<def_working_tree,working tree>>, which are used - when merging. + as objects. The index is a stored version of your + <<def_working_tree,working tree>>. Truth be told, it can also contain a second, and even + a third version of a working tree, which are used + when <<def_merge,merging>>. [[def_index_entry]]index entry:: The information regarding a particular file, stored in the @@ -249,16 +253,16 @@ This commit is referred to as a "merge commit", or sometimes just a describing the type of an <<def_object,object>>. [[def_octopus]]octopus:: - To <<def_merge,merge>> more than two branches. Also denotes an + To <<def_merge,merge>> more than two <<def_branch,branches>>. Also denotes an intelligent predator. [[def_origin]]origin:: The default upstream <<def_repository,repository>>. Most projects have at least one upstream project which they track. By default 'origin' is used for that purpose. New upstream updates - will be fetched into remote tracking branches named + will be fetched into remote <<def_tracking_branch,tracking branches>> named origin/name-of-upstream-branch, which you can see using - "git <<def_branch,branch>> -r". + "`git branch -r`". [[def_pack]]pack:: A set of objects which have been compressed into one file (to save space @@ -327,7 +331,7 @@ This commit is referred to as a "merge commit", or sometimes just a `$GIT_DIR/refs/`. [[def_refspec]]refspec:: - A <<def_refspec,refspec>> is used by <<def_fetch,fetch>> and + A "refspec" is used by <<def_fetch,fetch>> and <<def_push,push>> to describe the mapping between remote <<def_ref,ref>> and local ref. They are combined with a colon in the format <src>:<dst>, preceded by an optional plus sign, +. @@ -340,11 +344,12 @@ This commit is referred to as a "merge commit", or sometimes just a gitlink:git-push[1] [[def_repository]]repository:: - A collection of refs together with an + A collection of <<def_ref,refs>> together with an <<def_object_database,object database>> containing all objects which are <<def_reachable,reachable>> from the refs, possibly - accompanied by meta data from one or more porcelains. A - repository can share an object database with other repositories. + accompanied by meta data from one or more <<def_porcelain,porcelains>>. A + repository can share an object database with other repositories + via <<def_alternate_object_database,alternates mechanism>>. [[def_resolve]]resolve:: The action of fixing up manually what a failed automatic @@ -366,8 +371,8 @@ This commit is referred to as a "merge commit", or sometimes just a Synonym for <<def_object_name,object name>>. [[def_shallow_repository]]shallow repository:: - A shallow repository has an incomplete - history some of whose commits have parents cauterized away (in other + A shallow <<def_repository,repository>> has an incomplete + history some of whose <<def_commit,commits>> have <<def_parent,parents>> cauterized away (in other words, git is told to pretend that these commits do not have the parents, even though they are recorded in the <<def_commit_object,commit object>>). This is sometimes useful when you are interested only in the @@ -385,7 +390,7 @@ This commit is referred to as a "merge commit", or sometimes just a command. [[def_tag]]tag:: - A <<def_ref,ref>> pointing to a tag or + A <<def_ref,ref>> pointing to a <<def_tag_object,tag>> or <<def_commit_object,commit object>>. In contrast to a <<def_head,head>>, a tag is not changed by a <<def_commit,commit>>. Tags (not <<def_tag_object,tag objects>>) are stored in `$GIT_DIR/refs/tags/`. A @@ -398,8 +403,7 @@ This commit is referred to as a "merge commit", or sometimes just a An <<def_object,object>> containing a <<def_ref,ref>> pointing to another object, which can contain a message just like a <<def_commit_object,commit object>>. It can also contain a (PGP) - signature, in which case it is called a "signed <<def_tag_object,tag - object>>". + signature, in which case it is called a "signed tag object". [[def_topic_branch]]topic branch:: A regular git <<def_branch,branch>> that is used by a developer to @@ -418,7 +422,7 @@ This commit is referred to as a "merge commit", or sometimes just a [[def_tree]]tree:: Either a <<def_working_tree,working tree>>, or a <<def_tree_object,tree - object>> together with the dependent blob and tree objects + object>> together with the dependent <<def_blob_object,blob>> and tree objects (i.e. a stored representation of a working tree). [[def_tree_object]]tree object:: diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 182cef54be..56f1d8d69d 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -1,3 +1,7 @@ +--summary:: + Show a diffstat at the end of the merge. The diffstat is also + controlled by the configuration option merge.diffstat. + -n, \--no-summary:: Do not show diffstat at the end of the merge. diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 52247aa713..4fabb8e2a9 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -872,7 +872,7 @@ Obviously, endless variations are possible; for example, to see all commits reachable from some head but not from any tag in the repository: ------------------------------------------------- -$ gitk ($ git show-ref --heads ) --not $( git show-ref --tags ) +$ gitk $( git show-ref --heads ) --not $( git show-ref --tags ) ------------------------------------------------- (See gitlink:git-rev-parse[1] for explanations of commit-selecting @@ -235,7 +235,7 @@ endif # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ - git-convert-objects$X git-fetch-pack$X git-fsck$X \ + git-convert-objects$X git-fetch-pack$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ git-fast-import$X \ git-merge-base$X \ @@ -246,7 +246,7 @@ PROGRAMS = \ git-show-index$X git-ssh-fetch$X \ git-ssh-upload$X git-unpack-file$X \ git-update-server-info$X \ - git-upload-pack$X git-verify-pack$X \ + git-upload-pack$X \ git-pack-redundant$X git-var$X \ git-merge-tree$X git-imap-send$X \ git-merge-recursive$X \ diff --git a/builtin-gc.c b/builtin-gc.c index 8ea165aef1..45025fba30 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -17,11 +17,11 @@ static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; -static int pack_refs = -1; +static int pack_refs = 1; static int aggressive_window = -1; #define MAX_ADD 10 -static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL}; +static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL}; static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL}; static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL}; static const char *argv_prune[] = {"prune", NULL}; diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index 9a5977fb99..cb4be4fabb 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -15,6 +15,7 @@ static int line_termination = '\n'; #define LS_TREE_ONLY 2 #define LS_SHOW_TREES 4 #define LS_NAME_ONLY 8 +#define LS_SHOW_SIZE 16 static int abbrev; static int ls_options; static const char **pathspec; @@ -22,7 +23,7 @@ static int chomp_prefix; static const char *ls_tree_prefix; static const char ls_tree_usage[] = - "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]"; + "git-ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]"; static int show_recursive(const char *base, int baselen, const char *pathname) { @@ -59,6 +60,7 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, { int retval = 0; const char *type = blob_type; + unsigned long size; if (S_ISGITLINK(mode)) { /* @@ -92,10 +94,24 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix))) return 0; - if (!(ls_options & LS_NAME_ONLY)) - printf("%06o %s %s\t", mode, type, - abbrev ? find_unique_abbrev(sha1,abbrev) - : sha1_to_hex(sha1)); + if (!(ls_options & LS_NAME_ONLY)) { + if (ls_options & LS_SHOW_SIZE) { + if (!strcmp(type, blob_type)) { + sha1_object_info(sha1, &size); + printf("%06o %s %s %7lu\t", mode, type, + abbrev ? find_unique_abbrev(sha1, abbrev) + : sha1_to_hex(sha1), + size); + } else + printf("%06o %s %s %7c\t", mode, type, + abbrev ? find_unique_abbrev(sha1, abbrev) + : sha1_to_hex(sha1), + '-'); + } else + printf("%06o %s %s\t", mode, type, + abbrev ? find_unique_abbrev(sha1, abbrev) + : sha1_to_hex(sha1)); + } write_name_quoted(base + chomp_prefix, baselen - chomp_prefix, pathname, line_termination, stdout); @@ -126,12 +142,19 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) case 't': ls_options |= LS_SHOW_TREES; break; + case 'l': + ls_options |= LS_SHOW_SIZE; + break; case '-': if (!strcmp(argv[1]+2, "name-only") || !strcmp(argv[1]+2, "name-status")) { ls_options |= LS_NAME_ONLY; break; } + if (!strcmp(argv[1]+2, "long")) { + ls_options |= LS_SHOW_SIZE; + break; + } if (!strcmp(argv[1]+2, "full-name")) { chomp_prefix = 0; break; diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 3bca855aae..c938425555 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -6,9 +6,10 @@ */ #include "cache.h" #include "builtin.h" +#include "path-list.h" static const char git_mailsplit_usage[] = -"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>..."; +"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>|<Maildir>..."; static int is_from_line(const char *line, int len) { @@ -96,44 +97,107 @@ static int split_one(FILE *mbox, const char *name, int allow_bare) exit(1); } -int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip) +static int populate_maildir_list(struct path_list *list, const char *path) { - char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip)); + DIR *dir; + struct dirent *dent; + + if ((dir = opendir(path)) == NULL) { + error("cannot opendir %s (%s)", path, strerror(errno)); + return -1; + } + + while ((dent = readdir(dir)) != NULL) { + if (dent->d_name[0] == '.') + continue; + path_list_insert(dent->d_name, list); + } + + closedir(dir); + + return 0; +} + +static int split_maildir(const char *maildir, const char *dir, + int nr_prec, int skip) +{ + char file[PATH_MAX]; + char curdir[PATH_MAX]; + char name[PATH_MAX]; int ret = -1; + int i; + struct path_list list = {NULL, 0, 0, 1}; - while (*mbox) { - const char *file = *mbox++; - FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); - int file_done = 0; + snprintf(curdir, sizeof(curdir), "%s/cur", maildir); + if (populate_maildir_list(&list, curdir) < 0) + goto out; - if ( !f ) { - error("cannot open mbox %s", file); + for (i = 0; i < list.nr; i++) { + FILE *f; + snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path); + f = fopen(file, "r"); + if (!f) { + error("cannot open mail %s (%s)", file, strerror(errno)); goto out; } if (fgets(buf, sizeof(buf), f) == NULL) { - if (f == stdin) - break; /* empty stdin is OK */ - error("cannot read mbox %s", file); + error("cannot read mail %s (%s)", file, strerror(errno)); goto out; } - while (!file_done) { - sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); - file_done = split_one(f, name, allow_bare); + sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); + split_one(f, name, 1); + + fclose(f); + } + + path_list_clear(&list, 1); + + ret = skip; +out: + return ret; +} + +int split_mbox(const char *file, const char *dir, int allow_bare, + int nr_prec, int skip) +{ + char name[PATH_MAX]; + int ret = -1; + + FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); + int file_done = 0; + + if (!f) { + error("cannot open mbox %s", file); + goto out; + } + + if (fgets(buf, sizeof(buf), f) == NULL) { + /* empty stdin is OK */ + if (f != stdin) { + error("cannot read mbox %s", file); + goto out; } + file_done = 1; + } - if (f != stdin) - fclose(f); + while (!file_done) { + sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); + file_done = split_one(f, name, allow_bare); } + + if (f != stdin) + fclose(f); + ret = skip; out: - free(name); return ret; } + int cmd_mailsplit(int argc, const char **argv, const char *prefix) { - int nr = 0, nr_prec = 4, ret; + int nr = 0, nr_prec = 4, num = 0; int allow_bare = 0; const char *dir = NULL; const char **argp; @@ -186,9 +250,41 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix) argp = stdin_only; } - ret = split_mbox(argp, dir, allow_bare, nr_prec, nr); - if (ret != -1) - printf("%d\n", ret); + while (*argp) { + const char *arg = *argp++; + struct stat argstat; + int ret = 0; + + if (arg[0] == '-' && arg[1] == 0) { + ret = split_mbox(arg, dir, allow_bare, nr_prec, nr); + if (ret < 0) { + error("cannot split patches from stdin"); + return 1; + } + num += (ret - nr); + nr = ret; + continue; + } + + if (stat(arg, &argstat) == -1) { + error("cannot stat %s (%s)", arg, strerror(errno)); + return 1; + } + + if (S_ISDIR(argstat.st_mode)) + ret = split_maildir(arg, dir, nr_prec, nr); + else + ret = split_mbox(arg, dir, allow_bare, nr_prec, nr); + + if (ret < 0) { + error("cannot split patches from %s", arg); + return 1; + } + num += (ret - nr); + nr = ret; + } + + printf("%d\n", num); - return ret == -1; + return 0; } diff --git a/builtin-name-rev.c b/builtin-name-rev.c index a639e2feda..d3c42ed67e 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -4,6 +4,8 @@ #include "tag.h" #include "refs.h" +#define CUTOFF_DATE_SLOP 86400 /* one day */ + static const char name_rev_usage[] = "git-name-rev [--tags | --refs=<pattern>] ( --all | --stdin | committish [committish...] )\n"; @@ -216,6 +218,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) add_object_array((struct object *)commit, *argv, &revs); } + if (cutoff) + cutoff = cutoff - CUTOFF_DATE_SLOP; for_each_ref(name_ref, &data); if (transform_stdin) { diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index d080e30d67..1952950c9a 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -12,9 +12,11 @@ struct ref_to_prune { char name[FLEX_ARRAY]; }; +#define PACK_REFS_PRUNE 0x0001 +#define PACK_REFS_ALL 0x0002 + struct pack_refs_cb_data { - int prune; - int all; + unsigned int flags; struct ref_to_prune *ref_to_prune; FILE *refs_file; }; @@ -39,7 +41,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, is_tag_ref = !prefixcmp(path, "refs/tags/"); /* ALWAYS pack refs that were already packed or are tags */ - if (!cb->all && !is_tag_ref && !(flags & REF_ISPACKED)) + if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED)) return 0; fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path); @@ -53,7 +55,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, } } - if (cb->prune && !do_not_prune(flags)) { + if ((cb->flags & PACK_REFS_PRUNE) && !do_not_prune(flags)) { int namelen = strlen(path) + 1; struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); hashcpy(n->sha1, sha1); @@ -85,26 +87,51 @@ static void prune_refs(struct ref_to_prune *r) static struct lock_file packed; -int cmd_pack_refs(int argc, const char **argv, const char *prefix) +static int pack_refs(unsigned int flags) { - int fd, i; + int fd; struct pack_refs_cb_data cbdata; memset(&cbdata, 0, sizeof(cbdata)); + cbdata.flags = flags; + + fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1); + cbdata.refs_file = fdopen(fd, "w"); + if (!cbdata.refs_file) + die("unable to create ref-pack file structure (%s)", + strerror(errno)); + + /* perhaps other traits later as well */ + fprintf(cbdata.refs_file, "# pack-refs with: peeled \n"); + + for_each_ref(handle_one_ref, &cbdata); + if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file)) + die("failed to write ref-pack file (%s)", strerror(errno)); + if (commit_lock_file(&packed) < 0) + die("unable to overwrite old ref-pack file (%s)", strerror(errno)); + if (cbdata.flags & PACK_REFS_PRUNE) + prune_refs(cbdata.ref_to_prune); + return 0; +} - cbdata.prune = 1; +int cmd_pack_refs(int argc, const char **argv, const char *prefix) +{ + int i; + unsigned int flags; + + flags = PACK_REFS_PRUNE; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--prune")) { - cbdata.prune = 1; /* now the default */ + flags |= PACK_REFS_PRUNE; /* now the default */ continue; } if (!strcmp(arg, "--no-prune")) { - cbdata.prune = 0; + flags &= ~PACK_REFS_PRUNE; continue; } if (!strcmp(arg, "--all")) { - cbdata.all = 1; + flags |= PACK_REFS_ALL; continue; } /* perhaps other parameters later... */ @@ -113,22 +140,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) if (i != argc) usage(builtin_pack_refs_usage); - fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1); - cbdata.refs_file = fdopen(fd, "w"); - if (!cbdata.refs_file) - die("unable to create ref-pack file structure (%s)", - strerror(errno)); - - /* perhaps other traits later as well */ - fprintf(cbdata.refs_file, "# pack-refs with: peeled \n"); - - for_each_ref(handle_one_ref, &cbdata); - fflush(cbdata.refs_file); - fsync(fd); - fclose(cbdata.refs_file); - if (commit_lock_file(&packed) < 0) - die("unable to overwrite old ref-pack file (%s)", strerror(errno)); - if (cbdata.prune) - prune_refs(cbdata.ref_to_prune); - return 0; + return pack_refs(flags); } diff --git a/builtin-revert.c b/builtin-revert.c index ea2f15b977..80c348c401 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -45,8 +45,10 @@ static void parse_options(int argc, const char **argv) if (argc < 2) usage(usage_str); - for (i = 1; i < argc - 1; i++) { + for (i = 1; i < argc; i++) { arg = argv[i]; + if (arg[0] != '-') + break; if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit")) no_commit = 1; else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit")) @@ -59,7 +61,8 @@ static void parse_options(int argc, const char **argv) else if (strcmp(arg, "-r")) usage(usage_str); } - + if (i != argc - 1) + usage(usage_str); arg = argv[argc - 1]; if (get_sha1(arg, sha1)) die ("Cannot find '%s'", arg); @@ -8,7 +8,7 @@ extern const char git_usage_string[]; extern void help_unknown_cmd(const char *cmd); extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch); -extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip); +extern int split_mbox(const char *file, const char *dir, int allow_bare, int nr_prec, int skip); extern void stripspace(FILE *in, FILE *out); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern void prune_packed_objects(int); @@ -391,6 +391,23 @@ static enum protocol get_protocol(const char *name) #ifndef NO_IPV6 +static const char *ai_name(const struct addrinfo *ai) +{ + static char addr[INET_ADDRSTRLEN]; + if ( AF_INET == ai->ai_family ) { + struct sockaddr_in *in; + in = (struct sockaddr_in *)ai->ai_addr; + inet_ntop(ai->ai_family, &in->sin_addr, addr, sizeof(addr)); + } else if ( AF_INET6 == ai->ai_family ) { + struct sockaddr_in6 *in; + in = (struct sockaddr_in6 *)ai->ai_addr; + inet_ntop(ai->ai_family, &in->sin6_addr, addr, sizeof(addr)); + } else { + strcpy(addr, "(unknown)"); + } + return addr; +} + /* * Returns a connected socket() fd, or else die()s. */ @@ -401,6 +418,7 @@ static int git_tcp_connect_sock(char *host, int flags) const char *port = STR(DEFAULT_GIT_PORT); struct addrinfo hints, *ai0, *ai; int gai; + int cnt = 0; if (host[0] == '[') { end = strchr(host + 1, ']'); @@ -444,10 +462,18 @@ static int git_tcp_connect_sock(char *host, int flags) } if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { saved_errno = errno; + fprintf(stderr, "%s[%d: %s]: net=%s, errno=%s\n", + host, + cnt, + ai_name(ai), + hstrerror(h_errno), + strerror(saved_errno)); close(sockfd); sockfd = -1; continue; } + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "%s ", ai_name(ai)); break; } @@ -476,6 +502,7 @@ static int git_tcp_connect_sock(char *host, int flags) struct sockaddr_in sa; char **ap; unsigned int nport; + int cnt; if (host[0] == '[') { end = strchr(host + 1, ']'); @@ -512,7 +539,7 @@ static int git_tcp_connect_sock(char *host, int flags) if (flags & CONNECT_VERBOSE) fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port); - for (ap = he->h_addr_list; *ap; ap++) { + for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) { sockfd = socket(he->h_addrtype, SOCK_STREAM, 0); if (sockfd < 0) { saved_errno = errno; @@ -526,10 +553,19 @@ static int git_tcp_connect_sock(char *host, int flags) if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) { saved_errno = errno; + fprintf(stderr, "%s[%d: %s]: net=%s, errno=%s\n", + host, + cnt, + inet_ntoa(*(struct in_addr *)&sa.sin_addr), + hstrerror(h_errno), + strerror(saved_errno)); close(sockfd); sockfd = -1; continue; } + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "%s ", + inet_ntoa(*(struct in_addr *)&sa.sin_addr)); break; } diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 46356e8a27..9e72f0f7b1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1,8 +1,9 @@ # # bash completion support for core Git. # -# Copyright (C) 2006,2007 Shawn Pearce +# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org> # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/). +# Distributed under the GNU General Public License, version 2.0. # # The contained completion routines provide support for completing: # @@ -11,6 +12,7 @@ # *) .git/remotes file names # *) git 'subcommands' # *) tree paths within 'ref:path/to/file' expressions +# *) common --long-options # # To use these routines: # @@ -31,6 +33,17 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # +# To submit patches: +# +# *) Read Documentation/SubmittingPatches +# *) Send all patches to the current maintainer: +# +# "Shawn O. Pearce" <spearce@spearce.org> +# +# *) Always CC the Git mailing list: +# +# git@vger.kernel.org +# __gitdir () { @@ -262,6 +275,7 @@ __git_commands () applypatch) : ask gittus;; archimport) : import;; cat-file) : plumbing;; + check-attr) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; convert-objects) : plumbing;; @@ -269,10 +283,15 @@ __git_commands () cvsimport) : import;; cvsserver) : daemon;; daemon) : daemon;; + diff-files) : plumbing;; + diff-index) : plumbing;; + diff-tree) : plumbing;; fast-import) : import;; fsck-objects) : plumbing;; + fetch--tool) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; + for-each-ref) : plumbing;; hash-object) : plumbing;; http-*) : transport;; index-pack) : plumbing;; @@ -573,13 +592,13 @@ _git_log () __gitcomp " --max-count= --max-age= --since= --after= --min-age= --before= --until= - --root --not --topo-order --date-order + --root --topo-order --date-order --reverse --no-merges --abbrev-commit --abbrev= --relative-date --author= --committer= --grep= --all-match - --pretty= --name-status --name-only + --pretty= --name-status --name-only --raw --not --all " return @@ -745,9 +764,11 @@ _git_config () case "$cur" in --*) __gitcomp " - --global --list --replace-all + --global --system + --list --replace-all --get --get-all --get-regexp --add --unset --unset-all + --remove-section --rename-section " return ;; @@ -766,7 +787,10 @@ _git_config () remote.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - __gitcomp "url fetch push" "$pfx" "$cur" + __gitcomp " + url fetch push skipDefaultUpdate + receivepack uploadpack tagopt + " "$pfx" "$cur" return ;; remote.*) @@ -816,6 +840,9 @@ _git_config () format.headers gitcvs.enabled gitcvs.logfile + gitcvs.allbinary + gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass + gc.packrefs gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved @@ -832,9 +859,11 @@ _git_config () i18n.commitEncoding i18n.logOutputEncoding log.showroot + merge.tool merge.summary merge.verbosity pack.window + pack.depth pull.octopus pull.twohead repack.useDeltaBaseOffset @@ -858,13 +887,13 @@ _git_remote () while [ $c -lt $COMP_CWORD ]; do i="${COMP_WORDS[c]}" case "$i" in - add|show|prune) command="$i"; break ;; + add|show|prune|update) command="$i"; break ;; esac c=$((++c)) done if [ $c -eq $COMP_CWORD -a -z "$command" ]; then - __gitcomp "add show prune" + __gitcomp "add show prune update" return fi @@ -872,6 +901,18 @@ _git_remote () show|prune) __gitcomp "$(__git_remotes)" ;; + update) + local i c='' IFS=$'\n' + for i in $(git --git-dir="$(__gitdir)" config --list); do + case "$i" in + remotes.*) + i="${i#remotes.}" + c="$c ${i/=*/}" + ;; + esac + done + __gitcomp "$c" + ;; *) COMPREPLY=() ;; @@ -890,6 +931,26 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_shortlog () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --max-count= --max-age= --since= --after= + --min-age= --before= --until= + --no-merges + --author= --committer= --grep= + --all-match + --not --all + --numbered --summary + " + return + ;; + esac + __git_complete_revlist +} + _git_show () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -947,7 +1008,6 @@ _git () commit) _git_commit ;; config) _git_config ;; diff) _git_diff ;; - diff-tree) _git_diff_tree ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; @@ -962,6 +1022,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; whatchanged) _git_log ;; @@ -992,7 +1053,6 @@ complete -o default -o nospace -F _git_cherry git-cherry complete -o default -o nospace -F _git_cherry_pick git-cherry-pick complete -o default -o nospace -F _git_commit git-commit complete -o default -o nospace -F _git_diff git-diff -complete -o default -o nospace -F _git_diff_tree git-diff-tree complete -o default -o nospace -F _git_fetch git-fetch complete -o default -o nospace -F _git_format_patch git-format-patch complete -o default -o nospace -F _git_gc git-gc @@ -1008,6 +1068,7 @@ complete -o default -o nospace -F _git_rebase git-rebase complete -o default -o nospace -F _git_config git-config complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset +complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged @@ -1023,7 +1084,6 @@ complete -o default -o nospace -F _git git.exe complete -o default -o nospace -F _git_branch git-branch.exe complete -o default -o nospace -F _git_cherry git-cherry.exe complete -o default -o nospace -F _git_diff git-diff.exe -complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe complete -o default -o nospace -F _git_log git-log.exe complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe @@ -1031,6 +1091,7 @@ complete -o default -o nospace -F _git_merge_base git-merge-base.exe complete -o default -o nospace -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe complete -o default -o nospace -F _git_config git-config +complete -o default -o nospace -F _git_shortlog git-shortlog.exe complete -o default -o nospace -F _git_show git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 9877b98508..f2a3615bbc 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -20,17 +20,19 @@ new_workdir=$2 branch=$3 # want to make sure that what is pointed to has a .git directory ... -test -d "$orig_git/.git" || die "\"$orig_git\" is not a git repository!" +git_dir=$(cd "$orig_git" 2>/dev/null && + git rev-parse --git-dir 2>/dev/null) || + die "\"$orig_git\" is not a git repository!" # don't link to a workdir -if test -L "$orig_git/.git/config" +if test -L "$git_dir/config" then die "\"$orig_git\" is a working directory only, please specify" \ "a complete repository." fi # make sure the the links use full paths -orig_git=$(cd "$orig_git"; pwd) +git_dir=$(cd "$git_dir"; pwd) # create the workdir mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!" @@ -45,13 +47,13 @@ do mkdir -p "$(dirname "$new_workdir/.git/$x")" ;; esac - ln -s "$orig_git/.git/$x" "$new_workdir/.git/$x" + ln -s "$git_dir/$x" "$new_workdir/.git/$x" done # now setup the workdir cd "$new_workdir" # copy the HEAD from the original repository as a default branch -cp "$orig_git/.git/HEAD" .git/HEAD +cp "$git_dir/HEAD" .git/HEAD # checkout the branch (either the same as HEAD from the original repository, or # the one that was asked for) git checkout -f $branch @@ -509,36 +509,71 @@ static char *ident_to_worktree(const char *path, const char *src, unsigned long for (dst = buf; size; size--) { const char *cp; + /* Fetch next source character, move the pointer on */ char ch = *src++; + /* Copy the current character to the destination */ *dst++ = ch; + /* If the current character is "$" or there are less than three + * remaining bytes or the two bytes following this one are not + * "Id", then simply read the next character */ if ((ch != '$') || (size < 3) || memcmp("Id", src, 2)) continue; + /* + * Here when + * - There are more than 2 bytes remaining + * - The current three bytes are "$Id" + * with + * - ch == "$" + * - src[0] == "I" + */ + /* + * It's possible that an expanded Id has crept its way into the + * repository, we cope with that by stripping the expansion out + */ if (src[2] == ':') { + /* Expanded keywords have "$Id:" at the front */ + /* discard up to but not including the closing $ */ unsigned long rem = size - 3; + /* Point at first byte after the ":" */ cp = src + 3; + /* + * Throw away characters until either + * - we reach a "$" + * - we run out of bytes (rem == 0) + */ do { - ch = *cp++; + ch = *cp; if (ch == '$') break; + cp++; rem--; } while (rem); + /* If the above finished because it ran out of characters, then + * this is an incomplete keyword, so don't run the expansion */ if (!rem) continue; - size -= (cp - src); } else if (src[2] == '$') cp = src + 2; else + /* Anything other than "$Id:XXX$" or $Id$ and we skip the + * expansion */ continue; + /* cp is now pointing at the last $ of the keyword */ + memcpy(dst, "Id: ", 4); dst += 4; memcpy(dst, sha1_to_hex(sha1), 40); dst += 40; *dst++ = ' '; + + /* Adjust for the characters we've discarded */ size -= (cp - src); src = cp; + + /* Copy the final "$" */ *dst++ = *src++; size--; } @@ -1349,7 +1349,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, } /* - * Given a name and sha1 pair, if the dircache tells us the file in + * Given a name and sha1 pair, if the index tells us the file in * the work tree has that object contents, return true, so that * prepare_temp_file() does not have to inflate and extract. */ diff --git a/fast-import.c b/fast-import.c index ffa00fd3c6..f9bfcc72c8 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1013,7 +1013,7 @@ static void load_tree(struct tree_entry *root) return; myoe = find_object(sha1); - if (myoe) { + if (myoe && myoe->pack_id != MAX_PACK_ID) { if (myoe->type != OBJ_TREE) die("Not a tree: %s", sha1_to_hex(sha1)); t->delta_depth = 0; @@ -1122,6 +1122,7 @@ static void store_tree(struct tree_entry *root) || le->pack_id != pack_id) { lo.data = NULL; lo.depth = 0; + lo.no_free = 0; } else { mktree(t, 0, &lo.len, &old_tree); lo.data = old_tree.buffer; @@ -1656,6 +1657,33 @@ static void file_change_deleteall(struct branch *b) load_tree(&b->branch_tree); } +static void cmd_from_commit(struct branch *b, char *buf, unsigned long size) +{ + if (!buf || size < 46) + die("Not a valid commit: %s", sha1_to_hex(b->sha1)); + if (memcmp("tree ", buf, 5) + || get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1)) + die("The commit %s is corrupt", sha1_to_hex(b->sha1)); + hashcpy(b->branch_tree.versions[0].sha1, + b->branch_tree.versions[1].sha1); +} + +static void cmd_from_existing(struct branch *b) +{ + if (is_null_sha1(b->sha1)) { + hashclr(b->branch_tree.versions[0].sha1); + hashclr(b->branch_tree.versions[1].sha1); + } else { + unsigned long size; + char *buf; + + buf = read_object_with_reference(b->sha1, + commit_type, &size, b->sha1); + cmd_from_commit(b, buf, size); + free(buf); + } +} + static void cmd_from(struct branch *b) { const char *from; @@ -1681,40 +1709,19 @@ static void cmd_from(struct branch *b) } else if (*from == ':') { uintmax_t idnum = strtoumax(from + 1, NULL, 10); struct object_entry *oe = find_mark(idnum); - unsigned long size; - char *buf; if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); hashcpy(b->sha1, oe->sha1); - buf = gfi_unpack_entry(oe, &size); - if (!buf || size < 46) - die("Not a valid commit: %s", from); - if (memcmp("tree ", buf, 5) - || get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1)) - die("The commit %s is corrupt", sha1_to_hex(b->sha1)); - free(buf); - hashcpy(b->branch_tree.versions[0].sha1, - b->branch_tree.versions[1].sha1); - } else if (!get_sha1(from, b->sha1)) { - if (is_null_sha1(b->sha1)) { - hashclr(b->branch_tree.versions[0].sha1); - hashclr(b->branch_tree.versions[1].sha1); - } else { + if (oe->pack_id != MAX_PACK_ID) { unsigned long size; - char *buf; - - buf = read_object_with_reference(b->sha1, - commit_type, &size, b->sha1); - if (!buf || size < 46) - die("Not a valid commit: %s", from); - if (memcmp("tree ", buf, 5) - || get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1)) - die("The commit %s is corrupt", sha1_to_hex(b->sha1)); + char *buf = gfi_unpack_entry(oe, &size); + cmd_from_commit(b, buf, size); free(buf); - hashcpy(b->branch_tree.versions[0].sha1, - b->branch_tree.versions[1].sha1); - } - } else + } else + cmd_from_existing(b); + } else if (!get_sha1(from, b->sha1)) + cmd_from_existing(b); + else die("Invalid ref name or SHA1 expression: %s", from); read_next_command(); @@ -18,7 +18,7 @@ stop_here () { stop_here_user_resolve () { if [ -n "$resolvemsg" ]; then - echo "$resolvemsg" + printf '%s\n' "$resolvemsg" stop_here $1 fi cmdline=$(basename $0) @@ -146,7 +146,7 @@ do git_apply_opt="$git_apply_opt $1"; shift ;; --resolvemsg=*) - resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//"); shift ;; + resolvemsg=${1#--resolvemsg=}; shift ;; --) shift; break ;; @@ -331,7 +331,7 @@ do ADD_SIGNOFF= fi { - echo "$SUBJECT" + printf '%s\n' "$SUBJECT" if test -s "$dotest/msg-clean" then echo @@ -394,7 +394,7 @@ do fi echo - echo "Applying '$SUBJECT'" + printf 'Applying %s\n' "$SUBJECT" echo case "$resolved" in diff --git a/git-commit.sh b/git-commit.sh index f28fc24224..e8b60f7049 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -376,12 +376,12 @@ t,) rm -f "$TMP_INDEX" fi || exit - echo "$commit_only" | + printf '%s\n' "$commit_only" | GIT_INDEX_FILE="$TMP_INDEX" \ git-update-index --add --remove --stdin && save_index && - echo "$commit_only" | + printf '%s\n' "$commit_only" | ( GIT_INDEX_FILE="$NEXT_INDEX" export GIT_INDEX_FILE @@ -432,7 +432,7 @@ fi if test "$log_message" != '' then - echo "$log_message" + printf '%s\n' "$log_message" elif test "$logfile" != "" then if test "$logfile" = - @@ -475,7 +475,7 @@ if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then echo "#" echo "# It looks like you may be committing a MERGE." echo "# If this is not correct, please remove the file" - echo "# $GIT_DIR/MERGE_HEAD" + printf '%s\n' "# $GIT_DIR/MERGE_HEAD" echo "# and try again" echo "#" fi >>"$GIT_DIR"/COMMIT_EDITMSG diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index d6ae99b8c0..42060ef6e1 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -15,9 +15,9 @@ unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){ die "GIT_DIR is not defined or is unreadable"; } -our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d); +our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u); -getopts('hPpvcfam:d:'); +getopts('uhPpvcfam:d:'); $opt_h && usage(); @@ -178,6 +178,10 @@ foreach my $f (@files) { my %cvsstat; if (@canstatusfiles) { + if ($opt_u) { + my @updated = safe_pipe_capture(@cvs, 'update', @canstatusfiles); + print @updated; + } my @cvsoutput; @cvsoutput= safe_pipe_capture(@cvs, 'status', @canstatusfiles); my $matchcount = 0; diff --git a/git-cvsimport.perl b/git-cvsimport.perl index ac74bc51b3..f68afe78a0 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -559,11 +559,6 @@ unless (-d $git_dir) { $last_branch = $opt_o; $orig_branch = ""; } else { - -f "$git_dir/refs/heads/$opt_o" - or die "Branch '$opt_o' does not exist.\n". - "Either use the correct '-o branch' option,\n". - "or import to a new repository.\n"; - open(F, "git-symbolic-ref HEAD |") or die "Cannot run git-symbolic-ref: $!\n"; chomp ($last_branch = <F>); @@ -588,6 +583,11 @@ unless (-d $git_dir) { $branch_date{$head} = $1; } close(H); + if (!exists $branch_date{$opt_o}) { + die "Branch '$opt_o' does not exist.\n". + "Either use the correct '-o branch' option,\n". + "or import to a new repository.\n"; + } } -d $git_dir diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 1de517791f..2b4825a8ee 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -95,9 +95,10 @@ $state->{method} = 'ext'; if (@ARGV && $ARGV[0] eq 'pserver') { $state->{method} = 'pserver'; my $line = <STDIN>; chomp $line; - unless( $line eq 'BEGIN AUTH REQUEST') { + unless( $line =~ /^BEGIN (AUTH|VERIFICATION) REQUEST$/) { die "E Do not understand $line - expecting BEGIN AUTH REQUEST\n"; } + my $request = $1; $line = <STDIN>; chomp $line; req_Root('root', $line) # reuse Root or die "E Invalid root $line \n"; @@ -109,10 +110,11 @@ if (@ARGV && $ARGV[0] eq 'pserver') { } $line = <STDIN>; chomp $line; # validate the password? $line = <STDIN>; chomp $line; - unless ($line eq 'END AUTH REQUEST') { - die "E Do not understand $line -- expecting END AUTH REQUEST\n"; + unless ($line eq "END $request REQUEST") { + die "E Do not understand $line -- expecting END $request REQUEST\n"; } print "I LOVE YOU\n"; + exit if $request eq 'VERIFICATION'; # cvs login # and now back to our regular programme... } diff --git a/git-fetch.sh b/git-fetch.sh index 0e05cf1195..6d3a3468b3 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -61,7 +61,7 @@ do quiet=--quiet ;; -v|--verbose) - verbose=Yes + verbose="$verbose"Yes ;; -k|--k|--ke|--kee|--keep) keep='-k -k' @@ -201,8 +201,14 @@ fetch_all_at_once () { echo "$ls_remote_result" | \ git-fetch--tool pick-rref "$rref" "-" else + flags= + case $verbose in + YesYes*) + flags="-v" + ;; + esac git-fetch-pack --thin $exec $keep $shallow_depth \ - $quiet $no_progress "$remote" $rref || + $quiet $no_progress $flags "$remote" $rref || echo failed "$remote" fi fi diff --git a/git-gui/Makefile b/git-gui/Makefile index ee564219c0..3de0de1a23 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -22,7 +22,7 @@ ifndef gitexecdir endif ifndef sharedir - sharedir := $(dir $(gitexecdir))/share + sharedir := $(dir $(gitexecdir))share endif ifndef INSTALL @@ -53,12 +53,19 @@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH)) libdir ?= $(sharedir)/git-gui/lib libdir_SQ = $(subst ','\'',$(libdir)) +exedir = $(dir $(gitexecdir))share/git-gui/lib +exedir_SQ = $(subst ','\'',$(exedir)) + $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh $(QUIET_GEN)rm -f $@ $@+ && \ + if test '$(exedir_SQ)' = '$(libdir_SQ)'; then \ + GITGUI_RELATIVE=1; \ + fi && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's|^exec wish "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' \ -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \ - -e 's|@@GITGUI_LIBDIR@@|$(libdir_SQ)|' \ + -e 's|@@GITGUI_RELATIVE@@|'$$GITGUI_RELATIVE'|' \ + -e $$GITGUI_RELATIVE's|@@GITGUI_LIBDIR@@|$(libdir_SQ)|' \ $@.sh >$@+ && \ chmod +x $@+ && \ mv $@+ $@ @@ -88,6 +95,7 @@ TRACK_VARS = \ $(subst ','\'',SHELL_PATH='$(SHELL_PATH_SQ)') \ $(subst ','\'',TCL_PATH='$(TCL_PATH_SQ)') \ $(subst ','\'',TCLTK_PATH='$(TCLTK_PATH_SQ)') \ + $(subst ','\'',gitexecdir='$(gitexecdir_SQ)') \ $(subst ','\'',libdir='$(libdir_SQ)') \ #end TRACK_VARS diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 0a471a5c7d..dba585111c 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -25,7 +25,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA} ## configure our library set oguilib {@@GITGUI_LIBDIR@@} -if {[string match @@* $oguilib]} { +set oguirel {@@GITGUI_RELATIVE@@} +if {$oguirel eq {1}} { + set oguilib [file dirname [file dirname [file normalize $argv0]]] + set oguilib [file join $oguilib share git-gui lib] +} elseif {[string match @@* $oguirel]} { set oguilib [file join [file dirname [file normalize $argv0]] lib] } set idx [file join $oguilib tclIndex] @@ -55,7 +59,7 @@ if {$idx ne {}} { } else { set auto_path [concat [list $oguilib] $auto_path] } -unset -nocomplain fd idx +unset -nocomplain oguilib oguirel idx fd if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} { unset _verbose @@ -1206,15 +1210,12 @@ foreach class {Button Checkbutton Entry Label } unset class -if {[is_Windows]} { - set M1B Control - set M1T Ctrl -} elseif {[is_MacOSX]} { +if {[is_MacOSX]} { set M1B M1 set M1T Cmd } else { - set M1B M1 - set M1T M1 + set M1B Control + set M1T Ctrl } proc apply_config {} { diff --git a/git-merge.sh b/git-merge.sh index 351676f6d4..981d69d35f 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+' +USAGE='[-n] [--summary] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+' SUBDIRECTORY_OK=Yes . git-sh-setup @@ -88,12 +88,11 @@ finish () { '') ;; ?*) - case "$no_summary" in - '') + if test "$show_diffstat" = t + then # We want color (if set), but no pager GIT_PAGER='' git-diff --stat --summary -M "$head" "$1" - ;; - esac + fi ;; esac } @@ -126,7 +125,9 @@ do case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ --no-summa|--no-summar|--no-summary) - no_summary=t ;; + show_diffstat=false ;; + --summary) + show_diffstat=t ;; --sq|--squ|--squa|--squas|--squash) squash=t no_commit=t ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) @@ -168,6 +169,11 @@ do shift done +if test -z "$show_diffstat"; then + test "$(git-config --bool merge.diffstat)" = false && show_diffstat=false + test -z "$show_diffstat" && show_diffstat=t +fi + # This could be traditional "merge <msg> HEAD <commit>..." and the # way we can tell it is to see if the second token is HEAD, but some # people might have misused the interface and used a committish that @@ -329,7 +335,7 @@ f,*) then echo "Wonderful." result_commit=$( - echo "$merge_msg" | + printf '%s\n' "$merge_msg" | git-commit-tree $result_tree -p HEAD -p "$1" ) || exit finish "$result_commit" "In-index merge" @@ -434,7 +440,7 @@ done if test '' != "$result_tree" then parents=$(git-show-branch --independent "$head" "$@" | sed -e 's/^/-p /') - result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree $parents) || exit + result_commit=$(printf '%s\n' "$merge_msg" | git-commit-tree $result_tree $parents) || exit finish "$result_commit" "Merge made by $wt_strategy." dropsave exit 0 @@ -473,7 +479,7 @@ else do echo $remote done >"$GIT_DIR/MERGE_HEAD" - echo "$merge_msg" >"$GIT_DIR/MERGE_MSG" + printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" fi if test "$merge_was_ok" = t diff --git a/git-pull.sh b/git-pull.sh index a3665d7751..ba0ca079e2 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -22,6 +22,9 @@ do -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ --no-summa|--no-summar|--no-summary) no_summary=-n ;; + --summary) + no_summary=$1 + ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) no_commit=--no-commit ;; --sq|--squ|--squa|--squas|--squash) diff --git a/git-svn.perl b/git-svn.perl index eda9969f50..e35006142a 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2472,12 +2472,16 @@ sub close_file { my $hash; my $path = $self->git_path($fb->{path}); if (my $fh = $fb->{fh}) { - seek($fh, 0, 0) or croak $!; - my $md5 = Digest::MD5->new; - $md5->addfile($fh); - my $got = $md5->hexdigest; - die "Checksum mismatch: $path\n", - "expected: $exp\n got: $got\n" if ($got ne $exp); + if (defined $exp) { + seek($fh, 0, 0) or croak $!; + my $md5 = Digest::MD5->new; + $md5->addfile($fh); + my $got = $md5->hexdigest; + if ($got ne $exp) { + die "Checksum mismatch: $path\n", + "expected: $exp\n got: $got\n"; + } + } sysseek($fh, 0, 0) or croak $!; if ($fb->{mode_b} == 120000) { sysread($fh, my $buf, 5) == 5 or croak $!; diff --git a/git-tag.sh b/git-tag.sh index 4a0a7b6607..6f0b7a7219 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -126,7 +126,7 @@ if [ "$annotate" ]; then echo "#" ) > "$GIT_DIR"/TAG_EDITMSG ${VISUAL:-${EDITOR:-vi}} "$GIT_DIR"/TAG_EDITMSG || exit else - echo "$message" >"$GIT_DIR"/TAG_EDITMSG + printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG fi grep -v '^#' <"$GIT_DIR"/TAG_EDITMSG | diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 5c7011a37b..c3921cb0ba 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -132,7 +132,7 @@ our %feature = ( # $feature{'snapshot'}{'default'} = [undef]; # To have project specific config enable override in $GITWEB_CONFIG # $feature{'snapshot'}{'override'} = 1; - # and in project config gitweb.snapshot = none|gzip|bzip2; + # and in project config gitweb.snapshot = none|gzip|bzip2|zip; 'snapshot' => { 'sub' => \&feature_snapshot, 'override' => 0, @@ -244,6 +244,8 @@ sub feature_snapshot { return ('x-gzip', 'gz', 'gzip'); } elsif ($val eq 'bzip2') { return ('x-bzip2', 'bz2', 'bzip2'); + } elsif ($val eq 'zip') { + return ('x-zip', 'zip', ''); } elsif ($val eq 'none') { return (); } @@ -3976,19 +3978,26 @@ sub git_snapshot { $hash = git_get_head_hash($project); } - my $filename = decode_utf8(basename($project)) . "-$hash.tar.$suffix"; + my $git = git_cmd_str(); + my $name = $project; + $name =~ s/\047/\047\\\047\047/g; + my $filename = decode_utf8(basename($project)); + my $cmd; + if ($suffix eq 'zip') { + $filename .= "-$hash.$suffix"; + $cmd = "$git archive --format=zip --prefix=\'$name\'/ $hash"; + } else { + $filename .= "-$hash.tar.$suffix"; + $cmd = "$git archive --format=tar --prefix=\'$name\'/ $hash | $command"; + } print $cgi->header( -type => "application/$ctype", -content_disposition => 'inline; filename="' . "$filename" . '"', -status => '200 OK'); - my $git = git_cmd_str(); - my $name = $project; - $name =~ s/\047/\047\\\047\047/g; - open my $fd, "-|", - "$git archive --format=tar --prefix=\'$name\'/ $hash | $command" - or die_error(undef, "Execute git-tar-tree failed"); + open my $fd, "-|", $cmd + or die_error(undef, "Execute git-archive failed"); binmode STDOUT, ':raw'; print <$fd>; binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi @@ -176,6 +176,7 @@ struct object *parse_object(const unsigned char *sha1) if (buffer) { struct object *obj; if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) { + free(buffer); error("sha1 mismatch %s\n", sha1_to_hex(sha1)); return NULL; } diff --git a/pack-check.c b/pack-check.c index d04536bbff..c168642c0c 100644 --- a/pack-check.c +++ b/pack-check.c @@ -73,12 +73,11 @@ static int verify_packfile(struct packed_git *p, } -#define MAX_CHAIN 40 +#define MAX_CHAIN 50 static void show_pack_info(struct packed_git *p) { - uint32_t nr_objects, i, chain_histogram[MAX_CHAIN]; - + uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1]; nr_objects = p->num_objects; memset(chain_histogram, 0, sizeof(chain_histogram)); @@ -109,22 +108,22 @@ static void show_pack_info(struct packed_git *p) printf("%-6s %lu %"PRIuMAX" %u %s\n", type, size, (uintmax_t)offset, delta_chain_length, sha1_to_hex(base_sha1)); - if (delta_chain_length < MAX_CHAIN) + if (delta_chain_length <= MAX_CHAIN) chain_histogram[delta_chain_length]++; else chain_histogram[0]++; } } - for (i = 0; i < MAX_CHAIN; i++) { + for (i = 0; i <= MAX_CHAIN; i++) { if (!chain_histogram[i]) continue; - printf("chain length %s %d: %d object%s\n", - i ? "=" : ">=", - i ? i : MAX_CHAIN, - chain_histogram[i], - 1 < chain_histogram[i] ? "s" : ""); + printf("chain length = %d: %d object%s\n", i, + chain_histogram[i], chain_histogram[i] > 1 ? "s" : ""); } + if (chain_histogram[0]) + printf("chain length > %d: %d object%s\n", MAX_CHAIN, + chain_histogram[0], chain_histogram[0] > 1 ? "s" : ""); } int verify_pack(struct packed_git *p, int verbose) diff --git a/progress.c b/progress.c index 05f7890314..4344f4eed5 100644 --- a/progress.c +++ b/progress.c @@ -62,11 +62,13 @@ int display_progress(struct progress *progress, unsigned n) fprintf(stderr, "%s%4u%% (%u/%u) done\r", progress->prefix, percent, n, progress->total); progress_update = 0; + progress->need_lf = 1; return 1; } } else if (progress_update) { fprintf(stderr, "%s%u\r", progress->prefix, n); progress_update = 0; + progress->need_lf = 1; return 1; } return 0; @@ -80,6 +82,7 @@ void start_progress(struct progress *progress, const char *title, progress->total = total; progress->last_percent = -1; progress->delay = 0; + progress->need_lf = 0; if (snprintf(buf, sizeof(buf), title, total)) fprintf(stderr, "%s\n", buf); set_progress_signal(); @@ -95,12 +98,13 @@ void start_progress_delay(struct progress *progress, const char *title, progress->delayed_percent_treshold = percent_treshold; progress->delayed_title = title; progress->delay = delay; + progress->need_lf = 0; set_progress_signal(); } void stop_progress(struct progress *progress) { clear_progress_signal(); - if (progress->total) + if (progress->need_lf) fputc('\n', stderr); } diff --git a/progress.h b/progress.h index 5ae1a89e5a..a7c17ca7c4 100644 --- a/progress.h +++ b/progress.h @@ -8,6 +8,7 @@ struct progress { unsigned delay; unsigned delayed_percent_treshold; const char *delayed_title; + int need_lf; }; int display_progress(struct progress *progress, unsigned n); diff --git a/run-command.c b/run-command.c index eff523e191..7e779d33ee 100644 --- a/run-command.c +++ b/run-command.c @@ -73,6 +73,17 @@ int start_command(struct child_process *cmd) close(cmd->out); } + if (cmd->dir && chdir(cmd->dir)) + die("exec %s: cd to %s failed (%s)", cmd->argv[0], + cmd->dir, strerror(errno)); + if (cmd->env) { + for (; *cmd->env; cmd->env++) { + if (strchr(*cmd->env, '=')) + putenv((char*)*cmd->env); + else + unsetenv(*cmd->env); + } + } if (cmd->git_cmd) { execv_git_cmd(cmd->argv); } else { @@ -133,13 +144,37 @@ int run_command(struct child_process *cmd) return finish_command(cmd); } +static void prepare_run_command_v_opt(struct child_process *cmd, + const char **argv, + int opt) +{ + memset(cmd, 0, sizeof(*cmd)); + cmd->argv = argv; + cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; + cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0; + cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; +} + int run_command_v_opt(const char **argv, int opt) { struct child_process cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.argv = argv; - cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; - cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0; - cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; + prepare_run_command_v_opt(&cmd, argv, opt); + return run_command(&cmd); +} + +int run_command_v_opt_cd(const char **argv, int opt, const char *dir) +{ + struct child_process cmd; + prepare_run_command_v_opt(&cmd, argv, opt); + cmd.dir = dir; + return run_command(&cmd); +} + +int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env) +{ + struct child_process cmd; + prepare_run_command_v_opt(&cmd, argv, opt); + cmd.dir = dir; + cmd.env = env; return run_command(&cmd); } diff --git a/run-command.h b/run-command.h index 3680ef9d45..7958eb1e0b 100644 --- a/run-command.h +++ b/run-command.h @@ -16,6 +16,8 @@ struct child_process { pid_t pid; int in; int out; + const char *dir; + const char *const *env; unsigned close_in:1; unsigned close_out:1; unsigned no_stdin:1; @@ -32,5 +34,12 @@ int run_command(struct child_process *); #define RUN_GIT_CMD 2 /*If this is to be git sub-command */ #define RUN_COMMAND_STDOUT_TO_STDERR 4 int run_command_v_opt(const char **argv, int opt); +int run_command_v_opt_cd(const char **argv, int opt, const char *dir); + +/* + * env (the environment) is to be formatted like environ: "VAR=VALUE". + * To unset an environment variable use just "VAR". + */ +int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env); #endif diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 6c26fd829d..a839f4e074 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -45,4 +45,40 @@ test_expect_success check ' test "z$id" = "z$embedded" ' +# If an expanded ident ever gets into the repository, we want to make sure that +# it is collapsed before being expanded again on checkout +test_expect_success expanded_in_repo ' + { + echo "File with expanded keywords" + echo "\$Id\$" + echo "\$Id:\$" + echo "\$Id: 0000000000000000000000000000000000000000 \$" + echo "\$Id: NoSpaceAtEnd\$" + echo "\$Id:NoSpaceAtFront \$" + echo "\$Id:NoSpaceAtEitherEnd\$" + echo "\$Id: NoTerminatingSymbol" + } > expanded-keywords && + + { + echo "File with expanded keywords" + echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$" + echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$" + echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$" + echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$" + echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$" + echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$" + echo "\$Id: NoTerminatingSymbol" + } > expected-output && + + git add expanded-keywords && + git commit -m "File with keywords expanded" && + + echo "expanded-keywords ident" >> .gitattributes && + + rm -f expanded-keywords && + git checkout -- expanded-keywords && + cat expanded-keywords && + cmp expanded-keywords expected-output +' + test_done diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 4795872a77..df969bb69c 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -16,16 +16,16 @@ test_expect_success setup ' for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file && git update-index file && - git commit -m "Side change #1" && + git commit -m "Side changes #1" && for i in D E F; do echo "$i"; done >>file && git update-index file && - git commit -m "Side change #2" && + git commit -m "Side changes #2" && git tag C2 && for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file && git update-index file && - git commit -m "Side change #3" && + git commit -m "Side changes #3 with \\n backslash-n in it." && git checkout master && git diff-tree -p C2 | git apply --index && @@ -66,4 +66,23 @@ test_expect_success "format-patch --ignore-if-in-upstream result applies" ' test $cnt = 2 ' +test_expect_success 'commit did not screw up the log message' ' + + git cat-file commit side | grep "^Side .* with .* backslash-n" + +' + +test_expect_success 'format-patch did not screw up the log message' ' + + grep "^Subject: .*Side changes #3 with .* backslash-n" patch0 && + grep "^Subject: .*Side changes #3 with .* backslash-n" patch1 + +' + +test_expect_success 'replay did not screw up the log message' ' + + git cat-file commit rebuild-1 | grep "^Side .* with .* backslash-n" + +' + test_done diff --git a/t/t9112-git-svn-md5less-file.sh b/t/t9112-git-svn-md5less-file.sh new file mode 100755 index 0000000000..08313bb545 --- /dev/null +++ b/t/t9112-git-svn-md5less-file.sh @@ -0,0 +1,45 @@ +test_description='test that git handles an svn repository with missing md5sums' + +. ./lib-git-svn.sh + +# Loading a node from a svn dumpfile without a Text-Content-Length +# field causes svn to neglect to store or report an md5sum. (it will +# calculate one if you had put Text-Content-Length: 0). This showed +# up in a repository creted with cvs2svn. + +cat > dumpfile.svn <<EOF +SVN-fs-dump-format-version: 1 + +Revision-number: 1 +Prop-content-length: 98 +Content-length: 98 + +K 7 +svn:log +V 0 + +K 10 +svn:author +V 4 +test +K 8 +svn:date +V 27 +2007-05-06T12:37:01.153339Z +PROPS-END + +Node-path: md5less-file +Node-kind: file +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + +EOF + +test_expect_success 'load svn dumpfile' "svnadmin load $rawsvnrepo < dumpfile.svn" + +test_expect_success 'initialize git-svn' "git-svn init $svnrepo" +test_expect_success 'fetch revisions from svn' 'git-svn fetch' +test_done diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 8e958da536..72e49f5d3b 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -119,6 +119,35 @@ test_expect_success \ </dev/null && git diff -u expect marks.new' +test_tick +cat >input <<INPUT_END +commit refs/heads/verify--import-marks +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +recreate from :5 +COMMIT + +from :5 +M 755 :2 copy-of-file2 + +INPUT_END +test_expect_success \ + 'A: verify marks import does not crash' \ + 'git-fast-import --import-marks=marks.out <input && + git-whatchanged verify--import-marks' +test_expect_success \ + 'A: verify pack' \ + 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done' +cat >expect <<EOF +:000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A copy-of-file2 +EOF +git-diff-tree -M -r master verify--import-marks >actual +test_expect_success \ + 'A: verify diff' \ + 'compare_diff_raw expect actual && + test `git-rev-parse --verify master:file2` \ + = `git-rev-parse --verify verify--import-marks:copy-of-file2`' + ### ### series B ### diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index d406a8824a..e9ef315173 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -47,6 +47,138 @@ test_expect_success 'basic checkout' \ 'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master && test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5))" = "empty/1.1/"' +#------------------------ +# PSERVER AUTHENTICATION +#------------------------ + +cat >request-anonymous <<EOF +BEGIN AUTH REQUEST +$SERVERDIR +anonymous + +END AUTH REQUEST +EOF + +cat >request-git <<EOF +BEGIN AUTH REQUEST +$SERVERDIR +git + +END AUTH REQUEST +EOF + +cat >login-anonymous <<EOF +BEGIN VERIFICATION REQUEST +$SERVERDIR +anonymous + +END VERIFICATION REQUEST +EOF + +cat >login-git <<EOF +BEGIN VERIFICATION REQUEST +$SERVERDIR +git + +END VERIFICATION REQUEST +EOF + +test_expect_success 'pserver authentication' \ + 'cat request-anonymous | git-cvsserver pserver >log 2>&1 && + tail -n1 log | grep -q "^I LOVE YOU$"' + +test_expect_success 'pserver authentication failure (non-anonymous user)' \ + 'if cat request-git | git-cvsserver pserver >log 2>&1 + then + false + else + true + fi && + tail -n1 log | grep -q "^I HATE YOU$"' + +test_expect_success 'pserver authentication (login)' \ + 'cat login-anonymous | git-cvsserver pserver >log 2>&1 && + tail -n1 log | grep -q "^I LOVE YOU$"' + +test_expect_success 'pserver authentication failure (login/non-anonymous user)' \ + 'if cat login-git | git-cvsserver pserver >log 2>&1 + then + false + else + true + fi && + tail -n1 log | grep -q "^I HATE YOU$"' + + +#-------------- +# CONFIG TESTS +#-------------- + +test_expect_success 'gitcvs.enabled = false' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false && + if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 + then + echo unexpected cvs success + false + else + true + fi && + cat cvs.log | grep -q "GITCVS emulation disabled" && + test ! -d cvswork2' + +rm -fr cvswork2 +test_expect_success 'gitcvs.ext.enabled = true' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true && + GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 && + diff -q cvswork cvswork2' + +rm -fr cvswork2 +test_expect_success 'gitcvs.ext.enabled = false' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false && + GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && + if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 + then + echo unexpected cvs success + false + else + true + fi && + cat cvs.log | grep -q "GITCVS emulation disabled" && + test ! -d cvswork2' + +rm -fr cvswork2 +test_expect_success 'gitcvs.dbname' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true && + GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 && + diff -q cvswork cvswork2 && + test -f "$SERVERDIR/gitcvs.ext.master.sqlite" && + cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs.ext.master.sqlite"' + +rm -fr cvswork2 +test_expect_success 'gitcvs.ext.dbname' \ + 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true && + GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite && + GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 && + diff -q cvswork cvswork2 && + test -f "$SERVERDIR/gitcvs1.ext.master.sqlite" && + test ! -f "$SERVERDIR/gitcvs2.ext.master.sqlite" && + cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs1.ext.master.sqlite"' + + +#------------ +# CVS UPDATE +#------------ + +rm -fr "$SERVERDIR" +cd "$WORKDIR" && +git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && +GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && +GIT_DIR="$SERVERDIR" git config --bool gitcvs.logfile "$SERVERDIR/gitcvs.log" || +exit 1 + test_expect_success 'cvs update (create new file)' \ 'echo testfile1 >testfile1 && git add testfile1 && @@ -123,4 +255,75 @@ test_expect_success 'cvs update (re-add deleted file)' \ test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" && diff -q testfile1 ../testfile1' +cd "$WORKDIR" +test_expect_success 'cvs update (merge)' \ + 'echo Line 0 >expected && + for i in 1 2 3 4 5 6 7 + do + echo Line $i >>merge + echo Line $i >>expected + done && + echo Line 8 >>expected && + git add merge && + git commit -q -m "Merge test (pre-merge)" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update && + test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" && + diff -q merge ../merge && + ( echo Line 0; cat merge ) >merge.tmp && + mv merge.tmp merge && + cd "$WORKDIR" && + echo Line 8 >>merge && + git add merge && + git commit -q -m "Merge test (merge)" && + git push gitcvs.git >/dev/null && + cd cvswork && + sleep 1 && touch merge && + GIT_CONFIG="$git_config" cvs -Q update && + diff -q merge ../expected' + +cd "$WORKDIR" + +cat >expected.C <<EOF +<<<<<<< merge.mine +Line 0 +======= +LINE 0 +>>>>>>> merge.3 +EOF + +for i in 1 2 3 4 5 6 7 8 +do + echo Line $i >>expected.C +done + +test_expect_success 'cvs update (conflict merge)' \ + '( echo LINE 0; cat merge ) >merge.tmp && + mv merge.tmp merge && + git add merge && + git commit -q -m "Merge test (conflict)" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update && + diff -q merge ../expected.C' + +cd "$WORKDIR" +test_expect_success 'cvs update (-C)' \ + 'cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update -C && + diff -q merge ../merge' + +cd "$WORKDIR" +test_expect_success 'cvs update (merge no-op)' \ + 'echo Line 9 >>merge && + cp merge cvswork/merge && + git add merge && + git commit -q -m "Merge test (no-op)" && + git push gitcvs.git >/dev/null && + cd cvswork && + sleep 1 && touch merge && + GIT_CONFIG="$git_config" cvs -Q update && + diff -q merge ../merge' + test_done @@ -26,7 +26,7 @@ struct tag *lookup_tag(const unsigned char *sha1) if (!obj->type) obj->type = OBJ_TAG; if (obj->type != OBJ_TAG) { - error("Object %s is a %s, not a tree", + error("Object %s is a %s, not a tag", sha1_to_hex(sha1), typename(obj->type)); return NULL; } |