diff options
103 files changed, 1338 insertions, 1635 deletions
diff --git a/.gitignore b/.gitignore index a78367c17..b5f9defed 100644 --- a/.gitignore +++ b/.gitignore @@ -76,7 +76,6 @@ /git-init-db /git-instaweb /git-log -/git-lost-found /git-ls-files /git-ls-remote /git-ls-tree @@ -106,7 +105,6 @@ /git-pack-refs /git-parse-remote /git-patch-id -/git-peek-remote /git-prune /git-prune-packed /git-pull @@ -132,7 +130,6 @@ /git-remote-testsvn /git-repack /git-replace -/git-repo-config /git-request-pull /git-rerere /git-reset @@ -160,7 +157,6 @@ /git-svn /git-symbolic-ref /git-tag -/git-tar-tree /git-unpack-file /git-unpack-objects /git-update-index diff --git a/Documentation/RelNotes/1.9.txt b/Documentation/RelNotes/1.9.txt index 9debcc41e..7120c220e 100644 --- a/Documentation/RelNotes/1.9.txt +++ b/Documentation/RelNotes/1.9.txt @@ -15,6 +15,13 @@ Read-only support for experimental loose-object format, in which users could optionally choose to write in their loose objects for a short while between v1.4.3 to v1.5.3 era, has been dropped. +The meanings of "--tags" option to "git fetch" has changed; the +command fetches tags _in addition to_ what are fetched by the same +command line without the option. + +A handful of ancient commands that have long been deprecated are +finally gone (repo-config, tar-tree, lost-found, and peek-remote). + Backward compatibility notes (for Git 2.0) ------------------------------------------ @@ -68,7 +75,7 @@ Updates since v1.8.5 Foreign interfaces, subsystems and ports. - * The HTTP transport, when talking GSS-Negotinate, uses "100 + * The HTTP transport, when talking GSS-Negotiate, uses "100 Continue" response to avoid having to rewind and resend a large payload, which may not be always doable. @@ -91,6 +98,10 @@ UI, Workflows & Features * "git rev-parse --parseopt" learned a new "--stuck-long" option to help scripts parse options with an optional parameter. + * The "--tags" option to "git fetch" no longer tells the command to + fetch _only_ the tags. It instead fetches tags _in addition to_ + what are fetched by the same command line without the option. + Performance, Internal Implementation, etc. @@ -103,6 +114,11 @@ Performance, Internal Implementation, etc. point out of the reflog entries for the remote-tracking branch the work has been based on. + * A third-party "receive-pack" (the responder to "git push") can + advertise the "no-thin" capability to tell "git push" not to use + the thin-pack optimization. Our receive-pack has always been + capable of accepting and fattening a thin-pack, and will continue + not to ask "git push" to use a non-thin pack. Also contains various documentation updates and code clean-ups. diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 705557689..e6d46edbe 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -139,8 +139,15 @@ People on the Git mailing list need to be able to read and comment on the changes you are submitting. It is important for a developer to be able to "quote" your changes, using standard e-mail tools, so that they may comment on specific portions of -your code. For this reason, all patches should be submitted -"inline". If your log message (including your name on the +your code. For this reason, each patch should be submitted +"inline" in a separate message. + +Multiple related patches should be grouped into their own e-mail +thread to help readers find all parts of the series. To that end, +send them as replies to either an additional "cover letter" message +(see below), the first patch, or the respective preceding patch. + +If your log message (including your name on the Signed-off-by line) is not writable in ASCII, make sure that you send off a message in the correct encoding. diff --git a/Documentation/config.txt b/Documentation/config.txt index ab26963d6..a4058063c 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2087,8 +2087,8 @@ remote.<name>.vcs:: remote.<name>.prune:: When set to true, fetching from this remote by default will also - remove any remote-tracking branches which no longer exist on the - remote (as if the `--prune` option was give on the command line). + remove any remote-tracking references that no longer exist on the + remote (as if the `--prune` option was given on the command line). Overrides `fetch.prune` settings, if any. remotes.<group>:: diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index ba1fe4958..f0ef7d02a 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -41,17 +41,20 @@ ifndef::git-pull[] -p:: --prune:: - After fetching, remove any remote-tracking branches which - no longer exist on the remote. + After fetching, remove any remote-tracking references that no + longer exist on the remote. Tags are not subject to pruning + if they are fetched only because of the default tag + auto-following or due to a --tags option. However, if tags + are fetched due to an explicit refspec (either on the command + line or in the remote configuration, for example if the remote + was cloned with the --mirror option), then they are also + subject to pruning. endif::git-pull[] -ifdef::git-pull[] ---no-tags:: -endif::git-pull[] ifndef::git-pull[] -n:: ---no-tags:: endif::git-pull[] +--no-tags:: By default, tags that point at objects that are downloaded from the remote repository are fetched and stored locally. This option disables this automatic tag following. The default @@ -61,11 +64,12 @@ endif::git-pull[] ifndef::git-pull[] -t:: --tags:: - This is a short-hand for giving `refs/tags/*:refs/tags/*` - refspec from the command line, to ask all tags to be fetched - and stored locally. Because this acts as an explicit - refspec, the default refspecs (configured with the - remote.$name.fetch variable) are overridden and not used. + Fetch all tags from the remote (i.e., fetch remote tags + `refs/tags/*` into local tags with the same name), in addition + to whatever else would otherwise be fetched. Using this + option alone does not subject tags to pruning, even if --prune + is used (though tags may be pruned anyway if they are also the + destination of an explicit refspec; see '--prune'). --recurse-submodules[=yes|on-demand|no]:: This option controls if and under what conditions new commits of diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt index 2d0daae62..0ea921a59 100644 --- a/Documentation/git-cherry.txt +++ b/Documentation/git-cherry.txt @@ -3,7 +3,7 @@ git-cherry(1) NAME ---- -git-cherry - Find commits not merged upstream +git-cherry - Find commits yet to be applied to upstream SYNOPSIS -------- @@ -12,46 +12,26 @@ SYNOPSIS DESCRIPTION ----------- -The changeset (or "diff") of each commit between the fork-point and <head> -is compared against each commit between the fork-point and <upstream>. -The diffs are compared after removing any whitespace and line numbers. +Determine whether there are commits in `<head>..<upstream>` that are +equivalent to those in the range `<limit>..<head>`. -Every commit that doesn't exist in the <upstream> branch -has its id (sha1) reported, prefixed by a symbol. The ones that have -equivalent change already -in the <upstream> branch are prefixed with a minus (-) sign, and those -that only exist in the <head> branch are prefixed with a plus (+) symbol: - - __*__*__*__*__> <upstream> - / - fork-point - \__+__+__-__+__+__-__+__> <head> - - -If a <limit> has been given then the commits along the <head> branch up -to and including <limit> are not reported: - - __*__*__*__*__> <upstream> - / - fork-point - \__*__*__<limit>__-__+__> <head> - - -Because 'git cherry' compares the changeset rather than the commit id -(sha1), you can use 'git cherry' to find out if a commit you made locally -has been applied <upstream> under a different commit id. For example, -this will happen if you're feeding patches <upstream> via email rather -than pushing or pulling commits directly. +The equivalence test is based on the diff, after removing whitespace +and line numbers. git-cherry therefore detects when commits have been +"copied" by means of linkgit:git-cherry-pick[1], linkgit:git-am[1] or +linkgit:git-rebase[1]. +Outputs the SHA1 of every commit in `<limit>..<head>`, prefixed with +`-` for commits that have an equivalent in <upstream>, and `+` for +commits that do not. OPTIONS ------- -v:: - Verbose. + Show the commit subjects next to the SHA1s. <upstream>:: - Upstream branch to compare against. - Defaults to the first tracked remote branch, if available. + Upstream branch to search for equivalent commits. + Defaults to the upstream branch of HEAD. <head>:: Working branch; defaults to HEAD. @@ -59,6 +39,103 @@ OPTIONS <limit>:: Do not report commits up to (and including) limit. +EXAMPLES +-------- + +Patch workflows +~~~~~~~~~~~~~~~ + +git-cherry is frequently used in patch-based workflows (see +linkgit:gitworkflows[7]) to determine if a series of patches has been +applied by the upstream maintainer. In such a workflow you might +create and send a topic branch like this: + +------------ +$ git checkout -b topic origin/master +# work and create some commits +$ git format-patch origin/master +$ git send-email ... 00* +------------ + +Later, you can see whether your changes have been applied by saying +(still on `topic`): + +------------ +$ git fetch # update your notion of origin/master +$ git cherry -v +------------ + +Concrete example +~~~~~~~~~~~~~~~~ + +In a situation where topic consisted of three commits, and the +maintainer applied two of them, the situation might look like: + +------------ +$ git log --graph --oneline --decorate --boundary origin/master...topic +* 7654321 (origin/master) upstream tip commit +[... snip some other commits ...] +* cccc111 cherry-pick of C +* aaaa111 cherry-pick of A +[... snip a lot more that has happened ...] +| * cccc000 (topic) commit C +| * bbbb000 commit B +| * aaaa000 commit A +|/ +o 1234567 branch point +------------ + +In such cases, git-cherry shows a concise summary of what has yet to +be applied: + +------------ +$ git cherry origin/master topic +- cccc000... commit C ++ bbbb000... commit B +- aaaa000... commit A +------------ + +Here, we see that the commits A and C (marked with `-`) can be +dropped from your `topic` branch when you rebase it on top of +`origin/master`, while the commit B (marked with `+`) still needs to +be kept so that it will be sent to be applied to `origin/master`. + + +Using a limit +~~~~~~~~~~~~~ + +The optional <limit> is useful in cases where your topic is based on +other work that is not in upstream. Expanding on the previous +example, this might look like: + +------------ +$ git log --graph --oneline --decorate --boundary origin/master...topic +* 7654321 (origin/master) upstream tip commit +[... snip some other commits ...] +* cccc111 cherry-pick of C +* aaaa111 cherry-pick of A +[... snip a lot more that has happened ...] +| * cccc000 (topic) commit C +| * bbbb000 commit B +| * aaaa000 commit A +| * 0000fff (base) unpublished stuff F +[... snip ...] +| * 0000aaa unpublished stuff A +|/ +o 1234567 merge-base between upstream and topic +------------ + +By specifying `base` as the limit, you can avoid listing commits +between `base` and `topic`: + +------------ +$ git cherry origin/master topic base +- cccc000... commit C ++ bbbb000... commit B +- aaaa000... commit A +------------ + + SEE ALSO -------- linkgit:git-patch-id[1] diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index a7b245d94..5809aa4eb 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -24,13 +24,13 @@ The ref names and their object names of fetched refs are stored in `.git/FETCH_HEAD`. This information is left for a later merge operation done by 'git merge'. -When <refspec> stores the fetched result in remote-tracking branches, -the tags that point at these branches are automatically -followed. This is done by first fetching from the remote using -the given <refspec>s, and if the repository has objects that are -pointed by remote tags that it does not yet have, then fetch -those missing tags. If the other end has tags that point at -branches you are not interested in, you will not get them. +By default, tags are auto-followed. This means that when fetching +from a remote, any tags on the remote that point to objects that exist +in the local repository are fetched. The effect is to fetch tags that +point at branches that you are interested in. This default behavior +can be changed by using the --tags or --no-tags options, by +configuring remote.<name>.tagopt, or by using a refspec that fetches +tags explicitly. 'git fetch' can fetch from either a single named repository, or from several repositories at once if <group> is given and diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt deleted file mode 100644 index d54932889..000000000 --- a/Documentation/git-lost-found.txt +++ /dev/null @@ -1,74 +0,0 @@ -git-lost-found(1) -================= - -NAME ----- -git-lost-found - Recover lost refs that luckily have not yet been pruned - -SYNOPSIS --------- -[verse] -'git lost-found' - -DESCRIPTION ------------ - -*NOTE*: this command is deprecated. Use linkgit:git-fsck[1] with -the option '--lost-found' instead. - -Finds dangling commits and tags from the object database, and -creates refs to them in the .git/lost-found/ directory. Commits and -tags that dereference to commits are stored in .git/lost-found/commit, -and other objects are stored in .git/lost-found/other. - - -OUTPUT ------- -Prints to standard output the object names and one-line descriptions -of any commits or tags found. - -EXAMPLE -------- - -Suppose you run 'git tag -f' and mistype the tag to overwrite. -The ref to your tag is overwritten, but until you run 'git -prune', the tag itself is still there. - ------------- -$ git lost-found -[1ef2b196d909eed523d4f3c9bf54b78cdd6843c6] GIT 0.99.9c -... ------------- - -Also you can use gitk to browse how any tags found relate to each -other. - ------------- -$ gitk $(cd .git/lost-found/commit && echo ??*) ------------- - -After making sure you know which the object is the tag you are looking -for, you can reconnect it to your regular `refs` hierarchy by using -the `update-ref` command. - ------------- -$ git cat-file -t 1ef2b196 -tag -$ git cat-file tag 1ef2b196 -object fa41bbce8e38c67a218415de6cfa510c7e50032a -type commit -tag v0.99.9c -tagger Junio C Hamano <junkio@cox.net> 1131059594 -0800 - -GIT 0.99.9c - -This contains the following changes from the "master" branch, since -... -$ git update-ref refs/tags/not-lost-anymore 1ef2b196 -$ git rev-parse not-lost-anymore -1ef2b196d909eed523d4f3c9bf54b78cdd6843c6 ------------- - -GIT ---- -Part of the linkgit:git[1] suite diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt deleted file mode 100644 index 87ea3fb05..000000000 --- a/Documentation/git-peek-remote.txt +++ /dev/null @@ -1,43 +0,0 @@ -git-peek-remote(1) -================== - -NAME ----- -git-peek-remote - List the references in a remote repository - - -SYNOPSIS --------- -[verse] -'git peek-remote' [--upload-pack=<git-upload-pack>] [<host>:]<directory> - -DESCRIPTION ------------ -This command is deprecated; use 'git ls-remote' instead. - -OPTIONS -------- ---upload-pack=<git-upload-pack>:: - Use this to specify the path to 'git-upload-pack' on the - remote side, if it is not found on your $PATH. Some - installations of sshd ignores the user's environment - setup scripts for login shells (e.g. .bash_profile) and - your privately installed git may not be found on the system - default $PATH. Another workaround suggested is to set - up your $PATH in ".bashrc", but this flag is for people - who do not want to pay the overhead for non-interactive - shells, but prefer having a lean .bashrc file (they set most of - the things up in .bash_profile). - -<host>:: - A remote host that houses the repository. When this - part is specified, 'git-upload-pack' is invoked via - ssh. - -<directory>:: - The repository to sync from. - - -GIT ---- -Part of the linkgit:git[1] suite diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt deleted file mode 100644 index 9ec115b9e..000000000 --- a/Documentation/git-repo-config.txt +++ /dev/null @@ -1,23 +0,0 @@ -git-repo-config(1) -================== - -NAME ----- -git-repo-config - Get and set repository or global options - - -SYNOPSIS --------- -[verse] -'git repo-config' ... - - -DESCRIPTION ------------ - -This is a synonym for linkgit:git-config[1]. Please refer to the -documentation of that command. - -GIT ---- -Part of the linkgit:git[1] suite diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt deleted file mode 100644 index f7362dc2d..000000000 --- a/Documentation/git-tar-tree.txt +++ /dev/null @@ -1,82 +0,0 @@ -git-tar-tree(1) -=============== - -NAME ----- -git-tar-tree - Create a tar archive of the files in the named tree object - - -SYNOPSIS --------- -[verse] -'git tar-tree' [--remote=<repo>] <tree-ish> [ <base> ] - -DESCRIPTION ------------ -THIS COMMAND IS DEPRECATED. Use 'git archive' with `--format=tar` -option instead (and move the <base> argument to `--prefix=base/`). - -Creates a tar archive containing the tree structure for the named tree. -When <base> is specified it is added as a leading path to the files in the -generated tar archive. - -'git tar-tree' behaves differently when given a tree ID versus when given -a commit ID or tag ID. In the first case the current time is used as -modification time of each file in the archive. In the latter case the -commit time as recorded in the referenced commit object is used instead. -Additionally the commit ID is stored in a global extended pax header. -It can be extracted using 'git get-tar-commit-id'. - -OPTIONS -------- - -<tree-ish>:: - The tree or commit to produce tar archive for. If it is - the object name of a commit object. - -<base>:: - Leading path to the files in the resulting tar archive. - ---remote=<repo>:: - Instead of making a tar archive from local repository, - retrieve a tar archive from a remote repository. - -CONFIGURATION -------------- - -tar.umask:: - This variable can be used to restrict the permission bits of - tar archive entries. The default is 0002, which turns off the - world write bit. The special value "user" indicates that the - archiving user's umask will be used instead. See umask(2) for - details. - -EXAMPLES --------- -`git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)`:: - - Create a tar archive that contains the contents of the - latest commit on the current branch, and extracts it in - `/var/tmp/junk` directory. - -`git tar-tree v1.4.0 git-1.4.0 | gzip >git-1.4.0.tar.gz`:: - - Create a tarball for v1.4.0 release. - -`git tar-tree v1.4.0^{tree} git-1.4.0 | gzip >git-1.4.0.tar.gz`:: - - Create a tarball for v1.4.0 release, but without a - global extended pax header. - -`git tar-tree --remote=example.com:git.git v1.4.0 >git-1.4.0.tar`:: - - Get a tarball v1.4.0 from example.com. - -`git tar-tree HEAD:Documentation/ git-docs > git-1.4.0-docs.tar`:: - - Put everything in the current head's Documentation/ directory - into 'git-1.4.0-docs.tar', with the prefix 'git-docs/'. - -GIT ---- -Part of the linkgit:git[1] suite diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index f97196051..205e80ef8 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -79,8 +79,10 @@ PATTERN FORMAT - An optional prefix "`!`" which negates the pattern; any matching file excluded by a previous pattern will become - included again. If a negated pattern matches, this will - override lower precedence patterns sources. + included again. It is not possible to re-include a file if a parent + directory of that file is excluded. Git doesn't list excluded + directories for performance reasons, so any patterns on contained + files have no effect, no matter where they are defined. Put a backslash ("`\`") in front of the first "`!`" for patterns that begin with a literal "`!`", for example, "`\!important!.txt`". @@ -182,6 +184,19 @@ Another example: The second .gitignore prevents Git from ignoring `arch/foo/kernel/vmlinux.lds.S`. +Example to exclude everything except a specific directory `foo/bar` +(note the `/*` - without the slash, the wildcard would also exclude +everything within `foo/bar`): + +-------------------------------------------------------------- + $ cat .gitignore + # exclude everything except directory foo/bar + /* + !/foo + /foo/* + !/foo/bar +-------------------------------------------------------------- + SEE ALSO -------- linkgit:git-rm[1], diff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt index 4be87768f..5d245aa9d 100644 --- a/Documentation/technical/api-remote.txt +++ b/Documentation/technical/api-remote.txt @@ -58,16 +58,16 @@ default remote, given the current branch and configuration. struct refspec -------------- -A struct refspec holds the parsed interpretation of a refspec. If it -will force updates (starts with a '+'), force is true. If it is a -pattern (sides end with '*') pattern is true. src and dest are the two -sides (if a pattern, only the part outside of the wildcards); if there -is only one side, it is src, and dst is NULL; if sides exist but are -empty (i.e., the refspec either starts or ends with ':'), the -corresponding side is "". - -This parsing can be done to an array of strings to give an array of -struct refpsecs with parse_ref_spec(). +A struct refspec holds the parsed interpretation of a refspec. If it +will force updates (starts with a '+'), force is true. If it is a +pattern (sides end with '*') pattern is true. src and dest are the +two sides (including '*' characters if present); if there is only one +side, it is src, and dst is NULL; if sides exist but are empty (i.e., +the refspec either starts or ends with ':'), the corresponding side is +"". + +An array of strings can be parsed into an array of struct refspecs +using parse_fetch_refspec() or parse_push_refspec(). remote_find_tracking(), given a remote and a struct refspec with either src or dst filled out, will fill out the other such that the diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt index fd8ffa5df..e3e792476 100644 --- a/Documentation/technical/protocol-capabilities.txt +++ b/Documentation/technical/protocol-capabilities.txt @@ -72,14 +72,29 @@ interleaved with S-R-Q. thin-pack --------- -This capability means that the server can send a 'thin' pack, a pack -which does not contain base objects; if those base objects are available -on client side. Client requests 'thin-pack' capability when it -understands how to "thicken" it by adding required delta bases making -it self-contained. - -Client MUST NOT request 'thin-pack' capability if it cannot turn a thin -pack into a self-contained pack. +A thin pack is one with deltas which reference base objects not +contained within the pack (but are known to exist at the receiving +end). This can reduce the network traffic significantly, but it +requires the receiving end to know how to "thicken" these packs by +adding the missing bases to the pack. + +The upload-pack server advertises 'thin-pack' when it can generate +and send a thin pack. A client requests the 'thin-pack' capability +when it understands how to "thicken" it, notifying the server that +it can receive such a pack. A client MUST NOT request the +'thin-pack' capability if it cannot turn a thin pack into a +self-contained pack. + +Receive-pack, on the other hand, is assumed by default to be able to +handle thin packs, but can ask the client not to use the feature by +advertising the 'no-thin' capability. A client MUST NOT send a thin +pack if the server advertises the 'no-thin' capability. + +The reasons for this asymmetry are historical. The receive-pack +program did not exist until after the invention of thin packs, so +historically the reference implementation of receive-pack always +understood thin packs. Adding 'no-thin' later allowed receive-pack +to disable the feature in a backwards-compatible manner. side-band, side-band-64k @@ -452,7 +452,6 @@ SCRIPT_SH += git-am.sh SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-difftool--helper.sh SCRIPT_SH += git-filter-branch.sh -SCRIPT_SH += git-lost-found.sh SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-resolve.sh @@ -587,11 +586,8 @@ BUILT_INS += git-cherry$X BUILT_INS += git-cherry-pick$X BUILT_INS += git-format-patch$X BUILT_INS += git-fsck-objects$X -BUILT_INS += git-get-tar-commit-id$X BUILT_INS += git-init$X BUILT_INS += git-merge-subtree$X -BUILT_INS += git-peek-remote$X -BUILT_INS += git-repo-config$X BUILT_INS += git-show$X BUILT_INS += git-stage$X BUILT_INS += git-status$X @@ -932,6 +928,7 @@ BUILTIN_OBJS += builtin/fmt-merge-msg.o BUILTIN_OBJS += builtin/for-each-ref.o BUILTIN_OBJS += builtin/fsck.o BUILTIN_OBJS += builtin/gc.o +BUILTIN_OBJS += builtin/get-tar-commit-id.o BUILTIN_OBJS += builtin/grep.o BUILTIN_OBJS += builtin/hash-object.o BUILTIN_OBJS += builtin/help.o @@ -983,7 +980,6 @@ BUILTIN_OBJS += builtin/show-ref.o BUILTIN_OBJS += builtin/stripspace.o BUILTIN_OBJS += builtin/symbolic-ref.o BUILTIN_OBJS += builtin/tag.o -BUILTIN_OBJS += builtin/tar-tree.o BUILTIN_OBJS += builtin/unpack-file.o BUILTIN_OBJS += builtin/unpack-objects.o BUILTIN_OBJS += builtin/update-index.o @@ -103,7 +103,6 @@ extern int cmd_remote(int argc, const char **argv, const char *prefix); extern int cmd_remote_ext(int argc, const char **argv, const char *prefix); extern int cmd_remote_fd(int argc, const char **argv, const char *prefix); extern int cmd_repack(int argc, const char **argv, const char *prefix); -extern int cmd_repo_config(int argc, const char **argv, const char *prefix); extern int cmd_rerere(int argc, const char **argv, const char *prefix); extern int cmd_reset(int argc, const char **argv, const char *prefix); extern int cmd_rev_list(int argc, const char **argv, const char *prefix); diff --git a/builtin/config.c b/builtin/config.c index 20e89fe4e..92ebf23f0 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -671,9 +671,3 @@ int cmd_config(int argc, const char **argv, const char *prefix) return 0; } - -int cmd_repo_config(int argc, const char **argv, const char *prefix) -{ - fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n"); - return cmd_config(argc, argv, prefix); -} diff --git a/builtin/fetch.c b/builtin/fetch.c index bd7a10164..3d978eb58 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -160,48 +160,156 @@ static void add_merge_config(struct ref **head, } } +static int add_existing(const char *refname, const unsigned char *sha1, + int flag, void *cbdata) +{ + struct string_list *list = (struct string_list *)cbdata; + struct string_list_item *item = string_list_insert(list, refname); + item->util = xmalloc(20); + hashcpy(item->util, sha1); + return 0; +} + +static int will_fetch(struct ref **head, const unsigned char *sha1) +{ + struct ref *rm = *head; + while (rm) { + if (!hashcmp(rm->old_sha1, sha1)) + return 1; + rm = rm->next; + } + return 0; +} + static void find_non_local_tags(struct transport *transport, struct ref **head, - struct ref ***tail); + struct ref ***tail) +{ + struct string_list existing_refs = STRING_LIST_INIT_DUP; + struct string_list remote_refs = STRING_LIST_INIT_NODUP; + const struct ref *ref; + struct string_list_item *item = NULL; + + for_each_ref(add_existing, &existing_refs); + for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { + if (prefixcmp(ref->name, "refs/tags/")) + continue; + + /* + * The peeled ref always follows the matching base + * ref, so if we see a peeled ref that we don't want + * to fetch then we can mark the ref entry in the list + * as one to ignore by setting util to NULL. + */ + if (!suffixcmp(ref->name, "^{}")) { + if (item && !has_sha1_file(ref->old_sha1) && + !will_fetch(head, ref->old_sha1) && + !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + item = NULL; + continue; + } + + /* + * If item is non-NULL here, then we previously saw a + * ref not followed by a peeled reference, so we need + * to check if it is a lightweight tag that we want to + * fetch. + */ + if (item && !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + + item = NULL; + + /* skip duplicates and refs that we already have */ + if (string_list_has_string(&remote_refs, ref->name) || + string_list_has_string(&existing_refs, ref->name)) + continue; + + item = string_list_insert(&remote_refs, ref->name); + item->util = (void *)ref->old_sha1; + } + string_list_clear(&existing_refs, 1); + + /* + * We may have a final lightweight tag that needs to be + * checked to see if it needs fetching. + */ + if (item && !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + + /* + * For all the tags in the remote_refs string list, + * add them to the list of refs to be fetched + */ + for_each_string_list_item(item, &remote_refs) { + /* Unless we have already decided to ignore this item... */ + if (item->util) + { + struct ref *rm = alloc_ref(item->string); + rm->peer_ref = alloc_ref(item->string); + hashcpy(rm->old_sha1, item->util); + **tail = rm; + *tail = &rm->next; + } + } + + string_list_clear(&remote_refs, 0); +} static struct ref *get_ref_map(struct transport *transport, - struct refspec *refs, int ref_count, int tags, - int *autotags) + struct refspec *refspecs, int refspec_count, + int tags, int *autotags) { int i; struct ref *rm; struct ref *ref_map = NULL; struct ref **tail = &ref_map; - const struct ref *remote_refs = transport_get_remote_refs(transport); + /* opportunistically-updated references: */ + struct ref *orefs = NULL, **oref_tail = &orefs; - if (ref_count || tags == TAGS_SET) { - struct ref **old_tail; + const struct ref *remote_refs = transport_get_remote_refs(transport); - for (i = 0; i < ref_count; i++) { - get_fetch_map(remote_refs, &refs[i], &tail, 0); - if (refs[i].dst && refs[i].dst[0]) + if (refspec_count) { + for (i = 0; i < refspec_count; i++) { + get_fetch_map(remote_refs, &refspecs[i], &tail, 0); + if (refspecs[i].dst && refspecs[i].dst[0]) *autotags = 1; } - /* Merge everything on the command line, but not --tags */ + /* Merge everything on the command line (but not --tags) */ for (rm = ref_map; rm; rm = rm->next) rm->fetch_head_status = FETCH_HEAD_MERGE; - if (tags == TAGS_SET) - get_fetch_map(remote_refs, tag_refspec, &tail, 0); /* - * For any refs that we happen to be fetching via command-line - * arguments, take the opportunity to update their configured - * counterparts. However, we do not want to mention these - * entries in FETCH_HEAD at all, as they would simply be - * duplicates of existing entries. + * For any refs that we happen to be fetching via + * command-line arguments, the destination ref might + * have been missing or have been different than the + * remote-tracking ref that would be derived from the + * configured refspec. In these cases, we want to + * take the opportunity to update their configured + * remote-tracking reference. However, we do not want + * to mention these entries in FETCH_HEAD at all, as + * they would simply be duplicates of existing + * entries, so we set them FETCH_HEAD_IGNORE below. + * + * We compute these entries now, based only on the + * refspecs specified on the command line. But we add + * them to the list following the refspecs resulting + * from the tags option so that one of the latter, + * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed + * by ref_remove_duplicates() in favor of one of these + * opportunistic entries with FETCH_HEAD_IGNORE. */ - old_tail = tail; for (i = 0; i < transport->remote->fetch_refspec_nr; i++) get_fetch_map(ref_map, &transport->remote->fetch[i], - &tail, 1); - for (rm = *old_tail; rm; rm = rm->next) - rm->fetch_head_status = FETCH_HEAD_IGNORE; + &oref_tail, 1); + + if (tags == TAGS_SET) + get_fetch_map(remote_refs, tag_refspec, &tail, 0); } else { /* Use the defaults */ struct remote *remote = transport->remote; @@ -238,11 +346,21 @@ static struct ref *get_ref_map(struct transport *transport, tail = &ref_map->next; } } - if (tags == TAGS_DEFAULT && *autotags) + + if (tags == TAGS_SET) + /* also fetch all tags */ + get_fetch_map(remote_refs, tag_refspec, &tail, 0); + else if (tags == TAGS_DEFAULT && *autotags) find_non_local_tags(transport, &ref_map, &tail); - ref_remove_duplicates(ref_map); - return ref_map; + /* Now append any refs to be updated opportunistically: */ + *tail = orefs; + for (rm = orefs; rm; rm = rm->next) { + rm->fetch_head_status = FETCH_HEAD_IGNORE; + tail = &rm->next; + } + + return ref_remove_duplicates(ref_map); } #define STORE_REF_ERROR_OTHER 1 @@ -612,106 +730,6 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map) return result; } -static int add_existing(const char *refname, const unsigned char *sha1, - int flag, void *cbdata) -{ - struct string_list *list = (struct string_list *)cbdata; - struct string_list_item *item = string_list_insert(list, refname); - item->util = xmalloc(20); - hashcpy(item->util, sha1); - return 0; -} - -static int will_fetch(struct ref **head, const unsigned char *sha1) -{ - struct ref *rm = *head; - while (rm) { - if (!hashcmp(rm->old_sha1, sha1)) - return 1; - rm = rm->next; - } - return 0; -} - -static void find_non_local_tags(struct transport *transport, - struct ref **head, - struct ref ***tail) -{ - struct string_list existing_refs = STRING_LIST_INIT_DUP; - struct string_list remote_refs = STRING_LIST_INIT_NODUP; - const struct ref *ref; - struct string_list_item *item = NULL; - - for_each_ref(add_existing, &existing_refs); - for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { - if (prefixcmp(ref->name, "refs/tags/")) - continue; - - /* - * The peeled ref always follows the matching base - * ref, so if we see a peeled ref that we don't want - * to fetch then we can mark the ref entry in the list - * as one to ignore by setting util to NULL. - */ - if (!suffixcmp(ref->name, "^{}")) { - if (item && !has_sha1_file(ref->old_sha1) && - !will_fetch(head, ref->old_sha1) && - !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - item = NULL; - continue; - } - - /* - * If item is non-NULL here, then we previously saw a - * ref not followed by a peeled reference, so we need - * to check if it is a lightweight tag that we want to - * fetch. - */ - if (item && !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - - item = NULL; - - /* skip duplicates and refs that we already have */ - if (string_list_has_string(&remote_refs, ref->name) || - string_list_has_string(&existing_refs, ref->name)) - continue; - - item = string_list_insert(&remote_refs, ref->name); - item->util = (void *)ref->old_sha1; - } - string_list_clear(&existing_refs, 1); - - /* - * We may have a final lightweight tag that needs to be - * checked to see if it needs fetching. - */ - if (item && !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - - /* - * For all the tags in the remote_refs string list, - * add them to the list of refs to be fetched - */ - for_each_string_list_item(item, &remote_refs) { - /* Unless we have already decided to ignore this item... */ - if (item->util) - { - struct ref *rm = alloc_ref(item->string); - rm->peer_ref = alloc_ref(item->string); - hashcpy(rm->old_sha1, item->util); - **tail = rm; - *tail = &rm->next; - } - } - - string_list_clear(&remote_refs, 0); -} - static void check_not_current_branch(struct ref *ref_map) { struct branch *current_branch = branch_get(NULL); @@ -831,30 +849,16 @@ static int do_fetch(struct transport *transport, } if (prune) { /* - * If --tags was specified, pretend that the user gave us - * the canonical tags refspec + * We only prune based on refspecs specified + * explicitly (via command line or configuration); we + * don't care whether --tags was specified. */ - if (tags == TAGS_SET) { - const char *tags_str = "refs/tags/*:refs/tags/*"; - struct refspec *tags_refspec, *refspec; - - /* Copy the refspec and add the tags to it */ - refspec = xcalloc(ref_count + 1, sizeof(struct refspec)); - tags_refspec = parse_fetch_refspec(1, &tags_str); - memcpy(refspec, refs, ref_count * sizeof(struct refspec)); - memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec)); - ref_count++; - - prune_refs(refspec, ref_count, ref_map); - - ref_count--; - /* The rest of the strings belong to fetch_one */ - free_refspec(1, tags_refspec); - free(refspec); - } else if (ref_count) { + if (ref_count) { prune_refs(refs, ref_count, ref_map); } else { - prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map); + prune_refs(transport->remote->fetch, + transport->remote->fetch_refspec_nr, + ref_map); } } free_refs(ref_map); @@ -930,8 +934,8 @@ static void add_options_to_argv(struct argv_array *argv) { if (dry_run) argv_array_push(argv, "--dry-run"); - if (prune > 0) - argv_array_push(argv, "--prune"); + if (prune != -1) + argv_array_push(argv, prune ? "--prune" : "--no-prune"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); if (force) diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c new file mode 100644 index 000000000..aa7259608 --- /dev/null +++ b/builtin/get-tar-commit-id.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005, 2006 Rene Scharfe + */ +#include "cache.h" +#include "commit.h" +#include "tar.h" +#include "builtin.h" +#include "quote.h" + +static const char builtin_get_tar_commit_id_usage[] = +"git get-tar-commit-id < <tarfile>"; + +/* ustar header + extended global header content */ +#define RECORDSIZE (512) +#define HEADERSIZE (2 * RECORDSIZE) + +int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) +{ + char buffer[HEADERSIZE]; + struct ustar_header *header = (struct ustar_header *)buffer; + char *content = buffer + RECORDSIZE; + ssize_t n; + + if (argc != 1) + usage(builtin_get_tar_commit_id_usage); + + n = read_in_full(0, buffer, HEADERSIZE); + if (n < HEADERSIZE) + die("git get-tar-commit-id: read error"); + if (header->typeflag[0] != 'g') + return 1; + if (memcmp(content, "52 comment=", 11)) + return 1; + + n = write_in_full(1, content + 11, 41); + if (n < 41) + die_errno("git get-tar-commit-id: write error"); + + return 0; +} diff --git a/builtin/remote.c b/builtin/remote.c index 5e4ab66c2..119e9151a 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -6,6 +6,7 @@ #include "strbuf.h" #include "run-command.h" #include "refs.h" +#include "argv-array.h" static const char * const builtin_remote_usage[] = { N_("git remote [-v | --verbose]"), @@ -77,9 +78,6 @@ static const char * const builtin_remote_seturl_usage[] = { static int verbose; -static int show_all(void); -static int prune_remote(const char *remote, int dry_run); - static inline int postfixcmp(const char *string, const char *postfix) { int len1 = strlen(string), len2 = strlen(postfix); @@ -1089,6 +1087,64 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data) return 0; } +static int get_one_entry(struct remote *remote, void *priv) +{ + struct string_list *list = priv; + struct strbuf url_buf = STRBUF_INIT; + const char **url; + int i, url_nr; + + if (remote->url_nr > 0) { + strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]); + string_list_append(list, remote->name)->util = + strbuf_detach(&url_buf, NULL); + } else + string_list_append(list, remote->name)->util = NULL; + if (remote->pushurl_nr) { + url = remote->pushurl; + url_nr = remote->pushurl_nr; + } else { + url = remote->url; + url_nr = remote->url_nr; + } + for (i = 0; i < url_nr; i++) + { + strbuf_addf(&url_buf, "%s (push)", url[i]); + string_list_append(list, remote->name)->util = + strbuf_detach(&url_buf, NULL); + } + + return 0; +} + +static int show_all(void) +{ + struct string_list list = STRING_LIST_INIT_NODUP; + int result; + + list.strdup_strings = 1; + result = for_each_remote(get_one_entry, &list); + + if (!result) { + int i; + + sort_string_list(&list); + for (i = 0; i < list.nr; i++) { + struct string_list_item *item = list.items + i; + if (verbose) + printf("%s\t%s\n", item->string, + item->util ? (const char *)item->util : ""); + else { + if (i && !strcmp((item - 1)->string, item->string)) + continue; + printf("%s\n", item->string); + } + } + } + string_list_clear(&list, 1); + return result; +} + static int show(int argc, const char **argv) { int no_query = 0, result = 0, query_flag = 0; @@ -1251,26 +1307,6 @@ static int set_head(int argc, const char **argv) return result; } -static int prune(int argc, const char **argv) -{ - int dry_run = 0, result = 0; - struct option options[] = { - OPT__DRY_RUN(&dry_run, N_("dry run")), - OPT_END() - }; - - argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, - 0); - - if (argc < 1) - usage_with_options(builtin_remote_prune_usage, options); - - for (; argc; argc--, argv++) - result |= prune_remote(*argv, dry_run); - - return result; -} - static int prune_remote(const char *remote, int dry_run) { int result = 0, i; @@ -1309,6 +1345,26 @@ static int prune_remote(const char *remote, int dry_run) return result; } +static int prune(int argc, const char **argv) +{ + int dry_run = 0, result = 0; + struct option options[] = { + OPT__DRY_RUN(&dry_run, N_("dry run")), + OPT_END() + }; + + argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, + 0); + + if (argc < 1) + usage_with_options(builtin_remote_prune_usage, options); + + for (; argc; argc--, argv++) + result |= prune_remote(*argv, dry_run); + + return result; +} + static int get_remote_default(const char *key, const char *value, void *priv) { if (strcmp(key, "remotes.default") == 0) { @@ -1320,42 +1376,42 @@ static int get_remote_default(const char *key, const char *value, void *priv) static int update(int argc, const char **argv) { - int i, prune = 0; + int i, prune = -1; struct option options[] = { OPT_BOOL('p', "prune", &prune, N_("prune remotes after fetching")), OPT_END() }; - const char **fetch_argv; - int fetch_argc = 0; + struct argv_array fetch_argv = ARGV_ARRAY_INIT; int default_defined = 0; - - fetch_argv = xmalloc(sizeof(char *) * (argc+5)); + int retval; argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage, PARSE_OPT_KEEP_ARGV0); - fetch_argv[fetch_argc++] = "fetch"; + argv_array_push(&fetch_argv, "fetch"); - if (prune) - fetch_argv[fetch_argc++] = "--prune"; + if (prune != -1) + argv_array_push(&fetch_argv, prune ? "--prune" : "--no-prune"); if (verbose) - fetch_argv[fetch_argc++] = "-v"; - fetch_argv[fetch_argc++] = "--multiple"; + argv_array_push(&fetch_argv, "-v"); + argv_array_push(&fetch_argv, "--multiple"); if (argc < 2) - fetch_argv[fetch_argc++] = "default"; + argv_array_push(&fetch_argv, "default"); for (i = 1; i < argc; i++) - fetch_argv[fetch_argc++] = argv[i]; + argv_array_push(&fetch_argv, argv[i]); - if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) { + if (strcmp(fetch_argv.argv[fetch_argv.argc-1], "default") == 0) { git_config(get_remote_default, &default_defined); - if (!default_defined) - fetch_argv[fetch_argc-1] = "--all"; + if (!default_defined) { + argv_array_pop(&fetch_argv); + argv_array_push(&fetch_argv, "--all"); + } } - fetch_argv[fetch_argc] = NULL; - - return run_command_v_opt(fetch_argv, RUN_GIT_CMD); + retval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD); + argv_array_clear(&fetch_argv); + return retval; } static int remove_all_fetch_refspecs(const char *remote, const char *key) @@ -1510,64 +1566,6 @@ static int set_url(int argc, const char **argv) return 0; } -static int get_one_entry(struct remote *remote, void *priv) -{ - struct string_list *list = priv; - struct strbuf url_buf = STRBUF_INIT; - const char **url; - int i, url_nr; - - if (remote->url_nr > 0) { - strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]); - string_list_append(list, remote->name)->util = - strbuf_detach(&url_buf, NULL); - } else - string_list_append(list, remote->name)->util = NULL; - if (remote->pushurl_nr) { - url = remote->pushurl; - url_nr = remote->pushurl_nr; - } else { - url = remote->url; - url_nr = remote->url_nr; - } - for (i = 0; i < url_nr; i++) - { - strbuf_addf(&url_buf, "%s (push)", url[i]); - string_list_append(list, remote->name)->util = - strbuf_detach(&url_buf, NULL); - } - - return 0; -} - -static int show_all(void) -{ - struct string_list list = STRING_LIST_INIT_NODUP; - int result; - - list.strdup_strings = 1; - result = for_each_remote(get_one_entry, &list); - - if (!result) { - int i; - - sort_string_list(&list); - for (i = 0; i < list.nr; i++) { - struct string_list_item *item = list.items + i; - if (verbose) - printf("%s\t%s\n", item->string, - item->util ? (const char *)item->util : ""); - else { - if (i && !strcmp((item - 1)->string, item->string)) - continue; - printf("%s\n", item->string); - } - } - } - string_list_clear(&list, 1); - return result; -} - int cmd_remote(int argc, const char **argv, const char *prefix) { struct option options[] = { diff --git a/builtin/tar-tree.c b/builtin/tar-tree.c deleted file mode 100644 index ba3ffe69a..000000000 --- a/builtin/tar-tree.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2005, 2006 Rene Scharfe - */ -#include "cache.h" -#include "commit.h" -#include "tar.h" -#include "builtin.h" -#include "quote.h" - -static const char tar_tree_usage[] = -"git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n" -"*** Note that this command is now deprecated; use \"git archive\" instead."; - -static const char builtin_get_tar_commit_id_usage[] = -"git get-tar-commit-id < <tarfile>"; - -int cmd_tar_tree(int argc, const char **argv, const char *prefix) -{ - /* - * "git tar-tree" is now a wrapper around "git archive --format=tar" - * - * $0 --remote=<repo> arg... ==> - * git archive --format=tar --remote=<repo> arg... - * $0 tree-ish ==> - * git archive --format=tar tree-ish - * $0 tree-ish basedir ==> - * git archive --format-tar --prefix=basedir tree-ish - */ - const char **nargv = xcalloc(sizeof(*nargv), argc + 3); - struct strbuf sb = STRBUF_INIT; - char *basedir_arg; - int nargc = 0; - - nargv[nargc++] = "archive"; - nargv[nargc++] = "--format=tar"; - - if (2 <= argc && !prefixcmp(argv[1], "--remote=")) { - nargv[nargc++] = argv[1]; - argv++; - argc--; - } - - /* - * Because it's just a compatibility wrapper, tar-tree supports only - * the old behaviour of reading attributes from the work tree. - */ - nargv[nargc++] = "--worktree-attributes"; - - switch (argc) { - default: - usage(tar_tree_usage); - break; - case 3: - /* base-path */ - basedir_arg = xmalloc(strlen(argv[2]) + 11); - sprintf(basedir_arg, "--prefix=%s/", argv[2]); - nargv[nargc++] = basedir_arg; - /* fallthru */ - case 2: - /* tree-ish */ - nargv[nargc++] = argv[1]; - } - nargv[nargc] = NULL; - - fprintf(stderr, - "*** \"git tar-tree\" is now deprecated.\n" - "*** Running \"git archive\" instead.\n***"); - sq_quote_argv(&sb, nargv, 0); - strbuf_addch(&sb, '\n'); - fputs(sb.buf, stderr); - strbuf_release(&sb); - return cmd_archive(nargc, nargv, prefix); -} - -/* ustar header + extended global header content */ -#define RECORDSIZE (512) -#define HEADERSIZE (2 * RECORDSIZE) - -int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) -{ - char buffer[HEADERSIZE]; - struct ustar_header *header = (struct ustar_header *)buffer; - char *content = buffer + RECORDSIZE; - ssize_t n; - - if (argc != 1) - usage(builtin_get_tar_commit_id_usage); - - n = read_in_full(0, buffer, HEADERSIZE); - if (n < HEADERSIZE) - die("git get-tar-commit-id: read error"); - if (header->typeflag[0] != 'g') - return 1; - if (memcmp(content, "52 comment=", 11)) - return 1; - - n = write_in_full(1, content + 11, 41); - if (n < 41) - die_errno("git get-tar-commit-id: write error"); - - return 0; -} diff --git a/command-list.txt b/command-list.txt index 08b04e2c6..cf36c3d71 100644 --- a/command-list.txt +++ b/command-list.txt @@ -64,7 +64,6 @@ git-init mainporcelain common git-instaweb ancillaryinterrogators gitk mainporcelain git-log mainporcelain common -git-lost-found ancillarymanipulators deprecated git-ls-files plumbinginterrogators git-ls-remote plumbinginterrogators git-ls-tree plumbinginterrogators @@ -88,7 +87,6 @@ git-pack-redundant plumbinginterrogators git-pack-refs ancillarymanipulators git-parse-remote synchelpers git-patch-id purehelpers -git-peek-remote purehelpers deprecated git-prune ancillarymanipulators git-prune-packed plumbingmanipulators git-pull mainporcelain common @@ -102,7 +100,6 @@ git-relink ancillarymanipulators git-remote ancillarymanipulators git-repack ancillarymanipulators git-replace ancillarymanipulators -git-repo-config ancillarymanipulators deprecated git-request-pull foreignscminterface git-rerere ancillaryinterrogators git-reset mainporcelain common @@ -127,7 +124,6 @@ git-submodule mainporcelain git-svn foreignscminterface git-symbolic-ref plumbingmanipulators git-tag mainporcelain common -git-tar-tree plumbinginterrogators deprecated git-unpack-file plumbinginterrogators git-unpack-objects plumbingmanipulators git-update-index plumbingmanipulators diff --git a/commit-slab.h b/commit-slab.h index d4c828647..cc114b53b 100644 --- a/commit-slab.h +++ b/commit-slab.h @@ -22,8 +22,17 @@ * * Initializes the indegree slab that associates an array of integers * to each commit. 'stride' specifies how big each array is. The slab - * that id initialied by the variant without "_with_stride" associates + * that is initialized by the variant without "_with_stride" associates * each commit with an array of one integer. + * + * - void clear_indegree(struct indegree *); + * + * Empties the slab. The slab can be reused with the same stride + * without calling init_indegree() again or can be reconfigured to a + * different stride by calling init_indegree_with_stride(). + * + * Call this function before the slab falls out of scope to avoid + * leaking memory. */ /* allocate ~512kB at once, allowing for malloc overhead */ @@ -31,6 +40,8 @@ #define COMMIT_SLAB_SIZE (512*1024-32) #endif +#define MAYBE_UNUSED __attribute__((__unused__)) + #define define_commit_slab(slabname, elemtype) \ \ struct slabname { \ @@ -41,8 +52,8 @@ struct slabname { \ }; \ static int stat_ ##slabname## realloc; \ \ -static void init_ ##slabname## _with_stride(struct slabname *s, \ - unsigned stride) \ +static MAYBE_UNUSED void init_ ##slabname## _with_stride(struct slabname *s, \ + unsigned stride) \ { \ unsigned int elem_size; \ if (!stride) \ @@ -54,12 +65,12 @@ static void init_ ##slabname## _with_stride(struct slabname *s, \ s->slab = NULL; \ } \ \ -static void init_ ##slabname(struct slabname *s) \ +static MAYBE_UNUSED void init_ ##slabname(struct slabname *s) \ { \ init_ ##slabname## _with_stride(s, 1); \ } \ \ -static void clear_ ##slabname(struct slabname *s) \ +static MAYBE_UNUSED void clear_ ##slabname(struct slabname *s) \ { \ int i; \ for (i = 0; i < s->slab_count; i++) \ @@ -69,8 +80,8 @@ static void clear_ ##slabname(struct slabname *s) \ s->slab = NULL; \ } \ \ -static elemtype *slabname## _at(struct slabname *s, \ - const struct commit *c) \ +static MAYBE_UNUSED elemtype *slabname## _at(struct slabname *s, \ + const struct commit *c) \ { \ int nth_slab, nth_slot; \ \ @@ -80,7 +91,7 @@ static elemtype *slabname## _at(struct slabname *s, \ if (s->slab_count <= nth_slab) { \ int i; \ s->slab = xrealloc(s->slab, \ - (nth_slab + 1) * sizeof(s->slab)); \ + (nth_slab + 1) * sizeof(*s->slab)); \ stat_ ##slabname## realloc++; \ for (i = s->slab_count; i <= nth_slab; i++) \ s->slab[i] = NULL; \ @@ -94,4 +105,16 @@ static elemtype *slabname## _at(struct slabname *s, \ \ static int stat_ ##slabname## realloc +/* + * Note that this seemingly redundant second declaration is required + * to allow a terminating semicolon, which makes instantiations look + * like function declarations. I.e., the expansion of + * + * define_commit_slab(indegree, int); + * + * ends in 'static int stat_indegreerealloc;'. This would otherwise + * be a syntax error according (at least) to ISO C. It's hard to + * catch because GCC silently parses it by default. + */ + #endif /* COMMIT_SLAB_H */ diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl index 4374771df..4374771df 100644..100755 --- a/compat/vcbuild/scripts/clink.pl +++ b/compat/vcbuild/scripts/clink.pl diff --git a/compat/vcbuild/scripts/lib.pl b/compat/vcbuild/scripts/lib.pl index d8054e469..d8054e469 100644..100755 --- a/compat/vcbuild/scripts/lib.pl +++ b/compat/vcbuild/scripts/lib.pl @@ -1210,15 +1210,14 @@ int git_config(config_fn_t fn, void *data) * Find all the stuff for git_config_set() below. */ -#define MAX_MATCHES 512 - static struct { int baselen; char *key; int do_not_match; regex_t *value_regex; int multi_replace; - size_t offset[MAX_MATCHES]; + size_t *offset; + unsigned int offset_alloc; enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state; int seen; } store; @@ -1241,11 +1240,11 @@ static int store_aux(const char *key, const char *value, void *cb) if (matches(key, value)) { if (store.seen == 1 && store.multi_replace == 0) { warning("%s has multiple values", key); - } else if (store.seen >= MAX_MATCHES) { - error("too many matches for %s", key); - return 1; } + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); + store.offset[store.seen] = cf->do_ftell(cf); store.seen++; } @@ -1273,11 +1272,15 @@ static int store_aux(const char *key, const char *value, void *cb) * Do not increment matches: this is no match, but we * just made sure we are in the desired section. */ + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); /* fallthru */ case SECTION_END_SEEN: case START: if (matches(key, value)) { + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); store.state = KEY_SEEN; store.seen++; @@ -1285,6 +1288,9 @@ static int store_aux(const char *key, const char *value, void *cb) if (strrchr(key, '.') - key == store.baselen && !strncmp(key, store.key, store.baselen)) { store.state = SECTION_SEEN; + ALLOC_GROW(store.offset, + store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); } } @@ -1583,6 +1589,7 @@ int git_config_set_multivar_in_file(const char *config_filename, } } + ALLOC_GROW(store.offset, 1, store.offset_alloc); store.offset[0] = 0; store.state = START; store.seen = 0; diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index 23da787dc..23da787dc 100644..100755 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl diff --git a/contrib/buildsystems/generate b/contrib/buildsystems/generate index bc10f25ff..bc10f25ff 100644..100755 --- a/contrib/buildsystems/generate +++ b/contrib/buildsystems/generate diff --git a/contrib/buildsystems/parse.pl b/contrib/buildsystems/parse.pl index c9656ece9..c9656ece9 100644..100755 --- a/contrib/buildsystems/parse.pl +++ b/contrib/buildsystems/parse.pl diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index dba3c1570..51c2dd4de 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1,5 +1,3 @@ -#!bash -# # bash/zsh completion support for core Git. # # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org> @@ -673,7 +671,6 @@ __git_list_porcelain_commands () index-pack) : plumbing;; init-db) : deprecated;; local-fetch) : plumbing;; - lost-found) : infrequent;; ls-files) : plumbing;; ls-remote) : plumbing;; ls-tree) : plumbing;; @@ -687,14 +684,12 @@ __git_list_porcelain_commands () pack-refs) : plumbing;; parse-remote) : plumbing;; patch-id) : plumbing;; - peek-remote) : plumbing;; prune) : plumbing;; prune-packed) : plumbing;; quiltimport) : import;; read-tree) : plumbing;; receive-pack) : plumbing;; remote-*) : transport;; - repo-config) : deprecated;; rerere) : plumbing;; rev-list) : plumbing;; rev-parse) : plumbing;; @@ -707,7 +702,6 @@ __git_list_porcelain_commands () ssh-*) : transport;; stripspace) : plumbing;; symbolic-ref) : plumbing;; - tar-tree) : deprecated;; unpack-file) : plumbing;; unpack-objects) : plumbing;; update-index) : plumbing;; diff --git a/contrib/completion/git-completion.tcsh b/contrib/completion/git-completion.tcsh index eaacaf0c3..6104a42a2 100644 --- a/contrib/completion/git-completion.tcsh +++ b/contrib/completion/git-completion.tcsh @@ -1,5 +1,3 @@ -#!tcsh -# # tcsh completion support for core Git. # # Copyright (C) 2012 Marc Khouzam <marc.khouzam@gmail.com> diff --git a/contrib/examples/git-whatchanged.sh b/contrib/examples/git-whatchanged.sh index 1fb9feb34..2edbdc6d9 100755 --- a/contrib/examples/git-whatchanged.sh +++ b/contrib/examples/git-whatchanged.sh @@ -9,12 +9,12 @@ case "$0" in *whatchanged) count= test -z "$diff_tree_flags" && - diff_tree_flags=$(git-repo-config --get whatchanged.difftree) + diff_tree_flags=$(git config --get whatchanged.difftree) diff_tree_default_flags='-c -M --abbrev' ;; *show) count=-n1 test -z "$diff_tree_flags" && - diff_tree_flags=$(git-repo-config --get show.difftree) + diff_tree_flags=$(git config --get show.difftree) diff_tree_default_flags='--cc --always' ;; esac test -z "$diff_tree_flags" && diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 8ee410f84..8747b8433 100755 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -22,7 +22,6 @@ # For example, on debian the hook is stored in # /usr/share/git-core/contrib/hooks/post-receive-email: # -# chmod a+x post-receive-email # cd /path/to/your/repository.git # ln -sf /usr/share/git-core/contrib/hooks/post-receive-email hooks/post-receive # diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery index 1f914c94a..9d0c2d199 100644..100755 --- a/contrib/hooks/pre-auto-gc-battery +++ b/contrib/hooks/pre-auto-gc-battery @@ -13,7 +13,6 @@ # For example, if the hook is stored in # /usr/share/git-core/contrib/hooks/pre-auto-gc-battery: # -# chmod a+x pre-auto-gc-battery # cd /path/to/your/repository.git # ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \ # hooks/pre-auto-gc diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl index 2770a1b1d..2770a1b1d 100644..100755 --- a/contrib/hooks/setgitperms.perl +++ b/contrib/hooks/setgitperms.perl diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index d18b317b2..d18b317b2 100644..100755 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid diff --git a/contrib/p4import/README b/contrib/p4import/README deleted file mode 100644 index b9892b679..000000000 --- a/contrib/p4import/README +++ /dev/null @@ -1 +0,0 @@ -Please see contrib/fast-import/git-p4 for a better Perforce importer. diff --git a/contrib/p4import/git-p4import.py b/contrib/p4import/git-p4import.py deleted file mode 100644 index 593d6a068..000000000 --- a/contrib/p4import/git-p4import.py +++ /dev/null @@ -1,365 +0,0 @@ -#!/usr/bin/env python -# -# This tool is copyright (c) 2006, Sean Estabrooks. -# It is released under the Gnu Public License, version 2. -# -# Import Perforce branches into Git repositories. -# Checking out the files is done by calling the standard p4 -# client which you must have properly configured yourself -# - -import marshal -import os -import sys -import time -import getopt - -if sys.hexversion < 0x02020000: - # The behavior of the marshal module changed significantly in 2.2 - sys.stderr.write("git-p4import.py: requires Python 2.2 or later.\n") - sys.exit(1) - -from signal import signal, \ - SIGPIPE, SIGINT, SIG_DFL, \ - default_int_handler - -signal(SIGPIPE, SIG_DFL) -s = signal(SIGINT, SIG_DFL) -if s != default_int_handler: - signal(SIGINT, s) - -def die(msg, *args): - for a in args: - msg = "%s %s" % (msg, a) - print "git-p4import fatal error:", msg - sys.exit(1) - -def usage(): - print "USAGE: git-p4import [-q|-v] [--authors=<file>] [-t <timezone>] [//p4repo/path <branch>]" - sys.exit(1) - -verbosity = 1 -logfile = "/dev/null" -ignore_warnings = False -stitch = 0 -tagall = True - -def report(level, msg, *args): - global verbosity - global logfile - for a in args: - msg = "%s %s" % (msg, a) - fd = open(logfile, "a") - fd.writelines(msg) - fd.close() - if level <= verbosity: - print msg - -class p4_command: - def __init__(self, _repopath): - try: - global logfile - self.userlist = {} - if _repopath[-1] == '/': - self.repopath = _repopath[:-1] - else: - self.repopath = _repopath - if self.repopath[-4:] != "/...": - self.repopath= "%s/..." % self.repopath - f=os.popen('p4 -V 2>>%s'%logfile, 'rb') - a = f.readlines() - if f.close(): - raise - except: - die("Could not find the \"p4\" command") - - def p4(self, cmd, *args): - global logfile - cmd = "%s %s" % (cmd, ' '.join(args)) - report(2, "P4:", cmd) - f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb') - list = [] - while 1: - try: - list.append(marshal.load(f)) - except EOFError: - break - self.ret = f.close() - return list - - def sync(self, id, force=False, trick=False, test=False): - if force: - ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0] - elif trick: - ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0] - elif test: - ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0] - else: - ret = self.p4("sync %s@%s"%(self.repopath, id))[0] - if ret['code'] == "error": - data = ret['data'].upper() - if data.find('VIEW') > 0: - die("Perforce reports %s is not in client view"% self.repopath) - elif data.find('UP-TO-DATE') < 0: - die("Could not sync files from perforce", self.repopath) - - def changes(self, since=0): - try: - list = [] - for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)): - list.append(rec['change']) - list.reverse() - return list - except: - return [] - - def authors(self, filename): - f=open(filename) - for l in f.readlines(): - self.userlist[l[:l.find('=')].rstrip()] = \ - (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')]) - f.close() - for f,e in self.userlist.items(): - report(2, f, ":", e[0], " <", e[1], ">") - - def _get_user(self, id): - if not self.userlist.has_key(id): - try: - user = self.p4("users", id)[0] - self.userlist[id] = (user['FullName'], user['Email']) - except: - self.userlist[id] = (id, "") - return self.userlist[id] - - def _format_date(self, ticks): - symbol='+' - name = time.tzname[0] - offset = time.timezone - if ticks[8]: - name = time.tzname[1] - offset = time.altzone - if offset < 0: - offset *= -1 - symbol = '-' - localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name) - tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks) - return "%s %s" % (tickso, localo) - - def where(self): - try: - return self.p4("where %s" % self.repopath)[-1]['path'] - except: - return "" - - def describe(self, num): - desc = self.p4("describe -s", num)[0] - self.msg = desc['desc'] - self.author, self.email = self._get_user(desc['user']) - self.date = self._format_date(time.localtime(long(desc['time']))) - return self - -class git_command: - def __init__(self): - try: - self.version = self.git("--version")[0][12:].rstrip() - except: - die("Could not find the \"git\" command") - try: - self.gitdir = self.get_single("rev-parse --git-dir") - report(2, "gdir:", self.gitdir) - except: - die("Not a git repository... did you forget to \"git init\" ?") - try: - self.cdup = self.get_single("rev-parse --show-cdup") - if self.cdup != "": - os.chdir(self.cdup) - self.topdir = os.getcwd() - report(2, "topdir:", self.topdir) - except: - die("Could not find top git directory") - - def git(self, cmd): - global logfile - report(2, "GIT:", cmd) - f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb') - r=f.readlines() - self.ret = f.close() - return r - - def get_single(self, cmd): - return self.git(cmd)[0].rstrip() - - def current_branch(self): - try: - testit = self.git("rev-parse --verify HEAD")[0] - return self.git("symbolic-ref HEAD")[0][11:].rstrip() - except: - return None - - def get_config(self, variable): - try: - return self.git("config --get %s" % variable)[0].rstrip() - except: - return None - - def set_config(self, variable, value): - try: - self.git("config %s %s"%(variable, value) ) - except: - die("Could not set %s to " % variable, value) - - def make_tag(self, name, head): - self.git("tag -f %s %s"%(name,head)) - - def top_change(self, branch): - try: - a=self.get_single("name-rev --tags refs/heads/%s" % branch) - loc = a.find(' tags/') + 6 - if a[loc:loc+3] != "p4/": - raise - return int(a[loc+3:][:-2]) - except: - return 0 - - def update_index(self): - self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin") - - def checkout(self, branch): - self.git("checkout %s" % branch) - - def repoint_head(self, branch): - self.git("symbolic-ref HEAD refs/heads/%s" % branch) - - def remove_files(self): - self.git("ls-files | xargs rm") - - def clean_directories(self): - self.git("clean -d") - - def fresh_branch(self, branch): - report(1, "Creating new branch", branch) - self.git("ls-files | xargs rm") - os.remove(".git/index") - self.repoint_head(branch) - self.git("clean -d") - - def basedir(self): - return self.topdir - - def commit(self, author, email, date, msg, id): - self.update_index() - fd=open(".msg", "w") - fd.writelines(msg) - fd.close() - try: - current = self.get_single("rev-parse --verify HEAD") - head = "-p HEAD" - except: - current = "" - head = "" - tree = self.get_single("write-tree") - for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]: - os.environ['GIT_AUTHOR_%s'%r] = l - os.environ['GIT_COMMITTER_%s'%r] = l - commit = self.get_single("commit-tree %s %s < .msg" % (tree,head)) - os.remove(".msg") - self.make_tag("p4/%s"%id, commit) - self.git("update-ref HEAD %s %s" % (commit, current) ) - -try: - opts, args = getopt.getopt(sys.argv[1:], "qhvt:", - ["authors=","help","stitch=","timezone=","log=","ignore","notags"]) -except getopt.GetoptError: - usage() - -for o, a in opts: - if o == "-q": - verbosity = 0 - if o == "-v": - verbosity += 1 - if o in ("--log"): - logfile = a - if o in ("--notags"): - tagall = False - if o in ("-h", "--help"): - usage() - if o in ("--ignore"): - ignore_warnings = True - -git = git_command() -branch=git.current_branch() - -for o, a in opts: - if o in ("-t", "--timezone"): - git.set_config("perforce.timezone", a) - if o in ("--stitch"): - git.set_config("perforce.%s.path" % branch, a) - stitch = 1 - -if len(args) == 2: - branch = args[1] - git.checkout(branch) - if branch == git.current_branch(): - die("Branch %s already exists!" % branch) - report(1, "Setting perforce to ", args[0]) - git.set_config("perforce.%s.path" % branch, args[0]) -elif len(args) != 0: - die("You must specify the perforce //depot/path and git branch") - -p4path = git.get_config("perforce.%s.path" % branch) -if p4path == None: - die("Do not know Perforce //depot/path for git branch", branch) - -p4 = p4_command(p4path) - -for o, a in opts: - if o in ("-a", "--authors"): - p4.authors(a) - -localdir = git.basedir() -if p4.where()[:len(localdir)] != localdir: - report(1, "**WARNING** Appears p4 client is misconfigured") - report(1, " for sync from %s to %s" % (p4.repopath, localdir)) - if ignore_warnings != True: - die("Reconfigure or use \"--ignore\" on command line") - -if stitch == 0: - top = git.top_change(branch) -else: - top = 0 -changes = p4.changes(top) -count = len(changes) -if count == 0: - report(1, "Already up to date...") - sys.exit(0) - -ptz = git.get_config("perforce.timezone") -if ptz: - report(1, "Setting timezone to", ptz) - os.environ['TZ'] = ptz - time.tzset() - -if stitch == 1: - git.remove_files() - git.clean_directories() - p4.sync(changes[0], force=True) -elif top == 0 and branch != git.current_branch(): - p4.sync(changes[0], test=True) - report(1, "Creating new initial commit"); - git.fresh_branch(branch) - p4.sync(changes[0], force=True) -else: - p4.sync(changes[0], trick=True) - -report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch)) -for id in changes: - report(1, "Importing changeset", id) - change = p4.describe(id) - p4.sync(id) - if tagall : - git.commit(change.author, change.email, change.date, change.msg, id) - else: - git.commit(change.author, change.email, change.date, change.msg, "import") - if stitch == 1: - git.clean_directories() - stitch = 0 diff --git a/contrib/p4import/git-p4import.txt b/contrib/p4import/git-p4import.txt deleted file mode 100644 index 9967587fe..000000000 --- a/contrib/p4import/git-p4import.txt +++ /dev/null @@ -1,167 +0,0 @@ -git-p4import(1) -=============== - -NAME ----- -git-p4import - Import a Perforce repository into git - - -SYNOPSIS --------- -[verse] -`git-p4import` [-q|-v] [--notags] [--authors <file>] [-t <timezone>] - <//p4repo/path> <branch> -`git-p4import` --stitch <//p4repo/path> -`git-p4import` - - -DESCRIPTION ------------ -Import a Perforce repository into an existing git repository. When -a <//p4repo/path> and <branch> are specified a new branch with the -given name will be created and the initial import will begin. - -Once the initial import is complete you can do an incremental import -of new commits from the Perforce repository. You do this by checking -out the appropriate git branch and then running `git-p4import` without -any options. - -The standard p4 client is used to communicate with the Perforce -repository; it must be configured correctly in order for `git-p4import` -to operate (see below). - - -OPTIONS -------- --q:: - Do not display any progress information. - --v:: - Give extra progress information. - -\--authors:: - Specify an authors file containing a mapping of Perforce user - ids to full names and email addresses (see Notes below). - -\--notags:: - Do not create a tag for each imported commit. - -\--stitch:: - Import the contents of the given perforce branch into the - currently checked out git branch. - -\--log:: - Store debugging information in the specified file. - --t:: - Specify that the remote repository is in the specified timezone. - Timezone must be in the format "US/Pacific" or "Europe/London" - etc. You only need to specify this once, it will be saved in - the git config file for the repository. - -<//p4repo/path>:: - The Perforce path that will be imported into the specified branch. - -<branch>:: - The new branch that will be created to hold the Perforce imports. - - -P4 Client ---------- -You must make the `p4` client command available in your $PATH and -configure it to communicate with the target Perforce repository. -Typically this means you must set the "$P4PORT" and "$P4CLIENT" -environment variables. - -You must also configure a `p4` client "view" which maps the Perforce -branch into the top level of your git repository, for example: - ------------- -Client: myhost - -Root: /home/sean/import - -Options: noallwrite clobber nocompress unlocked modtime rmdir - -View: - //public/jam/... //myhost/jam/... ------------- - -With the above `p4` client setup, you could import the "jam" -perforce branch into a branch named "jammy", like so: - ------------- -$ mkdir -p /home/sean/import/jam -$ cd /home/sean/import/jam -$ git init -$ git p4import //public/jam jammy ------------- - - -Multiple Branches ------------------ -Note that by creating multiple "views" you can use `git-p4import` -to import additional branches into the same git repository. -However, the `p4` client has a limitation in that it silently -ignores all but the last "view" that maps into the same local -directory. So the following will *not* work: - ------------- -View: - //public/jam/... //myhost/jam/... - //public/other/... //myhost/jam/... - //public/guest/... //myhost/jam/... ------------- - -If you want more than one Perforce branch to be imported into the -same directory you must employ a workaround. A simple option is -to adjust your `p4` client before each import to only include a -single view. - -Another option is to create multiple symlinks locally which all -point to the same directory in your git repository and then use -one per "view" instead of listing the actual directory. - - -Tags ----- -A git tag of the form p4/xx is created for every change imported from -the Perforce repository where xx is the Perforce changeset number. -Therefore after the import you can use git to access any commit by its -Perforce number, e.g. git show p4/327. - -The tag associated with the HEAD commit is also how `git-p4import` -determines if there are new changes to incrementally import from the -Perforce repository. - -If you import from a repository with many thousands of changes -you will have an equal number of p4/xxxx git tags. Git tags can -be expensive in terms of disk space and repository operations. -If you don't need to perform further incremental imports, you -may delete the tags. - - -Notes ------ -You can interrupt the import (e.g. ctrl-c) at any time and restart it -without worry. - -Author information is automatically determined by querying the -Perforce "users" table using the id associated with each change. -However, if you want to manually supply these mappings you can do -so with the "--authors" option. It accepts a file containing a list -of mappings with each line containing one mapping in the format: - ------------- - perforce_id = Full Name <email@address.com> ------------- - - -Author ------- -Written by Sean Estabrooks <seanlkml@sympatico.ca> - - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/git-gui/Makefile b/git-gui/Makefile index e9c2bc347..cde8b2ea3 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -4,7 +4,7 @@ all:: # # Define NO_MSGFMT if you do not have msgfmt from the GNU gettext # package and want to use our rough pure Tcl po->msg translator. -# TCL_PATH must be vaild for this to work. +# TCL_PATH must be valid for this to work. # GIT-VERSION-FILE: FORCE diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 153f85da0..db46ac50b 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -894,6 +894,7 @@ set default_config(gui.textconv) true set default_config(gui.pruneduringfetch) false set default_config(gui.trustmtime) false set default_config(gui.fastcopyblame) false +set default_config(gui.maxrecentrepo) 10 set default_config(gui.copyblamethreshold) 40 set default_config(gui.blamehistoryctx) 7 set default_config(gui.diffcontext) 5 @@ -912,6 +913,7 @@ set font_descs { {fontdiff font_diff {mc "Diff/Console Font"}} } set default_config(gui.stageuntracked) ask +set default_config(gui.displayuntracked) true ###################################################################### ## @@ -1282,7 +1284,11 @@ apply_config # v1.7.0 introduced --show-toplevel to return the canonical work-tree if {[package vsatisfies $_git_version 1.7.0]} { - set _gitworktree [git rev-parse --show-toplevel] + if { [is_Cygwin] } { + catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]} + } else { + set _gitworktree [git rev-parse --show-toplevel] + } } else { # try to set work tree from environment, core.worktree or use # cdup to obtain a relative path to the top of the worktree. If @@ -1550,18 +1556,23 @@ proc rescan_stage2 {fd after} { set buf_rdf {} set buf_rlo {} - set rescan_active 3 + set rescan_active 2 ui_status [mc "Scanning for modified files ..."] set fd_di [git_read diff-index --cached -z [PARENT]] set fd_df [git_read diff-files -z] - set fd_lo [eval git_read ls-files --others -z $ls_others] fconfigure $fd_di -blocking 0 -translation binary -encoding binary fconfigure $fd_df -blocking 0 -translation binary -encoding binary - fconfigure $fd_lo -blocking 0 -translation binary -encoding binary + fileevent $fd_di readable [list read_diff_index $fd_di $after] fileevent $fd_df readable [list read_diff_files $fd_df $after] - fileevent $fd_lo readable [list read_ls_others $fd_lo $after] + + if {[is_config_true gui.displayuntracked]} { + set fd_lo [eval git_read ls-files --others -z $ls_others] + fconfigure $fd_lo -blocking 0 -translation binary -encoding binary + fileevent $fd_lo readable [list read_ls_others $fd_lo $after] + incr rescan_active + } } proc load_message {file {encoding {}}} { @@ -2654,6 +2665,16 @@ if {![is_bare]} { .mbar.repository add command \ -label [mc "Explore Working Copy"] \ -command {do_explore} +} + +if {[is_Windows]} { + .mbar.repository add command \ + -label [mc "Git Bash"] \ + -command {eval exec [auto_execok start] \ + [list "Git Bash" bash --login -l &]} +} + +if {[is_Windows] || ![is_bare]} { .mbar.repository add separator } @@ -3203,13 +3224,19 @@ unset i # -- Diff and Commit Area # -${NS}::frame .vpane.lower -height 300 -width 400 +${NS}::panedwindow .vpane.lower -orient vertical ${NS}::frame .vpane.lower.commarea -${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1 -pack .vpane.lower.diff -fill both -expand 1 -pack .vpane.lower.commarea -side bottom -fill x +${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1 -height 500 +.vpane.lower add .vpane.lower.diff +.vpane.lower add .vpane.lower.commarea .vpane add .vpane.lower -if {!$use_ttk} {.vpane paneconfigure .vpane.lower -sticky nsew} +if {$use_ttk} { + .vpane.lower pane .vpane.lower.diff -weight 1 + .vpane.lower pane .vpane.lower.commarea -weight 0 +} else { + .vpane.lower paneconfigure .vpane.lower.diff -stretch always + .vpane.lower paneconfigure .vpane.lower.commarea -stretch never +} # -- Commit Area Buttons # diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 324f7744c..b1d15f462 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -5,7 +5,7 @@ class blame { image create photo ::blame::img_back_arrow -data {R0lGODlhGAAYAIUAAPwCBEzKXFTSZIz+nGzmhGzqfGTidIT+nEzGXHTqhGzmfGzifFzadETCVES+VARWDFzWbHzyjAReDGTadFTOZDSyRDyyTCymPARaFGTedFzSbDy2TCyqRCyqPARaDAyCHES6VDy6VCyiPAR6HCSeNByWLARyFARiDARqFGTifARiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAYABgAAAajQIBwSCwaj8ikcsk0BppJwRPqHEypQwHBis0WDAdEFyBIKBaMAKLBdjQeSkFBYTBAIvgEoS6JmhUTEwIUDQ4VFhcMGEhyCgoZExoUaxsWHB0THkgfAXUGAhoBDSAVFR0XBnCbDRmgog0hpSIiDJpJIyEQhBUcJCIlwA22SSYVogknEg8eD82qSigdDSknY0IqJQXPYxIl1dZCGNvWw+Dm510GQQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} -# Persistant data (survives loads) +# Persistent data (survives loads) # field history {}; # viewer history: {commit path} field header ; # array commit,key -> header field diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl index ee58981f5..3c10bc656 100644 --- a/git-gui/lib/choose_repository.tcl +++ b/git-gui/lib/choose_repository.tcl @@ -24,6 +24,10 @@ field sorted_recent ; # recent repositories (sorted) constructor pick {} { global M1T M1B use_ttk NS + if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} { + set maxrecent 10 + } + make_dialog top w wm title $top [mc "Git Gui"] @@ -148,7 +152,7 @@ constructor pick {} { -background [get_bg_color $w_body.recentlabel] \ -wrap none \ -width 50 \ - -height 10 + -height $maxrecent $w_recentlist tag conf link \ -foreground blue \ -underline 1 @@ -264,7 +268,11 @@ proc _append_recentrepos {path} { git config --global --add gui.recentrepo $path load_config 1 - while {[llength $recent] > 10} { + if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} { + set maxrecent 10 + } + + while {[llength $recent] > $maxrecent} { _unset_recentrepo [lindex $recent 0] set recent [lrange $recent 1 end] } diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index 8efbbdde2..74a81a7b4 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -414,7 +414,7 @@ proc revert_helper {txt paths} { # such distinction is needed in some languages. Previously, the # code used "Revert changes in" for both, but that can't work # in languages where 'in' must be combined with word from - # rest of string (in diffrent way for both cases of course). + # rest of string (in different way for both cases of course). # # FIXME: Unfortunately, even that isn't enough in some languages # as they have quite complex plural-form rules. Unfortunately, diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index 0cf1da1d7..23c9ae72a 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl @@ -150,6 +150,7 @@ proc do_options {} { {b gui.matchtrackingbranch {mc "Match Tracking Branches"}} {b gui.textconv {mc "Use Textconv For Diffs and Blames"}} {b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}} + {i-0..100 gui.maxrecentrepo {mc "Maximum Length of Recent Repositories List"}} {i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}} {i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}} {i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}} @@ -159,6 +160,7 @@ proc do_options {} { {c gui.encoding {mc "Default File Contents Encoding"}} {b gui.warndetachedcommit {mc "Warn before committing to a detached head"}} {s gui.stageuntracked {mc "Staging of untracked files"} {list "yes" "no" "ask"}} + {b gui.displayuntracked {mc "Show untracked files"}} } { set type [lindex $option 0] set name [lindex $option 1] diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl index e6120303e..538d61c79 100644 --- a/git-gui/lib/spellcheck.tcl +++ b/git-gui/lib/spellcheck.tcl @@ -14,7 +14,7 @@ field w_menu ; # context menu for the widget field s_menuidx 0 ; # last index of insertion into $w_menu field s_i {} ; # timer registration for _run callbacks -field s_clear 0 ; # did we erase mispelled tags yet? +field s_clear 0 ; # did we erase misspelled tags yet? field s_seen [list] ; # lines last seen from $w_text in _run field s_checked [list] ; # lines already checked field s_pending [list] ; # [$line $data] sent to ispell/aspell @@ -259,7 +259,7 @@ method _run {} { if {$n == $cur_line && ![regexp {^\W$} [$w_text get $cur_pos insert]]} { - # If the current word is mispelled remove the tag + # If the current word is misspelled remove the tag # but force a spellcheck later. # set tags [$w_text tag names $cur_pos] diff --git a/git-gui/macosx/Info.plist b/git-gui/macosx/Info.plist index b3bf15fa1..1ade121c4 100644 --- a/git-gui/macosx/Info.plist +++ b/git-gui/macosx/Info.plist @@ -24,5 +24,7 @@ <string>GITg</string> <key>CFBundleVersion</key> <string>@@GITGUI_VERSION@@</string> + <key>NSHighResolutionCapable</key> + <true/> </dict> </plist> diff --git a/git-lost-found.sh b/git-lost-found.sh deleted file mode 100755 index 0b3e8c7a8..000000000 --- a/git-lost-found.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -USAGE='' -SUBDIRECTORY_OK='Yes' -OPTIONS_SPEC= -. git-sh-setup - -echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2 - -if [ "$#" != "0" ] -then - usage -fi - -laf="$GIT_DIR/lost-found" -rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit - -git fsck --full --no-reflogs | -while read dangling type sha1 -do - case "$dangling" in - dangling) - if git rev-parse -q --verify "$sha1^0" >/dev/null - then - dir="$laf/commit" - git show-branch "$sha1" - else - dir="$laf/other" - fi - echo "$sha1" >"$dir/$sha1" - ;; - esac -done diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index a280f4981..c45a02030 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -1,5 +1,4 @@ -#!/bin/sh -# git-mergetool--lib is a library for common merge tool functions +# git-mergetool--lib is a shell library for common merge tool functions : ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools} @@ -1311,7 +1311,7 @@ class P4Submit(Command, P4UserMap): else: die("unknown modifier %s for %s" % (modifier, path)) - diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) + diffcmd = "git diff-tree -p \"%s\"" % (id) patchcmd = diffcmd + " | git apply " tryPatchCmd = patchcmd + "--check -" applyPatchCmd = patchcmd + "--check --apply -" diff --git a/git-parse-remote.sh b/git-parse-remote.sh index 0e87e0915..55fe8d56c 100644 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -1,4 +1,6 @@ -#!/bin/sh +# This is a shell library to calculate the remote repository and +# upstream branch that should be pulled by "git pull" from the current +# branch. # git-ls-remote could be called from outside a git managed repository; # this would fail in that case and would issue an error message. diff --git a/git-pull.sh b/git-pull.sh index b946fd975..97716b885 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -172,7 +172,7 @@ error_on_no_merge_candidates () { do case "$opt" in -t|--t|--ta|--tag|--tags) - echo "Fetching tags only, you probably meant:" + echo "It doesn't make sense to pull all tags; you probably meant:" echo " git fetch --tags" exit 1 esac diff --git a/git-rebase--am.sh b/git-rebase--am.sh index 34e3102fc..a4f683a5d 100644 --- a/git-rebase--am.sh +++ b/git-rebase--am.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# This shell script fragment is sourced by git-rebase to implement +# its default, fast, patch-based, non-interactive mode. # # Copyright (c) 2010 Junio C Hamano. # diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 3c6bed9a2..43c19e082 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -1,11 +1,8 @@ -#!/bin/sh +# This shell script fragment is sourced by git-rebase to implement +# its interactive mode. "git rebase --interactive" makes it easy +# to fix up commits in the middle of a series and rearrange commits. # # Copyright (c) 2006 Johannes E. Schindelin - -# SHORT DESCRIPTION -# -# This script makes it easy to fix up commits in the middle of a series, -# and rearrange commits. # # The original idea comes from Eric W. Biederman, in # http://article.gmane.org/gmane.comp.version-control.git/22407 diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh index 16d18176e..e7d96de9a 100644 --- a/git-rebase--merge.sh +++ b/git-rebase--merge.sh @@ -1,4 +1,6 @@ -#!/bin/sh +# This shell script fragment is sourced by git-rebase to implement +# its merge-based non-interactive mode that copes well with renamed +# files. # # Copyright (c) 2010 Junio C Hamano. # diff --git a/git-sh-i18n.sh b/git-sh-i18n.sh index 6a27f6813..e6c3116e1 100644 --- a/git-sh-i18n.sh +++ b/git-sh-i18n.sh @@ -1,9 +1,8 @@ -#!/bin/sh +# This shell library is Git's interface to gettext.sh. See po/README +# for usage instructions. # # Copyright (c) 2010 Ævar Arnfjörð Bjarmason # -# This is Git's interface to gettext.sh. See po/README for usage -# instructions. # Export the TEXTDOMAIN* data that we need for Git TEXTDOMAIN=git diff --git a/git-sh-setup.sh b/git-sh-setup.sh index ebfe8f7a4..190a5394b 100644 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -1,9 +1,6 @@ -#!/bin/sh -# -# This is included in commands that either have to be run from the toplevel -# of the repository, or with GIT_DIR environment variable properly. -# If the GIT_DIR does not look like the right correct git-repository, -# it dies. +# This shell scriplet is meant to be included by other shell scripts +# to set up some variables pointing at the normal git directories and +# a few helper shell functions. # Having this variable in your environment would break scripts because # you would cause "cd" to be taken to unexpected places. If you @@ -408,7 +408,6 @@ static void handle_internal_command(int argc, const char **argv) { "pack-redundant", cmd_pack_redundant, RUN_SETUP }, { "pack-refs", cmd_pack_refs, RUN_SETUP }, { "patch-id", cmd_patch_id }, - { "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY }, { "pickaxe", cmd_blame, RUN_SETUP }, { "prune", cmd_prune, RUN_SETUP }, { "prune-packed", cmd_prune_packed, RUN_SETUP }, @@ -421,7 +420,6 @@ static void handle_internal_command(int argc, const char **argv) { "remote-fd", cmd_remote_fd }, { "repack", cmd_repack, RUN_SETUP }, { "replace", cmd_replace, RUN_SETUP }, - { "repo-config", cmd_repo_config, RUN_SETUP_GENTLY }, { "rerere", cmd_rerere, RUN_SETUP }, { "reset", cmd_reset, RUN_SETUP }, { "rev-list", cmd_rev_list, RUN_SETUP }, @@ -438,7 +436,6 @@ static void handle_internal_command(int argc, const char **argv) { "stripspace", cmd_stripspace }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, { "tag", cmd_tag, RUN_SETUP }, - { "tar-tree", cmd_tar_tree }, { "unpack-file", cmd_unpack_file, RUN_SETUP }, { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP }, diff --git a/gitk-git/gitk b/gitk-git/gitk index 5cd00d80f..33c3a6c6b 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -156,10 +156,12 @@ proc unmerged_files {files} { proc parseviewargs {n arglist} { global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env + global vinlinediff global worddiff git_version set vdatemode($n) 0 set vmergeonly($n) 0 + set vinlinediff($n) 0 set glflags {} set diffargs {} set nextisval 0 @@ -227,12 +229,20 @@ proc parseviewargs {n arglist} { "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" - "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" - "--remove-empty" - "--first-parent" - "--cherry-pick" - - "-S*" - "--pickaxe-all" - "--pickaxe-regex" - + "-S*" - "-G*" - "--pickaxe-all" - "--pickaxe-regex" - "--simplify-by-decoration" { # These mean that we get a subset of the commits set filtered 1 lappend glflags $arg } + "-L*" { + # Line-log with 'stuck' argument (unstuck form is + # not supported) + set filtered 1 + set vinlinediff($n) 1 + set allknown 0 + lappend glflags $arg + } "-n" { # This appears to be the only one that has a value as a # separate word following it @@ -1704,8 +1714,17 @@ proc parsecommit {id contents listed} { set comment $newcomment } set hasnote [string first "\nNotes:\n" $contents] + set diff "" + # If there is diff output shown in the git-log stream, split it + # out. But get rid of the empty line that always precedes the + # diff. + set i [string first "\n\ndiff" $comment] + if {$i >= 0} { + set diff [string range $comment $i+1 end] + set comment [string range $comment 0 $i-1] + } set commitinfo($id) [list $headline $auname $audate \ - $comname $comdate $comment $hasnote] + $comname $comdate $comment $hasnote $diff] } proc getcommit {id} { @@ -2385,6 +2404,7 @@ proc makewindow {} { $ctext tag conf found -back $foundbgcolor $ctext tag conf currentsearchhit -back $currentsearchhitbgcolor $ctext tag conf wwrap -wrap word + $ctext tag conf bold -font textfontbold .pwbottom add .bleft if {!$use_ttk} { @@ -6387,6 +6407,25 @@ proc bindline {t id} { $canv bind $t <Button-1> "lineclick %x %y $id 1" } +proc graph_pane_width {} { + global use_ttk + + if {$use_ttk} { + set g [.tf.histframe.pwclist sashpos 0] + } else { + set g [.tf.histframe.pwclist sash coord 0] + } + return [lindex $g 0] +} + +proc totalwidth {l font extra} { + set tot 0 + foreach str $l { + set tot [expr {$tot + [font measure $font $str] + $extra}] + } + return $tot +} + proc drawtags {id x xt y1} { global idtags idheads idotherrefs mainhead global linespc lthickness @@ -6398,9 +6437,27 @@ proc drawtags {id x xt y1} { set marks {} set ntags 0 set nheads 0 + set singletag 0 + set maxtags 3 + set maxtagpct 25 + set maxwidth [expr {[graph_pane_width] * $maxtagpct / 100}] + set delta [expr {int(0.5 * ($linespc - $lthickness))}] + set extra [expr {$delta + $lthickness + $linespc}] + if {[info exists idtags($id)]} { set marks $idtags($id) set ntags [llength $marks] + if {$ntags > $maxtags || + [totalwidth $marks mainfont $extra] > $maxwidth} { + # show just a single "n tags..." tag + set singletag 1 + if {$ntags == 1} { + set marks [list "tag..."] + } else { + set marks [list [format "%d tags..." $ntags]] + } + set ntags 1 + } } if {[info exists idheads($id)]} { set marks [concat $marks $idheads($id)] @@ -6413,7 +6470,6 @@ proc drawtags {id x xt y1} { return $xt } - set delta [expr {int(0.5 * ($linespc - $lthickness))}] set yt [expr {$y1 - 0.5 * $linespc}] set yb [expr {$yt + $linespc - 1}] set xvals {} @@ -6428,7 +6484,7 @@ proc drawtags {id x xt y1} { } lappend xvals $xt lappend wvals $wid - set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}] + set xt [expr {$xt + $wid + $extra}] } set t [$canv create line $x $y1 [lindex $xvals end] $y1 \ -width $lthickness -fill $reflinecolor -tags tag.$id] @@ -6444,7 +6500,12 @@ proc drawtags {id x xt y1} { $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \ -width 1 -outline $tagoutlinecolor -fill $tagbgcolor \ -tags tag.$id] - $canv bind $t <1> [list showtag $tag_quoted 1] + if {$singletag} { + set tagclick [list showtags $id 1] + } else { + set tagclick [list showtag $tag_quoted 1] + } + $canv bind $t <1> $tagclick set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}] } else { # draw a head or other ref @@ -6471,7 +6532,7 @@ proc drawtags {id x xt y1} { set t [$canv create text $xl $y1 -anchor w -text $tag -fill $headfgcolor \ -font $font -tags [list tag.$id text]] if {$ntags >= 0} { - $canv bind $t <1> [list showtag $tag_quoted 1] + $canv bind $t <1> $tagclick } elseif {$nheads >= 0} { $canv bind $t $ctxbut [list headmenu %X %Y $id $tag_quoted] } @@ -7080,6 +7141,7 @@ proc selectline {l isnew {desired_loc {}}} { global cmitmode showneartags allcommits global targetrow targetid lastscrollrows global autoselect autosellen jump_to_here + global vinlinediff catch {unset pending_select} $canv delete hover @@ -7221,6 +7283,8 @@ proc selectline {l isnew {desired_loc {}}} { init_flist [mc "Comments"] if {$cmitmode eq "tree"} { gettree $id + } elseif {$vinlinediff($curview) == 1} { + showinlinediff $id } elseif {[llength $olds] <= 1} { startdiff $id } else { @@ -7557,6 +7621,39 @@ proc startdiff {ids} { } } +proc showinlinediff {ids} { + global commitinfo commitdata ctext + global treediffs + + set info $commitinfo($ids) + set diff [lindex $info 7] + set difflines [split $diff "\n"] + + initblobdiffvars + set treediff {} + + set inhdr 0 + foreach line $difflines { + if {![string compare -length 5 "diff " $line]} { + set inhdr 1 + } elseif {$inhdr && ![string compare -length 4 "+++ " $line]} { + # offset also accounts for the b/ prefix + lappend treediff [string range $line 6 end] + set inhdr 0 + } + } + + set treediffs($ids) $treediff + add_flist $treediff + + $ctext conf -state normal + foreach line $difflines { + parseblobdiffline $ids $line + } + maybe_scroll_ctext 1 + $ctext conf -state disabled +} + # If the filename (name) is under any of the passed filter paths # then return true to include the file in the listing. proc path_filter {filter name} { @@ -7710,15 +7807,25 @@ proc changeworddiff {name ix op} { reselectline } +proc initblobdiffvars {} { + global diffencoding targetline diffnparents + global diffinhdr currdiffsubmod diffseehere + set targetline {} + set diffnparents 0 + set diffinhdr 0 + set diffencoding [get_path_encoding {}] + set currdiffsubmod "" + set diffseehere -1 +} + proc getblobdiffs {ids} { global blobdifffd diffids env - global diffinhdr treediffs + global treediffs global diffcontext global ignorespace global worddiff global limitdiffs vfilelimit curview - global diffencoding targetline diffnparents - global git_version currdiffsubmod + global git_version set textconv {} if {[package vcompare $git_version "1.6.1"] >= 0} { @@ -7742,13 +7849,9 @@ proc getblobdiffs {ids} { error_popup [mc "Error getting diffs: %s" $err] return } - set targetline {} - set diffnparents 0 - set diffinhdr 0 - set diffencoding [get_path_encoding {}] fconfigure $bdf -blocking 0 -encoding binary -eofchar {} set blobdifffd($ids) $bdf - set currdiffsubmod "" + initblobdiffvars filerun $bdf [list getblobdiffline $bdf $diffids] } @@ -7814,13 +7917,17 @@ proc makediffhdr {fname ids} { set diffline 0 } +proc blobdiffmaybeseehere {ateof} { + global diffseehere + if {$diffseehere >= 0} { + mark_ctext_line [lindex [split $diffseehere .] 0] + } + maybe_scroll_ctext ateof +} + proc getblobdiffline {bdf ids} { - global diffids blobdifffd ctext curdiffstart - global diffnexthead diffnextnote difffilestart - global ctext_file_names ctext_file_lines - global diffinhdr treediffs mergemax diffnparents - global diffencoding jump_to_here targetline diffline currdiffsubmod - global worddiff + global diffids blobdifffd + global ctext set nr 0 $ctext conf -state normal @@ -7829,212 +7936,220 @@ proc getblobdiffline {bdf ids} { catch {close $bdf} return 0 } - if {![string compare -length 5 "diff " $line]} { - if {![regexp {^diff (--cc|--git) } $line m type]} { - set line [encoding convertfrom $line] - $ctext insert end "$line\n" hunksep - continue + parseblobdiffline $ids $line + } + $ctext conf -state disabled + blobdiffmaybeseehere [eof $bdf] + if {[eof $bdf]} { + catch {close $bdf} + return 0 + } + return [expr {$nr >= 1000? 2: 1}] +} + +proc parseblobdiffline {ids line} { + global ctext curdiffstart + global diffnexthead diffnextnote difffilestart + global ctext_file_names ctext_file_lines + global diffinhdr treediffs mergemax diffnparents + global diffencoding jump_to_here targetline diffline currdiffsubmod + global worddiff diffseehere + + if {![string compare -length 5 "diff " $line]} { + if {![regexp {^diff (--cc|--git) } $line m type]} { + set line [encoding convertfrom $line] + $ctext insert end "$line\n" hunksep + continue + } + # start of a new file + set diffinhdr 1 + $ctext insert end "\n" + set curdiffstart [$ctext index "end - 1c"] + lappend ctext_file_names "" + lappend ctext_file_lines [lindex [split $curdiffstart "."] 0] + $ctext insert end "\n" filesep + + if {$type eq "--cc"} { + # start of a new file in a merge diff + set fname [string range $line 10 end] + if {[lsearch -exact $treediffs($ids) $fname] < 0} { + lappend treediffs($ids) $fname + add_flist [list $fname] } - # start of a new file - set diffinhdr 1 - $ctext insert end "\n" - set curdiffstart [$ctext index "end - 1c"] - lappend ctext_file_names "" - lappend ctext_file_lines [lindex [split $curdiffstart "."] 0] - $ctext insert end "\n" filesep - - if {$type eq "--cc"} { - # start of a new file in a merge diff - set fname [string range $line 10 end] - if {[lsearch -exact $treediffs($ids) $fname] < 0} { - lappend treediffs($ids) $fname - add_flist [list $fname] - } + } else { + set line [string range $line 11 end] + # If the name hasn't changed the length will be odd, + # the middle char will be a space, and the two bits either + # side will be a/name and b/name, or "a/name" and "b/name". + # If the name has changed we'll get "rename from" and + # "rename to" or "copy from" and "copy to" lines following + # this, and we'll use them to get the filenames. + # This complexity is necessary because spaces in the + # filename(s) don't get escaped. + set l [string length $line] + set i [expr {$l / 2}] + if {!(($l & 1) && [string index $line $i] eq " " && + [string range $line 2 [expr {$i - 1}]] eq \ + [string range $line [expr {$i + 3}] end])} { + return + } + # unescape if quoted and chop off the a/ from the front + if {[string index $line 0] eq "\""} { + set fname [string range [lindex $line 0] 2 end] } else { - set line [string range $line 11 end] - # If the name hasn't changed the length will be odd, - # the middle char will be a space, and the two bits either - # side will be a/name and b/name, or "a/name" and "b/name". - # If the name has changed we'll get "rename from" and - # "rename to" or "copy from" and "copy to" lines following - # this, and we'll use them to get the filenames. - # This complexity is necessary because spaces in the - # filename(s) don't get escaped. - set l [string length $line] - set i [expr {$l / 2}] - if {!(($l & 1) && [string index $line $i] eq " " && - [string range $line 2 [expr {$i - 1}]] eq \ - [string range $line [expr {$i + 3}] end])} { - continue - } - # unescape if quoted and chop off the a/ from the front - if {[string index $line 0] eq "\""} { - set fname [string range [lindex $line 0] 2 end] - } else { - set fname [string range $line 2 [expr {$i - 1}]] - } + set fname [string range $line 2 [expr {$i - 1}]] } - makediffhdr $fname $ids + } + makediffhdr $fname $ids + + } elseif {![string compare -length 16 "* Unmerged path " $line]} { + set fname [encoding convertfrom [string range $line 16 end]] + $ctext insert end "\n" + set curdiffstart [$ctext index "end - 1c"] + lappend ctext_file_names $fname + lappend ctext_file_lines [lindex [split $curdiffstart "."] 0] + $ctext insert end "$line\n" filesep + set i [lsearch -exact $treediffs($ids) $fname] + if {$i >= 0} { + setinlist difffilestart $i $curdiffstart + } - } elseif {![string compare -length 16 "* Unmerged path " $line]} { - set fname [encoding convertfrom [string range $line 16 end]] - $ctext insert end "\n" - set curdiffstart [$ctext index "end - 1c"] - lappend ctext_file_names $fname - lappend ctext_file_lines [lindex [split $curdiffstart "."] 0] + } elseif {![string compare -length 2 "@@" $line]} { + regexp {^@@+} $line ats + set line [encoding convertfrom $diffencoding $line] + $ctext insert end "$line\n" hunksep + if {[regexp { \+(\d+),\d+ @@} $line m nl]} { + set diffline $nl + } + set diffnparents [expr {[string length $ats] - 1}] + set diffinhdr 0 + + } elseif {![string compare -length 10 "Submodule " $line]} { + # start of a new submodule + if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} { + set fname [string range $line 10 [expr [lindex $nameend 0] - 2]] + } else { + set fname [string range $line 10 [expr [string first "contains " $line] - 2]] + } + if {$currdiffsubmod != $fname} { + $ctext insert end "\n"; # Add newline after commit message + } + set curdiffstart [$ctext index "end - 1c"] + lappend ctext_file_names "" + if {$currdiffsubmod != $fname} { + lappend ctext_file_lines $fname + makediffhdr $fname $ids + set currdiffsubmod $fname + $ctext insert end "\n$line\n" filesep + } else { $ctext insert end "$line\n" filesep + } + } elseif {![string compare -length 3 " >" $line]} { + set $currdiffsubmod "" + set line [encoding convertfrom $diffencoding $line] + $ctext insert end "$line\n" dresult + } elseif {![string compare -length 3 " <" $line]} { + set $currdiffsubmod "" + set line [encoding convertfrom $diffencoding $line] + $ctext insert end "$line\n" d0 + } elseif {$diffinhdr} { + if {![string compare -length 12 "rename from " $line]} { + set fname [string range $line [expr 6 + [string first " from " $line] ] end] + if {[string index $fname 0] eq "\""} { + set fname [lindex $fname 0] + } + set fname [encoding convertfrom $fname] set i [lsearch -exact $treediffs($ids) $fname] if {$i >= 0} { setinlist difffilestart $i $curdiffstart } - - } elseif {![string compare -length 2 "@@" $line]} { - regexp {^@@+} $line ats - set line [encoding convertfrom $diffencoding $line] - $ctext insert end "$line\n" hunksep - if {[regexp { \+(\d+),\d+ @@} $line m nl]} { - set diffline $nl + } elseif {![string compare -length 10 $line "rename to "] || + ![string compare -length 8 $line "copy to "]} { + set fname [string range $line [expr 4 + [string first " to " $line] ] end] + if {[string index $fname 0] eq "\""} { + set fname [lindex $fname 0] } - set diffnparents [expr {[string length $ats] - 1}] + makediffhdr $fname $ids + } elseif {[string compare -length 3 $line "---"] == 0} { + # do nothing + return + } elseif {[string compare -length 3 $line "+++"] == 0} { set diffinhdr 0 + return + } + $ctext insert end "$line\n" filesep - } elseif {![string compare -length 10 "Submodule " $line]} { - # start of a new submodule - if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} { - set fname [string range $line 10 [expr [lindex $nameend 0] - 2]] - } else { - set fname [string range $line 10 [expr [string first "contains " $line] - 2]] - } - if {$currdiffsubmod != $fname} { - $ctext insert end "\n"; # Add newline after commit message - } - set curdiffstart [$ctext index "end - 1c"] - lappend ctext_file_names "" - if {$currdiffsubmod != $fname} { - lappend ctext_file_lines $fname - makediffhdr $fname $ids - set currdiffsubmod $fname - $ctext insert end "\n$line\n" filesep - } else { - $ctext insert end "$line\n" filesep - } - } elseif {![string compare -length 3 " >" $line]} { - set $currdiffsubmod "" - set line [encoding convertfrom $diffencoding $line] - $ctext insert end "$line\n" dresult - } elseif {![string compare -length 3 " <" $line]} { - set $currdiffsubmod "" - set line [encoding convertfrom $diffencoding $line] - $ctext insert end "$line\n" d0 - } elseif {$diffinhdr} { - if {![string compare -length 12 "rename from " $line]} { - set fname [string range $line [expr 6 + [string first " from " $line] ] end] - if {[string index $fname 0] eq "\""} { - set fname [lindex $fname 0] - } - set fname [encoding convertfrom $fname] - set i [lsearch -exact $treediffs($ids) $fname] - if {$i >= 0} { - setinlist difffilestart $i $curdiffstart + } else { + set line [string map {\x1A ^Z} \ + [encoding convertfrom $diffencoding $line]] + # parse the prefix - one ' ', '-' or '+' for each parent + set prefix [string range $line 0 [expr {$diffnparents - 1}]] + set tag [expr {$diffnparents > 1? "m": "d"}] + set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}] + set words_pre_markup "" + set words_post_markup "" + if {[string trim $prefix " -+"] eq {}} { + # prefix only has " ", "-" and "+" in it: normal diff line + set num [string first "-" $prefix] + if {$dowords} { + set line [string range $line 1 end] + } + if {$num >= 0} { + # removed line, first parent with line is $num + if {$num >= $mergemax} { + set num "max" } - } elseif {![string compare -length 10 $line "rename to "] || - ![string compare -length 8 $line "copy to "]} { - set fname [string range $line [expr 4 + [string first " to " $line] ] end] - if {[string index $fname 0] eq "\""} { - set fname [lindex $fname 0] + if {$dowords && $worddiff eq [mc "Markup words"]} { + $ctext insert end "\[-$line-\]" $tag$num + } else { + $ctext insert end "$line" $tag$num } - makediffhdr $fname $ids - } elseif {[string compare -length 3 $line "---"] == 0} { - # do nothing - continue - } elseif {[string compare -length 3 $line "+++"] == 0} { - set diffinhdr 0 - continue - } - $ctext insert end "$line\n" filesep - - } else { - set line [string map {\x1A ^Z} \ - [encoding convertfrom $diffencoding $line]] - # parse the prefix - one ' ', '-' or '+' for each parent - set prefix [string range $line 0 [expr {$diffnparents - 1}]] - set tag [expr {$diffnparents > 1? "m": "d"}] - set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}] - set words_pre_markup "" - set words_post_markup "" - if {[string trim $prefix " -+"] eq {}} { - # prefix only has " ", "-" and "+" in it: normal diff line - set num [string first "-" $prefix] - if {$dowords} { - set line [string range $line 1 end] + if {!$dowords} { + $ctext insert end "\n" $tag$num } - if {$num >= 0} { - # removed line, first parent with line is $num - if {$num >= $mergemax} { - set num "max" - } - if {$dowords && $worddiff eq [mc "Markup words"]} { - $ctext insert end "\[-$line-\]" $tag$num - } else { - $ctext insert end "$line" $tag$num - } - if {!$dowords} { - $ctext insert end "\n" $tag$num - } - } else { - set tags {} - if {[string first "+" $prefix] >= 0} { - # added line - lappend tags ${tag}result - if {$diffnparents > 1} { - set num [string first " " $prefix] - if {$num >= 0} { - if {$num >= $mergemax} { - set num "max" - } - lappend tags m$num + } else { + set tags {} + if {[string first "+" $prefix] >= 0} { + # added line + lappend tags ${tag}result + if {$diffnparents > 1} { + set num [string first " " $prefix] + if {$num >= 0} { + if {$num >= $mergemax} { + set num "max" } + lappend tags m$num } - set words_pre_markup "{+" - set words_post_markup "+}" } - if {$targetline ne {}} { - if {$diffline == $targetline} { - set seehere [$ctext index "end - 1 chars"] - set targetline {} - } else { - incr diffline - } - } - if {$dowords && $worddiff eq [mc "Markup words"]} { - $ctext insert end "$words_pre_markup$line$words_post_markup" $tags + set words_pre_markup "{+" + set words_post_markup "+}" + } + if {$targetline ne {}} { + if {$diffline == $targetline} { + set diffseehere [$ctext index "end - 1 chars"] + set targetline {} } else { - $ctext insert end "$line" $tags - } - if {!$dowords} { - $ctext insert end "\n" $tags + incr diffline } } - } elseif {$dowords && $prefix eq "~"} { - $ctext insert end "\n" {} - } else { - # "\ No newline at end of file", - # or something else we don't recognize - $ctext insert end "$line\n" hunksep + if {$dowords && $worddiff eq [mc "Markup words"]} { + $ctext insert end "$words_pre_markup$line$words_post_markup" $tags + } else { + $ctext insert end "$line" $tags + } + if {!$dowords} { + $ctext insert end "\n" $tags + } } + } elseif {$dowords && $prefix eq "~"} { + $ctext insert end "\n" {} + } else { + # "\ No newline at end of file", + # or something else we don't recognize + $ctext insert end "$line\n" hunksep } } - if {[info exists seehere]} { - mark_ctext_line [lindex [split $seehere .] 0] - } - maybe_scroll_ctext [eof $bdf] - $ctext conf -state disabled - if {[eof $bdf]} { - catch {close $bdf} - return 0 - } - return [expr {$nr >= 1000? 2: 1}] } proc changediffdisp {} { @@ -10878,6 +10993,23 @@ proc listrefs {id} { return [list $x $y $z] } +proc add_tag_ctext {tag} { + global ctext cached_tagcontent tagids + + if {![info exists cached_tagcontent($tag)]} { + catch { + set cached_tagcontent($tag) [exec git cat-file -p $tag] + } + } + $ctext insert end "[mc "Tag"]: $tag\n" bold + if {[info exists cached_tagcontent($tag)]} { + set text $cached_tagcontent($tag) + } else { + set text "[mc "Id"]: $tagids($tag)" + } + appendwithlinks $text {} +} + proc showtag {tag isnew} { global ctext cached_tagcontent tagids linknum tagobjid @@ -10888,17 +11020,28 @@ proc showtag {tag isnew} { clear_ctext settabs 0 set linknum 0 - if {![info exists cached_tagcontent($tag)]} { - catch { - set cached_tagcontent($tag) [exec git cat-file -p $tag] - } + add_tag_ctext $tag + maybe_scroll_ctext 1 + $ctext conf -state disabled + init_flist {} +} + +proc showtags {id isnew} { + global idtags ctext linknum + + if {$isnew} { + addtohistory [list showtags $id 0] savectextpos } - if {[info exists cached_tagcontent($tag)]} { - set text $cached_tagcontent($tag) - } else { - set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)" + $ctext conf -state normal + clear_ctext + settabs 0 + set linknum 0 + set sep {} + foreach tag $idtags($id) { + $ctext insert end $sep + add_tag_ctext $tag + set sep "\n\n" } - appendwithlinks $text {} maybe_scroll_ctext 1 $ctext conf -state disabled init_flist {} @@ -745,35 +745,66 @@ int for_each_remote(each_remote_fn fn, void *priv) return result; } -void ref_remove_duplicates(struct ref *ref_map) +static void handle_duplicate(struct ref *ref1, struct ref *ref2) +{ + if (strcmp(ref1->name, ref2->name)) { + if (ref1->fetch_head_status != FETCH_HEAD_IGNORE && + ref2->fetch_head_status != FETCH_HEAD_IGNORE) { + die(_("Cannot fetch both %s and %s to %s"), + ref1->name, ref2->name, ref2->peer_ref->name); + } else if (ref1->fetch_head_status != FETCH_HEAD_IGNORE && + ref2->fetch_head_status == FETCH_HEAD_IGNORE) { + warning(_("%s usually tracks %s, not %s"), + ref2->peer_ref->name, ref2->name, ref1->name); + } else if (ref1->fetch_head_status == FETCH_HEAD_IGNORE && + ref2->fetch_head_status == FETCH_HEAD_IGNORE) { + die(_("%s tracks both %s and %s"), + ref2->peer_ref->name, ref1->name, ref2->name); + } else { + /* + * This last possibility doesn't occur because + * FETCH_HEAD_IGNORE entries always appear at + * the end of the list. + */ + die(_("Internal error")); + } + } + free(ref2->peer_ref); + free(ref2); +} + +struct ref *ref_remove_duplicates(struct ref *ref_map) { struct string_list refs = STRING_LIST_INIT_NODUP; - struct string_list_item *item = NULL; - struct ref *prev = NULL, *next = NULL; - for (; ref_map; prev = ref_map, ref_map = next) { - next = ref_map->next; - if (!ref_map->peer_ref) - continue; + struct ref *retval = NULL; + struct ref **p = &retval; - item = string_list_lookup(&refs, ref_map->peer_ref->name); - if (item) { - if (strcmp(((struct ref *)item->util)->name, - ref_map->name)) - die("%s tracks both %s and %s", - ref_map->peer_ref->name, - ((struct ref *)item->util)->name, - ref_map->name); - prev->next = ref_map->next; - free(ref_map->peer_ref); - free(ref_map); - ref_map = prev; /* skip this; we freed it */ - continue; - } + while (ref_map) { + struct ref *ref = ref_map; + + ref_map = ref_map->next; + ref->next = NULL; - item = string_list_insert(&refs, ref_map->peer_ref->name); - item->util = ref_map; + if (!ref->peer_ref) { + *p = ref; + p = &ref->next; + } else { + struct string_list_item *item = + string_list_insert(&refs, ref->peer_ref->name); + + if (item->util) { + /* Entry already existed */ + handle_duplicate((struct ref *)item->util, ref); + } else { + *p = ref; + p = &ref->next; + item->util = ref; + } + } } + string_list_clear(&refs, 0); + return retval; } int remote_has_url(struct remote *remote, const char *url) @@ -825,6 +856,8 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q { int i; int find_src = !query->src; + const char *needle = find_src ? query->dst : query->src; + char **result = find_src ? &query->src : &query->dst; if (find_src && !query->dst) return error("query_refspecs: need either src or dst"); @@ -833,8 +866,6 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q struct refspec *refspec = &refs[i]; const char *key = find_src ? refspec->dst : refspec->src; const char *value = find_src ? refspec->src : refspec->dst; - const char *needle = find_src ? query->dst : query->src; - char **result = find_src ? &query->src : &query->dst; if (!refspec->dst) continue; @@ -1553,6 +1584,13 @@ static int ignore_symref_update(const char *refname) return (flag & REF_ISSYMREF); } +/* + * Create and return a list of (struct ref) consisting of copies of + * each remote_ref that matches refspec. refspec must be a pattern. + * Fill in the copies' peer_ref to describe the local tracking refs to + * which they map. Omit any references that would map to an existing + * local symbolic ref. + */ static struct ref *get_expanded_map(const struct ref *remote_refs, const struct refspec *refspec) { @@ -1560,9 +1598,9 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, struct ref *ret = NULL; struct ref **tail = &ret; - char *expn_name; - for (ref = remote_refs; ref; ref = ref->next) { + char *expn_name = NULL; + if (strchr(ref->name, '^')) continue; /* a dereference item */ if (match_name_with_pattern(refspec->src, ref->name, @@ -1571,12 +1609,12 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, struct ref *cpy = copy_ref(ref); cpy->peer_ref = alloc_ref(expn_name); - free(expn_name); if (refspec->force) cpy->peer_ref->force = 1; *tail = cpy; tail = &cpy->next; } + free(expn_name); } return ret; @@ -149,9 +149,13 @@ int resolve_remote_symref(struct ref *ref, struct ref *list); int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1); /* - * Removes and frees any duplicate refs in the map. + * Remove and free all but the first of any entries in the input list + * that map the same remote reference to the same local reference. If + * there are two entries that map different remote references to the + * same local reference, emit an error message and die. Return a + * pointer to the head of the resulting list. */ -void ref_remove_duplicates(struct ref *ref_map); +struct ref *ref_remove_duplicates(struct ref *ref_map); int valid_fetch_refspec(const char *refspec); struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec); diff --git a/send-pack.c b/send-pack.c index fab62e3da..9ee8cf50a 100644 --- a/send-pack.c +++ b/send-pack.c @@ -206,6 +206,8 @@ int send_pack(struct send_pack_args *args, quiet_supported = 1; if (server_supports("agent")) agent_supported = 1; + if (server_supports("no-thin")) + args->use_thin_pack = 0; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" diff --git a/t/Git-SVN/00compile.t b/t/Git-SVN/00compile.t index c92fee453..c92fee453 100644..100755 --- a/t/Git-SVN/00compile.t +++ b/t/Git-SVN/00compile.t diff --git a/t/Git-SVN/Utils/add_path_to_url.t b/t/Git-SVN/Utils/add_path_to_url.t index bfbd87845..bfbd87845 100644..100755 --- a/t/Git-SVN/Utils/add_path_to_url.t +++ b/t/Git-SVN/Utils/add_path_to_url.t diff --git a/t/Git-SVN/Utils/can_compress.t b/t/Git-SVN/Utils/can_compress.t index d7b49b8d5..d7b49b8d5 100644..100755 --- a/t/Git-SVN/Utils/can_compress.t +++ b/t/Git-SVN/Utils/can_compress.t diff --git a/t/Git-SVN/Utils/canonicalize_url.t b/t/Git-SVN/Utils/canonicalize_url.t index 05795ab63..05795ab63 100644..100755 --- a/t/Git-SVN/Utils/canonicalize_url.t +++ b/t/Git-SVN/Utils/canonicalize_url.t diff --git a/t/Git-SVN/Utils/collapse_dotdot.t b/t/Git-SVN/Utils/collapse_dotdot.t index 1da1cce15..1da1cce15 100644..100755 --- a/t/Git-SVN/Utils/collapse_dotdot.t +++ b/t/Git-SVN/Utils/collapse_dotdot.t diff --git a/t/Git-SVN/Utils/fatal.t b/t/Git-SVN/Utils/fatal.t index 49e143829..49e143829 100644..100755 --- a/t/Git-SVN/Utils/fatal.t +++ b/t/Git-SVN/Utils/fatal.t diff --git a/t/Git-SVN/Utils/join_paths.t b/t/Git-SVN/Utils/join_paths.t index d4488e716..d4488e716 100644..100755 --- a/t/Git-SVN/Utils/join_paths.t +++ b/t/Git-SVN/Utils/join_paths.t diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh index 8cf909a6c..d5dab5a94 100644 --- a/t/gitweb-lib.sh +++ b/t/gitweb-lib.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# Initialization and helpers for Gitweb tests, which source this +# shell library instead of test-lib.sh. # # Copyright (c) 2007 Jakub Narebski # diff --git a/t/lib-bash.sh b/t/lib-bash.sh index 11397f747..2be955faf 100644 --- a/t/lib-bash.sh +++ b/t/lib-bash.sh @@ -1,7 +1,6 @@ -#!/bin/sh -# -# Ensures that tests are run under Bash; primarily intended for running tests -# of the completion script. +# Shell library sourced instead of ./test-lib.sh by tests that need +# to run under Bash; primarily intended for tests of the completion +# script. if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then # we are in full-on bash mode diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh index 44263ade2..507671891 100644 --- a/t/lib-cvs.sh +++ b/t/lib-cvs.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Shell library sourced instead of ./test-lib.sh by cvsimport tests. . ./test-lib.sh diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh index 75ffd9174..8b4dbf22d 100644 --- a/t/lib-diff-alternative.sh +++ b/t/lib-diff-alternative.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# Helpers shared by the test scripts for diff algorithms (patience, +# histogram, etc). test_diff_frobnitz() { cat >file1 <<\EOF diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh index ae8883a07..eec757f10 100644 --- a/t/lib-gettext.sh +++ b/t/lib-gettext.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# Initialization and Icelandic locale for basic git i18n tests, +# which source this scriptlet instead of ./test-lib.sh. # # Copyright (c) 2010 Ævar Arnfjörð Bjarmason # diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh index 87f0ad8f4..394b06b32 100644 --- a/t/lib-git-daemon.sh +++ b/t/lib-git-daemon.sh @@ -1,4 +1,20 @@ -#!/bin/sh +# Shell library to run git-daemon in tests. Ends the test early if +# GIT_TEST_GIT_DAEMON is not set. +# +# Usage: +# +# . ./test-lib.sh +# . "$TEST_DIRECTORY"/lib-git-daemon.sh +# start_git_daemon +# +# test_expect_success '...' ' +# ... +# ' +# +# test_expect_success ... +# +# stop_git_daemon +# test_done if test -z "$GIT_TEST_GIT_DAEMON" then diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index ad8f1ef71..c4707843c 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -1,4 +1,31 @@ -#!/bin/sh +# Shell library to run an HTTP server for use in tests. +# Ends the test early if httpd tests should not be run, +# for example because the user has not enabled them. +# +# Usage: +# +# . ./test-lib.sh +# . "$TEST_DIRECTORY"/lib-httpd.sh +# start_httpd +# +# test_expect_success '...' ' +# ... +# ' +# +# test_expect_success ... +# +# stop_httpd +# test_done +# +# Can be configured using the following variables. +# +# GIT_TEST_HTTPD enable HTTPD tests +# LIB_HTTPD_PATH web server path +# LIB_HTTPD_MODULE_PATH web server modules path +# LIB_HTTPD_PORT listening port +# LIB_HTTPD_DAV enable DAV +# LIB_HTTPD_SVN enable SVN +# LIB_HTTPD_SSL enable SSL # # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at> # diff --git a/t/lib-pack.sh b/t/lib-pack.sh index b96e1254d..750984657 100644 --- a/t/lib-pack.sh +++ b/t/lib-pack.sh @@ -1,5 +1,3 @@ -#!/bin/sh -# # Support routines for hand-crafting weird or malicious packs. # # You can make a complete pack like: diff --git a/t/lib-pager.sh b/t/lib-pager.sh index ba03eab14..3aa7a3ffd 100644 --- a/t/lib-pager.sh +++ b/t/lib-pager.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Helpers for tests of git's choice of pager. test_expect_success 'determine default pager' ' test_might_fail git config --unset core.pager && diff --git a/t/lib-prereq-FILEMODE.sh b/t/lib-prereq-FILEMODE.sh deleted file mode 100644 index bce5a4c8b..000000000 --- a/t/lib-prereq-FILEMODE.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2010 Ævar Arnfjörð Bjarmason -# - -if test "$(git config --bool core.filemode)" = false -then - say 'filemode disabled on the filesystem' -else - test_set_prereq FILEMODE -fi diff --git a/t/lib-read-tree.sh b/t/lib-read-tree.sh index ef079afc4..b95f48560 100644 --- a/t/lib-read-tree.sh +++ b/t/lib-read-tree.sh @@ -1,5 +1,3 @@ -#!/bin/sh -# # Helper functions to check if read-tree would succeed/fail as expected with # and without the dry-run option. They also test that the dry-run does not # write the index and that together with -u it doesn't touch the work tree. diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh index 8ff87fb3f..6bd252212 100644 --- a/t/lib-rebase.sh +++ b/t/lib-rebase.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Helper functions used by interactive rebase tests. # After setting the fake editor with this function, you can # diff --git a/t/lib-terminal.sh b/t/lib-terminal.sh index 737df289a..9a2dca506 100644 --- a/t/lib-terminal.sh +++ b/t/lib-terminal.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Helpers for terminal output tests. test_expect_success PERL 'set up terminal for tests' ' # Reading from the pty master seems to get stuck _sometimes_ diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index f4eecaa17..a8c957429 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -1,4 +1,6 @@ -#!/bin/sh +# Performance testing framework. Each perf script starts much like +# a normal test script, except it sources this library instead of +# test-lib.sh. See t/perf/README for documentation. # # Copyright (c) 2011 Thomas Rast # diff --git a/t/t0202/test.pl b/t/t0202/test.pl index 2c10cb469..2c10cb469 100644..100755 --- a/t/t0202/test.pl +++ b/t/t0202/test.pl diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh index 46103a159..3a2c81968 100755 --- a/t/t1303-wacky-config.sh +++ b/t/t1303-wacky-config.sh @@ -3,17 +3,28 @@ test_description='Test wacky input to git config' . ./test-lib.sh +# Leaving off the newline is intentional! setup() { (printf "[section]\n" && printf " key = foo") >.git/config } +# 'check section.key value' verifies that the entry for section.key is +# 'value' check() { echo "$2" >expected git config --get "$1" >actual 2>&1 test_cmp actual expected } +# 'check section.key regex value' verifies that the entry for +# section.key *that matches 'regex'* is 'value' +check_regex() { + echo "$3" >expected + git config --get "$1" "$2" >actual 2>&1 + test_cmp actual expected +} + test_expect_success 'modify same key' ' setup && git config section.key bar && @@ -47,4 +58,57 @@ test_expect_success 'do not crash on special long config line' ' check section.key "$LONG_VALUE" ' +setup_many() { + setup && + # This time we want the newline so that we can tack on more + # entries. + echo >>.git/config && + # Semi-efficient way of concatenating 5^5 = 3125 lines. Note + # that because 'setup' already put one line, this means 3126 + # entries for section.key in the config file. + cat >5to1 <<-\EOF && + key = foo + key = foo + key = foo + key = foo + key = foo + EOF + cat 5to1 5to1 5to1 5to1 5to1 >5to2 && # 25 + cat 5to2 5to2 5to2 5to2 5to2 >5to3 && # 125 + cat 5to3 5to3 5to3 5to3 5to3 >5to4 && # 635 + cat 5to4 5to4 5to4 5to4 5to4 >>.git/config # 3125 +} + +test_expect_success 'get many entries' ' + setup_many && + git config --get-all section.key >actual && + test_line_count = 3126 actual +' + +test_expect_success 'get many entries by regex' ' + setup_many && + git config --get-regexp "sec.*ke." >actual && + test_line_count = 3126 actual +' + +test_expect_success 'add and replace one of many entries' ' + setup_many && + git config --add section.key bar && + check_regex section.key "b.*r" bar && + git config section.key beer "b.*r" && + check_regex section.key "b.*r" beer +' + +test_expect_success 'replace many entries' ' + setup_many && + git config --replace-all section.key bar && + check section.key bar +' + +test_expect_success 'unset many entries' ' + setup_many && + git config --unset-all section.key && + test_must_fail git config section.key +' + test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 9dc91d09d..24ddd8a70 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -2,7 +2,6 @@ test_description='add -i basic tests' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh if ! test_have_prereq PERL then diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh index e3ea3d511..49e2d6c34 100755 --- a/t/t4102-apply-rename.sh +++ b/t/t4102-apply-rename.sh @@ -7,7 +7,6 @@ test_description='git apply handling copy/rename patch. ' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh # setup diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh index 2298ece80..1e4d4380b 100755 --- a/t/t4116-apply-reverse.sh +++ b/t/t4116-apply-reverse.sh @@ -48,12 +48,12 @@ test_expect_success 'apply in reverse' ' test_expect_success 'setup separate repository lacking postimage' ' - git tar-tree initial initial | $TAR xf - && + git archive --format=tar --prefix=initial/ initial | $TAR xf - && ( cd initial && git init && git add . ) && - git tar-tree second second | $TAR xf - && + git archive --format=tar --prefix=second/ second | $TAR xf - && ( cd second && git init && git add . ) diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh index c5fecdfed..497b62868 100755 --- a/t/t4120-apply-popt.sh +++ b/t/t4120-apply-popt.sh @@ -6,7 +6,6 @@ test_description='git apply -p handling.' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh test_expect_success setup ' mkdir sub && diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh index 0d36ebdc8..c268298ea 100755 --- a/t/t4129-apply-samemode.sh +++ b/t/t4129-apply-samemode.sh @@ -3,7 +3,6 @@ test_description='applying patch with mode bits' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh test_expect_success setup ' echo original >file && diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index c2023b1a3..05f011d38 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -3,7 +3,7 @@ # Copyright (C) 2005 Rene Scharfe # -test_description='git tar-tree and git get-tar-commit-id test +test_description='git archive and git get-tar-commit-id test This test covers the topics of file contents, commit date handling and commit id embedding: @@ -13,11 +13,11 @@ commit id embedding: binary file (/bin/sh). Only paths shorter than 99 characters are used. - git tar-tree applies the commit date to every file in the archive it + git archive applies the commit date to every file in the archive it creates. The test sets the commit date to a specific value and checks if the tar archive contains that value. - When giving git tar-tree a commit id (in contrast to a tree id) it + When giving git archive a commit id (in contrast to a tree id) it embeds this commit id into the tar archive as a comment. The test checks the ability of git get-tar-commit-id to figure it out from the tar file. @@ -25,8 +25,6 @@ commit id embedding: ' . ./test-lib.sh -GZIP=${GZIP:-gzip} -GUNZIP=${GUNZIP:-gzip -d} SUBSTFORMAT=%H%n @@ -39,6 +37,8 @@ test_lazy_prereq TAR_NEEDS_PAX_FALLBACK ' ) ' +test_lazy_prereq GZIP 'gzip --version' + get_pax_header() { file=$1 header=$2= @@ -196,16 +196,6 @@ test_expect_success \ 'git get-tar-commit-id <b.tar >b.commitid && test_cmp .git/$(git symbolic-ref HEAD) b.commitid' -test_expect_success 'git tar-tree' ' - git tar-tree HEAD >tar-tree.tar && - test_cmp b.tar tar-tree.tar -' - -test_expect_success 'git tar-tree with prefix' ' - git tar-tree HEAD prefix >tar-tree_with_prefix.tar && - test_cmp with_prefix.tar tar-tree_with_prefix.tar -' - test_expect_success 'git archive with --output, override inferred format' ' git archive --format=tar --output=d4.zip HEAD && test_cmp b.tar d4.zip @@ -275,12 +265,6 @@ test_expect_success 'only enabled filters are available remotely' ' test_cmp remote.bar config.bar ' -if $GZIP --version >/dev/null 2>&1; then - test_set_prereq GZIP -else - say "Skipping some tar.gz tests because gzip not found" -fi - test_expect_success GZIP 'git archive --format=tgz' ' git archive --format=tgz HEAD >j.tgz ' @@ -300,14 +284,8 @@ test_expect_success GZIP 'infer tgz from .tar.gz filename' ' test_cmp j.tgz j3.tar.gz ' -if $GUNZIP --version >/dev/null 2>&1; then - test_set_prereq GUNZIP -else - say "Skipping some tar.gz tests because gunzip was not found" -fi - -test_expect_success GZIP,GUNZIP 'extract tgz file' ' - $GUNZIP -c <j.tgz >j.tar && +test_expect_success GZIP 'extract tgz file' ' + gzip -d -c <j.tgz >j.tar && test_cmp b.tar j.tar ' diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh index f47d8717f..51dedab29 100755 --- a/t/t5001-archive-attr.sh +++ b/t/t5001-archive-attr.sh @@ -87,14 +87,4 @@ test_expect_success 'export-subst' ' test_cmp substfile2 archive/substfile2 ' -test_expect_success 'git tar-tree vs. git archive with worktree attributes' ' - git tar-tree HEAD >tar-tree.tar && - test_cmp worktree.tar tar-tree.tar -' - -test_expect_success 'git tar-tree vs. git archive with worktree attrs, bare' ' - (cd bare && git tar-tree HEAD) >bare-tar-tree.tar && - test_cmp bare-worktree.tar bare-tar-tree.tar -' - test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 1f0f8e682..5d4581dac 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -88,7 +88,7 @@ test_expect_success 'fetch --prune on its own works as expected' ' cd "$D" && git clone . prune && cd prune && - git fetch origin refs/heads/master:refs/remotes/origin/extrabranch && + git update-ref refs/remotes/origin/extrabranch master && git fetch --prune origin && test_must_fail git rev-parse origin/extrabranch @@ -98,7 +98,7 @@ test_expect_success 'fetch --prune with a branch name keeps branches' ' cd "$D" && git clone . prune-branch && cd prune-branch && - git fetch origin refs/heads/master:refs/remotes/origin/extrabranch && + git update-ref refs/remotes/origin/extrabranch master && git fetch --prune origin master && git rev-parse origin/extrabranch @@ -113,25 +113,45 @@ test_expect_success 'fetch --prune with a namespace keeps other namespaces' ' git rev-parse origin/master ' -test_expect_success 'fetch --prune --tags does not delete the remote-tracking branches' ' +test_expect_success 'fetch --prune --tags prunes branches but not tags' ' cd "$D" && git clone . prune-tags && cd prune-tags && - git fetch origin refs/heads/master:refs/tags/sometag && + git tag sometag master && + # Create what looks like a remote-tracking branch from an earlier + # fetch that has since been deleted from the remote: + git update-ref refs/remotes/origin/fake-remote master && git fetch --prune --tags origin && git rev-parse origin/master && - test_must_fail git rev-parse somebranch + test_must_fail git rev-parse origin/fake-remote && + git rev-parse sometag ' -test_expect_success 'fetch --prune --tags with branch does not delete other remote-tracking branches' ' +test_expect_success 'fetch --prune --tags with branch does not prune other things' ' cd "$D" && git clone . prune-tags-branch && cd prune-tags-branch && - git fetch origin refs/heads/master:refs/remotes/origin/extrabranch && + git tag sometag master && + git update-ref refs/remotes/origin/extrabranch master && git fetch --prune --tags origin master && - git rev-parse origin/extrabranch + git rev-parse origin/extrabranch && + git rev-parse sometag +' + +test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' ' + cd "$D" && + git clone . prune-tags-refspec && + cd prune-tags-refspec && + git tag sometag master && + git update-ref refs/remotes/origin/foo/otherbranch master && + git update-ref refs/remotes/origin/extrabranch master && + + git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* && + test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch && + git rev-parse origin/extrabranch && + git rev-parse sometag ' test_expect_success 'fetch tags when there is no tags' ' diff --git a/t/t5515/fetch.br-unconfig_--tags_.._.git b/t/t5515/fetch.br-unconfig_--tags_.._.git index 1669cc4af..0f70f66c7 100644 --- a/t/t5515/fetch.br-unconfig_--tags_.._.git +++ b/t/t5515/fetch.br-unconfig_--tags_.._.git @@ -1,4 +1,5 @@ # br-unconfig --tags ../.git +0567da4d5edd2ff4bb292a465ba9e64dcad9536b ../ 6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../ 8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../ 22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../ diff --git a/t/t5515/fetch.master_--tags_.._.git b/t/t5515/fetch.master_--tags_.._.git index 8a7493537..ab473a6e1 100644 --- a/t/t5515/fetch.master_--tags_.._.git +++ b/t/t5515/fetch.master_--tags_.._.git @@ -1,4 +1,5 @@ # master --tags ../.git +0567da4d5edd2ff4bb292a465ba9e64dcad9536b ../ 6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../ 8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../ 22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../ diff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh index 4fbf7a120..45815f737 100755 --- a/t/t5525-fetch-tagopt.sh +++ b/t/t5525-fetch-tagopt.sh @@ -8,7 +8,8 @@ setup_clone () { git clone --mirror . $1 && git remote add remote_$1 $1 && (cd $1 && - git tag tag_$1) + git tag tag_$1 && + git branch branch_$1) } test_expect_success setup ' @@ -21,21 +22,33 @@ test_expect_success setup ' test_expect_success "fetch with tagopt=--no-tags does not get tag" ' git fetch remote_one && - test_must_fail git show-ref tag_one + test_must_fail git show-ref tag_one && + git show-ref remote_one/branch_one ' test_expect_success "fetch --tags with tagopt=--no-tags gets tag" ' + ( + cd one && + git branch second_branch_one + ) && git fetch --tags remote_one && - git show-ref tag_one + git show-ref tag_one && + git show-ref remote_one/second_branch_one ' test_expect_success "fetch --no-tags with tagopt=--tags does not get tag" ' git fetch --no-tags remote_two && - test_must_fail git show-ref tag_two + test_must_fail git show-ref tag_two && + git show-ref remote_two/branch_two ' test_expect_success "fetch with tagopt=--tags gets tag" ' + ( + cd two && + git branch second_branch_two + ) && git fetch remote_two && - git show-ref tag_two + git show-ref tag_two && + git show-ref remote_two/second_branch_two ' test_done diff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh new file mode 100755 index 000000000..6c5d3a4ce --- /dev/null +++ b/t/t5536-fetch-conflicts.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +test_description='fetch handles conflicting refspecs correctly' + +. ./test-lib.sh + +D=$(pwd) + +setup_repository () { + git init "$1" && ( + cd "$1" && + git config remote.origin.url "$D" && + shift && + for refspec in "$@" + do + git config --add remote.origin.fetch "$refspec" + done + ) +} + +verify_stderr () { + cat >expected && + # We're not interested in the error + # "fatal: The remote end hung up unexpectedly": + grep -E '^(fatal|warning):' <error | grep -v 'hung up' >actual | sort && + test_cmp expected actual +} + +test_expect_success 'setup' ' + git commit --allow-empty -m "Initial" && + git branch branch1 && + git tag tag1 && + git commit --allow-empty -m "First" && + git branch branch2 && + git tag tag2 +' + +test_expect_success 'fetch with no conflict' ' + setup_repository ok "+refs/heads/*:refs/remotes/origin/*" && ( + cd ok && + git fetch origin + ) +' + +test_expect_success 'fetch conflict: config vs. config' ' + setup_repository ccc \ + "+refs/heads/branch1:refs/remotes/origin/branch1" \ + "+refs/heads/branch2:refs/remotes/origin/branch1" && ( + cd ccc && + test_must_fail git fetch origin 2>error && + verify_stderr <<-\EOF + fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1 + EOF + ) +' + +test_expect_success 'fetch duplicate: config vs. config' ' + setup_repository dcc \ + "+refs/heads/*:refs/remotes/origin/*" \ + "+refs/heads/branch1:refs/remotes/origin/branch1" && ( + cd dcc && + git fetch origin + ) +' + +test_expect_success 'fetch conflict: arg overrides config' ' + setup_repository aoc \ + "+refs/heads/*:refs/remotes/origin/*" && ( + cd aoc && + git fetch origin refs/heads/branch2:refs/remotes/origin/branch1 + ) +' + +test_expect_success 'fetch conflict: arg vs. arg' ' + setup_repository caa && ( + cd caa && + test_must_fail git fetch origin \ + refs/heads/*:refs/remotes/origin/* \ + refs/heads/branch2:refs/remotes/origin/branch1 2>error && + verify_stderr <<-\EOF + fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1 + EOF + ) +' + +test_expect_success 'fetch conflict: criss-cross args' ' + setup_repository xaa \ + "+refs/heads/*:refs/remotes/origin/*" && ( + cd xaa && + git fetch origin \ + refs/heads/branch1:refs/remotes/origin/branch2 \ + refs/heads/branch2:refs/remotes/origin/branch1 2>error && + verify_stderr <<-\EOF + warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2 + warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1 + EOF + ) +' + +test_done diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh index 1cd649e24..a953f1b55 100755 --- a/t/t6031-merge-recursive.sh +++ b/t/t6031-merge-recursive.sh @@ -2,7 +2,6 @@ test_description='merge-recursive: handle file mode' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh test_expect_success 'mode change in one branch: keep changed version' ' : >file1 && diff --git a/t/t9150/make-svk-dump b/t/t9150/make-svk-dump index 2242f14eb..2242f14eb 100644..100755 --- a/t/t9150/make-svk-dump +++ b/t/t9150/make-svk-dump diff --git a/t/t9151/make-svnmerge-dump b/t/t9151/make-svnmerge-dump index e1e138cb1..e1e138cb1 100644..100755 --- a/t/t9151/make-svnmerge-dump +++ b/t/t9151/make-svnmerge-dump diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 3fb336890..812c9cd46 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -5,7 +5,6 @@ test_description='Test export of commits to CVS' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh if ! test_have_prereq PERL; then skip_all='skipping git cvsexportcommit tests, perl not available' diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 2f79146e6..aeae3ca76 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# Library of functions shared by all tests scripts, included by +# test-lib.sh. # # Copyright (c) 2005 Junio C Hamano # diff --git a/t/test-lib.sh b/t/test-lib.sh index d303e6c94..1cf78d5b1 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Test framework for git. See t/README for usage. # # Copyright (c) 2005 Junio C Hamano # @@ -577,11 +577,9 @@ then make_valgrind_symlink () { # handle only executables, unless they are shell libraries that - # need to be in the exec-path. We will just use "#!" as a - # guess for a shell-script, since we have no idea what the user - # may have configured as the shell path. + # need to be in the exec-path. test -x "$1" || - test "#!" = "$(head -c 2 <"$1")" || + test "# " = "$(head -c 2 <"$1")" || return; base=$(basename "$1") @@ -834,6 +832,10 @@ test_lazy_prereq SYMLINKS ' ln -s x y && test -h y ' +test_lazy_prereq FILEMODE ' + test "$(git config --bool core.filemode)" = true +' + test_lazy_prereq CASE_INSENSITIVE_FS ' echo good >CamelCase && echo bad >camelcase && |