diff options
43 files changed, 747 insertions, 284 deletions
diff --git a/.gitignore b/.gitignore index 20ee642420..63c918c667 100644 --- a/.gitignore +++ b/.gitignore @@ -148,6 +148,7 @@ git-write-tree git-core-*/?* gitk-wish gitweb/gitweb.cgi +test-absolute-path test-chmtime test-date test-delta diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt index 785bd38455..21bb1fc6f2 100644 --- a/Documentation/RelNotes-1.5.3.txt +++ b/Documentation/RelNotes-1.5.3.txt @@ -40,16 +40,22 @@ Updates since v1.5.2 $GIT_DIR to work in a subdirectory of a working tree that is not located at "$GIT_DIR/..". + - Giving "--file=<file>" option to "git config" is the same as + running the command with GIT_CONFIG=<file> environment. + - "git log" learned a new option "--follow", to follow renaming history of a single file. - "git-filter-branch" lets you rewrite the revision history of - the current branch, creating a new branch. You can specify a - number of filters to modify the commits, files and trees. + specified branches. You can specify a number of filters to + modify the commits, files and trees. - "git-cvsserver" learned new options (--base-path, --export-all, --strict-paths) inspired by git-daemon. + - "git daemon --base-path-relaxed" can help migrating a repository URL + that did not use to use --base-path to use --base-path. + - "git-commit" can use "-t templatefile" option and commit.template configuration variable to prime the commit message given to you in the editor. @@ -120,6 +126,9 @@ Updates since v1.5.2 of the format ('tgz', 'tbz2' or 'zip'). Please update the your configuration file accordingly. + - "git diff" (but not the plumbing level "git diff-tree") now + recursively descends into trees by default. + - The editor to use with many interactive commands can be overridden with GIT_EDITOR environment variable, or if it does not exist, with core.editor configuration variable. As @@ -165,6 +174,9 @@ Updates since v1.5.2 - The diffstat given after a merge (or a pull) honors the color.diff configuration. + - "git commit --amend" is now compatible with various message source + options such as -m/-C/-c/-F. + - "git-apply --whitespace=strip" removes blank lines added at the end of the file. @@ -262,6 +274,6 @@ this release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.5.3-rc3 +O=v1.5.3-rc4 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index a46bf6ce70..17379f0576 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -64,11 +64,11 @@ of lines before or after the line given by <start>. assigns blame to the lines that were moved down (i.e. A) to the child commit. With this option, both groups of lines are blamed on the parent. - - <num> is optional but it is the lower bound on the number of - alphanumeric characters that git must detect as moving - within a file for it to associate those lines with the parent - commit. ++ +<num> is optional but it is the lower bound on the number of +alphanumeric characters that git must detect as moving +within a file for it to associate those lines with the parent +commit. -C|<num>|:: In addition to `-M`, detect lines copied from other @@ -77,11 +77,11 @@ of lines before or after the line given by <start>. around across files. When this option is given twice, the command looks for copies from all other files in the parent for the commit that creates the file in addition. - - <num> is optional but it is the lower bound on the number of - alphanumeric characters that git must detect as moving - between files for it to associate those lines with the parent - commit. ++ +<num> is optional but it is the lower bound on the number of +alphanumeric characters that git must detect as moving +between files for it to associate those lines with the parent +commit. -h, --help:: Show help message. diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index 2143995ece..4ee76eaf99 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -68,6 +68,8 @@ for my $cat (qw(ancillaryinterrogators } } +# The following list is sorted with "sort -d" to make it easier +# to find entry in the resulting git.html manual page. __DATA__ git-add mainporcelain git-am mainporcelain @@ -80,9 +82,9 @@ git-blame ancillaryinterrogators git-branch mainporcelain git-bundle mainporcelain git-cat-file plumbinginterrogators -git-checkout-index plumbingmanipulators -git-checkout mainporcelain git-check-attr purehelpers +git-checkout mainporcelain +git-checkout-index plumbingmanipulators git-check-ref-format purehelpers git-cherry ancillaryinterrogators git-cherry-pick mainporcelain @@ -91,6 +93,7 @@ git-clean mainporcelain git-clone mainporcelain git-commit mainporcelain git-commit-tree plumbingmanipulators +git-config ancillarymanipulators git-convert-objects ancillarymanipulators git-count-objects ancillaryinterrogators git-cvsexportcommit foreignscminterface @@ -98,9 +101,9 @@ git-cvsimport foreignscminterface git-cvsserver foreignscminterface git-daemon synchingrepositories git-describe mainporcelain +git-diff mainporcelain git-diff-files plumbinginterrogators git-diff-index plumbinginterrogators -git-diff mainporcelain git-diff-tree plumbinginterrogators git-fast-import ancillarymanipulators git-fetch mainporcelain @@ -130,13 +133,13 @@ git-ls-remote plumbinginterrogators git-ls-tree plumbinginterrogators git-mailinfo purehelpers git-mailsplit purehelpers +git-merge mainporcelain git-merge-base plumbinginterrogators git-merge-file plumbingmanipulators git-merge-index plumbingmanipulators -git-merge mainporcelain git-merge-one-file purehelpers -git-merge-tree ancillaryinterrogators git-mergetool ancillarymanipulators +git-merge-tree ancillaryinterrogators git-mktag plumbingmanipulators git-mktree plumbingmanipulators git-mv mainporcelain @@ -157,9 +160,8 @@ git-rebase mainporcelain git-receive-pack synchelpers git-reflog ancillarymanipulators git-relink ancillarymanipulators -git-repack ancillarymanipulators -git-config ancillarymanipulators git-remote ancillarymanipulators +git-repack ancillarymanipulators git-request-pull foreignscminterface git-rerere ancillaryinterrogators git-reset mainporcelain diff --git a/Documentation/config.txt b/Documentation/config.txt index 3135cb7a66..de9e72b562 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -101,7 +101,7 @@ Example # Proxy settings [core] - gitProxy="ssh" for "ssh://kernel.org/" + gitProxy="ssh" for "kernel.org" gitProxy=default-proxy ; for the rest Variables diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 76d2b05854..4af3a9b0d7 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -3,7 +3,7 @@ git-add(1) NAME ---- -git-add - Add file contents to the changeset to be committed next +git-add - Add file contents to the index SYNOPSIS -------- @@ -11,24 +11,27 @@ SYNOPSIS DESCRIPTION ----------- -All the changed file contents to be committed together in a single set -of changes must be "added" with the 'add' command before using the -'commit' command. This is not only for adding new files. Even modified -files must be added to the set of changes about to be committed. - -This command can be performed multiple times before a commit. The added -content corresponds to the state of specified file(s) at the time the -'add' command is used. This means the 'commit' command will not consider -subsequent changes to already added content if it is not added again before -the commit. - -The 'git status' command can be used to obtain a summary of what is included -for the next commit. - -This command can be used to add ignored files with `-f` (force) -option, but they have to be -explicitly and exactly specified from the command line. File globbing -and recursive behaviour do not add ignored files. +This command adds the current content of new or modified files to the +index, thus staging that content for inclusion in the next commit. + +The "index" holds a snapshot of the content of the working tree, and it +is this snapshot that is taken as the contents of the next commit. Thus +after making any changes to the working directory, and before running +the commit command, you must use the 'add' command to add any new or +modified files to the index. + +This command can be performed multiple times before a commit. It only +adds the content of the specified file(s) at the time the add command is +run; if you want subsequent changes included in the next commit, then +you must run 'git add' again to add the new content to the index. + +The 'git status' command can be used to obtain a summary of which +files have changes that are staged for the next commit. + +The 'add' command can be used to add ignored files with `-f` (force) +option, but they have to be explicitly and exactly specified from the +command line. File globbing and recursive behaviour do not add ignored +files. Please see gitlink:git-commit[1] for alternative ways to add content to a commit. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 627994eb97..e54fb12103 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -15,26 +15,27 @@ SYNOPSIS DESCRIPTION ----------- -Use 'git commit' when you want to record your changes into the repository -along with a log message describing what the commit is about. All changes -to be committed must be explicitly identified using one of the following -methods: +Use 'git commit' to store the current contents of the index in a new +commit along with a log message describing the changes you have made. + +The content to be added can be specified in several ways: 1. by using gitlink:git-add[1] to incrementally "add" changes to the - next commit before using the 'commit' command (Note: even modified + index before using the 'commit' command (Note: even modified files must be "added"); -2. by using gitlink:git-rm[1] to identify content removal for the next - commit, again before using the 'commit' command; +2. by using gitlink:git-rm[1] to remove files from the working tree + and the index, again before using the 'commit' command; -3. by directly listing files containing changes to be committed as arguments - to the 'commit' command, in which cases only those files alone will be - considered for the commit; +3. by listing files as arguments to the 'commit' command, in which + case the commit will ignore changes staged in the index, and instead + record the current content of the listed files; -4. by using the -a switch with the 'commit' command to automatically "add" - changes from all known files i.e. files that have already been committed - before, and to automatically "rm" files that have been - removed from the working tree, and perform the actual commit. +4. by using the -a switch with the 'commit' command to automatically + "add" changes from all known files (i.e. all files that are already + listed in the index) and to automatically "rm" files in the index + that have been removed from the working tree, and then perform the + actual commit; 5. by using the --interactive switch with the 'commit' command to decide one by one which files should be part of the commit, before finalizing the diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 8451cccf8a..c3dffffe32 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -214,9 +214,7 @@ Given a .git/config like this: ; Proxy settings [core] - gitproxy="ssh" for "ssh://kernel.org/" gitproxy="proxy-command" for kernel.org - gitproxy="myprotocol-command" for "my://" gitproxy=default-proxy ; for all the rest you can set the filemode to true with @@ -291,7 +289,7 @@ To actually match only values with an exclamation mark, you have to To add a new proxy, without altering any of the existing ones, use ------------ -% git config core.gitproxy '"proxy" for example.com' +% git config core.gitproxy '"proxy-command" for example.com' ------------ diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 74a0da1ed4..0dd9caf867 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -79,7 +79,7 @@ the remote repository. -f, \--force:: Usually, the command refuses to update a remote ref that is - not a descendant of the local ref used to overwrite it. + not an ancestor of the local ref used to overwrite it. This flag disables the check. This can cause the remote repository to lose commits; use it with care. diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index eea9c9cfe9..4b4d229e60 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -224,7 +224,7 @@ left-to-right. G H I J \ / \ / D E F - \ | / \ + \ | / \ \ | / | \|/ | B C diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 17121ade56..05f40cff6c 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -29,8 +29,8 @@ you create one. The latest stash you created is stored in `$GIT_DIR/refs/stash`; older stashes are found in the reflog of this reference and can be named using -the usual reflog syntax (e.g. `stash@\{1}` is the most recently -created stash, `stash@\{2}` is the one before it, `stash@\{2.hours.ago}` +the usual reflog syntax (e.g. `stash@\{0}` is the most recently +created stash, `stash@\{1}` is the one before it, `stash@\{2.hours.ago}` is also possible). OPTIONS @@ -45,7 +45,7 @@ save:: list:: List the stashes that you currently have. Each 'stash' is listed - with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1} is + with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is the one before, etc.), the name of the branch that was current when the stash was made, and a short description of the commit the stash was based on. @@ -61,7 +61,7 @@ show [<stash>]:: stashed state and its original parent. When no `<stash>` is given, shows the latest one. By default, the command shows the diffstat, but it will accept any format known to `git-diff` (e.g., `git-stash show - -p stash@\{2}` to view the second most recent stash in patch form). + -p stash@\{1}` to view the second most recent stash in patch form). apply [<stash>]:: diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 6f16eb0328..8fd0fc6236 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -27,6 +27,13 @@ The command takes the same set of options as `git-commit`; it shows what would be committed if the same options are given to `git-commit`. +If any paths have been touched in the working tree (that is, +their modification times have changed) but their contents and +permissions are identical to those in the index file, the command +updates the index file. Running `git-status` can thus speed up +subsequent operations such as `git-diff` if the working tree +contains many paths that have been touched but not modified. + OUTPUT ------ diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 0a210e4bea..816340b944 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -435,6 +435,26 @@ Tracking and contributing to an entire Subversion-managed project # of dcommit/rebase/show-ignore should be the same as above. ------------------------------------------------------------------------ +The initial 'git-svn clone' can be quite time-consuming +(especially for large Subversion repositories). If multiple +people (or one person with multiple machines) want to use +git-svn to interact with the same Subversion repository, you can +do the initial 'git-svn clone' to a repository on a server and +have each person clone that repository with 'git clone': + +------------------------------------------------------------------------ +# Do the initial import on a server + ssh server "cd /pub && git-svn clone http://svn.foo.org/project +# Clone locally + git clone server:/pub/project +# Tell git-svn which branch contains the Subversion commits + git update-ref refs/remotes/git-svn origin/master +# Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server) + git-svn init http://svn.foo.org/project +# Pull the latest changes from Subversion + git-svn rebase +------------------------------------------------------------------------ + REBASE VS. PULL/MERGE --------------------- diff --git a/Documentation/git.txt b/Documentation/git.txt index 4c4d1746e0..18f8b6a0a1 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -421,6 +421,22 @@ other to an empty string or to the value "cat", git will not launch a pager. +'GIT_SSH':: + If this environment variable is set then gitlink:git-fetch[1] + and gitlink:git-push[1] will use this command instead + of `ssh` when they need to connect to a remote system. + The 'GIT_SSH' command will be given exactly two arguments: + the 'username@host' (or just 'host') from the URL and the + shell command to execute on that remote system. ++ +To pass options to the program that you want to list in GIT_SSH +you will need to wrap the program and options into a shell script, +then set GIT_SSH to refer to the shell script. ++ +Usually it is easier to configure any desired options through your +personal `.ssh/config` file. Please consult your ssh documentation +for further details. + 'GIT_FLUSH':: If this environment variable is set to "1", then commands such as git-blame (in incremental mode), git-rev-list, git-log, diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh index e6601bdd82..5433cf8ced 100755 --- a/Documentation/install-doc-quick.sh +++ b/Documentation/install-doc-quick.sh @@ -7,7 +7,7 @@ mandir="$2" SUBDIRECTORY_OK=t USAGE='<refname> <target directory>' . git-sh-setup -export GIT_DIR +cd_to_toplevel test -z "$mandir" && usage if ! git rev-parse --verify "$head^0" >/dev/null; then @@ -18,14 +18,14 @@ fi GIT_INDEX_FILE=`pwd`/.quick-doc.index export GIT_INDEX_FILE rm -f "$GIT_INDEX_FILE" +trap 'rm -f "$GIT_INDEX_FILE"' 0 + git read-tree $head git checkout-index -a -f --prefix="$mandir"/ if test -n "$GZ"; then - cd "$mandir" - for i in `git ls-tree -r --name-only $head` - do - gzip < $i > $i.gz && rm $i - done + git ls-tree -r --name-only $head | + xargs printf "$mandir/%s\n" | + xargs gzip -f fi rm -f "$GIT_INDEX_FILE" diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 0071cd070e..f89952ad84 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1,4 +1,4 @@ -Git User's Manual (for version 1.5.1 or newer) +Git User's Manual (for version 1.5.3 or newer) ______________________________________________ @@ -1079,6 +1079,11 @@ $ git diff HEAD # difference between HEAD and working tree; what $ git status # a brief per-file summary of the above. ------------------------------------------------- +You can also use gitlink:git-gui[1] to create commits, view changes in +the index and the working tree files, and individually select diff hunks +for inclusion in the index (by right-clicking on the diff hunk and +choosing "Stage Hunk For Commit"). + [[creating-good-commit-messages]] Creating good commit messages ----------------------------- @@ -1484,6 +1489,38 @@ $ git show HEAD^:path/to/file which will display the given version of the file. +[[interrupted-work]] +Temporarily setting aside work in progress +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While you are in the middle of working on something complicated, you +find an unrelated but obvious and trivial bug. You would like to fix it +before continuing. You can use gitlink:git-stash[1] to save the current +state of your work, and after fixing the bug (or, optionally after doing +so on a different branch and then coming back), unstash the +work-in-progress changes. + +------------------------------------------------ +$ git stash "work in progress for foo feature" +------------------------------------------------ + +This command will save your changes away to the `stash`, and +reset your working tree and the index to match the tip of your +current branch. Then you can make your fix as usual. + +------------------------------------------------ +... edit and test ... +$ git commit -a -m "blorpl: typofix" +------------------------------------------------ + +After that, you can go back to what you were working on with +`git stash apply`: + +------------------------------------------------ +$ git stash apply +------------------------------------------------ + + [[ensuring-good-performance]] Ensuring good performance ------------------------- @@ -1667,24 +1704,19 @@ one step: $ git pull origin master ------------------------------------------------- -In fact, "origin" is normally the default repository to pull from, -and the default branch is normally the HEAD of the remote repository, -so often you can accomplish the above with just +In fact, if you have "master" checked out, then by default "git pull" +merges from the HEAD branch of the origin repository. So often you can +accomplish the above with just a simple ------------------------------------------------- $ git pull ------------------------------------------------- -See the descriptions of the branch.<name>.remote and branch.<name>.merge -options in gitlink:git-config[1] to learn how to control these defaults -depending on the current branch. Also note that the --track option to -gitlink:git-branch[1] and gitlink:git-checkout[1] can be used to -automatically set the default remote branch to pull from at the time -that a branch is created: - -------------------------------------------------- -$ git checkout --track -b maint origin/maint -------------------------------------------------- +More generally, a branch that is created from a remote branch will pull +by default from that branch. See the descriptions of the +branch.<name>.remote and branch.<name>.merge options in +gitlink:git-config[1], and the discussion of the --track option in +gitlink:git-checkout[1], to learn how to control these defaults. In addition to saving you keystrokes, "git pull" also helps you by producing a default commit message documenting the branch and @@ -2479,8 +2511,10 @@ $ gitk origin..mywork & And browse through the list of patches in the mywork branch using gitk, applying them (possibly in a different order) to mywork-new using -cherry-pick, and possibly modifying them as you go using commit ---amend. +cherry-pick, and possibly modifying them as you go using commit --amend. +The git-gui[1] command may also help as it allows you to individually +select diff hunks for inclusion in the index (by right-clicking on the +diff hunk and choosing "Stage Hunk for Commit"). Another technique is to use git-format-patch to create a series of patches, then reset the state to before the patches: @@ -116,3 +116,7 @@ Issues of note: would instead give you a copy of what you see at: http://www.kernel.org/pub/software/scm/git/docs/ + + It has been reported that docbook-xsl version 1.72 and 1.73 are + buggy; 1.72 misformats manual pages for callouts, and 1.73 needs + the patch in contrib/patches/docbook-xsl-manpages-charmap.patch @@ -458,6 +458,10 @@ ifeq ($(uname_S),AIX) NO_STRLCPY = YesPlease NEEDS_LIBICONV=YesPlease endif +ifeq ($(uname_S),GNU) + # GNU/Hurd + NO_STRLCPY=YesPlease +endif ifeq ($(uname_S),IRIX64) NO_IPV6=YesPlease NO_SETENV=YesPlease diff --git a/builtin-apply.c b/builtin-apply.c index 0a0b4a9e3f..da270755a7 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2508,7 +2508,7 @@ static void write_out_one_result(struct patch *patch, int phase) * thing: remove the old, write the new */ if (phase == 0) - remove_file(patch, 0); + remove_file(patch, patch->is_rename); if (phase == 1) create_file(patch); } diff --git a/builtin-bundle.c b/builtin-bundle.c index 6ae5ab04c9..f4b4f034f4 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -44,38 +44,21 @@ struct bundle_header { struct ref_list references; }; -/* this function returns the length of the string */ -static int read_string(int fd, char *buffer, int size) -{ - int i; - for (i = 0; i < size - 1; i++) { - ssize_t count = xread(fd, buffer + i, 1); - if (count < 0) - return error("Read error: %s", strerror(errno)); - if (count == 0) { - i--; - break; - } - if (buffer[i] == '\n') - break; - } - buffer[i + 1] = '\0'; - return i + 1; -} - /* returns an fd */ static int read_header(const char *path, struct bundle_header *header) { char buffer[1024]; - int fd = open(path, O_RDONLY); + int fd; + long fpos; + FILE *ffd = fopen(path, "rb"); - if (fd < 0) + if (!ffd) return error("could not open '%s'", path); - if (read_string(fd, buffer, sizeof(buffer)) < 0 || + if (!fgets(buffer, sizeof(buffer), ffd) || strcmp(buffer, bundle_signature)) { - close(fd); + fclose(ffd); return error("'%s' does not look like a v2 bundle file", path); } - while (read_string(fd, buffer, sizeof(buffer)) > 0 + while (fgets(buffer, sizeof(buffer), ffd) && buffer[0] != '\n') { int is_prereq = buffer[0] == '-'; int offset = is_prereq ? 1 : 0; @@ -97,6 +80,12 @@ static int read_header(const char *path, struct bundle_header *header) { add_to_ref_list(sha1, isspace(delim) ? buffer + 41 + offset : "", list); } + fpos = ftell(ffd); + fclose(ffd); + fd = open(path, O_RDONLY); + if (fd < 0) + return error("could not open '%s'", path); + lseek(fd, fpos, SEEK_SET); return fd; } @@ -207,7 +196,12 @@ static int create_bundle(struct bundle_header *header, const char *path, char buffer[1024]; struct rev_info revs; struct child_process rls; + FILE *rls_fout; + /* + * NEEDSWORK: this should use something like lock-file + * to create temporary that is cleaned up upon error. + */ bundle_fd = (!strcmp(path, "-") ? 1 : open(path, O_CREAT | O_EXCL | O_WRONLY, 0666)); if (bundle_fd < 0) @@ -232,10 +226,11 @@ static int create_bundle(struct bundle_header *header, const char *path, rls.git_cmd = 1; if (start_command(&rls)) return -1; - while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) { + rls_fout = fdopen(rls.out, "r"); + while (fgets(buffer, sizeof(buffer), rls_fout)) { unsigned char sha1[20]; if (buffer[0] == '-') { - write_or_die(bundle_fd, buffer, i); + write_or_die(bundle_fd, buffer, strlen(buffer)); if (!get_sha1_hex(buffer + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; @@ -246,6 +241,7 @@ static int create_bundle(struct bundle_header *header, const char *path, object->flags |= SHOWN; } } + fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); @@ -267,12 +263,49 @@ static int create_bundle(struct bundle_header *header, const char *path, * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. + * + * Non commit objects such as tags and blobs do not have + * this issue as they are not affected by those extra + * constraints. */ - if (!(e->item->flags & SHOWN)) { + if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); + free(ref); continue; } + /* + * If you run "git bundle create bndl v1.0..v2.0", the + * name of the positive ref is "v2.0" but that is the + * commit that is referenced by the tag, and not the tag + * itself. + */ + if (hashcmp(sha1, e->item->sha1)) { + /* + * Is this the positive end of a range expressed + * in terms of a tag (e.g. v2.0 from the range + * "v1.0..v2.0")? + */ + struct commit *one = lookup_commit_reference(sha1); + struct object *obj; + + if (e->item == &(one->object)) { + /* + * Need to include e->name as an + * independent ref to the pack-objects + * input, so that the tag is included + * in the output; otherwise we would + * end up triggering "empty bundle" + * error. + */ + obj = parse_object(sha1); + obj->flags |= SHOWN; + add_pending_object(&revs, obj, e->name); + } + free(ref); + continue; + } + ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 41f8110238..1967d100f2 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -13,14 +13,19 @@ #include "dir.h" #include "builtin.h" -static struct object_list *trees; +static int nr_trees; +static struct tree *trees[4]; static int list_tree(unsigned char *sha1) { - struct tree *tree = parse_tree_indirect(sha1); + struct tree *tree; + + if (nr_trees >= 4) + return -1; + tree = parse_tree_indirect(sha1); if (!tree) return -1; - object_list_append(&tree->object, &trees); + trees[nr_trees++] = tree; return 0; } @@ -76,11 +81,10 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree) static void prime_cache_tree(void) { - struct tree *tree = (struct tree *)trees->item; - if (!tree) + if (!nr_trees) return; active_cache_tree = cache_tree(); - prime_cache_tree_rec(active_cache_tree, tree); + prime_cache_tree_rec(active_cache_tree, trees[0]); } @@ -92,12 +96,12 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) { int i, newfd, stage = 0; unsigned char sha1[20]; + struct tree_desc t[3]; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; - setup_git_directory(); git_config(git_default_config); newfd = hold_locked_index(&lock_file, 1); @@ -259,7 +263,12 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) opts.head_idx = 1; } - unpack_trees(trees, &opts); + for (i = 0; i < nr_trees; i++) { + struct tree *tree = trees[i]; + parse_tree(tree); + init_tree_desc(t+i, tree->buffer, tree->size); + } + unpack_trees(nr_trees, t, &opts); /* * When reading only one tree (either the most basic form, @@ -267,7 +276,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) * valid cache-tree because the index must match exactly * what came from the tree. */ - if (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) { + if (nr_trees && !opts.prefix && (!opts.merge || (stage == 2))) { cache_tree_free(&active_cache_tree); prime_cache_tree(); } diff --git a/builtin-update-index.c b/builtin-update-index.c index 509369e9e7..a7a4574f2b 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -86,9 +86,15 @@ static int process_lstat_error(const char *path, int err) static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st) { - int option, size = cache_entry_size(len); - struct cache_entry *ce = xcalloc(1, size); + int option, size; + struct cache_entry *ce; + + /* Was the old index entry already up-to-date? */ + if (old && !ce_stage(old) && !ce_match_stat(old, st, 0)) + return 0; + size = cache_entry_size(len); + ce = xcalloc(1, size); memcpy(ce->name, path, len); ce->ce_flags = htons(len); fill_stat_cache_info(ce, st); @@ -258,6 +258,7 @@ extern int index_name_pos(struct index_state *, const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ #define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */ +#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option); extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern int remove_index_entry_at(struct index_state *, int pos); diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f2b10fa5f6..82b9ed40d8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -972,6 +972,11 @@ _git_show () __git_complete_file } +_git_stash () +{ + __gitcomp 'list show apply clear' +} + _git () { local i c=1 command __git_dir @@ -1028,6 +1033,7 @@ _git () shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; + stash) _git_stash ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; esac @@ -1073,6 +1079,7 @@ complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show +complete -o default -o nospace -F _git_stash git-stash complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1f5a56ee7f..805d632a68 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -390,6 +390,30 @@ class P4Submit(Command): return result + def prepareSubmitTemplate(self): + # remove lines in the Files section that show changes to files outside the depot path we're committing into + template = "" + inFilesSection = False + for line in read_pipe_lines("p4 change -o"): + if inFilesSection: + if line.startswith("\t"): + # path starts and ends with a tab + path = line[1:] + lastTab = path.rfind("\t") + if lastTab != -1: + path = path[:lastTab] + if not path.startswith(self.depotPath): + continue + else: + inFilesSection = False + else: + if line.startswith("Files:"): + inFilesSection = True + + template += line + + return template + def applyCommit(self, id): if self.directSubmit: print "Applying local change in working directory/index" @@ -467,7 +491,7 @@ class P4Submit(Command): logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() - template = read_pipe("p4 change -o") + template = self.prepareSubmitTemplate() if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) @@ -558,24 +582,24 @@ class P4Submit(Command): return False [upstream, settings] = findUpstreamBranchPoint() - depotPath = settings['depot-paths'][0] + self.depotPath = settings['depot-paths'][0] if len(self.origin) == 0: self.origin = upstream if self.verbose: print "Origin branch is " + self.origin - if len(depotPath) == 0: + if len(self.depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - self.clientPath = p4Where(depotPath) + self.clientPath = p4Where(self.depotPath) if len(self.clientPath) == 0: - print "Error: Cannot locate perforce checkout of %s in client view" % depotPath + print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath sys.exit(128) - print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) + print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() if self.directSubmit: @@ -839,16 +863,20 @@ class P4Sync(Command): if file["action"] == "delete": self.gitStream.write("D %s\n" % relPath) else: - mode = 644 - if file["type"].startswith("x"): - mode = 755 - data = file['data'] + mode = "644" + if file["type"].startswith("x"): + mode = "755" + elif file["type"] == "symlink": + mode = "120000" + # p4 print on a symlink contains "target\n", so strip it off + data = data[:-1] + if self.isWindows and file["type"].endswith("text"): data = data.replace("\r\n", "\n") - self.gitStream.write("M %d inline %s\n" % (mode, relPath)) + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) self.gitStream.write("data %s\n" % len(data)) self.gitStream.write(data) self.gitStream.write("\n") @@ -1181,11 +1209,11 @@ class P4Sync(Command): elif ',' not in self.changeRange: self.revision = self.changeRange self.changeRange = "" - p = p[0:atIdx] + p = p[:atIdx] elif p.find("#") != -1: hashIdx = p.index("#") self.revision = p[hashIdx:] - p = p[0:hashIdx] + p = p[:hashIdx] elif self.previousDepotPaths == []: self.revision = "#head" @@ -1296,10 +1324,10 @@ class P4Sync(Command): changeNum = line.split(" ")[1] changes.append(changeNum) - changes.reverse() + changes.sort() if len(self.maxChanges) > 0: - changes = changes[0:min(int(self.maxChanges), len(changes))] + changes = changes[:min(int(self.maxChanges), len(changes))] if len(changes) == 0: if not self.silent: diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 5ee1835c80..068fa37083 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -102,6 +102,8 @@ my ($this_user) = getpwuid $<; # REAL_USER_ID my $repository_name; my %user_committer; my @allow_rules; +my @path_rules; +my %diff_cache; sub deny ($) { print STDERR "-Deny- $_[0]\n" if $debug; @@ -118,22 +120,36 @@ sub info ($) { print STDERR "-Info- $_[0]\n" if $debug; } -sub parse_config ($$) { - my ($data, $fn) = @_; - info "Loading $fn"; - open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn); +sub git_value (@) { + open(T,'-|','git',@_); local $_ = <T>; chop; close T; $_; +} + +sub match_string ($$) { + my ($acl_n, $ref) = @_; + ($acl_n eq $ref) + || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) + || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:); +} + +sub parse_config ($$$$) { + my $data = shift; + local $ENV{GIT_DIR} = shift; + my $br = shift; + my $fn = shift; + info "Loading $br:$fn"; + open(I,'-|','git','cat-file','blob',"$br:$fn"); my $section = ''; while (<I>) { chomp; if (/^\s*$/ || /^\s*#/) { } elsif (/^\[([a-z]+)\]$/i) { - $section = $1; + $section = lc $1; } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) { - $section = "$1.$2"; + $section = join('.',lc $1,$2); } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) { - push @{$data->{"$section.$1"}}, $2; + push @{$data->{join('.',$section,lc $1)}}, $2; } else { - deny "bad config file line $. in $fn"; + deny "bad config file line $. in $br:$fn"; } } close I; @@ -202,9 +218,40 @@ sub check_committers (@) { } } -sub git_value (@) { - open(T,'-|','git',@_); local $_ = <T>; chop; close T; - $_; +sub load_diff ($) { + my $base = shift; + my $d = $diff_cache{$base}; + unless ($d) { + local $/ = "\0"; + my %this_diff; + if ($base =~ /^0{40}$/) { + open(T,'-|','git','ls-tree', + '-r','--name-only','-z', + $new) or return undef; + while (<T>) { + chop; + $this_diff{$_} = 'A'; + } + close T or return undef; + } else { + open(T,'-|','git','diff-tree', + '-r','--name-status','-z', + $base,$new) or return undef; + while (<T>) { + my $op = $_; + chop $op; + + my $path = <T>; + chop $path; + + $this_diff{$path} = $op; + } + close T or return undef; + } + $d = \%this_diff; + $diff_cache{$base} = $d; + } + return $d; } deny "No GIT_DIR inherited from caller" unless $git_dir; @@ -231,14 +278,52 @@ $op = 'U' if ($op eq 'R' && $ref =~ m,^heads/, && $old eq git_value('merge-base',$old,$new)); -# Load the user's ACL file. +# Load the user's ACL file. Expand groups (user.memberof) one level. { my %data = ('user.committer' => []); - parse_config(\%data, "$acl_branch:users/$this_user.acl"); + parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl"); + + %data = ( + 'user.committer' => $data{'user.committer'}, + 'user.memberof' => [], + ); + parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl"); + %user_committer = map {$_ => $_} @{$data{'user.committer'}}; - my $rules = $data{"repository.$repository_name.allow"} || []; + my $rule_key = "repository.$repository_name.allow"; + my $rules = $data{$rule_key} || []; + + foreach my $group (@{$data{'user.memberof'}}) { + my %g; + parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl"); + my $group_rules = $g{$rule_key}; + push @$rules, @$group_rules if $group_rules; + } + +RULE: foreach (@$rules) { - if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { + while (/\${user\.([a-z][a-zA-Z0-9]+)}/) { + my $k = lc $1; + my $v = $data{"user.$k"}; + next RULE unless defined $v; + next RULE if @$v != 1; + next RULE unless defined $v->[0]; + s/\${user\.$k}/$v->[0]/g; + } + + if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) { + my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4); + $ops =~ s/ //g; + $pth =~ s/\\\\/\\/g; + $ref =~ s/\\\\/\\/g; + push @path_rules, [$ops, $pth, $ref, $bst]; + } elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) { + my ($ops, $pth, $ref) = ($1, $2, $3); + $ops =~ s/ //g; + $pth =~ s/\\\\/\\/g; + $ref =~ s/\\\\/\\/g; + push @path_rules, [$ops, $pth, $ref, $old]; + } elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { my $ops = $1; my $ref = $2; $ops =~ s/ //g; @@ -272,13 +357,65 @@ foreach my $acl_entry (@allow_rules) { next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen. next unless $acl_n; next unless $op =~ /^[$acl_ops]$/; + next unless match_string $acl_n, $ref; + + # Don't test path rules on branch deletes. + # + grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D'; + + # Aggregate matching path rules; allow if there aren't + # any matching this ref. + # + my %pr; + foreach my $p_entry (@path_rules) { + my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; + next unless $p_ref; + push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref; + } + grant "Allowed by: $acl_ops for $acl_n" unless %pr; - grant "Allowed by: $acl_ops for $acl_n" - if ( - ($acl_n eq $ref) - || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) - || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:) - ); + # Allow only if all changes against a single base are + # allowed by file path rules. + # + my @bad; + foreach my $p_bst (keys %pr) { + my $diff_ref = load_diff $p_bst; + deny "Cannot difference trees." unless ref $diff_ref; + + my %fd = %$diff_ref; + foreach my $p_entry (@{$pr{$p_bst}}) { + my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; + next unless $p_ops =~ /^[AMD]+$/; + next unless $p_n; + + foreach my $f_n (keys %fd) { + my $f_op = $fd{$f_n}; + next unless $f_op; + next unless $f_op =~ /^[$p_ops]$/; + delete $fd{$f_n} if match_string $p_n, $f_n; + } + last unless %fd; + } + + if (%fd) { + push @bad, [$p_bst, \%fd]; + } else { + # All changes relative to $p_bst were allowed. + # + grant "Allowed by: $acl_ops for $acl_n diff $p_bst"; + } + } + + foreach my $bad_ref (@bad) { + my ($p_bst, $fd) = @$bad_ref; + print STDERR "\n"; + print STDERR "Not allowed to make the following changes:\n"; + print STDERR "(base: $p_bst)\n"; + foreach my $f_n (sort keys %$fd) { + print STDERR " $fd->{$f_n} $f_n\n"; + } + } + deny "You are not permitted to $op $ref"; } close A; deny "You are not permitted to $op $ref"; diff --git a/contrib/patches/docbook-xsl-manpages-charmap.patch b/contrib/patches/docbook-xsl-manpages-charmap.patch new file mode 100644 index 0000000000..f2b08b4f4a --- /dev/null +++ b/contrib/patches/docbook-xsl-manpages-charmap.patch @@ -0,0 +1,21 @@ +From: Ismail Dönmez <ismail@pardus.org.tr> + +Trying to build the documentation with docbook-xsl 1.73 may result in +the following error. This patch fixes it. + +$ xmlto -m callouts.xsl man git-add.xml +runtime error: file +file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line +129 element call-template +The called template 'read-character-map' was not found. + +--- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap 2007-07-23 16:24:23.000000000 +0100 ++++ docbook-xsl-1.73.0/manpages/docbook.xsl 2007-07-23 16:25:16.000000000 +0100 +@@ -37,6 +37,7 @@ + <xsl:include href="lists.xsl"/> + <xsl:include href="endnotes.xsl"/> + <xsl:include href="table.xsl"/> ++ <xsl:include href="../common/charmap.xsl"/> + + <!-- * we rename the following just to avoid using params with "man" --> + <!-- * prefixes in the table.xsl stylesheet (because that stylesheet --> @@ -103,7 +103,8 @@ It does not apply to blobs recorded in its index." } prec=4 -dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg= +dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= +resolvemsg= resume= git_apply_opt= while case "$#" in 0) break;; esac diff --git a/git-cvsserver.perl b/git-cvsserver.perl index ae7d511589..13dbd27a80 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1196,6 +1196,7 @@ sub req_ci $log->info("Lockless commit start, basing commit on '$tmpdir', index file is '$file_index'"); $ENV{GIT_DIR} = $state->{CVSROOT} . "/"; + $ENV{GIT_WORK_TREE} = "."; $ENV{GIT_INDEX_FILE} = $file_index; # Remember where the head was at the beginning. @@ -1721,6 +1722,7 @@ sub req_annotate $log->info("Temp checkoutdir creation successful, basing annotate session work on '$tmpdir', index file is '$file_index'"); $ENV{GIT_DIR} = $state->{CVSROOT} . "/"; + $ENV{GIT_WORK_TREE} = "."; $ENV{GIT_INDEX_FILE} = $file_index; chdir $tmpdir; diff --git a/git-send-email.perl b/git-send-email.perl index f43f92f957..69559b289a 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -137,7 +137,7 @@ my $compose_filename = ".msg.$$"; # Variables we fill in automatically, or via prompting: my (@to,@cc,@initial_cc,@bcclist,@xh, - $initial_reply_to,$initial_subject,@files,$from,$compose,$time); + $initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time); my $smtp_server; my $envelope_sender; @@ -179,7 +179,7 @@ if (!@bcclist or !$bcclist[0]) { # Begin by accumulating all the variables (defined above), that we will end up # needing, first, from the command line: -my $rc = GetOptions("from=s" => \$from, +my $rc = GetOptions("sender|from=s" => \$sender, "in-reply-to=s" => \$initial_reply_to, "subject=s" => \$initial_subject, "to=s" => \@to, @@ -216,8 +216,8 @@ foreach my $entry (@bcclist) { # Now, let's fill any that aren't set in with defaults: -my ($author) = $repo->ident_person('author'); -my ($committer) = $repo->ident_person('committer'); +my ($repoauthor) = $repo->ident_person('author'); +my ($repocommitter) = $repo->ident_person('committer'); my %aliases; my @alias_files = $repo->config('sendemail.aliasesfile'); @@ -254,17 +254,17 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) { } } -($from) = expand_aliases($from) if defined $from; +($sender) = expand_aliases($sender) if defined $sender; my $prompting = 0; -if (!defined $from) { - $from = $author || $committer; +if (!defined $sender) { + $sender = $repoauthor || $repocommitter; do { - $_ = $term->readline("Who should the emails appear to be from? [$from] "); + $_ = $term->readline("Who should the emails appear to be from? [$sender] "); } while (!defined $_); - $from = $_ if ($_); - print "Emails will be sent from: ", $from, "\n"; + $sender = $_ if ($_); + print "Emails will be sent from: ", $sender, "\n"; $prompting++; } @@ -289,7 +289,7 @@ sub expand_aliases { } @to = expand_aliases(@to); -@to = (map { sanitize_address_rfc822($_) } @to); +@to = (map { sanitize_address($_) } @to); @initial_cc = expand_aliases(@initial_cc); @bcclist = expand_aliases(@bcclist); @@ -330,7 +330,7 @@ if ($compose) { # effort to have it be unique open(C,">",$compose_filename) or die "Failed to open for writing $compose_filename: $!"; - print C "From $from # This line is ignored.\n"; + print C "From $sender # This line is ignored.\n"; printf C "Subject: %s\n\n", $initial_subject; printf C <<EOT; GIT: Please enter your email below. @@ -408,8 +408,8 @@ sub extract_valid_address { # check for a local address: return $address if ($address =~ /^($local_part_regexp)$/); + $address =~ s/^\s*<(.*)>\s*$/$1/; if ($have_email_valid) { - $address =~ s/^\s*<(.*)>\s*$/$1/; return scalar Email::Valid->address($address); } else { # less robust/correct than the monster regexp in Email::Valid, @@ -433,11 +433,11 @@ sub make_message_id my $date = time; my $pseudo_rand = int (rand(4200)); my $du_part; - for ($from, $committer, $author) { - $du_part = extract_valid_address($_); - last if ($du_part ne ''); + for ($sender, $repocommitter, $repoauthor) { + $du_part = extract_valid_address(sanitize_address($_)); + last if (defined $du_part and $du_part ne ''); } - if ($du_part eq '') { + if (not defined $du_part or $du_part eq '') { use Sys::Hostname qw(); $du_part = 'user@' . Sys::Hostname::hostname(); } @@ -459,22 +459,41 @@ sub unquote_rfc2047 { return "$_"; } -# If an address contains a . in the name portion, the name must be quoted. -sub sanitize_address_rfc822 +# use the simplest quoting being able to handle the recipient +sub sanitize_address { my ($recipient) = @_; - my ($recipient_name) = ($recipient =~ /^(.*?)\s+</); - if ($recipient_name && $recipient_name =~ /\./ && $recipient_name !~ /^".*"$/) { - my ($name, $addr) = ($recipient =~ /^(.*?)(\s+<.*)/); - $recipient = "\"$name\"$addr"; + my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/); + + if (not $recipient_name) { + return "$recipient"; + } + + # if recipient_name is already quoted, do nothing + if ($recipient_name =~ /^(".*"|=\?utf-8\?q\?.*\?=)$/) { + return $recipient; + } + + # rfc2047 is needed if a non-ascii char is included + if ($recipient_name =~ /[^[:ascii:]]/) { + $recipient_name =~ s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg; + $recipient_name =~ s/(.*)/=\?utf-8\?q\?$1\?=/; } - return $recipient; + + # double quotes are needed if specials or CTLs are included + elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) { + $recipient_name =~ s/(["\\\r])/\\$1/; + $recipient_name = "\"$recipient_name\""; + } + + return "$recipient_name $recipient_addr"; + } sub send_message { my @recipients = unique_email_list(@to); - @cc = (map { sanitize_address_rfc822($_) } @cc); + @cc = (map { sanitize_address($_) } @cc); my $to = join (",\n\t", @recipients); @recipients = unique_email_list(@recipients,@cc,@bcclist); @recipients = (map { extract_valid_address($_) } @recipients); @@ -489,10 +508,10 @@ sub send_message if ($cc ne '') { $ccline = "\nCc: $cc"; } - $from = sanitize_address_rfc822($from); + my $sanitized_sender = sanitize_address($sender); make_message_id(); - my $header = "From: $from + my $header = "From: $sanitized_sender To: $to${ccline} Subject: $subject Date: $date @@ -509,7 +528,7 @@ X-Mailer: git-send-email $gitversion } my @sendmail_parameters = ('-i', @recipients); - my $raw_from = $from; + my $raw_from = $sanitized_sender; $raw_from = $envelope_sender if (defined $envelope_sender); $raw_from = extract_valid_address($raw_from); unshift (@sendmail_parameters, @@ -546,7 +565,7 @@ X-Mailer: git-send-email $gitversion } else { print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n"; } - print "From: $from\nSubject: $subject\nCc: $cc\nTo: $to\n\n"; + print "From: $sanitized_sender\nSubject: $subject\nCc: $cc\nTo: $to\n\n"; if ($smtp) { print "Result: ", $smtp->code, ' ', ($smtp->message =~ /\n([^\n]+\n)$/s), "\n"; @@ -563,7 +582,7 @@ $subject = $initial_subject; foreach my $t (@files) { open(F,"<",$t) or die "can't open file $t"; - my $author_not_sender = undef; + my $author = undef; @cc = @initial_cc; @xh = (); my $input_format = undef; @@ -585,12 +604,11 @@ foreach my $t (@files) { $subject = $1; } elsif (/^(Cc|From):\s+(.*)$/) { - if (unquote_rfc2047($2) eq $from) { - $from = $2; + if (unquote_rfc2047($2) eq $sender) { next if ($suppress_from); } elsif ($1 eq 'From') { - $author_not_sender = $2; + $author = unquote_rfc2047($2); } printf("(mbox) Adding cc: %s from line '%s'\n", $2, $_) unless $quiet; @@ -634,9 +652,8 @@ foreach my $t (@files) { } } close F; - if (defined $author_not_sender) { - $author_not_sender = unquote_rfc2047($author_not_sender); - $message = "From: $author_not_sender\n\n$message"; + if (defined $author) { + $message = "From: $author\n\n$message"; } @@ -315,7 +315,8 @@ static void handle_internal_command(int argc, const char **argv) { "branch", cmd_branch, RUN_SETUP }, { "bundle", cmd_bundle }, { "cat-file", cmd_cat_file, RUN_SETUP }, - { "checkout-index", cmd_checkout_index, RUN_SETUP }, + { "checkout-index", cmd_checkout_index, + RUN_SETUP | NEED_WORK_TREE}, { "check-ref-format", cmd_check_ref_format }, { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE }, { "cherry", cmd_cherry, RUN_SETUP }, diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 498b936dd4..f282a677aa 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1515,6 +1515,7 @@ sub git_get_projects_list { File::Find::find({ follow_fast => 1, # follow symbolic links + follow_skip => 2, # ignore duplicates dangling_symlinks => 0, # ignore dangling symlinks, silently wanted => sub { # skip project-list toplevel, if we get it. @@ -5365,7 +5366,7 @@ sub git_feed { # log/feed of current (HEAD) branch, log of given branch, history of file/directory my $head = $hash || 'HEAD'; - my @commitlist = parse_commits($head, 150); + my @commitlist = parse_commits($head, 150, 0, undef, $file_name); my %latest_commit; my %latest_date; diff --git a/merge-recursive.c b/merge-recursive.c index c8539ec0ba..f7d1b84999 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -216,13 +216,19 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, */ static int index_only = 0; +static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) +{ + parse_tree(tree); + init_tree_desc(desc, tree->buffer, tree->size); +} + static int git_merge_trees(int index_only, struct tree *common, struct tree *head, struct tree *merge) { int rc; - struct object_list *trees = NULL; + struct tree_desc t[3]; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); @@ -234,11 +240,11 @@ static int git_merge_trees(int index_only, opts.head_idx = 2; opts.fn = threeway_merge; - object_list_append(&common->object, &trees); - object_list_append(&head->object, &trees); - object_list_append(&merge->object, &trees); + init_tree_desc_from_tree(t+0, common); + init_tree_desc_from_tree(t+1, head); + init_tree_desc_from_tree(t+2, merge); - rc = unpack_trees(trees, &opts); + rc = unpack_trees(3, t, &opts); cache_tree_free(&active_cache_tree); return rc; } @@ -31,8 +31,11 @@ void setup_pager(void) if (!isatty(1)) return; - if (!pager) + if (!pager) { + if (!pager_program) + git_config(git_default_config); pager = pager_program; + } if (!pager) pager = getenv("PAGER"); if (!pager) diff --git a/read-cache.c b/read-cache.c index e060392d1d..865369df0e 100644 --- a/read-cache.c +++ b/read-cache.c @@ -665,7 +665,7 @@ static int check_file_directory_conflict(struct index_state *istate, return retval + has_dir_name(istate, ce, pos, ok_to_replace); } -int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option) +static int add_index_entry_with_check(struct index_state *istate, struct cache_entry *ce, int option) { int pos; int ok_to_add = option & ADD_CACHE_OK_TO_ADD; @@ -707,6 +707,22 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags)); pos = -pos-1; } + return pos + 1; +} + +int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option) +{ + int pos; + + if (option & ADD_CACHE_JUST_APPEND) + pos = istate->cache_nr; + else { + int ret; + ret = add_index_entry_with_check(istate, ce, option); + if (ret <= 0) + return ret; + pos = ret - 1; + } /* Make sure the array is big enough .. */ if (istate->cache_nr == istate->cache_alloc) { @@ -717,7 +733,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti /* Add it in.. */ istate->cache_nr++; - if (istate->cache_nr > pos) + if (istate->cache_nr > pos + 1) memmove(istate->cache + pos + 1, istate->cache + pos, (istate->cache_nr - pos - 1) * sizeof(ce)); @@ -869,6 +869,7 @@ static int repack_without_ref(const char *refname) die("too long a refname '%s'", list->name); write_or_die(fd, line, len); } + close(fd); return commit_lock_file(&packlock); } @@ -107,7 +107,7 @@ void verify_non_filename(const char *prefix, const char *arg) if (!lstat(name, &st)) die("ambiguous argument '%s': both revision and filename\n" "Use '--' to separate filenames from revisions", arg); - if (errno != ENOENT) + if (errno != ENOENT && errno != ENOTDIR) die("'%s': %s", arg, strerror(errno)); } @@ -189,47 +189,21 @@ int is_inside_work_tree(void) } /* - * If no worktree was given, and we are outside of a default work tree, - * now is the time to set it. - * - * In other words, if the user calls git with something like - * - * git --git-dir=/some/where/else/.git bla - * - * default to /some/where/else as working directory; if the specified - * git-dir does not end in "/.git", the cwd is used as working directory. + * set_work_tree() is only ever called if you set GIT_DIR explicitely. + * The old behaviour (which we retain here) is to set the work tree root + * to the cwd, unless overridden by the config, the command line, or + * GIT_WORK_TREE. */ -const char *set_work_tree(const char *dir) +static const char *set_work_tree(const char *dir) { - char dir_buffer[PATH_MAX]; - static char buffer[PATH_MAX + 1], *rel = NULL; - int len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1; - - /* strip the variable 'dir' of the postfix "/.git" if it has it */ - len = strlen(dir); - if (len > postfix_len && !strcmp(dir + len - postfix_len, - "/" DEFAULT_GIT_DIR_ENVIRONMENT)) { - strncpy(dir_buffer, dir, len - postfix_len); - - /* are we inside the default work tree? */ - rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer); - } - /* if rel is set, the cwd is _not_ the current working tree */ - if (rel && *rel) { - if (!is_absolute_path(dir)) - set_git_dir(make_absolute_path(dir)); - dir = dir_buffer; - chdir(dir); - strcat(rel, "/"); - inside_git_dir = 0; - } else { - rel = NULL; - dir = getcwd(buffer, sizeof(buffer)); - } - git_work_tree_cfg = xstrdup(dir); + char buffer[PATH_MAX + 1]; + + if (!getcwd(buffer, sizeof(buffer))) + die ("Could not get the current working directory"); + git_work_tree_cfg = xstrdup(buffer); inside_work_tree = 1; - return rel; + return NULL; } /* diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index bea40cba8d..e474b3f1d5 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -28,6 +28,8 @@ test_rev_parse() { [ $# -eq 0 ] && return } +# label is-bare is-inside-git is-inside-work prefix + test_rev_parse toplevel false false true '' cd .git || exit 1 @@ -53,13 +55,13 @@ export GIT_DIR=../.git export GIT_CONFIG="$(pwd)"/../.git/config git config core.bare false -test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/ +test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true '' git config core.bare true test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false '' git config --unset core.bare -test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/ +test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true '' mv ../.git ../repo.git || exit 1 export GIT_DIR=../repo.git diff --git a/t/t3700-add.sh b/t/t3700-add.sh index b52fde8577..213e9249da 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -131,8 +131,8 @@ test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over st ( echo "100644 $(git hash-object -w stage1) 1 file" echo "100755 $(git hash-object -w stage2) 2 file" - echo "100644 $(printf $s | git hash-object -w -t blob --stdin) 1 symlink" - echo "120000 $(printf $s | git hash-object -w -t blob --stdin) 2 symlink" + echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink" + echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2 symlink" ) | git update-index --index-info && git config core.filemode 0 && git config core.symlinks 0 && diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 426017e1d0..439430f569 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -145,4 +145,12 @@ test_expect_success 'bundle does not prerequisite objects' ' test 4 = $(git verify-pack -v bundle.pack | wc -l) ' +test_expect_success 'bundle should be able to create a full history' ' + + cd "$D" && + git tag -a -m '1.0' v1.0 master && + git bundle create bundle4 v1.0 + +' + test_done @@ -1,4 +1,5 @@ #include "cache.h" +#include "cache-tree.h" #include "tree.h" #include "blob.h" #include "commit.h" @@ -7,7 +8,7 @@ const char *tree_type = "tree"; -static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) +static int read_one_entry_opt(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, int opt) { int len; unsigned int size; @@ -25,7 +26,23 @@ static int read_one_entry(const unsigned char *sha1, const char *base, int basel memcpy(ce->name, base, baselen); memcpy(ce->name + baselen, pathname, len+1); hashcpy(ce->sha1, sha1); - return add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); + return add_cache_entry(ce, opt); +} + +static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) +{ + return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage, + ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); +} + +/* + * This is used when the caller knows there is no existing entries at + * the stage that will conflict with the entry being added. + */ +static int read_one_entry_quick(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) +{ + return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage, + ADD_CACHE_JUST_APPEND); } static int match_tree_entry(const char *base, int baselen, const char *path, unsigned int mode, const char **paths) @@ -119,9 +136,55 @@ int read_tree_recursive(struct tree *tree, return 0; } +static int cmp_cache_name_compare(const void *a_, const void *b_) +{ + const struct cache_entry *ce1, *ce2; + + ce1 = *((const struct cache_entry **)a_); + ce2 = *((const struct cache_entry **)b_); + return cache_name_compare(ce1->name, ntohs(ce1->ce_flags), + ce2->name, ntohs(ce2->ce_flags)); +} + int read_tree(struct tree *tree, int stage, const char **match) { - return read_tree_recursive(tree, "", 0, stage, match, read_one_entry); + read_tree_fn_t fn = NULL; + int i, err; + + /* + * Currently the only existing callers of this function all + * call it with stage=1 and after making sure there is nothing + * at that stage; we could always use read_one_entry_quick(). + * + * But when we decide to straighten out git-read-tree not to + * use unpack_trees() in some cases, this will probably start + * to matter. + */ + + /* + * See if we have cache entry at the stage. If so, + * do it the original slow way, otherwise, append and then + * sort at the end. + */ + for (i = 0; !fn && i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce_stage(ce) == stage) + fn = read_one_entry; + } + + if (!fn) + fn = read_one_entry_quick; + err = read_tree_recursive(tree, "", 0, stage, match, fn); + if (fn == read_one_entry || err) + return err; + + /* + * Sort the cache entry -- we need to nuke the cache tree, though. + */ + cache_tree_free(&active_cache_tree); + qsort(active_cache, active_nr, sizeof(active_cache[0]), + cmp_cache_name_compare); + return 0; } struct tree *lookup_tree(const unsigned char *sha1) diff --git a/unpack-trees.c b/unpack-trees.c index 3b32718436..ccfeb6e245 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -16,19 +16,13 @@ struct tree_entry_list { const unsigned char *sha1; }; -static struct tree_entry_list *create_tree_entry_list(struct tree *tree) +static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc) { - struct tree_desc desc; struct name_entry one; struct tree_entry_list *ret = NULL; struct tree_entry_list **list_p = &ret; - if (!tree->object.parsed) - parse_tree(tree); - - init_tree_desc(&desc, tree->buffer, tree->size); - - while (tree_entry(&desc, &one)) { + while (tree_entry(desc, &one)) { struct tree_entry_list *entry; entry = xmalloc(sizeof(struct tree_entry_list)); @@ -64,10 +58,17 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2) return ret; } +static inline void remove_entry(int remove) +{ + if (remove >= 0) + remove_cache_entry_at(remove); +} + static int unpack_trees_rec(struct tree_entry_list **posns, int len, const char *base, struct unpack_trees_options *o, struct tree_entry_list *df_conflict_list) { + int remove; int baselen = strlen(base); int src_size = len + 1; int i_stk = i_stk; @@ -151,10 +152,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, subposns = xcalloc(len, sizeof(struct tree_list_entry *)); + remove = -1; if (cache_name && !strcmp(cache_name, first)) { any_files = 1; src[0] = active_cache[o->pos]; - remove_cache_entry_at(o->pos); + remove = o->pos; } for (i = 0; i < len; i++) { @@ -173,9 +175,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, if (S_ISDIR(posns[i]->mode)) { struct tree *tree = lookup_tree(posns[i]->sha1); + struct tree_desc t; any_dirs = 1; parse_tree(tree); - subposns[i] = create_tree_entry_list(tree); + init_tree_desc(&t, tree->buffer, tree->size); + subposns[i] = create_tree_entry_list(&t); posns[i] = posns[i]->next; src[i + o->merge] = o->df_conflict_entry; continue; @@ -218,13 +222,14 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, printf("\n"); } #endif - ret = o->fn(src, o); + ret = o->fn(src, o, remove); #if DBRT_DEBUG > 1 printf("Added %d entries\n", ret); #endif o->pos += ret; } else { + remove_entry(remove); for (i = 0; i < src_size; i++) { if (src[i]) { add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); @@ -331,12 +336,10 @@ static void check_updates(struct cache_entry **src, int nr, stop_progress(&progress);; } -int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) +int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) { - unsigned len = object_list_length(trees); struct tree_entry_list **posns; int i; - struct object_list *posn = trees; struct tree_entry_list df_conflict_list; static struct cache_entry *dfc; @@ -356,10 +359,9 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) if (len) { posns = xmalloc(len * sizeof(struct tree_entry_list *)); - for (i = 0; i < len; i++) { - posns[i] = create_tree_entry_list((struct tree *) posn->item); - posn = posn->next; - } + for (i = 0; i < len; i++) + posns[i] = create_tree_entry_list(t+i); + if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "", o, &df_conflict_list)) return -1; @@ -407,6 +409,15 @@ static void verify_uptodate(struct cache_entry *ce, unsigned changed = ce_match_stat(ce, &st, 1); if (!changed) return; + /* + * NEEDSWORK: the current default policy is to allow + * submodule to be out of sync wrt the supermodule + * index. This needs to be tightened later for + * submodules that are marked to be automatically + * checked out. + */ + if (S_ISGITLINK(ntohl(ce->ce_mode))) + return; errno = 0; } if (errno == ENOENT) @@ -639,7 +650,8 @@ static void show_stage_entry(FILE *o, #endif int threeway_merge(struct cache_entry **stages, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *index; struct cache_entry *head; @@ -717,8 +729,10 @@ int threeway_merge(struct cache_entry **stages, } /* #1 */ - if (!head && !remote && any_anc_missing) + if (!head && !remote && any_anc_missing) { + remove_entry(remove); return 0; + } /* Under the new "aggressive" rule, we resolve mostly trivial * cases that we historically had git-merge-one-file resolve. @@ -750,6 +764,7 @@ int threeway_merge(struct cache_entry **stages, if ((head_deleted && remote_deleted) || (head_deleted && remote && remote_match) || (remote_deleted && head && head_match)) { + remove_entry(remove); if (index) return deleted_entry(index, index, o); else if (ce && !head_deleted) @@ -772,6 +787,7 @@ int threeway_merge(struct cache_entry **stages, verify_uptodate(index, o); } + remove_entry(remove); o->nontrivial_merge = 1; /* #2, #3, #4, #6, #7, #9, #10, #11. */ @@ -807,7 +823,8 @@ int threeway_merge(struct cache_entry **stages, * */ int twoway_merge(struct cache_entry **src, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *current = src[0]; struct cache_entry *oldtree = src[1]; @@ -835,6 +852,7 @@ int twoway_merge(struct cache_entry **src, } else if (oldtree && !newtree && same(current, oldtree)) { /* 10 or 11 */ + remove_entry(remove); return deleted_entry(oldtree, current, o); } else if (oldtree && newtree && @@ -844,6 +862,7 @@ int twoway_merge(struct cache_entry **src, } else { /* all other failures */ + remove_entry(remove); if (oldtree) reject_merge(oldtree); if (current) @@ -855,8 +874,8 @@ int twoway_merge(struct cache_entry **src, } else if (newtree) return merged_entry(newtree, current, o); - else - return deleted_entry(oldtree, current, o); + remove_entry(remove); + return deleted_entry(oldtree, current, o); } /* @@ -866,7 +885,8 @@ int twoway_merge(struct cache_entry **src, * stage0 does not have anything there. */ int bind_merge(struct cache_entry **src, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; @@ -889,7 +909,8 @@ int bind_merge(struct cache_entry **src, * - take the stat information from stage0, take the data from stage1 */ int oneway_merge(struct cache_entry **src, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; @@ -898,8 +919,10 @@ int oneway_merge(struct cache_entry **src, return error("Cannot do a oneway merge of %d trees", o->merge_size); - if (!a) + if (!a) { + remove_entry(remove); return deleted_entry(old, old, o); + } if (old && same(old, a)) { if (o->reset) { struct stat st; diff --git a/unpack-trees.h b/unpack-trees.h index fee7da4382..5517faafad 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -4,7 +4,8 @@ struct unpack_trees_options; typedef int (*merge_fn_t)(struct cache_entry **src, - struct unpack_trees_options *options); + struct unpack_trees_options *options, + int remove); struct unpack_trees_options { int reset; @@ -26,12 +27,12 @@ struct unpack_trees_options { struct cache_entry *df_conflict_entry; }; -extern int unpack_trees(struct object_list *trees, +extern int unpack_trees(unsigned n, struct tree_desc *t, struct unpack_trees_options *options); -int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o); -int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o); -int bind_merge(struct cache_entry **src, struct unpack_trees_options *o); -int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o); +int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int); +int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int); +int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int); +int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int); #endif |