summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/CodingGuidelines12
-rw-r--r--Documentation/RelNotes-1.5.4.4.txt26
-rw-r--r--Documentation/RelNotes-1.5.5.txt91
-rw-r--r--Documentation/config.txt23
-rw-r--r--Documentation/diff-options.txt8
-rw-r--r--Documentation/git-am.txt2
-rw-r--r--Documentation/git-branch.txt32
-rw-r--r--Documentation/git-checkout.txt24
-rw-r--r--Documentation/git-cherry-pick.txt2
-rw-r--r--Documentation/git-describe.txt5
-rw-r--r--Documentation/git-filter-branch.txt15
-rw-r--r--Documentation/git-format-patch.txt25
-rw-r--r--Documentation/git-gc.txt5
-rw-r--r--Documentation/git-pack-objects.txt2
-rw-r--r--Documentation/git-rebase.txt1
-rw-r--r--Documentation/git-rev-list.txt4
-rw-r--r--Documentation/git-svn.txt4
-rw-r--r--Documentation/rev-list-options.txt7
-rw-r--r--Documentation/technical/api-diff.txt2
-rw-r--r--Documentation/urls.txt23
-rw-r--r--Makefile25
-rw-r--r--alias.c22
-rw-r--r--branch.c152
-rw-r--r--branch.h24
-rw-r--r--builtin-blame.c3
-rw-r--r--builtin-branch.c158
-rw-r--r--builtin-checkout.c568
-rw-r--r--builtin-commit.c3
-rw-r--r--builtin-describe.c35
-rw-r--r--builtin-diff.c11
-rwxr-xr-xbuiltin-fast-export.c5
-rw-r--r--builtin-fetch-pack.c4
-rw-r--r--builtin-gc.c4
-rw-r--r--builtin-http-fetch.c3
-rw-r--r--builtin-init-db.c21
-rw-r--r--builtin-log.c338
-rw-r--r--builtin-merge-file.c2
-rw-r--r--builtin-merge-recursive.c (renamed from merge-recursive.c)59
-rw-r--r--builtin-pack-objects.c17
-rw-r--r--builtin-push.c9
-rw-r--r--builtin-read-tree.c3
-rw-r--r--builtin-reflog.c5
-rw-r--r--builtin-rerere.c19
-rw-r--r--builtin-reset.c28
-rw-r--r--builtin-rev-list.c3
-rw-r--r--builtin-rev-parse.c2
-rw-r--r--builtin-revert.c3
-rw-r--r--builtin-send-pack.c19
-rw-r--r--builtin-shortlog.c149
-rw-r--r--builtin-tag.c3
-rw-r--r--builtin-verify-tag.c2
-rw-r--r--builtin.h2
-rw-r--r--bundle.c6
-rw-r--r--cache.h14
-rw-r--r--commit.h15
-rw-r--r--config.c8
-rw-r--r--connect.c3
-rwxr-xr-xcontrib/completion/git-completion.bash6
-rw-r--r--contrib/emacs/git.el4
-rwxr-xr-xcontrib/examples/git-checkout.sh (renamed from git-checkout.sh)0
-rwxr-xr-xcontrib/fast-import/git-p4216
-rw-r--r--copy.c21
-rw-r--r--daemon.c18
-rw-r--r--date.c6
-rw-r--r--diff-lib.c19
-rw-r--r--diff.c130
-rw-r--r--diff.h3
-rw-r--r--diffcore-rename.c7
-rw-r--r--dir.c3
-rw-r--r--environment.c1
-rwxr-xr-xgit-am.sh2
-rwxr-xr-xgit-bisect.sh14
-rwxr-xr-xgit-clone.sh37
-rw-r--r--git-gui/lib/about.tcl3
-rw-r--r--git-gui/lib/error.tcl2
-rw-r--r--git-gui/lib/spellcheck.tcl75
-rw-r--r--git-gui/po/de.po70
-rw-r--r--git-gui/po/git-gui.pot32
-rw-r--r--git-gui/po/glossary/de.po4
-rwxr-xr-xgit-rebase--interactive.sh4
-rwxr-xr-xgit-rebase.sh5
-rwxr-xr-xgit-svn.perl1
-rw-r--r--git.c20
-rwxr-xr-xgitweb/gitweb.perl73
-rw-r--r--hash-object.c12
-rw-r--r--help.c152
-rw-r--r--http-push.c18
-rw-r--r--imap-send.c5
-rw-r--r--interpolate.c3
-rw-r--r--log-tree.c126
-rw-r--r--log-tree.h2
-rw-r--r--merge-recursive.h20
-rw-r--r--path.c4
-rw-r--r--pretty.c35
-rw-r--r--read-cache.c10
-rw-r--r--receive-pack.c2
-rw-r--r--refs.c25
-rw-r--r--remote.c250
-rw-r--r--remote.h4
-rw-r--r--revision.c33
-rw-r--r--revision.h2
-rw-r--r--run-command.c28
-rw-r--r--run-command.h20
-rw-r--r--setup.c3
-rw-r--r--sha1_name.c19
-rw-r--r--shortlog.h26
-rwxr-xr-xt/t0000-basic.sh2
-rwxr-xr-xt/t0050-filesystem.sh93
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh43
-rwxr-xr-xt/t2008-checkout-subdir.sh8
-rwxr-xr-xt/t2009-checkout-statinfo.sh52
-rwxr-xr-xt/t3200-branch.sh22
-rwxr-xr-xt/t3404-rebase-interactive.sh2
-rwxr-xr-xt/t3407-rebase-abort.sh59
-rwxr-xr-xt/t4013-diff-various.sh1
-rw-r--r--t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^100
-rwxr-xr-xt/t4014-format-patch.sh113
-rwxr-xr-xt/t4027-diff-submodule.sh53
-rwxr-xr-xt/t5303-hash-object.sh35
-rwxr-xr-xt/t5505-remote.sh3
-rwxr-xr-xt/t5516-fetch-push.sh74
-rwxr-xr-xt/t5701-clone-local.sh45
-rwxr-xr-xt/t6023-merge-file.sh20
-rwxr-xr-xt/t6024-recursive-merge.sh2
-rwxr-xr-xt/t6029-merge-subtree.sh79
-rwxr-xr-xt/t6030-bisect-porcelain.sh2
-rwxr-xr-xt/t7201-co.sh83
-rw-r--r--t/test-lib.sh19
-rw-r--r--templates/Makefile6
-rw-r--r--thread-utils.c48
-rw-r--r--thread-utils.h6
-rw-r--r--transport.c9
-rw-r--r--unpack-trees.c154
-rw-r--r--unpack-trees.h2
-rw-r--r--ws.c2
-rw-r--r--xdiff-interface.c3
-rw-r--r--xdiff/xdiff.h1
-rw-r--r--xdiff/xmerge.c70
139 files changed, 3698 insertions, 1149 deletions
diff --git a/.gitignore b/.gitignore
index 165b256..4ff2fec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+GIT-BUILD-OPTIONS
GIT-CFLAGS
GIT-GUI-VARS
GIT-VERSION-FILE
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 3b042db..994eb91 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -53,6 +53,18 @@ For shell scripts specifically (not exhaustive):
- We do not write the noiseword "function" in front of shell
functions.
+ - As to use of grep, stick to a subset of BRE (namely, no \{m,n\},
+ [::], [==], nor [..]) for portability.
+
+ - We do not use \{m,n\};
+
+ - We do not use -E;
+
+ - We do not use ? nor + (which are \{0,1\} and \{1,\}
+ respectively in BRE) but that goes without saying as these
+ are ERE elements not BRE (note that \? and \+ are not even part
+ of BRE -- making them accessible from BRE is a GNU extension).
+
For C programs:
- We use tabs to indent, and interpret tabs as taking up to
diff --git a/Documentation/RelNotes-1.5.4.4.txt b/Documentation/RelNotes-1.5.4.4.txt
new file mode 100644
index 0000000..5bfdb35
--- /dev/null
+++ b/Documentation/RelNotes-1.5.4.4.txt
@@ -0,0 +1,26 @@
+GIT v1.5.4.4 Release Notes
+==========================
+
+Fixes since v1.5.4.3
+--------------------
+
+ * "git cvsexportcommit -w $cvsdir" misbehaved when GIT_DIR is set to a
+ relative directory.
+
+ * "git http-push" had an invalid memory access that could lead it to
+ segfault.
+
+ * When "git rebase -i" gave control back to the user for a commit that is
+ marked to be edited, it just said "modify it with commit --amend",
+ without saying what to do to continue after modifying it. Give an
+ explicit instruction to run "rebase --continue" to be more helpful.
+
+ * "git send-email" in 1.5.4.3 issued a bogus empty In-Reply-To: header.
+
+Also included are a handful documentation updates.
+
+---
+exec >/var/tmp/1
+echo O=$(git describe maint)
+O=v1.5.4.3
+git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes-1.5.5.txt
index c8b4f72..849b6b9 100644
--- a/Documentation/RelNotes-1.5.5.txt
+++ b/Documentation/RelNotes-1.5.5.txt
@@ -26,21 +26,85 @@ Updates since v1.5.4
* You can be warned when core.autocrlf conversion is applied in
such a way that results in an irreversible conversion.
+ * A catch-all "color.ui" configuration variable can be used to
+ enable coloring of all color-capable commands, instead of
+ individual ones such as "color.status" and "color.branch".
+
+ * The commands refused to take absolute pathnames where they
+ require pathnames relative to the work tree or the current
+ subdirectory. They now can take absolute pathnames in such a
+ case as long as the pathnames do not refer outside of the
+ work tree. E.g. "git add $(pwd)/foo" now works.
+
+ * Error messages used to be sent to stderr, only to get hidden,
+ when $PAGER was in use. They now are sent to stdout along
+ with the command output to be shown in the $PAGER.
+
* A pattern "foo/" in .gitignore file now matches a directory
"foo". Pattern "foo" also matches as before.
+ * bash completion's prompt helper function can talk about
+ operation in-progress (e.g. merge, rebase, etc.).
+
+ * Configuration variables "url.<usethis>.insteadof = <otherurl>" can be
+ used to tell "git-fetch" and "git-push" to use different URL than what
+ is given from the command line.
+
+ * "git push <somewhere> HEAD" and "git push <somewhere> +HEAD" works as
+ expected; they push the current branch (and only the current branch).
+ In addition, HEAD can be written as the value of "remote.<there>.push"
+ configuration variable.
+
+ * "git add -i" behaves better even before you make an initial commit.
+
+ * After "git apply --whitespace=fix" fixes whitespace errors in a patch,
+ a line before the fix can appear as a context or preimage line in a
+ later patch, causing the patch not to apply. The command now knows to
+ see through whitespace fixes done to context lines to successfully
+ apply such a patch series.
+
+ * "git branch" (and "git checkout -b") to branch from a local branch can
+ optionally set "branch.<name>.merge" to mark the new branch to build on
+ the other local branch, when "branch.autosetupmerge" is set to
+ "always". By default, this does not happen when branching from a local
+ branch.
+
+ * "git checkout" to switch to a branch that has "branch.<name>.merge" set
+ (i.e. marked to build on another branch) reports how much the branch
+ and the other branch diverged.
+
+ * When "git checkout" has to update a lot of paths, it used to be silent
+ for 4 seconds before it showed any progress report. It is now a bit
+ more impatient and starts showing progress report early.
+
+ * "git commit" learned a new hook "prepare-commit-msg" that can
+ inspect what is going to be committed and prepare the commit
+ log message template to be edited.
+
* "git describe" learned to limit the tags to be used for
naming with --match option.
* "git describe --contains" now barfs when the named commit
cannot be described.
- * bash completion's prompt helper function can talk about
- operation in-progress (e.g. merge, rebase, etc.).
+ * "git describe --exact-match" describes only commits that are tagged.
- * "git commit" learned a new hook "prepare-commit-msg" that can
- inspect what is going to be committed and prepare the commit
- log message template to be edited.
+ * "git diff" learned "--relative" option to limit and output paths
+ relative to the current directory when working in a subdirectory.
+
+ * "git diff" learned "--dirstat" option to show birds-eye-summary of
+ changes more concisely than "--diffstat".
+
+ * "git format-patch" learned --cover-letter option to generate a cover
+ letter template.
+
+ * "git grep" now knows "--name-only" is a synonym for the "-l" option.
+
+ * "git help <alias>" now reports "'git <alias>' is alias to <what>",
+ instead of saying "No manual entry for git-<alias>".
+
+ * "git log --grep=<what>" learned "--fixed-strings" option to look for
+ <what> without treating it as a regular expression.
* "git gui" learned an auto-spell checking.
@@ -50,6 +114,9 @@ Updates since v1.5.4
* "git send-email" learned an easier way to suppress CC
recipients.
+ * When the configuration variable "pack.threads" is set to 0, "git
+ repack" auto detects the number of CPUs and uses that many threads.
+
* Various "git cvsimport", "git cvsexportcommit", "git svn" and
"git p4" improvements.
@@ -61,6 +128,15 @@ Updates since v1.5.4
* It is now easier to write test scripts that records known
breakages.
+ * "git checkout" is rewritten in C.
+
+ * Two conflict hunks that are separated by a very short span of common
+ lines are now coalesced into one larger hunk, to make the result easier
+ to read.
+
+ * Run-command API's use of file descriptors is documented clearer and
+ is more consistent now.
+
Fixes since v1.5.4
------------------
@@ -68,11 +144,8 @@ Fixes since v1.5.4
All of the fixes in v1.5.4 maintenance series are included in
this release, unless otherwise noted.
-
---
exec >/var/tmp/1
-O=v1.5.4
-O=v1.5.4.2-122-g7cb97da
+O=v1.5.4.3-339-g7cf7f54
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
-
diff --git a/Documentation/config.txt b/Documentation/config.txt
index fb6dae0..4027726 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -379,10 +379,14 @@ apply.whitespace::
branch.autosetupmerge::
Tells `git-branch` and `git-checkout` to setup new branches
- so that linkgit:git-pull[1] will appropriately merge from that
- remote branch. Note that even if this option is not set,
+ so that linkgit:git-pull[1] will appropriately merge from the
+ starting point branch. Note that even if this option is not set,
this behavior can be chosen per-branch using the `--track`
- and `--no-track` options. This option defaults to true.
+ and `--no-track` options. The valid settings are: `false` -- no
+ automatic setup is done; `true` -- automatic setup is done when the
+ starting point is a remote branch; `always` -- automatic setup is
+ done when the starting point is either a local branch or remote
+ branch. This option defaults to true.
branch.<name>.remote::
When in branch <name>, it tells `git fetch` which remote to fetch.
@@ -812,6 +816,8 @@ pack.threads::
warning. This is meant to reduce packing time on multiprocessor
machines. The required amount of memory for the delta search window
is however multiplied by the number of threads.
+ Specifying 0 will cause git to auto-detect the number of CPU's
+ and set the number of threads accordingly.
pack.indexVersion::
Specify the default pack index version. Valid values are 1 for
@@ -897,6 +903,17 @@ tar.umask::
archiving user's umask will be used instead. See umask(2) and
linkgit:git-archive[1].
+url.<base>.insteadOf::
+ Any URL that starts with this value will be rewritten to
+ start, instead, with <base>. In cases where some site serves a
+ large number of repositories, and serves them with multiple
+ access methods, and some users need to use different access
+ methods, this feature allows people to specify any of the
+ equivalent URLs and have git automatically rewrite the URL to
+ the best alternative for the particular user, even for a
+ never-before-seen repository on the site. When more than one
+ insteadOf strings match a given URL, the longest match is used.
+
user.email::
Your email address to be recorded in any newly created commits.
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 8d35cbd..8dc5b00 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -170,6 +170,14 @@ endif::git-format-patch[]
Swap two inputs; that is, show differences from index or
on-disk file to tree contents.
+--relative[=<path>]::
+ When run from a subdirectory of the project, it can be
+ told to exclude changes outside the directory and show
+ pathnames relative to it with this option. When you are
+ not in a subdirectory (e.g. in a bare repository), you
+ can name which subdirectory to make the output relative
+ to by giving a <path> as an argument.
+
--text::
Treat all files as text.
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 2ffba21..e640fc7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -138,7 +138,7 @@ aborts in the middle,. You can recover from this in one of two ways:
The command refuses to process new mailboxes while `.dotest`
directory exists, so if you decide to start over from scratch,
-run `rm -f .dotest` before running the command with mailbox
+run `rm -f -r .dotest` before running the command with mailbox
names.
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 7e8874a..6f07a17 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -35,11 +35,10 @@ working tree to it; use "git checkout <newbranch>" to switch to the
new branch.
When a local branch is started off a remote branch, git sets up the
-branch so that linkgit:git-pull[1] will appropriately merge from that
-remote branch. If this behavior is not desired, it is possible to
-disable it using the global `branch.autosetupmerge` configuration
-flag. That setting can be overridden by using the `--track`
-and `--no-track` options.
+branch so that linkgit:git-pull[1] will appropriately merge from
+the remote branch. This behavior may be changed via the global
+`branch.autosetupmerge` configuration flag. That setting can be
+overridden by using the `--track` and `--no-track` options.
With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
If <oldbranch> had a corresponding reflog, it is renamed to match
@@ -105,20 +104,19 @@ OPTIONS
Display the full sha1s in output listing rather than abbreviating them.
--track::
- Set up configuration so that git-pull will automatically
- retrieve data from the remote branch. Use this if you always
- pull from the same remote branch into the new branch, or if you
- don't want to use "git pull <repository> <refspec>" explicitly.
- This behavior is the default. Set the
- branch.autosetupmerge configuration variable to false if you
- want git-checkout and git-branch to always behave as if
- '--no-track' were given.
+ When creating a new branch, set up configuration so that git-pull
+ will automatically retrieve data from the start point, which must be
+ a branch. Use this if you always pull from the same upstream branch
+ into the new branch, and if you don't want to use "git pull
+ <repository> <refspec>" explicitly. This behavior is the default
+ when the start point is a remote branch. Set the
+ branch.autosetupmerge configuration variable to `false` if you want
+ git-checkout and git-branch to always behave as if '--no-track' were
+ given. Set it to `always` if you want this behavior when the
+ start-point is either a local or remote branch.
--no-track::
- When a branch is created off a remote branch,
- set up configuration so that git-pull will not retrieve data
- from the remote branch, ignoring the branch.autosetupmerge
- configuration variable.
+ Ignore the branch.autosetupmerge configuration variable.
<branchname>::
The name of the branch to create or delete.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index b4cfa04..4014e72 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -48,21 +48,19 @@ OPTIONS
may restrict the characters allowed in a branch name.
--track::
- When -b is given and a branch is created off a remote branch,
- set up configuration so that git-pull will automatically
- retrieve data from the remote branch. Use this if you always
- pull from the same remote branch into the new branch, or if you
- don't want to use "git pull <repository> <refspec>" explicitly.
- This behavior is the default. Set the
- branch.autosetupmerge configuration variable to false if you
- want git-checkout and git-branch to always behave as if
- '--no-track' were given.
+ When creating a new branch, set up configuration so that git-pull
+ will automatically retrieve data from the start point, which must be
+ a branch. Use this if you always pull from the same upstream branch
+ into the new branch, and if you don't want to use "git pull
+ <repository> <refspec>" explicitly. This behavior is the default
+ when the start point is a remote branch. Set the
+ branch.autosetupmerge configuration variable to `false` if you want
+ git-checkout and git-branch to always behave as if '--no-track' were
+ given. Set it to `always` if you want this behavior when the
+ start-point is either a local or remote branch.
--no-track::
- When -b is given and a branch is created off a remote branch,
- set up configuration so that git-pull will not retrieve data
- from the remote branch, ignoring the branch.autosetupmerge
- configuration variable.
+ Ignore the branch.autosetupmerge configuration variable.
-l::
Create the new branch's reflog. This activates recording of
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 877ab66..f0beb41 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -45,7 +45,7 @@ OPTIONS
default is not to do `-x` so this option is a no-op.
-m parent-number|--mainline parent-number::
- Usually you cannot revert a merge because you do not know which
+ Usually you cannot cherry-pick a merge because you do not know which
side of the merge should be considered the mainline. This
option specifies the parent number (starting from 1) of
the mainline and allows cherry-pick to replay the change
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 270b808..d9aa2f2 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -45,6 +45,11 @@ OPTIONS
candidates to describe the input committish consider
up to <n> candidates. Increasing <n> above 10 will take
slightly longer but may produce a more accurate result.
+ An <n> of 0 will cause only exact matches to be output.
+
+--exact-match::
+ Only output exact matches (a tag directly references the
+ supplied commit). This is a synonym for --candidates=0.
--debug::
Verbosely display information about the searching strategy
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index e22dfa5..543a1cf 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -56,7 +56,9 @@ notable exception of the commit filter, for technical reasons).
Prior to that, the $GIT_COMMIT environment variable will be set to contain
the id of the commit being rewritten. Also, GIT_AUTHOR_NAME,
GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL,
-and GIT_COMMITTER_DATE are set according to the current commit.
+and GIT_COMMITTER_DATE are set according to the current commit. If any
+evaluation of <command> returns a non-zero exit status, the whole operation
+will be aborted.
A 'map' function is available that takes an "original sha1 id" argument
and outputs a "rewritten sha1 id" if the commit has been already
@@ -197,7 +199,7 @@ happened). If this is not the case, use:
--------------------------------------------------------------------------
git filter-branch --parent-filter \
- 'cat; test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>"' HEAD
+ 'test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>" || cat' HEAD
--------------------------------------------------------------------------
or even simpler:
@@ -240,6 +242,15 @@ committed a merge between P1 and P2, it will be propagated properly
and all children of the merge will become merge commits with P1,P2
as their parents instead of the merge commit.
+You can rewrite the commit log messages using `--message-filter`. For
+example, `git-svn-id` strings in a repository created by `git-svn` can
+be removed this way:
+
+-------------------------------------------------------
+git filter-branch --message-filter '
+ sed -e "/^git-svn-id:/d"
+'
+-------------------------------------------------------
To restrict rewriting to only part of the history, specify a revision
range in addition to the new branch name. The new branch name will
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 651efe6..b5207b7 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -10,13 +10,15 @@ SYNOPSIS
--------
[verse]
'git-format-patch' [-k] [-o <dir> | --stdout] [--thread]
- [--attach[=<boundary>] | --inline[=<boundary>]]
- [-s | --signoff] [<common diff options>]
- [-n | --numbered | -N | --no-numbered]
- [--start-number <n>] [--numbered-files]
- [--in-reply-to=Message-Id] [--suffix=.<sfx>]
- [--ignore-if-in-upstream]
- [--subject-prefix=Subject-Prefix]
+ [--attach[=<boundary>] | --inline[=<boundary>]]
+ [-s | --signoff] [<common diff options>]
+ [-n | --numbered | -N | --no-numbered]
+ [--start-number <n>] [--numbered-files]
+ [--in-reply-to=Message-Id] [--suffix=.<sfx>]
+ [--ignore-if-in-upstream]
+ [--subject-prefix=Subject-Prefix]
+ [--cc=<email>]
+ [--cover-letter]
[ <since> | <revision range> ]
DESCRIPTION
@@ -135,6 +137,15 @@ include::diff-options.txt[]
allows for useful naming of a patch series, and can be
combined with the --numbered option.
+--cc=<email>::
+ Add a "Cc:" header to the email headers. This is in addition
+ to any configured headers, and may be used multiple times.
+
+--cover-letter::
+ Generate a cover letter template. You still have to fill in
+ a description, but the shortlog and the diffstat will be
+ generated for you.
+
--suffix=.<sfx>::
Instead of using `.patch` as the suffix for generated
filenames, use specified suffix. A common alternative is
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 4b2dfef..2e7be91 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
SYNOPSIS
--------
-'git-gc' [--prune] [--aggressive] [--auto]
+'git-gc' [--prune] [--aggressive] [--auto] [--quiet]
DESCRIPTION
-----------
@@ -63,6 +63,9 @@ are consolidated into a single pack by using the `-A` option of
`git-repack`. Setting `gc.autopacklimit` to 0 disables
automatic consolidation of packs.
+--quiet::
+ Suppress all progress reports.
+
Configuration
-------------
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 8353be1..5c1bd3b 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -177,6 +177,8 @@ base-name::
This is meant to reduce packing time on multiprocessor machines.
The required amount of memory for the delta search window is
however multiplied by the number of threads.
+ Specifying 0 will cause git to auto-detect the number of CPU's
+ and set the number of threads accordingly.
--index-version=<version>[,<offset>]::
This is intended to be used by the test suite only. It allows
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index c11c645..4b10304 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -9,6 +9,7 @@ SYNOPSIS
--------
[verse]
'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
+ [-s <strategy> | --strategy=<strategy>]
[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
[--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 5b96eab..d80cdf5 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -20,6 +20,9 @@ SYNOPSIS
[ \--full-history ]
[ \--not ]
[ \--all ]
+ [ \--branches ]
+ [ \--tags ]
+ [ \--remotes ]
[ \--stdin ]
[ \--quiet ]
[ \--topo-order ]
@@ -31,6 +34,7 @@ SYNOPSIS
[ \--(author|committer|grep)=<pattern> ]
[ \--regexp-ignore-case | \-i ]
[ \--extended-regexp | \-E ]
+ [ \--fixed-strings | \-F ]
[ \--date={local|relative|default|iso|rfc|short} ]
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
[ \--pretty | \--header ]
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 340f1be..bec9acc 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -159,6 +159,10 @@ New features:
our version of --pretty=oneline
--
+
+NOTE: SVN itself only stores times in UTC and nothing else. The regular svn
+client converts the UTC time to the local time (or based on the TZ=
+environment). This command has the same behaviour.
++
Any other arguments are passed directly to `git log'
'blame'::
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index a8138e2..2648a55 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -130,9 +130,11 @@ limiting may be applied.
Show commits older than a specific date.
+ifdef::git-rev-list[]
--max-age='timestamp', --min-age='timestamp'::
Limit the commits output to specified time range.
+endif::git-rev-list[]
--author='pattern', --committer='pattern'::
@@ -153,6 +155,11 @@ limiting may be applied.
Consider the limiting patterns to be extended regular expressions
instead of the default basic regular expressions.
+-F, --fixed-strings::
+
+ Consider the limiting patterns to be fixed strings (don't interpret
+ pattern as a regular expression).
+
--remove-empty::
Stop when a given path disappears from the tree.
diff --git a/Documentation/technical/api-diff.txt b/Documentation/technical/api-diff.txt
index 83b007e..20b0241 100644
--- a/Documentation/technical/api-diff.txt
+++ b/Documentation/technical/api-diff.txt
@@ -39,7 +39,7 @@ Calling sequence
* Once you finish feeding the pairs of files, call `diffcore_std()`.
This will tell the diffcore library to go ahead and do its work.
-* Calling `diffcore_flush()` will produce the output.
+* Calling `diff_flush()` will produce the output.
Data structures
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 81ac17f..fa34c67 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -44,3 +44,26 @@ endif::git-clone[]
ifdef::git-clone[]
They are equivalent, except the former implies --local option.
endif::git-clone[]
+
+
+If there are a large number of similarly-named remote repositories and
+you want to use a different format for them (such that the URLs you
+use will be rewritten into URLs that work), you can create a
+configuration section of the form:
+
+------------
+ [url "<actual url base>"]
+ insteadOf = <other url base>
+------------
+
+For example, with this:
+
+------------
+ [url "git://git.host.xz/"]
+ insteadOf = host.xz:/path/to/
+ insteadOf = work:
+------------
+
+a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
+rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
+
diff --git a/Makefile b/Makefile
index a055f11..c2f84a7 100644
--- a/Makefile
+++ b/Makefile
@@ -226,7 +226,7 @@ BASIC_CFLAGS =
BASIC_LDFLAGS =
SCRIPT_SH = \
- git-bisect.sh git-checkout.sh \
+ git-bisect.sh \
git-clone.sh \
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
@@ -265,23 +265,23 @@ PROGRAMS = \
git-upload-pack$X \
git-pack-redundant$X git-var$X \
git-merge-tree$X git-imap-send$X \
- git-merge-recursive$X \
$(EXTRA_PROGRAMS)
# Empty...
EXTRA_PROGRAMS =
+# List built-in command $C whose implementation cmd_$C() is not in
+# builtin-$C.o but is linked in as part of some other command.
BUILT_INS = \
git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
git-get-tar-commit-id$X git-init$X git-repo-config$X \
git-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \
+ git-merge-subtree$X \
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
-ALL_PROGRAMS += git-merge-subtree$X
-
# what 'all' will build but not install in gitexecdir
OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
@@ -327,7 +327,8 @@ LIB_OBJS = \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
- transport.o bundle.o walker.o parse-options.o ws.o archive.o
+ transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o \
+ alias.o
BUILTIN_OBJS = \
builtin-add.o \
@@ -339,6 +340,7 @@ BUILTIN_OBJS = \
builtin-bundle.o \
builtin-cat-file.o \
builtin-check-attr.o \
+ builtin-checkout.o \
builtin-checkout-index.o \
builtin-check-ref-format.o \
builtin-clean.o \
@@ -369,6 +371,7 @@ BUILTIN_OBJS = \
builtin-merge-base.o \
builtin-merge-file.o \
builtin-merge-ours.o \
+ builtin-merge-recursive.o \
builtin-mv.o \
builtin-name-rev.o \
builtin-pack-objects.o \
@@ -741,6 +744,7 @@ endif
ifdef THREADED_DELTA_SEARCH
BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
EXTLIBS += -lpthread
+ LIB_OBJS += thread-utils.o
endif
ifeq ($(TCLTK_PATH),)
@@ -808,7 +812,7 @@ export TAR INSTALL DESTDIR SHELL_PATH
### Build rules
-all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS)
+all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
ifneq (,$X)
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$p';)
endif
@@ -838,9 +842,6 @@ help.o: help.c common-cmds.h GIT-CFLAGS
'-DGIT_MAN_PATH="$(mandir_SQ)"' \
'-DGIT_INFO_PATH="$(infodir_SQ)"' $<
-git-merge-subtree$X: git-merge-recursive$X
- $(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
-
$(BUILT_INS): git$X
$(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
@@ -1010,6 +1011,9 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
echo "$$FLAGS" >GIT-CFLAGS; \
fi
+GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
+ @echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@
+
### Detect Tck/Tk interpreter path changes
ifndef NO_TCLTK
TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
@@ -1165,10 +1169,11 @@ ifndef NO_TCLTK
$(MAKE) -C gitk-git clean
$(MAKE) -C git-gui clean
endif
- $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
+ $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
.PHONY: all install clean strip
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
+.PHONY: .FORCE-GIT-BUILD-OPTIONS
### Check documentation
#
diff --git a/alias.c b/alias.c
new file mode 100644
index 0000000..116cac8
--- /dev/null
+++ b/alias.c
@@ -0,0 +1,22 @@
+#include "cache.h"
+
+static const char *alias_key;
+static char *alias_val;
+static int alias_lookup_cb(const char *k, const char *v)
+{
+ if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
+ if (!v)
+ return config_error_nonbool(k);
+ alias_val = xstrdup(v);
+ return 0;
+ }
+ return 0;
+}
+
+char *alias_lookup(const char *alias)
+{
+ alias_key = alias;
+ alias_val = NULL;
+ git_config(alias_lookup_cb);
+ return alias_val;
+}
diff --git a/branch.c b/branch.c
new file mode 100644
index 0000000..daf862e
--- /dev/null
+++ b/branch.c
@@ -0,0 +1,152 @@
+#include "cache.h"
+#include "branch.h"
+#include "refs.h"
+#include "remote.h"
+#include "commit.h"
+
+struct tracking {
+ struct refspec spec;
+ char *src;
+ const char *remote;
+ int matches;
+};
+
+static int find_tracked_branch(struct remote *remote, void *priv)
+{
+ struct tracking *tracking = priv;
+
+ if (!remote_find_tracking(remote, &tracking->spec)) {
+ if (++tracking->matches == 1) {
+ tracking->src = tracking->spec.src;
+ tracking->remote = remote->name;
+ } else {
+ free(tracking->spec.src);
+ if (tracking->src) {
+ free(tracking->src);
+ tracking->src = NULL;
+ }
+ }
+ tracking->spec.src = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * This is called when new_ref is branched off of orig_ref, and tries
+ * to infer the settings for branch.<new_ref>.{remote,merge} from the
+ * config.
+ */
+static int setup_tracking(const char *new_ref, const char *orig_ref,
+ enum branch_track track)
+{
+ char key[1024];
+ struct tracking tracking;
+
+ if (strlen(new_ref) > 1024 - 7 - 7 - 1)
+ return error("Tracking not set up: name too long: %s",
+ new_ref);
+
+ memset(&tracking, 0, sizeof(tracking));
+ tracking.spec.dst = (char *)orig_ref;
+ if (for_each_remote(find_tracked_branch, &tracking))
+ return 1;
+
+ if (!tracking.matches)
+ switch (track) {
+ case BRANCH_TRACK_ALWAYS:
+ case BRANCH_TRACK_EXPLICIT:
+ break;
+ default:
+ return 1;
+ }
+
+ if (tracking.matches > 1)
+ return error("Not tracking: ambiguous information for ref %s",
+ orig_ref);
+
+ sprintf(key, "branch.%s.remote", new_ref);
+ git_config_set(key, tracking.remote ? tracking.remote : ".");
+ sprintf(key, "branch.%s.merge", new_ref);
+ git_config_set(key, tracking.src ? tracking.src : orig_ref);
+ free(tracking.src);
+ printf("Branch %s set up to track %s branch %s.\n", new_ref,
+ tracking.remote ? "remote" : "local", orig_ref);
+
+ return 0;
+}
+
+void create_branch(const char *head,
+ const char *name, const char *start_name,
+ int force, int reflog, enum branch_track track)
+{
+ struct ref_lock *lock;
+ struct commit *commit;
+ unsigned char sha1[20];
+ char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
+ int forcing = 0;
+
+ snprintf(ref, sizeof ref, "refs/heads/%s", name);
+ if (check_ref_format(ref))
+ die("'%s' is not a valid branch name.", name);
+
+ if (resolve_ref(ref, sha1, 1, NULL)) {
+ if (!force)
+ die("A branch named '%s' already exists.", name);
+ else if (!is_bare_repository() && !strcmp(head, name))
+ die("Cannot force update the current branch.");
+ forcing = 1;
+ }
+
+ real_ref = NULL;
+ if (get_sha1(start_name, sha1))
+ die("Not a valid object name: '%s'.", start_name);
+
+ switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
+ case 0:
+ /* Not branching from any existing branch */
+ if (track == BRANCH_TRACK_EXPLICIT)
+ die("Cannot setup tracking information; starting point is not a branch.");
+ break;
+ case 1:
+ /* Unique completion -- good */
+ break;
+ default:
+ die("Ambiguous object name: '%s'.", start_name);
+ break;
+ }
+
+ if ((commit = lookup_commit_reference(sha1)) == NULL)
+ die("Not a valid branch point: '%s'.", start_name);
+ hashcpy(sha1, commit->object.sha1);
+
+ lock = lock_any_ref_for_update(ref, NULL, 0);
+ if (!lock)
+ die("Failed to lock ref for update: %s.", strerror(errno));
+
+ if (reflog)
+ log_all_ref_updates = 1;
+
+ if (forcing)
+ snprintf(msg, sizeof msg, "branch: Reset from %s",
+ start_name);
+ else
+ snprintf(msg, sizeof msg, "branch: Created from %s",
+ start_name);
+
+ if (real_ref && track)
+ setup_tracking(name, real_ref, track);
+
+ if (write_ref_sha1(lock, sha1, msg) < 0)
+ die("Failed to write ref: %s.", strerror(errno));
+
+ free(real_ref);
+}
+
+void remove_branch_state(void)
+{
+ unlink(git_path("MERGE_HEAD"));
+ unlink(git_path("rr-cache/MERGE_RR"));
+ unlink(git_path("MERGE_MSG"));
+ unlink(git_path("SQUASH_MSG"));
+}
diff --git a/branch.h b/branch.h
new file mode 100644
index 0000000..9f0c2a2
--- /dev/null
+++ b/branch.h
@@ -0,0 +1,24 @@
+#ifndef BRANCH_H
+#define BRANCH_H
+
+/* Functions for acting on the information about branches. */
+
+/*
+ * Creates a new branch, where head is the branch currently checked
+ * out, name is the new branch name, start_name is the name of the
+ * existing branch that the new branch should start from, force
+ * enables overwriting an existing (non-head) branch, reflog creates a
+ * reflog for the branch, and track causes the new branch to be
+ * configured to merge the remote branch that start_name is a tracking
+ * branch for (if any).
+ */
+void create_branch(const char *head, const char *name, const char *start_name,
+ int force, int reflog, enum branch_track track);
+
+/*
+ * Remove information about the state of working on the current
+ * branch. (E.g., MERGE_HEAD)
+ */
+void remove_branch_state(void);
+
+#endif
diff --git a/builtin-blame.c b/builtin-blame.c
index 59d7237..bfd562d 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -123,8 +123,7 @@ static inline struct origin *origin_incref(struct origin *o)
static void origin_decref(struct origin *o)
{
if (o && --o->refcnt <= 0) {
- if (o->file.ptr)
- free(o->file.ptr);
+ free(o->file.ptr);
free(o);
}
}
diff --git a/builtin-branch.c b/builtin-branch.c
index 9edf2eb..5bc4526 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -12,6 +12,7 @@
#include "builtin.h"
#include "remote.h"
#include "parse-options.h"
+#include "branch.h"
static const char * const builtin_branch_usage[] = {
"git-branch [options] [-r | -a]",
@@ -29,8 +30,6 @@ static const char * const builtin_branch_usage[] = {
static const char *head;
static unsigned char head_sha1[20];
-static int branch_track = 1;
-
static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */
@@ -75,10 +74,6 @@ static int git_branch_config(const char *var, const char *value)
color_parse(value, var, branch_colors[slot]);
return 0;
}
- if (!strcmp(var, "branch.autosetupmerge")) {
- branch_track = git_config_bool(var, value);
- return 0;
- }
return git_color_default_config(var, value);
}
@@ -126,8 +121,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
continue;
}
- if (name)
- free(name);
+ free(name);
name = xstrdup(mkpath(fmt, argv[i]));
if (!resolve_ref(name, sha1, 1, NULL)) {
@@ -172,8 +166,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
}
}
- if (name)
- free(name);
+ free(name);
return(ret);
}
@@ -359,141 +352,6 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
free_ref_list(&ref_list);
}
-struct tracking {
- struct refspec spec;
- char *src;
- const char *remote;
- int matches;
-};
-
-static int find_tracked_branch(struct remote *remote, void *priv)
-{
- struct tracking *tracking = priv;
-
- if (!remote_find_tracking(remote, &tracking->spec)) {
- if (++tracking->matches == 1) {
- tracking->src = tracking->spec.src;
- tracking->remote = remote->name;
- } else {
- free(tracking->spec.src);
- if (tracking->src) {
- free(tracking->src);
- tracking->src = NULL;
- }
- }
- tracking->spec.src = NULL;
- }
-
- return 0;
-}
-
-
-/*
- * This is called when new_ref is branched off of orig_ref, and tries
- * to infer the settings for branch.<new_ref>.{remote,merge} from the
- * config.
- */
-static int setup_tracking(const char *new_ref, const char *orig_ref)
-{
- char key[1024];
- struct tracking tracking;
-
- if (strlen(new_ref) > 1024 - 7 - 7 - 1)
- return error("Tracking not set up: name too long: %s",
- new_ref);
-
- memset(&tracking, 0, sizeof(tracking));
- tracking.spec.dst = (char *)orig_ref;
- if (for_each_remote(find_tracked_branch, &tracking) ||
- !tracking.matches)
- return 1;
-
- if (tracking.matches > 1)
- return error("Not tracking: ambiguous information for ref %s",
- orig_ref);
-
- if (tracking.matches == 1) {
- sprintf(key, "branch.%s.remote", new_ref);
- git_config_set(key, tracking.remote ? tracking.remote : ".");
- sprintf(key, "branch.%s.merge", new_ref);
- git_config_set(key, tracking.src);
- free(tracking.src);
- printf("Branch %s set up to track remote branch %s.\n",
- new_ref, orig_ref);
- }
-
- return 0;
-}
-
-static void create_branch(const char *name, const char *start_name,
- int force, int reflog, int track)
-{
- struct ref_lock *lock;
- struct commit *commit;
- unsigned char sha1[20];
- char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
- int forcing = 0;
-
- snprintf(ref, sizeof ref, "refs/heads/%s", name);
- if (check_ref_format(ref))
- die("'%s' is not a valid branch name.", name);
-
- if (resolve_ref(ref, sha1, 1, NULL)) {
- if (!force)
- die("A branch named '%s' already exists.", name);
- else if (!is_bare_repository() && !strcmp(head, name))
- die("Cannot force update the current branch.");
- forcing = 1;
- }
-
- real_ref = NULL;
- if (get_sha1(start_name, sha1))
- die("Not a valid object name: '%s'.", start_name);
-
- switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
- case 0:
- /* Not branching from any existing branch */
- real_ref = NULL;
- break;
- case 1:
- /* Unique completion -- good */
- break;
- default:
- die("Ambiguous object name: '%s'.", start_name);
- break;
- }
-
- if ((commit = lookup_commit_reference(sha1)) == NULL)
- die("Not a valid branch point: '%s'.", start_name);
- hashcpy(sha1, commit->object.sha1);
-
- lock = lock_any_ref_for_update(ref, NULL, 0);
- if (!lock)
- die("Failed to lock ref for update: %s.", strerror(errno));
-
- if (reflog)
- log_all_ref_updates = 1;
-
- if (forcing)
- snprintf(msg, sizeof msg, "branch: Reset from %s",
- start_name);
- else
- snprintf(msg, sizeof msg, "branch: Created from %s",
- start_name);
-
- /* When branching off a remote branch, set up so that git-pull
- automatically merges from there. So far, this is only done for
- remotes registered via .git/config. */
- if (real_ref && track)
- setup_tracking(name, real_ref);
-
- if (write_ref_sha1(lock, sha1, msg) < 0)
- die("Failed to write ref: %s.", strerror(errno));
-
- if (real_ref)
- free(real_ref);
-}
-
static void rename_branch(const char *oldname, const char *newname, int force)
{
char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
@@ -554,14 +412,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
{
int delete = 0, rename = 0, force_create = 0;
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
- int reflog = 0, track;
+ int reflog = 0;
+ enum branch_track track;
int kinds = REF_LOCAL_BRANCH;
struct commit_list *with_commit = NULL;
struct option options[] = {
OPT_GROUP("Generic options"),
OPT__VERBOSE(&verbose),
- OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"),
+ OPT_SET_INT( 0 , "track", &track, "set up tracking mode (see git-pull(1))",
+ BRANCH_TRACK_EXPLICIT),
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
REF_REMOTE_BRANCH),
@@ -592,7 +452,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (branch_use_color == -1)
branch_use_color = git_use_color_default;
- track = branch_track;
+ track = git_branch_track;
argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
if (!!delete + !!rename + !!force_create > 1)
usage_with_options(builtin_branch_usage, options);
@@ -618,7 +478,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
else if (rename && (argc == 2))
rename_branch(argv[0], argv[1], rename > 1);
else if (argc <= 2)
- create_branch(argv[0], (argc == 2) ? argv[1] : head,
+ create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
force_create, reflog, track);
else
usage_with_options(builtin_branch_usage, options);
diff --git a/builtin-checkout.c b/builtin-checkout.c
new file mode 100644
index 0000000..6b08016
--- /dev/null
+++ b/builtin-checkout.c
@@ -0,0 +1,568 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "commit.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "unpack-trees.h"
+#include "dir.h"
+#include "run-command.h"
+#include "merge-recursive.h"
+#include "branch.h"
+#include "diff.h"
+#include "revision.h"
+#include "remote.h"
+
+static const char * const checkout_usage[] = {
+ "git checkout [options] <branch>",
+ "git checkout [options] [<branch>] -- <file>...",
+ NULL,
+};
+
+static int post_checkout_hook(struct commit *old, struct commit *new,
+ int changed)
+{
+ struct child_process proc;
+ const char *name = git_path("hooks/post-checkout");
+ const char *argv[5];
+
+ if (access(name, X_OK) < 0)
+ return 0;
+
+ memset(&proc, 0, sizeof(proc));
+ argv[0] = name;
+ argv[1] = xstrdup(sha1_to_hex(old->object.sha1));
+ argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
+ argv[3] = changed ? "1" : "0";
+ argv[4] = NULL;
+ proc.argv = argv;
+ proc.no_stdin = 1;
+ proc.stdout_to_stderr = 1;
+ return run_command(&proc);
+}
+
+static int update_some(const unsigned char *sha1, const char *base, int baselen,
+ const char *pathname, unsigned mode, int stage)
+{
+ int len;
+ struct cache_entry *ce;
+
+ if (S_ISGITLINK(mode))
+ return 0;
+
+ if (S_ISDIR(mode))
+ return READ_TREE_RECURSIVE;
+
+ len = baselen + strlen(pathname);
+ ce = xcalloc(1, cache_entry_size(len));
+ hashcpy(ce->sha1, sha1);
+ memcpy(ce->name, base, baselen);
+ memcpy(ce->name + baselen, pathname, len - baselen);
+ ce->ce_flags = create_ce_flags(len, 0);
+ ce->ce_mode = create_ce_mode(mode);
+ add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+ return 0;
+}
+
+static int read_tree_some(struct tree *tree, const char **pathspec)
+{
+ read_tree_recursive(tree, "", 0, 0, pathspec, update_some);
+
+ /* update the index with the given tree's info
+ * for all args, expanding wildcards, and exit
+ * with any non-zero return code.
+ */
+ return 0;
+}
+
+static int checkout_paths(struct tree *source_tree, const char **pathspec)
+{
+ int pos;
+ struct checkout state;
+ static char *ps_matched;
+ unsigned char rev[20];
+ int flag;
+ struct commit *head;
+
+ int newfd;
+ struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+ newfd = hold_locked_index(lock_file, 1);
+ read_cache();
+
+ if (source_tree)
+ read_tree_some(source_tree, pathspec);
+
+ for (pos = 0; pathspec[pos]; pos++)
+ ;
+ ps_matched = xcalloc(1, pos);
+
+ for (pos = 0; pos < active_nr; pos++) {
+ struct cache_entry *ce = active_cache[pos];
+ pathspec_match(pathspec, ps_matched, ce->name, 0);
+ }
+
+ if (report_path_error(ps_matched, pathspec, 0))
+ return 1;
+
+ memset(&state, 0, sizeof(state));
+ state.force = 1;
+ state.refresh_cache = 1;
+ for (pos = 0; pos < active_nr; pos++) {
+ struct cache_entry *ce = active_cache[pos];
+ if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+ checkout_entry(ce, &state, NULL);
+ }
+ }
+
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_locked_index(lock_file))
+ die("unable to write new index file");
+
+ resolve_ref("HEAD", rev, 0, &flag);
+ head = lookup_commit_reference_gently(rev, 1);
+
+ return post_checkout_hook(head, head, 0);
+}
+
+static void show_local_changes(struct object *head)
+{
+ struct rev_info rev;
+ /* I think we want full paths, even if we're in a subdirectory. */
+ init_revisions(&rev, NULL);
+ rev.abbrev = 0;
+ rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
+ add_pending_object(&rev, head, NULL);
+ run_diff_index(&rev, 0);
+}
+
+static void describe_detached_head(char *msg, struct commit *commit)
+{
+ struct strbuf sb;
+ strbuf_init(&sb, 0);
+ parse_commit(commit);
+ pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, "", "", 0, 0);
+ fprintf(stderr, "%s %s... %s\n", msg,
+ find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
+ strbuf_release(&sb);
+}
+
+static int reset_to_new(struct tree *tree, int quiet)
+{
+ struct unpack_trees_options opts;
+ struct tree_desc tree_desc;
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = -1;
+ opts.update = 1;
+ opts.reset = 1;
+ opts.merge = 1;
+ opts.fn = oneway_merge;
+ opts.verbose_update = !quiet;
+ parse_tree(tree);
+ init_tree_desc(&tree_desc, tree->buffer, tree->size);
+ if (unpack_trees(1, &tree_desc, &opts))
+ return 128;
+ return 0;
+}
+
+static void reset_clean_to_new(struct tree *tree, int quiet)
+{
+ struct unpack_trees_options opts;
+ struct tree_desc tree_desc;
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = -1;
+ opts.skip_unmerged = 1;
+ opts.reset = 1;
+ opts.merge = 1;
+ opts.fn = oneway_merge;
+ opts.verbose_update = !quiet;
+ parse_tree(tree);
+ init_tree_desc(&tree_desc, tree->buffer, tree->size);
+ if (unpack_trees(1, &tree_desc, &opts))
+ exit(128);
+}
+
+struct checkout_opts {
+ int quiet;
+ int merge;
+ int force;
+
+ char *new_branch;
+ int new_branch_log;
+ enum branch_track track;
+};
+
+struct branch_info {
+ const char *name; /* The short name used */
+ const char *path; /* The full name of a real branch */
+ struct commit *commit; /* The named commit */
+};
+
+static void setup_branch_path(struct branch_info *branch)
+{
+ struct strbuf buf;
+ strbuf_init(&buf, 0);
+ strbuf_addstr(&buf, "refs/heads/");
+ strbuf_addstr(&buf, branch->name);
+ branch->path = strbuf_detach(&buf, NULL);
+}
+
+static int merge_working_tree(struct checkout_opts *opts,
+ struct branch_info *old, struct branch_info *new)
+{
+ int ret;
+ struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+ int newfd = hold_locked_index(lock_file, 1);
+ read_cache();
+
+ if (opts->force) {
+ ret = reset_to_new(new->commit->tree, opts->quiet);
+ if (ret)
+ return ret;
+ } else {
+ struct tree_desc trees[2];
+ struct tree *tree;
+ struct unpack_trees_options topts;
+ memset(&topts, 0, sizeof(topts));
+ topts.head_idx = -1;
+
+ refresh_cache(REFRESH_QUIET);
+
+ if (unmerged_cache()) {
+ error("you need to resolve your current index first");
+ return 1;
+ }
+
+ /* 2-way merge to the new branch */
+ topts.update = 1;
+ topts.merge = 1;
+ topts.gently = opts->merge;
+ topts.verbose_update = !opts->quiet;
+ topts.fn = twoway_merge;
+ topts.dir = xcalloc(1, sizeof(*topts.dir));
+ topts.dir->show_ignored = 1;
+ topts.dir->exclude_per_dir = ".gitignore";
+ tree = parse_tree_indirect(old->commit->object.sha1);
+ init_tree_desc(&trees[0], tree->buffer, tree->size);
+ tree = parse_tree_indirect(new->commit->object.sha1);
+ init_tree_desc(&trees[1], tree->buffer, tree->size);
+
+ if (unpack_trees(2, trees, &topts)) {
+ /*
+ * Unpack couldn't do a trivial merge; either
+ * give up or do a real merge, depending on
+ * whether the merge flag was used.
+ */
+ struct tree *result;
+ struct tree *work;
+ if (!opts->merge)
+ return 1;
+ parse_commit(old->commit);
+
+ /* Do more real merge */
+
+ /*
+ * We update the index fully, then write the
+ * tree from the index, then merge the new
+ * branch with the current tree, with the old
+ * branch as the base. Then we reset the index
+ * (but not the working tree) to the new
+ * branch, leaving the working tree as the
+ * merged version, but skipping unmerged
+ * entries in the index.
+ */
+
+ add_files_to_cache(0, NULL, NULL);
+ work = write_tree_from_memory();
+
+ ret = reset_to_new(new->commit->tree, opts->quiet);
+ if (ret)
+ return ret;
+ merge_trees(new->commit->tree, work, old->commit->tree,
+ new->name, "local", &result);
+ reset_clean_to_new(new->commit->tree, opts->quiet);
+ }
+ }
+
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_locked_index(lock_file))
+ die("unable to write new index file");
+
+ if (!opts->force)
+ show_local_changes(&new->commit->object);
+
+ return 0;
+}
+
+static void report_tracking(struct branch_info *new, struct checkout_opts *opts)
+{
+ /*
+ * We have switched to a new branch; is it building on
+ * top of another branch, and if so does that other branch
+ * have changes we do not have yet?
+ */
+ char *base;
+ unsigned char sha1[20];
+ struct commit *ours, *theirs;
+ char symmetric[84];
+ struct rev_info revs;
+ const char *rev_argv[10];
+ int rev_argc;
+ int num_ours, num_theirs;
+ const char *remote_msg;
+ struct branch *branch = branch_get(new->name);
+
+ /*
+ * Nothing to report unless we are marked to build on top of
+ * somebody else.
+ */
+ if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
+ return;
+
+ /*
+ * If what we used to build on no longer exists, there is
+ * nothing to report.
+ */
+ base = branch->merge[0]->dst;
+ if (!resolve_ref(base, sha1, 1, NULL))
+ return;
+
+ theirs = lookup_commit(sha1);
+ ours = new->commit;
+ if (!hashcmp(sha1, ours->object.sha1))
+ return; /* we are the same */
+
+ /* Run "rev-list --left-right ours...theirs" internally... */
+ rev_argc = 0;
+ rev_argv[rev_argc++] = NULL;
+ rev_argv[rev_argc++] = "--left-right";
+ rev_argv[rev_argc++] = symmetric;
+ rev_argv[rev_argc++] = "--";
+ rev_argv[rev_argc] = NULL;
+
+ strcpy(symmetric, sha1_to_hex(ours->object.sha1));
+ strcpy(symmetric + 40, "...");
+ strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
+
+ init_revisions(&revs, NULL);
+ setup_revisions(rev_argc, rev_argv, &revs, NULL);
+ prepare_revision_walk(&revs);
+
+ /* ... and count the commits on each side. */
+ num_ours = 0;
+ num_theirs = 0;
+ while (1) {
+ struct commit *c = get_revision(&revs);
+ if (!c)
+ break;
+ if (c->object.flags & SYMMETRIC_LEFT)
+ num_ours++;
+ else
+ num_theirs++;
+ }
+
+ if (!prefixcmp(base, "refs/remotes/")) {
+ remote_msg = " remote";
+ base += strlen("refs/remotes/");
+ } else {
+ remote_msg = "";
+ }
+
+ if (!num_theirs)
+ printf("Your branch is ahead of the tracked%s branch '%s' "
+ "by %d commit%s.\n",
+ remote_msg, base,
+ num_ours, (num_ours == 1) ? "" : "s");
+ else if (!num_ours)
+ printf("Your branch is behind the tracked%s branch '%s' "
+ "by %d commit%s,\n"
+ "and can be fast-forwarded.\n",
+ remote_msg, base,
+ num_theirs, (num_theirs == 1) ? "" : "s");
+ else
+ printf("Your branch and the tracked%s branch '%s' "
+ "have diverged,\nand respectively "
+ "have %d and %d different commit(s) each.\n",
+ remote_msg, base,
+ num_ours, num_theirs);
+}
+
+static void update_refs_for_switch(struct checkout_opts *opts,
+ struct branch_info *old,
+ struct branch_info *new)
+{
+ struct strbuf msg;
+ const char *old_desc;
+ if (opts->new_branch) {
+ create_branch(old->name, opts->new_branch, new->name, 0,
+ opts->new_branch_log, opts->track);
+ new->name = opts->new_branch;
+ setup_branch_path(new);
+ }
+
+ strbuf_init(&msg, 0);
+ old_desc = old->name;
+ if (!old_desc)
+ old_desc = sha1_to_hex(old->commit->object.sha1);
+ strbuf_addf(&msg, "checkout: moving from %s to %s",
+ old_desc, new->name);
+
+ if (new->path) {
+ create_symref("HEAD", new->path, msg.buf);
+ if (!opts->quiet) {
+ if (old->path && !strcmp(new->path, old->path))
+ fprintf(stderr, "Already on \"%s\"\n",
+ new->name);
+ else
+ fprintf(stderr, "Switched to%s branch \"%s\"\n",
+ opts->new_branch ? " a new" : "",
+ new->name);
+ }
+ } else if (strcmp(new->name, "HEAD")) {
+ update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
+ REF_NODEREF, DIE_ON_ERR);
+ if (!opts->quiet) {
+ if (old->path)
+ fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b <new_branch_name>\n", new->name);
+ describe_detached_head("HEAD is now at", new->commit);
+ }
+ }
+ remove_branch_state();
+ strbuf_release(&msg);
+ if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
+ report_tracking(new, opts);
+}
+
+static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
+{
+ int ret = 0;
+ struct branch_info old;
+ unsigned char rev[20];
+ int flag;
+ memset(&old, 0, sizeof(old));
+ old.path = resolve_ref("HEAD", rev, 0, &flag);
+ old.commit = lookup_commit_reference_gently(rev, 1);
+ if (!(flag & REF_ISSYMREF))
+ old.path = NULL;
+
+ if (old.path && !prefixcmp(old.path, "refs/heads/"))
+ old.name = old.path + strlen("refs/heads/");
+
+ if (!new->name) {
+ new->name = "HEAD";
+ new->commit = old.commit;
+ if (!new->commit)
+ die("You are on a branch yet to be born");
+ parse_commit(new->commit);
+ }
+
+ /*
+ * If the new thing isn't a branch and isn't HEAD and we're
+ * not starting a new branch, and we want messages, and we
+ * weren't on a branch, and we're moving to a new commit,
+ * describe the old commit.
+ */
+ if (!new->path && strcmp(new->name, "HEAD") && !opts->new_branch &&
+ !opts->quiet && !old.path && new->commit != old.commit)
+ describe_detached_head("Previous HEAD position was", old.commit);
+
+ if (!old.commit) {
+ if (!opts->quiet) {
+ fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
+ fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
+ }
+ opts->force = 1;
+ }
+
+ ret = merge_working_tree(opts, &old, new);
+ if (ret)
+ return ret;
+
+ update_refs_for_switch(opts, &old, new);
+
+ return post_checkout_hook(old.commit, new->commit, 1);
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+ struct checkout_opts opts;
+ unsigned char rev[20];
+ const char *arg;
+ struct branch_info new;
+ struct tree *source_tree = NULL;
+ struct option options[] = {
+ OPT__QUIET(&opts.quiet),
+ OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+ OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
+ OPT_SET_INT( 0 , "track", &opts.track, "track",
+ BRANCH_TRACK_EXPLICIT),
+ OPT_BOOLEAN('f', NULL, &opts.force, "force"),
+ OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
+ OPT_END(),
+ };
+
+ memset(&opts, 0, sizeof(opts));
+ memset(&new, 0, sizeof(new));
+
+ git_config(git_default_config);
+
+ opts.track = git_branch_track;
+
+ argc = parse_options(argc, argv, options, checkout_usage, 0);
+ if (argc) {
+ arg = argv[0];
+ if (get_sha1(arg, rev))
+ ;
+ else if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
+ new.name = arg;
+ setup_branch_path(&new);
+ if (resolve_ref(new.path, rev, 1, NULL))
+ new.commit = lookup_commit_reference(rev);
+ else
+ new.path = NULL;
+ parse_commit(new.commit);
+ source_tree = new.commit->tree;
+ argv++;
+ argc--;
+ } else if ((source_tree = parse_tree_indirect(rev))) {
+ argv++;
+ argc--;
+ }
+ }
+
+ if (argc && !strcmp(argv[0], "--")) {
+ argv++;
+ argc--;
+ }
+
+ if (!opts.new_branch && (opts.track != git_branch_track))
+ die("git checkout: --track and --no-track require -b");
+
+ if (opts.force && opts.merge)
+ die("git checkout: -f and -m are incompatible");
+
+ if (argc) {
+ const char **pathspec = get_pathspec(prefix, argv);
+
+ if (!pathspec)
+ die("invalid path specification");
+
+ /* Checkout paths */
+ if (opts.new_branch || opts.force || opts.merge) {
+ if (argc == 1) {
+ die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
+ } else {
+ die("git checkout: updating paths is incompatible with switching branches/forcing");
+ }
+ }
+
+ return checkout_paths(source_tree, pathspec);
+ }
+
+ if (new.name && !new.commit) {
+ die("Cannot switch branch to a non-commit.");
+ }
+
+ return switch_branches(&opts, &new);
+}
diff --git a/builtin-commit.c b/builtin-commit.c
index 065e1f7..f49c22e 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -205,7 +205,8 @@ static void create_base_index(void)
die("failed to unpack HEAD tree object");
parse_tree(tree);
init_tree_desc(&t, tree->buffer, tree->size);
- unpack_trees(1, &t, &opts);
+ if (unpack_trees(1, &t, &opts))
+ exit(128); /* We've already reported the error, finish dying */
}
static char *prepare_index(int argc, const char **argv, const char *prefix)
diff --git a/builtin-describe.c b/builtin-describe.c
index 3fd2e73..2342913 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -47,19 +47,34 @@ static void add_to_known_names(const char *path,
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
- struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+ int might_be_tag = !prefixcmp(path, "refs/tags/");
+ struct commit *commit;
struct object *object;
- int prio;
+ unsigned char peeled[20];
+ int is_tag, prio;
- if (!commit)
+ if (!all && !might_be_tag)
return 0;
- object = parse_object(sha1);
+
+ if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
+ commit = lookup_commit_reference_gently(peeled, 1);
+ if (!commit)
+ return 0;
+ is_tag = !!hashcmp(sha1, commit->object.sha1);
+ } else {
+ commit = lookup_commit_reference_gently(sha1, 1);
+ object = parse_object(sha1);
+ if (!commit || !object)
+ return 0;
+ is_tag = object->type == OBJ_TAG;
+ }
+
/* If --all, then any refs are used.
* If --tags, then any tags are used.
* Otherwise only annotated tags are used.
*/
- if (!prefixcmp(path, "refs/tags/")) {
- if (object->type == OBJ_TAG) {
+ if (might_be_tag) {
+ if (is_tag) {
prio = 2;
if (pattern && fnmatch(pattern, path + 10, 0))
prio = 0;
@@ -164,6 +179,8 @@ static void describe(const char *arg, int last_one)
return;
}
+ if (!max_candidates)
+ die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1));
if (debug)
fprintf(stderr, "searching to describe %s\n", arg);
@@ -261,6 +278,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"),
OPT_BOOLEAN(0, "long", &longformat, "always use long format"),
OPT__ABBREV(&abbrev),
+ OPT_SET_INT(0, "exact-match", &max_candidates,
+ "only output exact matches", 0),
OPT_INTEGER(0, "candidates", &max_candidates,
"consider <n> most recent tags (default: 10)"),
OPT_STRING(0, "match", &pattern, "pattern",
@@ -269,8 +288,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
};
argc = parse_options(argc, argv, options, describe_usage, 0);
- if (max_candidates < 1)
- max_candidates = 1;
+ if (max_candidates < 0)
+ max_candidates = 0;
else if (max_candidates > MAX_TAGS)
max_candidates = MAX_TAGS;
diff --git a/builtin-diff.c b/builtin-diff.c
index 8f53f52..444ff2f 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -44,12 +44,17 @@ static void stuff_change(struct diff_options *opt,
tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
tmp_c = old_name; old_name = new_name; new_name = tmp_c;
}
+
+ if (opt->prefix &&
+ (strncmp(old_name, opt->prefix, opt->prefix_length) ||
+ strncmp(new_name, opt->prefix, opt->prefix_length)))
+ return;
+
one = alloc_filespec(old_name);
two = alloc_filespec(new_name);
fill_filespec(one, old_sha1, old_mode);
fill_filespec(two, new_sha1, new_mode);
- /* NEEDSWORK: shouldn't this part of diffopt??? */
diff_queue(&diff_queued_diff, one, two);
}
@@ -246,6 +251,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (diff_setup_done(&rev.diffopt) < 0)
die("diff_setup_done failed");
}
+ if (rev.diffopt.prefix && nongit) {
+ rev.diffopt.prefix = NULL;
+ rev.diffopt.prefix_length = 0;
+ }
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
index f741df5..e1c5630 100755
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -123,7 +123,7 @@ static void show_filemodify(struct diff_queue_struct *q,
printf("D %s\n", spec->path);
else {
struct object *object = lookup_object(spec->sha1);
- printf("M 0%06o :%d %s\n", spec->mode,
+ printf("M %06o :%d %s\n", spec->mode,
get_object_mark(object), spec->path);
}
}
@@ -196,8 +196,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
? strlen(reencoded) : message
? strlen(message) : 0),
reencoded ? reencoded : message ? message : "");
- if (reencoded)
- free(reencoded);
+ free(reencoded);
for (i = 0, p = commit->parents; p; p = p->next) {
int mark = get_object_mark(&p->item->object);
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index f401352..5ea48ca 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -538,8 +538,10 @@ static int get_pack(int xd[2], char **pack_lockfile)
cmd.git_cmd = 1;
if (start_command(&cmd))
die("fetch-pack: unable to fork off %s", argv[0]);
- if (do_keep && pack_lockfile)
+ if (do_keep && pack_lockfile) {
*pack_lockfile = index_pack_lockfile(cmd.out);
+ close(cmd.out);
+ }
if (finish_command(&cmd))
die("%s failed", argv[0]);
diff --git a/builtin-gc.c b/builtin-gc.c
index ad4a75e..045bf0e 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -172,12 +172,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
int prune = 0;
int aggressive = 0;
int auto_gc = 0;
+ int quiet = 0;
char buf[80];
struct option builtin_gc_options[] = {
OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
+ OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
OPT_END()
};
@@ -197,6 +199,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
append_option(argv_repack, buf, MAX_ADD);
}
}
+ if (quiet)
+ append_option(argv_repack, "-q", MAX_ADD);
if (auto_gc) {
/*
diff --git a/builtin-http-fetch.c b/builtin-http-fetch.c
index 7f450c6..299093f 100644
--- a/builtin-http-fetch.c
+++ b/builtin-http-fetch.c
@@ -80,8 +80,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
walker_free(walker);
- if (rewritten_url)
- free(rewritten_url);
+ free(rewritten_url);
return rc;
}
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 5d7cdda..79eaf8d 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -29,27 +29,6 @@ static void safe_create_dir(const char *dir, int share)
die("Could not make %s writable by group\n", dir);
}
-static int copy_file(const char *dst, const char *src, int mode)
-{
- int fdi, fdo, status;
-
- mode = (mode & 0111) ? 0777 : 0666;
- if ((fdi = open(src, O_RDONLY)) < 0)
- return fdi;
- if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
- close(fdi);
- return fdo;
- }
- status = copy_fd(fdi, fdo);
- if (close(fdo) != 0)
- return error("%s: write error: %s", dst, strerror(errno));
-
- if (!status && adjust_shared_perm(dst))
- return -1;
-
- return status;
-}
-
static void copy_templates_1(char *path, int baselen,
char *template, int template_baselen,
DIR *dir)
diff --git a/builtin-log.c b/builtin-log.c
index c67d63c..bbadbc0 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -15,6 +15,8 @@
#include "reflog-walk.h"
#include "patch-ids.h"
#include "refs.h"
+#include "run-command.h"
+#include "shortlog.h"
static int default_show_root = 1;
static const char *fmt_patch_subject_prefix = "PATCH";
@@ -428,24 +430,47 @@ static int istitlechar(char c)
(c >= '0' && c <= '9') || c == '.' || c == '_';
}
-static char *extra_headers = NULL;
-static int extra_headers_size = 0;
static const char *fmt_patch_suffix = ".patch";
static int numbered = 0;
static int auto_number = 0;
+static char **extra_hdr;
+static int extra_hdr_nr;
+static int extra_hdr_alloc;
+
+static char **extra_to;
+static int extra_to_nr;
+static int extra_to_alloc;
+
+static char **extra_cc;
+static int extra_cc_nr;
+static int extra_cc_alloc;
+
+static void add_header(const char *value)
+{
+ int len = strlen(value);
+ while (value[len - 1] == '\n')
+ len--;
+ if (!strncasecmp(value, "to: ", 4)) {
+ ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
+ extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4);
+ return;
+ }
+ if (!strncasecmp(value, "cc: ", 4)) {
+ ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+ extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
+ return;
+ }
+ ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
+ extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
+}
+
static int git_format_config(const char *var, const char *value)
{
if (!strcmp(var, "format.headers")) {
- int len;
-
if (!value)
die("format.headers without value");
- len = strlen(value);
- extra_headers_size += len + 1;
- extra_headers = xrealloc(extra_headers, extra_headers_size);
- extra_headers[extra_headers_size - len - 1] = 0;
- strcat(extra_headers, value);
+ add_header(value);
return 0;
}
if (!strcmp(var, "format.suffix")) {
@@ -470,74 +495,81 @@ static int git_format_config(const char *var, const char *value)
}
+static const char *get_oneline_for_filename(struct commit *commit,
+ int keep_subject)
+{
+ static char filename[PATH_MAX];
+ char *sol;
+ int len = 0;
+ int suffix_len = strlen(fmt_patch_suffix) + 1;
+
+ sol = strstr(commit->buffer, "\n\n");
+ if (!sol)
+ filename[0] = '\0';
+ else {
+ int j, space = 0;
+
+ sol += 2;
+ /* strip [PATCH] or [PATCH blabla] */
+ if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
+ char *eos = strchr(sol + 6, ']');
+ if (eos) {
+ while (isspace(*eos))
+ eos++;
+ sol = eos;
+ }
+ }
+
+ for (j = 0;
+ j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
+ len < sizeof(filename) - suffix_len &&
+ sol[j] && sol[j] != '\n';
+ j++) {
+ if (istitlechar(sol[j])) {
+ if (space) {
+ filename[len++] = '-';
+ space = 0;
+ }
+ filename[len++] = sol[j];
+ if (sol[j] == '.')
+ while (sol[j + 1] == '.')
+ j++;
+ } else
+ space = 1;
+ }
+ while (filename[len - 1] == '.'
+ || filename[len - 1] == '-')
+ len--;
+ filename[len] = '\0';
+ }
+ return filename;
+}
+
static FILE *realstdout = NULL;
static const char *output_directory = NULL;
-static int reopen_stdout(struct commit *commit, int nr, int keep_subject,
- int numbered_files)
+static int reopen_stdout(const char *oneline, int nr, int total)
{
char filename[PATH_MAX];
- char *sol;
int len = 0;
int suffix_len = strlen(fmt_patch_suffix) + 1;
if (output_directory) {
- if (strlen(output_directory) >=
+ len = snprintf(filename, sizeof(filename), "%s",
+ output_directory);
+ if (len >=
sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
return error("name of output directory is too long");
- strlcpy(filename, output_directory, sizeof(filename) - suffix_len);
- len = strlen(filename);
if (filename[len - 1] != '/')
filename[len++] = '/';
}
- if (numbered_files) {
- sprintf(filename + len, "%d", nr);
- len = strlen(filename);
-
- } else {
- sprintf(filename + len, "%04d", nr);
- len = strlen(filename);
-
- sol = strstr(commit->buffer, "\n\n");
- if (sol) {
- int j, space = 1;
-
- sol += 2;
- /* strip [PATCH] or [PATCH blabla] */
- if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
- char *eos = strchr(sol + 6, ']');
- if (eos) {
- while (isspace(*eos))
- eos++;
- sol = eos;
- }
- }
-
- for (j = 0;
- j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
- len < sizeof(filename) - suffix_len &&
- sol[j] && sol[j] != '\n';
- j++) {
- if (istitlechar(sol[j])) {
- if (space) {
- filename[len++] = '-';
- space = 0;
- }
- filename[len++] = sol[j];
- if (sol[j] == '.')
- while (sol[j + 1] == '.')
- j++;
- } else
- space = 1;
- }
- while (filename[len - 1] == '.'
- || filename[len - 1] == '-')
- len--;
- filename[len] = 0;
- }
- if (len + suffix_len >= sizeof(filename))
- return error("Patch pathname too long");
+ if (!oneline)
+ len += sprintf(filename + len, "%d", nr);
+ else {
+ len += sprintf(filename + len, "%04d-", nr);
+ len += snprintf(filename + len, sizeof(filename) - len - 1
+ - suffix_len, "%s", oneline);
strcpy(filename + len, fmt_patch_suffix);
}
@@ -594,16 +626,86 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
o2->flags = flags2;
}
-static void gen_message_id(char *dest, unsigned int length, char *base)
+static void gen_message_id(struct rev_info *info, char *base)
{
const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
const char *email_start = strrchr(committer, '<');
const char *email_end = strrchr(committer, '>');
- if(!email_start || !email_end || email_start > email_end - 1)
+ struct strbuf buf;
+ if (!email_start || !email_end || email_start > email_end - 1)
die("Could not extract email from committer identity.");
- snprintf(dest, length, "%s.%lu.git.%.*s", base,
- (unsigned long) time(NULL),
- (int)(email_end - email_start - 1), email_start + 1);
+ strbuf_init(&buf, 0);
+ strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
+ (unsigned long) time(NULL),
+ (int)(email_end - email_start - 1), email_start + 1);
+ info->message_id = strbuf_detach(&buf, NULL);
+}
+
+static void make_cover_letter(struct rev_info *rev, int use_stdout,
+ int numbered, int numbered_files,
+ struct commit *origin,
+ int nr, struct commit **list, struct commit *head)
+{
+ const char *committer;
+ char *head_sha1;
+ const char *subject_start = NULL;
+ const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
+ const char *msg;
+ const char *extra_headers = rev->extra_headers;
+ struct shortlog log;
+ struct strbuf sb;
+ int i;
+ const char *encoding = "utf-8";
+ struct diff_options opts;
+
+ if (rev->commit_format != CMIT_FMT_EMAIL)
+ die("Cover letter needs email format");
+
+ if (!use_stdout && reopen_stdout(numbered_files ?
+ NULL : "cover-letter", 0, rev->total))
+ return;
+
+ head_sha1 = sha1_to_hex(head->object.sha1);
+
+ log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers);
+
+ committer = git_committer_info(0);
+
+ msg = body;
+ strbuf_init(&sb, 0);
+ pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
+ encoding);
+ pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
+ encoding, 0);
+ pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
+ printf("%s\n", sb.buf);
+
+ strbuf_release(&sb);
+
+ shortlog_init(&log);
+ for (i = 0; i < nr; i++)
+ shortlog_add_commit(&log, list[i]);
+
+ shortlog_output(&log);
+
+ /*
+ * We can only do diffstat with a unique reference point
+ */
+ if (!origin)
+ return;
+
+ diff_setup(&opts);
+ opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+
+ diff_setup_done(&opts);
+
+ diff_tree_sha1(origin->tree->object.sha1,
+ head->tree->object.sha1,
+ "", &opts);
+ diffcore_std(&opts);
+ diff_flush(&opts);
+
+ printf("\n");
}
static const char *clean_message_id(const char *msg_id)
@@ -641,11 +743,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int subject_prefix = 0;
int ignore_if_in_upstream = 0;
int thread = 0;
+ int cover_letter = 0;
+ int boundary_count = 0;
+ struct commit *origin = NULL, *head = NULL;
const char *in_reply_to = NULL;
struct patch_ids ids;
char *add_signoff = NULL;
- char message_id[1024];
- char ref_message_id[1024];
+ struct strbuf buf;
git_config(git_format_config);
init_revisions(&rev, prefix);
@@ -658,7 +762,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
rev.subject_prefix = fmt_patch_subject_prefix;
- rev.extra_headers = extra_headers;
/*
* Parse the arguments before setup_revisions(), or something
@@ -686,6 +789,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
die("Need a number for --start-number");
start_number = strtol(argv[i], NULL, 10);
}
+ else if (!prefixcmp(argv[i], "--cc=")) {
+ ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+ extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5);
+ }
else if (!strcmp(argv[i], "-k") ||
!strcmp(argv[i], "--keep-subject")) {
keep_subject = 1;
@@ -742,11 +849,44 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.subject_prefix = argv[i] + 17;
} else if (!prefixcmp(argv[i], "--suffix="))
fmt_patch_suffix = argv[i] + 9;
+ else if (!strcmp(argv[i], "--cover-letter"))
+ cover_letter = 1;
else
argv[j++] = argv[i];
}
argc = j;
+ strbuf_init(&buf, 0);
+
+ for (i = 0; i < extra_hdr_nr; i++) {
+ strbuf_addstr(&buf, extra_hdr[i]);
+ strbuf_addch(&buf, '\n');
+ }
+
+ if (extra_to_nr)
+ strbuf_addstr(&buf, "To: ");
+ for (i = 0; i < extra_to_nr; i++) {
+ if (i)
+ strbuf_addstr(&buf, " ");
+ strbuf_addstr(&buf, extra_to[i]);
+ if (i + 1 < extra_to_nr)
+ strbuf_addch(&buf, ',');
+ strbuf_addch(&buf, '\n');
+ }
+
+ if (extra_cc_nr)
+ strbuf_addstr(&buf, "Cc: ");
+ for (i = 0; i < extra_cc_nr; i++) {
+ if (i)
+ strbuf_addstr(&buf, " ");
+ strbuf_addstr(&buf, extra_cc[i]);
+ if (i + 1 < extra_cc_nr)
+ strbuf_addch(&buf, ',');
+ strbuf_addch(&buf, '\n');
+ }
+
+ rev.extra_headers = strbuf_detach(&buf, 0);
+
if (start_number < 0)
start_number = 1;
if (numbered && keep_subject)
@@ -793,6 +933,18 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* get_revision() to do the usual traversal.
*/
}
+ if (cover_letter) {
+ /* remember the range */
+ int i;
+ for (i = 0; i < rev.pending.nr; i++) {
+ struct object *o = rev.pending.objects[i].item;
+ if (!(o->flags & UNINTERESTING))
+ head = (struct commit *)o;
+ }
+ /* We can't generate a cover letter without any patches */
+ if (!head)
+ return 0;
+ }
if (ignore_if_in_upstream)
get_patch_ids(&rev, &ids, prefix);
@@ -802,7 +954,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (prepare_revision_walk(&rev))
die("revision walk setup failed");
+ rev.boundary = 1;
while ((commit = get_revision(&rev)) != NULL) {
+ if (commit->object.flags & BOUNDARY) {
+ boundary_count++;
+ origin = (boundary_count == 1) ? commit : NULL;
+ continue;
+ }
+
/* ignore merges */
if (commit->parents && commit->parents->next)
continue;
@@ -820,29 +979,42 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
numbered = 1;
if (numbered)
rev.total = total + start_number - 1;
- rev.add_signoff = add_signoff;
if (in_reply_to)
rev.ref_message_id = clean_message_id(in_reply_to);
+ if (cover_letter) {
+ if (thread)
+ gen_message_id(&rev, "cover");
+ make_cover_letter(&rev, use_stdout, numbered, numbered_files,
+ origin, nr, list, head);
+ total++;
+ start_number--;
+ }
+ rev.add_signoff = add_signoff;
while (0 <= --nr) {
int shown;
commit = list[nr];
rev.nr = total - nr + (start_number - 1);
/* Make the second and subsequent mails replies to the first */
if (thread) {
- if (nr == (total - 2)) {
- strncpy(ref_message_id, message_id,
- sizeof(ref_message_id));
- ref_message_id[sizeof(ref_message_id)-1]='\0';
- rev.ref_message_id = ref_message_id;
+ /* Have we already had a message ID? */
+ if (rev.message_id) {
+ /*
+ * If we've got the ID to be a reply
+ * to, discard the current ID;
+ * otherwise, make everything a reply
+ * to that.
+ */
+ if (rev.ref_message_id)
+ free(rev.message_id);
+ else
+ rev.ref_message_id = rev.message_id;
}
- gen_message_id(message_id, sizeof(message_id),
- sha1_to_hex(commit->object.sha1));
- rev.message_id = message_id;
+ gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
}
- if (!use_stdout)
- if (reopen_stdout(commit, rev.nr, keep_subject,
- numbered_files))
- die("Failed to create output files");
+ if (!use_stdout && reopen_stdout(numbered_files ? NULL :
+ get_oneline_for_filename(commit, keep_subject),
+ rev.nr, rev.total))
+ die("Failed to create output files");
shown = log_tree_commit(&rev, commit);
free(commit->buffer);
commit->buffer = NULL;
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 58deb62..adce6d4 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -46,7 +46,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
}
ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
- &xpp, XDL_MERGE_ZEALOUS, &result);
+ &xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
for (i = 0; i < 3; i++)
free(mmfs[i].ptr);
diff --git a/merge-recursive.c b/builtin-merge-recursive.c
index 55ef76f..6fe4102 100644
--- a/merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -7,6 +7,7 @@
#include "cache-tree.h"
#include "commit.h"
#include "blob.h"
+#include "builtin.h"
#include "tree-walk.h"
#include "diff.h"
#include "diffcore.h"
@@ -17,6 +18,7 @@
#include "xdiff-interface.h"
#include "interpolate.h"
#include "attr.h"
+#include "merge-recursive.h"
static int subtree_merge;
@@ -221,22 +223,11 @@ static int git_merge_trees(int index_only,
return rc;
}
-static int unmerged_index(void)
-{
- int i;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (ce_stage(ce))
- return 1;
- }
- return 0;
-}
-
-static struct tree *git_write_tree(void)
+struct tree *write_tree_from_memory(void)
{
struct tree *result = NULL;
- if (unmerged_index()) {
+ if (unmerged_cache()) {
int i;
output(0, "There are unmerged index entries:");
for (i = 0; i < active_nr; i++) {
@@ -1496,12 +1487,12 @@ static int process_entry(const char *path, struct stage_data *entry,
return clean_merge;
}
-static int merge_trees(struct tree *head,
- struct tree *merge,
- struct tree *common,
- const char *branch1,
- const char *branch2,
- struct tree **result)
+int merge_trees(struct tree *head,
+ struct tree *merge,
+ struct tree *common,
+ const char *branch1,
+ const char *branch2,
+ struct tree **result)
{
int code, clean;
@@ -1523,7 +1514,7 @@ static int merge_trees(struct tree *head,
sha1_to_hex(head->object.sha1),
sha1_to_hex(merge->object.sha1));
- if (unmerged_index()) {
+ if (unmerged_cache()) {
struct path_list *entries, *re_head, *re_merge;
int i;
path_list_clear(&current_file_set, 1);
@@ -1553,7 +1544,7 @@ static int merge_trees(struct tree *head,
clean = 1;
if (index_only)
- *result = git_write_tree();
+ *result = write_tree_from_memory();
return clean;
}
@@ -1573,12 +1564,12 @@ static struct commit_list *reverse_commit_list(struct commit_list *list)
* Merge the commits h1 and h2, return the resulting virtual
* commit object and a flag indicating the cleanness of the merge.
*/
-static int merge(struct commit *h1,
- struct commit *h2,
- const char *branch1,
- const char *branch2,
- struct commit_list *ca,
- struct commit **result)
+int merge_recursive(struct commit *h1,
+ struct commit *h2,
+ const char *branch1,
+ const char *branch2,
+ struct commit_list *ca,
+ struct commit **result)
{
struct commit_list *iter;
struct commit *merged_common_ancestors;
@@ -1623,11 +1614,11 @@ static int merge(struct commit *h1,
* "conflicts" were already resolved.
*/
discard_cache();
- merge(merged_common_ancestors, iter->item,
- "Temporary merge branch 1",
- "Temporary merge branch 2",
- NULL,
- &merged_common_ancestors);
+ merge_recursive(merged_common_ancestors, iter->item,
+ "Temporary merge branch 1",
+ "Temporary merge branch 2",
+ NULL,
+ &merged_common_ancestors);
call_depth--;
if (!merged_common_ancestors)
@@ -1698,7 +1689,7 @@ static int merge_config(const char *var, const char *value)
return git_default_config(var, value);
}
-int main(int argc, char *argv[])
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
{
static const char *bases[20];
static unsigned bases_count = 0;
@@ -1752,7 +1743,7 @@ int main(int argc, char *argv[])
struct commit *ancestor = get_ref(bases[i]);
ca = commit_list_insert(ancestor, &ca);
}
- clean = merge(h1, h2, branch1, branch2, ca, &result);
+ clean = merge_recursive(h1, h2, branch1, branch2, ca, &result);
if (active_cache_changed &&
(write_cache(index_fd, active_cache, active_nr) ||
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index d2bb12e..1bba6e6 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -16,6 +16,7 @@
#include "progress.h"
#ifdef THREADED_DELTA_SEARCH
+#include "thread-utils.h"
#include <pthread.h>
#endif
@@ -1428,8 +1429,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
* accounting lock. Compiler will optimize the strangeness
* away when THREADED_DELTA_SEARCH is not defined.
*/
- if (trg_entry->delta_data)
- free(trg_entry->delta_data);
+ free(trg_entry->delta_data);
cache_lock();
if (trg_entry->delta_data) {
delta_cache_size -= trg_entry->delta_size;
@@ -1852,11 +1852,11 @@ static int git_pack_config(const char *k, const char *v)
}
if (!strcmp(k, "pack.threads")) {
delta_search_threads = git_config_int(k, v);
- if (delta_search_threads < 1)
+ if (delta_search_threads < 0)
die("invalid number of threads specified (%d)",
delta_search_threads);
#ifndef THREADED_DELTA_SEARCH
- if (delta_search_threads > 1)
+ if (delta_search_threads != 1)
warning("no threads support, ignoring %s", k);
#endif
return 0;
@@ -2122,10 +2122,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (!prefixcmp(arg, "--threads=")) {
char *end;
delta_search_threads = strtoul(arg+10, &end, 0);
- if (!arg[10] || *end || delta_search_threads < 1)
+ if (!arg[10] || *end || delta_search_threads < 0)
usage(pack_usage);
#ifndef THREADED_DELTA_SEARCH
- if (delta_search_threads > 1)
+ if (delta_search_threads != 1)
warning("no threads support, "
"ignoring %s", arg);
#endif
@@ -2235,6 +2235,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (!pack_to_stdout && thin)
die("--thin cannot be used to build an indexable pack.");
+#ifdef THREADED_DELTA_SEARCH
+ if (!delta_search_threads) /* --threads=0 means autodetect */
+ delta_search_threads = online_cpus();
+#endif
+
prepare_packed_git();
if (progress)
diff --git a/builtin-push.c b/builtin-push.c
index 9f727c0..b68c681 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -44,15 +44,6 @@ static void set_refspecs(const char **refs, int nr)
strcat(tag, refs[i]);
ref = tag;
}
- if (!strcmp("HEAD", ref)) {
- unsigned char sha1_dummy[20];
- ref = resolve_ref(ref, sha1_dummy, 1, NULL);
- if (!ref)
- die("HEAD cannot be resolved.");
- if (prefixcmp(ref, "refs/heads/"))
- die("HEAD cannot be resolved to branch.");
- ref = xstrdup(ref + 11);
- }
add_refspec(ref);
}
}
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 7bdc312..0138f5a 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -269,7 +269,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
parse_tree(tree);
init_tree_desc(t+i, tree->buffer, tree->size);
}
- unpack_trees(nr_trees, t, &opts);
+ if (unpack_trees(nr_trees, t, &opts))
+ return 128;
/*
* When reading only one tree (either the most basic form,
diff --git a/builtin-reflog.c b/builtin-reflog.c
index 4836ec9..ab53c8c 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -276,10 +276,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
finish:
if (cb.newlog) {
- if (fclose(cb.newlog))
+ if (fclose(cb.newlog)) {
status |= error("%s: %s", strerror(errno),
newlog_path);
- if (rename(newlog_path, log_file)) {
+ unlink(newlog_path);
+ } else if (rename(newlog_path, log_file)) {
status |= error("cannot rename %s to %s",
newlog_path, log_file);
unlink(newlog_path);
diff --git a/builtin-rerere.c b/builtin-rerere.c
index b0c17bd..c607aad 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -267,23 +267,6 @@ static int diff_two(const char *file1, const char *label1,
return 0;
}
-static int copy_file(const char *src, const char *dest)
-{
- FILE *in, *out;
- char buffer[32768];
- int count;
-
- if (!(in = fopen(src, "r")))
- return error("Could not open %s", src);
- if (!(out = fopen(dest, "w")))
- return error("Could not open %s", dest);
- while ((count = fread(buffer, 1, sizeof(buffer), in)))
- fwrite(buffer, 1, count, out);
- fclose(in);
- fclose(out);
- return 0;
-}
-
static int do_plain_rerere(struct path_list *rr, int fd)
{
struct path_list conflict = { NULL, 0, 0, 1 };
@@ -343,7 +326,7 @@ static int do_plain_rerere(struct path_list *rr, int fd)
continue;
fprintf(stderr, "Recorded resolution for '%s'.\n", path);
- copy_file(path, rr_path(name, "postimage"));
+ copy_file(rr_path(name, "postimage"), path, 0666);
tail_optimization:
if (i < rr->nr - 1)
memmove(rr->items + i,
diff --git a/builtin-reset.c b/builtin-reset.c
index 7ee811f..bb3e192 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -16,6 +16,7 @@
#include "diff.h"
#include "diffcore.h"
#include "tree.h"
+#include "branch.h"
static const char builtin_reset_usage[] =
"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
@@ -44,18 +45,6 @@ static inline int is_merge(void)
return !access(git_path("MERGE_HEAD"), F_OK);
}
-static int unmerged_files(void)
-{
- int i;
- read_cache();
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (ce_stage(ce))
- return 1;
- }
- return 0;
-}
-
static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
{
int i = 0;
@@ -74,14 +63,10 @@ static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
static void print_new_head_line(struct commit *commit)
{
- const char *hex, *dots = "...", *body;
+ const char *hex, *body;
hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
- if (!hex) {
- hex = sha1_to_hex(commit->object.sha1);
- dots = "";
- }
- printf("HEAD is now at %s%s", hex, dots);
+ printf("HEAD is now at %s", hex);
body = strstr(commit->buffer, "\n\n");
if (body) {
const char *eol;
@@ -250,7 +235,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
* at all, but requires them in a good order. Other resets reset
* the index file to the tree object we are switching to. */
if (reset_type == SOFT) {
- if (is_merge() || unmerged_files())
+ if (is_merge() || read_cache() < 0 || unmerged_cache())
die("Cannot do a soft reset in the middle of a merge.");
}
else if (reset_index_file(sha1, (reset_type == HARD)))
@@ -282,10 +267,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
break;
}
- unlink(git_path("MERGE_HEAD"));
- unlink(git_path("rr-cache/MERGE_RR"));
- unlink(git_path("MERGE_MSG"));
- unlink(git_path("SQUASH_MSG"));
+ remove_branch_state();
free(reflog_action);
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 6f7d5f8..6754e7f 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -25,6 +25,9 @@ static const char rev_list_usage[] =
" --no-merges\n"
" --remove-empty\n"
" --all\n"
+" --branches\n"
+" --tags\n"
+" --remotes\n"
" --stdin\n"
" --quiet\n"
" ordering output:\n"
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index b9af1a5..90dbb9d 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -315,7 +315,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
s = strchr(sb.buf, ' ');
if (!s || *sb.buf == ' ') {
o->type = OPTION_GROUP;
- o->help = xstrdup(skipspaces(s));
+ o->help = xstrdup(skipspaces(sb.buf));
continue;
}
diff --git a/builtin-revert.c b/builtin-revert.c
index e219859..b6dee6a 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -397,8 +397,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
else
return execl_git_cmd("commit", "-n", "-F", defmsg, NULL);
}
- if (reencoded_message)
- free(reencoded_message);
+ free(reencoded_message);
return 0;
}
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 8afb1d0..930e0fb 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -71,6 +71,7 @@ static int pack_objects(int fd, struct ref *refs)
refs = refs->next;
}
+ close(po.in);
if (finish_command(&po))
return error("pack-objects died with strange error");
return 0;
@@ -263,9 +264,7 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
static const char *status_abbrev(unsigned char sha1[20])
{
- const char *abbrev;
- abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
- return abbrev ? abbrev : sha1_to_hex(sha1);
+ return find_unique_abbrev(sha1, DEFAULT_ABBREV);
}
static void print_ok_ref_status(struct ref *ref)
@@ -403,12 +402,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
if (!remote_tail)
remote_tail = &remote_refs;
if (match_refs(local_refs, remote_refs, &remote_tail,
- nr_refspec, refspec, flags))
+ nr_refspec, refspec, flags)) {
+ close(out);
return -1;
+ }
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
"Perhaps you should specify a branch such as 'master'.\n");
+ close(out);
return 0;
}
@@ -495,12 +497,11 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
packet_flush(out);
if (new_refs && !args.dry_run) {
- if (pack_objects(out, remote_refs) < 0) {
- close(out);
+ if (pack_objects(out, remote_refs) < 0)
return -1;
- }
}
- close(out);
+ else
+ close(out);
if (expect_status_report)
ret = receive_status(in, remote_refs);
@@ -648,7 +649,7 @@ int send_pack(struct send_pack_args *my_args,
conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
close(fd[0]);
- close(fd[1]);
+ /* do_send_pack always closes fd[1] */
ret |= finish_connect(conn);
return !!ret;
}
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 0055a57..af31aba 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -6,13 +6,11 @@
#include "revision.h"
#include "utf8.h"
#include "mailmap.h"
+#include "shortlog.h"
static const char shortlog_usage[] =
"git-shortlog [-n] [-s] [-e] [<commit-id>... ]";
-static char *common_repo_prefix;
-static int email;
-
static int compare_by_number(const void *a1, const void *a2)
{
const struct path_list_item *i1 = a1, *i2 = a2;
@@ -26,13 +24,11 @@ static int compare_by_number(const void *a1, const void *a2)
return -1;
}
-static struct path_list mailmap = {NULL, 0, 0, 0};
-
-static void insert_one_record(struct path_list *list,
+static void insert_one_record(struct shortlog *log,
const char *author,
const char *oneline)
{
- const char *dot3 = common_repo_prefix;
+ const char *dot3 = log->common_repo_prefix;
char *buffer, *p;
struct path_list_item *item;
struct path_list *onelines;
@@ -47,7 +43,7 @@ static void insert_one_record(struct path_list *list,
eoemail = strchr(boemail, '>');
if (!eoemail)
return;
- if (!map_email(&mailmap, boemail+1, namebuf, sizeof(namebuf))) {
+ if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
while (author < boemail && isspace(*author))
author++;
for (len = 0;
@@ -61,14 +57,14 @@ static void insert_one_record(struct path_list *list,
else
len = strlen(namebuf);
- if (email) {
+ if (log->email) {
size_t room = sizeof(namebuf) - len - 1;
int maillen = eoemail - boemail + 1;
snprintf(namebuf + len, room, " %.*s", maillen, boemail);
}
buffer = xstrdup(namebuf);
- item = path_list_insert(buffer, list);
+ item = path_list_insert(buffer, &log->list);
if (item->util == NULL)
item->util = xcalloc(1, sizeof(struct path_list));
else
@@ -114,7 +110,7 @@ static void insert_one_record(struct path_list *list,
onelines->items[onelines->nr++].path = buffer;
}
-static void read_from_stdin(struct path_list *list)
+static void read_from_stdin(struct shortlog *log)
{
char author[1024], oneline[1024];
@@ -128,39 +124,43 @@ static void read_from_stdin(struct path_list *list)
while (fgets(oneline, sizeof(oneline), stdin) &&
oneline[0] == '\n')
; /* discard blanks */
- insert_one_record(list, author + 8, oneline);
+ insert_one_record(log, author + 8, oneline);
}
}
-static void get_from_rev(struct rev_info *rev, struct path_list *list)
+void shortlog_add_commit(struct shortlog *log, struct commit *commit)
{
- struct commit *commit;
-
- if (prepare_revision_walk(rev))
- die("revision walk setup failed");
- while ((commit = get_revision(rev)) != NULL) {
- const char *author = NULL, *buffer;
+ const char *author = NULL, *buffer;
- buffer = commit->buffer;
- while (*buffer && *buffer != '\n') {
- const char *eol = strchr(buffer, '\n');
+ buffer = commit->buffer;
+ while (*buffer && *buffer != '\n') {
+ const char *eol = strchr(buffer, '\n');
- if (eol == NULL)
- eol = buffer + strlen(buffer);
- else
- eol++;
+ if (eol == NULL)
+ eol = buffer + strlen(buffer);
+ else
+ eol++;
- if (!prefixcmp(buffer, "author "))
- author = buffer + 7;
- buffer = eol;
- }
- if (!author)
- die("Missing author: %s",
- sha1_to_hex(commit->object.sha1));
- if (*buffer)
- buffer++;
- insert_one_record(list, author, !*buffer ? "<none>" : buffer);
+ if (!prefixcmp(buffer, "author "))
+ author = buffer + 7;
+ buffer = eol;
}
+ if (!author)
+ die("Missing author: %s",
+ sha1_to_hex(commit->object.sha1));
+ if (*buffer)
+ buffer++;
+ insert_one_record(log, author, !*buffer ? "<none>" : buffer);
+}
+
+static void get_from_rev(struct rev_info *rev, struct shortlog *log)
+{
+ struct commit *commit;
+
+ if (prepare_revision_walk(rev))
+ die("revision walk setup failed");
+ while ((commit = get_revision(rev)) != NULL)
+ shortlog_add_commit(log, commit);
}
static int parse_uint(char const **arg, int comma)
@@ -212,29 +212,38 @@ static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
die(wrap_arg_usage);
}
+void shortlog_init(struct shortlog *log)
+{
+ memset(log, 0, sizeof(*log));
+
+ read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
+
+ log->list.strdup_paths = 1;
+ log->wrap = DEFAULT_WRAPLEN;
+ log->in1 = DEFAULT_INDENT1;
+ log->in2 = DEFAULT_INDENT2;
+}
+
int cmd_shortlog(int argc, const char **argv, const char *prefix)
{
+ struct shortlog log;
struct rev_info rev;
- struct path_list list = { NULL, 0, 0, 1 };
- int i, j, sort_by_number = 0, summary = 0;
- int wrap_lines = 0;
- int wrap = DEFAULT_WRAPLEN;
- int in1 = DEFAULT_INDENT1;
- int in2 = DEFAULT_INDENT2;
+
+ shortlog_init(&log);
/* since -n is a shadowed rev argument, parse our args first */
while (argc > 1) {
if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
- sort_by_number = 1;
+ log.sort_by_number = 1;
else if (!strcmp(argv[1], "-s") ||
!strcmp(argv[1], "--summary"))
- summary = 1;
+ log.summary = 1;
else if (!strcmp(argv[1], "-e") ||
!strcmp(argv[1], "--email"))
- email = 1;
+ log.email = 1;
else if (!prefixcmp(argv[1], "-w")) {
- wrap_lines = 1;
- parse_wrap_args(argv[1], &in1, &in2, &wrap);
+ log.wrap_lines = 1;
+ parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
}
else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
usage(shortlog_usage);
@@ -248,34 +257,38 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
if (argc > 1)
die ("unrecognized argument: %s", argv[1]);
- read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
-
/* assume HEAD if from a tty */
if (!rev.pending.nr && isatty(0))
add_head_to_pending(&rev);
if (rev.pending.nr == 0) {
- read_from_stdin(&list);
+ read_from_stdin(&log);
}
else
- get_from_rev(&rev, &list);
+ get_from_rev(&rev, &log);
- if (sort_by_number)
- qsort(list.items, list.nr, sizeof(struct path_list_item),
- compare_by_number);
+ shortlog_output(&log);
+ return 0;
+}
- for (i = 0; i < list.nr; i++) {
- struct path_list *onelines = list.items[i].util;
+void shortlog_output(struct shortlog *log)
+{
+ int i, j;
+ if (log->sort_by_number)
+ qsort(log->list.items, log->list.nr, sizeof(struct path_list_item),
+ compare_by_number);
+ for (i = 0; i < log->list.nr; i++) {
+ struct path_list *onelines = log->list.items[i].util;
- if (summary) {
- printf("%6d\t%s\n", onelines->nr, list.items[i].path);
+ if (log->summary) {
+ printf("%6d\t%s\n", onelines->nr, log->list.items[i].path);
} else {
- printf("%s (%d):\n", list.items[i].path, onelines->nr);
+ printf("%s (%d):\n", log->list.items[i].path, onelines->nr);
for (j = onelines->nr - 1; j >= 0; j--) {
const char *msg = onelines->items[j].path;
- if (wrap_lines) {
- int col = print_wrapped_text(msg, in1, in2, wrap);
- if (col != wrap)
+ if (log->wrap_lines) {
+ int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap);
+ if (col != log->wrap)
putchar('\n');
}
else
@@ -287,13 +300,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
onelines->strdup_paths = 1;
path_list_clear(onelines, 1);
free(onelines);
- list.items[i].util = NULL;
+ log->list.items[i].util = NULL;
}
- list.strdup_paths = 1;
- path_list_clear(&list, 1);
- mailmap.strdup_paths = 1;
- path_list_clear(&mailmap, 1);
-
- return 0;
+ log->list.strdup_paths = 1;
+ path_list_clear(&log->list, 1);
+ log->mailmap.strdup_paths = 1;
+ path_list_clear(&log->mailmap, 1);
}
diff --git a/builtin-tag.c b/builtin-tag.c
index 716b4ff..28c36fd 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -226,12 +226,13 @@ static int do_sign(struct strbuf *buffer)
if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
close(gpg.in);
+ close(gpg.out);
finish_command(&gpg);
return error("gpg did not accept the tag data");
}
close(gpg.in);
- gpg.close_in = 0;
len = strbuf_read(buffer, gpg.out, 1024);
+ close(gpg.out);
if (finish_command(&gpg) || !len || len < 0)
return error("gpg failed to sign the tag");
diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c
index cc4c55d..f3ef11f 100644
--- a/builtin-verify-tag.c
+++ b/builtin-verify-tag.c
@@ -45,14 +45,12 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
memset(&gpg, 0, sizeof(gpg));
gpg.argv = args_gpg;
gpg.in = -1;
- gpg.out = 1;
args_gpg[2] = path;
if (start_command(&gpg))
return error("could not run gpg.");
write_in_full(gpg.in, buf, len);
close(gpg.in);
- gpg.close_in = 0;
ret = finish_command(&gpg);
unlink(path);
diff --git a/builtin.h b/builtin.h
index 3d1628c..674c8a1 100644
--- a/builtin.h
+++ b/builtin.h
@@ -18,6 +18,7 @@ extern int cmd_blame(int argc, const char **argv, const char *prefix);
extern int cmd_branch(int argc, const char **argv, const char *prefix);
extern int cmd_bundle(int argc, const char **argv, const char *prefix);
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
+extern int cmd_checkout(int argc, const char **argv, const char *prefix);
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
@@ -56,6 +57,7 @@ extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
+extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
extern int cmd_mv(int argc, const char **argv, const char *prefix);
extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
diff --git a/bundle.c b/bundle.c
index bd12ec8..0ba5df1 100644
--- a/bundle.c
+++ b/bundle.c
@@ -333,10 +333,12 @@ int create_bundle(struct bundle_header *header, const char *path,
write_or_die(rls.in, sha1_to_hex(object->sha1), 40);
write_or_die(rls.in, "\n", 1);
}
+ close(rls.in);
if (finish_command(&rls))
return error ("pack-objects died");
-
- return bundle_to_stdout ? close(bundle_fd) : commit_lock_file(&lock);
+ if (!bundle_to_stdout)
+ commit_lock_file(&lock);
+ return 0;
}
int unbundle(struct bundle_header *header, int bundle_fd)
diff --git a/cache.h b/cache.h
index 4fa69f0..f16d341 100644
--- a/cache.h
+++ b/cache.h
@@ -252,6 +252,7 @@ extern struct index_state the_index;
#define read_cache_from(path) read_index_from(&the_index, (path))
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
#define discard_cache() discard_index(&the_index)
+#define unmerged_cache() unmerged_index(&the_index)
#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
#define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
@@ -346,6 +347,7 @@ extern int read_index(struct index_state *);
extern int read_index_from(struct index_state *, const char *path);
extern int write_index(struct index_state *, int newfd);
extern int discard_index(struct index_state *);
+extern int unmerged_index(struct index_state *);
extern int verify_path(const char *path);
extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
extern int index_name_pos(struct index_state *, const char *name, int namelen);
@@ -423,6 +425,15 @@ enum safe_crlf {
extern enum safe_crlf safe_crlf;
+enum branch_track {
+ BRANCH_TRACK_NEVER = 0,
+ BRANCH_TRACK_REMOTE,
+ BRANCH_TRACK_ALWAYS,
+ BRANCH_TRACK_EXPLICIT,
+};
+
+extern enum branch_track git_branch_track;
+
#define GIT_REPO_VERSION 0
extern int repository_format_version;
extern int check_repository_format(void);
@@ -698,6 +709,7 @@ extern const char *git_log_output_encoding;
/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);
extern int copy_fd(int ifd, int ofd);
+extern int copy_file(const char *dst, const char *src, int mode);
extern int read_in_full(int fd, void *buf, size_t count);
extern int write_in_full(int fd, const void *buf, size_t count);
extern void write_or_die(int fd, const void *buf, size_t count);
@@ -767,4 +779,6 @@ int pathspec_match(const char **spec, char *matched, const char *filename, int s
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
+char *alias_lookup(const char *alias);
+
#endif /* CACHE_H */
diff --git a/commit.h b/commit.h
index 10e2b5d..80d65b9 100644
--- a/commit.h
+++ b/commit.h
@@ -71,6 +71,21 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
int abbrev, const char *subject,
const char *after_subject, enum date_mode,
int non_ascii_present);
+void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
+ const char *line, enum date_mode dmode,
+ const char *encoding);
+void pp_title_line(enum cmit_fmt fmt,
+ const char **msg_p,
+ struct strbuf *sb,
+ const char *subject,
+ const char *after_subject,
+ const char *encoding,
+ int plain_non_ascii);
+void pp_remainder(enum cmit_fmt fmt,
+ const char **msg_p,
+ struct strbuf *sb,
+ int indent);
+
/** Removes the first commit from a list sorted by date, and adds all
* of its parents.
diff --git a/config.c b/config.c
index cba2bcf..0624494 100644
--- a/config.c
+++ b/config.c
@@ -471,6 +471,14 @@ int git_default_config(const char *var, const char *value)
whitespace_rule_cfg = parse_whitespace_rule(value);
return 0;
}
+ if (!strcmp(var, "branch.autosetupmerge")) {
+ if (value && !strcasecmp(value, "always")) {
+ git_branch_track = BRANCH_TRACK_ALWAYS;
+ return 0;
+ }
+ git_branch_track = git_config_bool(var, value);
+ return 0;
+ }
/* Add other config variables here and to Documentation/config.txt. */
return 0;
diff --git a/connect.c b/connect.c
index 5ac3572..d12b105 100644
--- a/connect.c
+++ b/connect.c
@@ -68,8 +68,7 @@ struct ref **get_remote_heads(int in, struct ref **list,
name_len = strlen(name);
if (len != name_len + 41) {
- if (server_capabilities)
- free(server_capabilities);
+ free(server_capabilities);
server_capabilities = xstrdup(name + name_len + 1);
}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 4ea727b..8f70e1e 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -91,7 +91,10 @@ __git_ps1 ()
fi
if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
then
- b="$(cut -c1-7 $g/HEAD)..."
+ if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
+ then
+ b="$(cut -c1-7 $g/HEAD)..."
+ fi
fi
fi
@@ -645,6 +648,7 @@ _git_format_patch ()
--in-reply-to=
--full-index --binary
--not --all
+ --cover-letter
"
return
;;
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index f69b697..c926823 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -728,7 +728,7 @@ Return the list of files that haven't been handled."
(defun git-run-ls-files-with-excludes (status files default-state &rest options)
"Run git-ls-files on FILES with appropriate --exclude-from options."
(let ((exclude-files (git-get-exclude-files)))
- (apply #'git-run-ls-files status files default-state "--directory"
+ (apply #'git-run-ls-files status files default-state "--directory" "--no-empty-directory"
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
(append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
@@ -1545,7 +1545,7 @@ Commands:
(with-current-buffer buffer
(when (and list-buffers-directory
(string-equal fulldir (expand-file-name list-buffers-directory))
- (string-match "\\*git-status\\*$" (buffer-name buffer)))
+ (eq major-mode 'git-status-mode))
(setq found buffer))))
(setq list (cdr list)))
found))
diff --git a/git-checkout.sh b/contrib/examples/git-checkout.sh
index 1a7689a..1a7689a 100755
--- a/git-checkout.sh
+++ b/contrib/examples/git-checkout.sh
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 781a0cb..be96600 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -464,71 +464,47 @@ class P4Submit(Command):
def __init__(self):
Command.__init__(self)
self.options = [
- optparse.make_option("--continue", action="store_false", dest="firstTime"),
optparse.make_option("--verbose", dest="verbose", action="store_true"),
optparse.make_option("--origin", dest="origin"),
- optparse.make_option("--reset", action="store_true", dest="reset"),
- optparse.make_option("--log-substitutions", dest="substFile"),
- optparse.make_option("--direct", dest="directSubmit", action="store_true"),
optparse.make_option("-M", dest="detectRename", action="store_true"),
]
self.description = "Submit changes from git to the perforce depot."
self.usage += " [name of git branch to submit into perforce depot]"
- self.firstTime = True
- self.reset = False
self.interactive = True
- self.substFile = ""
- self.firstTime = True
self.origin = ""
- self.directSubmit = False
self.detectRename = False
self.verbose = False
self.isWindows = (platform.system() == "Windows")
- self.logSubstitutions = {}
- self.logSubstitutions["<enter description here>"] = "%log%"
- self.logSubstitutions["\tDetails:"] = "\tDetails: %log%"
-
def check(self):
if len(p4CmdList("opened ...")) > 0:
die("You have files opened with perforce! Close them before starting the sync.")
- def start(self):
- if len(self.config) > 0 and not self.reset:
- die("Cannot start sync. Previous sync config found at %s\n"
- "If you want to start submitting again from scratch "
- "maybe you want to call git-p4 submit --reset" % self.configFile)
-
- commits = []
- if self.directSubmit:
- commits.append("0")
- else:
- for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
- commits.append(line.strip())
- commits.reverse()
-
- self.config["commits"] = commits
-
+ # replaces everything between 'Description:' and the next P4 submit template field with the
+ # commit message
def prepareLogMessage(self, template, message):
result = ""
+ inDescriptionSection = False
+
for line in template.split("\n"):
if line.startswith("#"):
result += line + "\n"
continue
- substituted = False
- for key in self.logSubstitutions.keys():
- if line.find(key) != -1:
- value = self.logSubstitutions[key]
- value = value.replace("%log%", message)
- if value != "@remove@":
- result += line.replace(key, value) + "\n"
- substituted = True
- break
+ if inDescriptionSection:
+ if line.startswith("Files:"):
+ inDescriptionSection = False
+ else:
+ continue
+ else:
+ if line.startswith("Description:"):
+ inDescriptionSection = True
+ line += "\n"
+ for messageLine in message.split("\n"):
+ line += "\t" + messageLine + "\n"
- if not substituted:
- result += line + "\n"
+ result += line + "\n"
return result
@@ -557,13 +533,9 @@ class P4Submit(Command):
return template
def applyCommit(self, id):
- if self.directSubmit:
- print "Applying local change in working directory/index"
- diff = self.diffStatus
- else:
- print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
- diffOpts = ("", "-M")[self.detectRename]
- diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
+ print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
+ diffOpts = ("", "-M")[self.detectRename]
+ diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
filesToAdd = set()
filesToDelete = set()
editedFiles = set()
@@ -598,10 +570,7 @@ class P4Submit(Command):
else:
die("unknown modifier %s for %s" % (modifier, path))
- if self.directSubmit:
- diffcmd = "cat \"%s\"" % self.diffFile
- else:
- diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
+ diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
patchcmd = diffcmd + " | git apply "
tryPatchCmd = patchcmd + "--check -"
applyPatchCmd = patchcmd + "--check --apply -"
@@ -649,13 +618,10 @@ class P4Submit(Command):
mode = filesToChangeExecBit[f]
setP4ExecBit(f, mode)
- logMessage = ""
- if not self.directSubmit:
- logMessage = extractLogMessageFromGitCommit(id)
- logMessage = logMessage.replace("\n", "\n\t")
- if self.isWindows:
- logMessage = logMessage.replace("\n", "\r\n")
- logMessage = logMessage.strip()
+ logMessage = extractLogMessageFromGitCommit(id)
+ if self.isWindows:
+ logMessage = logMessage.replace("\n", "\r\n")
+ logMessage = logMessage.strip()
template = self.prepareSubmitTemplate()
@@ -694,12 +660,6 @@ class P4Submit(Command):
if self.isWindows:
submitTemplate = submitTemplate.replace("\r\n", "\n")
- if self.directSubmit:
- print "Submitting to git first"
- os.chdir(self.oldWorkingDirectory)
- write_pipe("git commit -a -F -", submitTemplate)
- os.chdir(self.clientPath)
-
write_pipe("p4 submit -i", submitTemplate)
else:
fileName = "submit.txt"
@@ -741,65 +701,33 @@ class P4Submit(Command):
print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
self.oldWorkingDirectory = os.getcwd()
- if self.directSubmit:
- self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD")
- if len(self.diffStatus) == 0:
- print "No changes in working directory to submit."
- return True
- patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD")
- self.diffFile = self.gitdir + "/p4-git-diff"
- f = open(self.diffFile, "wb")
- f.write(patch)
- f.close();
-
os.chdir(self.clientPath)
print "Syncronizing p4 checkout..."
system("p4 sync ...")
- if self.reset:
- self.firstTime = True
-
- if len(self.substFile) > 0:
- for line in open(self.substFile, "r").readlines():
- tokens = line.strip().split("=")
- self.logSubstitutions[tokens[0]] = tokens[1]
-
self.check()
- self.configFile = self.gitdir + "/p4-git-sync.cfg"
- self.config = shelve.open(self.configFile, writeback=True)
-
- if self.firstTime:
- self.start()
- commits = self.config.get("commits", [])
+ commits = []
+ for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
+ commits.append(line.strip())
+ commits.reverse()
while len(commits) > 0:
- self.firstTime = False
commit = commits[0]
commits = commits[1:]
- self.config["commits"] = commits
self.applyCommit(commit)
if not self.interactive:
break
- self.config.close()
-
- if self.directSubmit:
- os.remove(self.diffFile)
-
if len(commits) == 0:
- if self.firstTime:
- print "No changes found to apply between %s and current HEAD" % self.origin
- else:
- print "All changes applied!"
- os.chdir(self.oldWorkingDirectory)
+ print "All changes applied!"
+ os.chdir(self.oldWorkingDirectory)
- sync = P4Sync()
- sync.run([])
+ sync = P4Sync()
+ sync.run([])
- rebase = P4Rebase()
- rebase.rebase()
- os.remove(self.configFile)
+ rebase = P4Rebase()
+ rebase.rebase()
return True
@@ -817,7 +745,9 @@ class P4Sync(Command):
help="Import into refs/heads/ , not refs/remotes"),
optparse.make_option("--max-changes", dest="maxChanges"),
optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
- help="Keep entire BRANCH/DIR/SUBDIR prefix during import")
+ help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
+ optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
+ help="Only sync files that are included in the Perforce Client Spec")
]
self.description = """Imports from Perforce into a git repository.\n
example:
@@ -843,18 +773,27 @@ class P4Sync(Command):
self.keepRepoPath = False
self.depotPaths = None
self.p4BranchesInGit = []
+ self.cloneExclude = []
+ self.useClientSpec = False
+ self.clientSpecDirs = []
if gitConfig("git-p4.syncFromOrigin") == "false":
self.syncWithOrigin = False
def extractFilesFromCommit(self, commit):
+ self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
+ for path in self.cloneExclude]
files = []
fnum = 0
while commit.has_key("depotFile%s" % fnum):
path = commit["depotFile%s" % fnum]
- found = [p for p in self.depotPaths
- if path.startswith (p)]
+ if [p for p in self.cloneExclude
+ if path.startswith (p)]:
+ found = False
+ else:
+ found = [p for p in self.depotPaths
+ if path.startswith (p)]
if not found:
fnum = fnum + 1
continue
@@ -911,11 +850,21 @@ class P4Sync(Command):
## Should move this out, doesn't use SELF.
def readP4Files(self, files):
+ for f in files:
+ for val in self.clientSpecDirs:
+ if f['path'].startswith(val[0]):
+ if val[1] > 0:
+ f['include'] = True
+ else:
+ f['include'] = False
+ break
+
files = [f for f in files
- if f['action'] != 'delete']
+ if f['action'] != 'delete' and
+ (f.has_key('include') == False or f['include'] == True)]
if not files:
- return
+ return []
filedata = p4CmdList('-x - print',
stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
@@ -950,6 +899,7 @@ class P4Sync(Command):
for f in files:
assert not f.has_key('data')
f['data'] = contents[f['path']]
+ return files
def commit(self, details, files, branch, branchPrefixes, parent = ""):
epoch = details["time"]
@@ -966,11 +916,7 @@ class P4Sync(Command):
new_files.append (f)
else:
sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
- files = new_files
- self.readP4Files(files)
-
-
-
+ files = self.readP4Files(new_files)
self.gitStream.write("commit %s\n" % branch)
# gitStream.write("mark :%s\n" % details["change"])
@@ -1385,6 +1331,26 @@ class P4Sync(Command):
print self.gitError.read()
+ def getClientSpec(self):
+ specList = p4CmdList( "client -o" )
+ temp = {}
+ for entry in specList:
+ for k,v in entry.iteritems():
+ if k.startswith("View"):
+ if v.startswith('"'):
+ start = 1
+ else:
+ start = 0
+ index = v.find("...")
+ v = v[start:index]
+ if v.startswith("-"):
+ v = v[1:]
+ temp[v] = -len(v)
+ else:
+ temp[v] = len(v)
+ self.clientSpecDirs = temp.items()
+ self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) )
+
def run(self, args):
self.depotPaths = []
self.changeRange = ""
@@ -1417,6 +1383,9 @@ class P4Sync(Command):
if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
+ if self.useClientSpec or gitConfig("p4.useclientspec") == "true":
+ self.getClientSpec()
+
# TODO: should always look at previous commits,
# merge with previous imports, if possible.
if args == []:
@@ -1634,13 +1603,23 @@ class P4Clone(P4Sync):
P4Sync.__init__(self)
self.description = "Creates a new git repository and imports from Perforce into it"
self.usage = "usage: %prog [options] //depot/path[@revRange]"
- self.options.append(
+ self.options += [
optparse.make_option("--destination", dest="cloneDestination",
action='store', default=None,
- help="where to leave result of the clone"))
+ help="where to leave result of the clone"),
+ optparse.make_option("-/", dest="cloneExclude",
+ action="append", type="string",
+ help="exclude depot path")
+ ]
self.cloneDestination = None
self.needsGit = False
+ # This is required for the "append" cloneExclude action
+ def ensure_value(self, attr, value):
+ if not hasattr(self, attr) or getattr(self, attr) is None:
+ setattr(self, attr, value)
+ return getattr(self, attr)
+
def defaultDestination(self, args):
## TODO: use common prefix of args?
depotPath = args[0]
@@ -1664,6 +1643,7 @@ class P4Clone(P4Sync):
self.cloneDestination = depotPaths[-1]
depotPaths = depotPaths[:-1]
+ self.cloneExclude = ["/"+p for p in self.cloneExclude]
for p in depotPaths:
if not p.startswith("//"):
return False
diff --git a/copy.c b/copy.c
index c225d1b..afc4fbf 100644
--- a/copy.c
+++ b/copy.c
@@ -34,3 +34,24 @@ int copy_fd(int ifd, int ofd)
close(ifd);
return 0;
}
+
+int copy_file(const char *dst, const char *src, int mode)
+{
+ int fdi, fdo, status;
+
+ mode = (mode & 0111) ? 0777 : 0666;
+ if ((fdi = open(src, O_RDONLY)) < 0)
+ return fdi;
+ if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
+ close(fdi);
+ return fdo;
+ }
+ status = copy_fd(fdi, fdo);
+ if (close(fdo) != 0)
+ return error("%s: write error: %s", dst, strerror(errno));
+
+ if (!status && adjust_shared_perm(dst))
+ return -1;
+
+ return status;
+}
diff --git a/daemon.c b/daemon.c
index 41a60af..2b4a6f1 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1149,6 +1149,11 @@ int main(int argc, char **argv)
usage(daemon_usage);
}
+ if (log_syslog) {
+ openlog("git-daemon", 0, LOG_DAEMON);
+ set_die_routine(daemon_die);
+ }
+
if (inetd_mode && (group_name || user_name))
die("--user and --group are incompatible with --inetd");
@@ -1176,14 +1181,17 @@ int main(int argc, char **argv)
}
}
- if (log_syslog) {
- openlog("git-daemon", 0, LOG_DAEMON);
- set_die_routine(daemon_die);
- }
-
if (strict_paths && (!ok_paths || !*ok_paths))
die("option --strict-paths requires a whitelist");
+ if (base_path) {
+ struct stat st;
+
+ if (stat(base_path, &st) || !S_ISDIR(st.st_mode))
+ die("base-path '%s' does not exist or "
+ "is not a directory", base_path);
+ }
+
if (inetd_mode) {
struct sockaddr_storage ss;
struct sockaddr *peer = (struct sockaddr *)&ss;
diff --git a/date.c b/date.c
index 8f70500..a74ed86 100644
--- a/date.c
+++ b/date.c
@@ -213,9 +213,9 @@ static const struct {
{ "EAST", +10, 0, }, /* Eastern Australian Standard */
{ "EADT", +10, 1, }, /* Eastern Australian Daylight */
{ "GST", +10, 0, }, /* Guam Standard, USSR Zone 9 */
- { "NZT", +11, 0, }, /* New Zealand */
- { "NZST", +11, 0, }, /* New Zealand Standard */
- { "NZDT", +11, 1, }, /* New Zealand Daylight */
+ { "NZT", +12, 0, }, /* New Zealand */
+ { "NZST", +12, 0, }, /* New Zealand Standard */
+ { "NZDT", +12, 1, }, /* New Zealand Daylight */
{ "IDLE", +12, 0, }, /* International Date Line East */
};
diff --git a/diff-lib.c b/diff-lib.c
index 03eaa7c..4581b59 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -472,22 +472,21 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
static void diff_index_show_file(struct rev_info *revs,
const char *prefix,
struct cache_entry *ce,
- unsigned char *sha1, unsigned int mode)
+ const unsigned char *sha1, unsigned int mode)
{
diff_addremove(&revs->diffopt, prefix[0], mode,
sha1, ce->name, NULL);
}
static int get_stat_data(struct cache_entry *ce,
- unsigned char **sha1p,
+ const unsigned char **sha1p,
unsigned int *modep,
int cached, int match_missing)
{
- unsigned char *sha1 = ce->sha1;
+ const unsigned char *sha1 = ce->sha1;
unsigned int mode = ce->ce_mode;
if (!cached) {
- static unsigned char no_sha1[20];
int changed;
struct stat st;
if (lstat(ce->name, &st) < 0) {
@@ -501,7 +500,7 @@ static int get_stat_data(struct cache_entry *ce,
changed = ce_match_stat(ce, &st, 0);
if (changed) {
mode = ce_mode_from_stat(ce, st.st_mode);
- sha1 = no_sha1;
+ sha1 = null_sha1;
}
}
@@ -514,7 +513,7 @@ static void show_new_file(struct rev_info *revs,
struct cache_entry *new,
int cached, int match_missing)
{
- unsigned char *sha1;
+ const unsigned char *sha1;
unsigned int mode;
/* New file in the index: it might actually be different in
@@ -533,7 +532,7 @@ static int show_modified(struct rev_info *revs,
int cached, int match_missing)
{
unsigned int mode, oldmode;
- unsigned char *sha1;
+ const unsigned char *sha1;
if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
if (report_missing)
@@ -737,7 +736,8 @@ int run_diff_index(struct rev_info *revs, int cached)
opts.unpack_data = revs;
init_tree_desc(&t, tree->buffer, tree->size);
- unpack_trees(1, &t, &opts);
+ if (unpack_trees(1, &t, &opts))
+ exit(128);
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
@@ -789,6 +789,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
opts.unpack_data = &revs;
init_tree_desc(&t, tree->buffer, tree->size);
- unpack_trees(1, &t, &opts);
+ if (unpack_trees(1, &t, &opts))
+ exit(128);
return 0;
}
diff --git a/diff.c b/diff.c
index 99daca6..00e1590 100644
--- a/diff.c
+++ b/diff.c
@@ -118,8 +118,7 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v
pp->next = funcname_pattern_list;
funcname_pattern_list = pp;
}
- if (pp->pattern)
- free(pp->pattern);
+ free(pp->pattern);
pp->pattern = xstrdup(value);
return 0;
}
@@ -272,8 +271,8 @@ static void print_line_count(int count)
}
}
-static void copy_file(int prefix, const char *data, int size,
- const char *set, const char *reset)
+static void copy_file_with_prefix(int prefix, const char *data, int size,
+ const char *set, const char *reset)
{
int ch, nl_just_seen = 1;
while (0 < size--) {
@@ -331,9 +330,9 @@ static void emit_rewrite_diff(const char *name_a,
print_line_count(lc_b);
printf(" @@%s\n", reset);
if (lc_a)
- copy_file('-', one->data, one->size, old, reset);
+ copy_file_with_prefix('-', one->data, one->size, old, reset);
if (lc_b)
- copy_file('+', two->data, two->size, new, reset);
+ copy_file_with_prefix('+', two->data, two->size, new, reset);
}
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@ -492,10 +491,8 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
ecbdata->diff_words->plus.text.size)
diff_words_show(ecbdata->diff_words);
- if (ecbdata->diff_words->minus.text.ptr)
- free (ecbdata->diff_words->minus.text.ptr);
- if (ecbdata->diff_words->plus.text.ptr)
- free (ecbdata->diff_words->plus.text.ptr);
+ free (ecbdata->diff_words->minus.text.ptr);
+ free (ecbdata->diff_words->plus.text.ptr);
free(ecbdata->diff_words);
ecbdata->diff_words = NULL;
}
@@ -1483,6 +1480,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
}
static void builtin_checkdiff(const char *name_a, const char *name_b,
+ const char *attr_path,
struct diff_filespec *one,
struct diff_filespec *two, struct diff_options *o)
{
@@ -1497,7 +1495,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
data.filename = name_b ? name_b : name_a;
data.lineno = 0;
data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
- data.ws_rule = whitespace_rule(data.filename);
+ data.ws_rule = whitespace_rule(attr_path);
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
@@ -1922,6 +1920,9 @@ static const char *external_diff_attr(const char *name)
{
struct git_attr_check attr_diff_check;
+ if (!name)
+ return NULL;
+
setup_diff_attr_check(&attr_diff_check);
if (!git_checkattr(name, 1, &attr_diff_check)) {
const char *value = attr_diff_check.value;
@@ -1941,6 +1942,7 @@ static const char *external_diff_attr(const char *name)
static void run_diff_cmd(const char *pgm,
const char *name,
const char *other,
+ const char *attr_path,
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
@@ -1950,7 +1952,7 @@ static void run_diff_cmd(const char *pgm,
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
pgm = NULL;
else {
- const char *cmd = external_diff_attr(name);
+ const char *cmd = external_diff_attr(attr_path);
if (cmd)
pgm = cmd;
}
@@ -1991,6 +1993,15 @@ static int similarity_index(struct diff_filepair *p)
return p->score * 100 / MAX_SCORE;
}
+static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
+{
+ /* Strip the prefix but do not molest /dev/null and absolute paths */
+ if (*namep && **namep != '/')
+ *namep += prefix_length;
+ if (*otherp && **otherp != '/')
+ *otherp += prefix_length;
+}
+
static void run_diff(struct diff_filepair *p, struct diff_options *o)
{
const char *pgm = external_diff();
@@ -2000,16 +2011,21 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
struct diff_filespec *two = p->two;
const char *name;
const char *other;
+ const char *attr_path;
int complete_rewrite = 0;
+ name = p->one->path;
+ other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+ attr_path = name;
+ if (o->prefix_length)
+ strip_prefix(o->prefix_length, &name, &other);
if (DIFF_PAIR_UNMERGED(p)) {
- run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
+ run_diff_cmd(pgm, name, NULL, attr_path,
+ NULL, NULL, NULL, o, 0);
return;
}
- name = p->one->path;
- other = (strcmp(name, p->two->path) ? p->two->path : NULL);
diff_fill_sha1_info(one);
diff_fill_sha1_info(two);
@@ -2072,15 +2088,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
* needs to be split into deletion and creation.
*/
struct diff_filespec *null = alloc_filespec(two->path);
- run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
+ run_diff_cmd(NULL, name, other, attr_path,
+ one, null, xfrm_msg, o, 0);
free(null);
null = alloc_filespec(one->path);
- run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
+ run_diff_cmd(NULL, name, other, attr_path,
+ null, two, xfrm_msg, o, 0);
free(null);
}
else
- run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
- complete_rewrite);
+ run_diff_cmd(pgm, name, other, attr_path,
+ one, two, xfrm_msg, o, complete_rewrite);
strbuf_release(&msg);
}
@@ -2101,6 +2119,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
name = p->one->path;
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+ if (o->prefix_length)
+ strip_prefix(o->prefix_length, &name, &other);
+
diff_fill_sha1_info(p->one);
diff_fill_sha1_info(p->two);
@@ -2113,6 +2134,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
{
const char *name;
const char *other;
+ const char *attr_path;
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
@@ -2121,11 +2143,15 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
name = p->one->path;
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+ attr_path = other ? other : name;
+
+ if (o->prefix_length)
+ strip_prefix(o->prefix_length, &name, &other);
diff_fill_sha1_info(p->one);
diff_fill_sha1_info(p->two);
- builtin_checkdiff(name, other, p->one, p->two, o);
+ builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
}
void diff_setup(struct diff_options *options)
@@ -2168,6 +2194,13 @@ int diff_setup_done(struct diff_options *options)
if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
options->detect_rename = DIFF_DETECT_COPY;
+ if (!DIFF_OPT_TST(options, RELATIVE_NAME))
+ options->prefix = NULL;
+ if (options->prefix)
+ options->prefix_length = strlen(options->prefix);
+ else
+ options->prefix_length = 0;
+
if (options->output_format & (DIFF_FORMAT_NAME |
DIFF_FORMAT_NAME_STATUS |
DIFF_FORMAT_CHECKDIFF |
@@ -2362,6 +2395,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
}
else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0;
+ else if (!strcmp(arg, "--relative"))
+ DIFF_OPT_SET(options, RELATIVE_NAME);
+ else if (!prefixcmp(arg, "--relative=")) {
+ DIFF_OPT_SET(options, RELATIVE_NAME);
+ options->prefix = arg + 11;
+ }
/* xdiff options */
else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
@@ -2542,8 +2581,6 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)
return sha1_to_hex(sha1);
abbrev = find_unique_abbrev(sha1, len);
- if (!abbrev)
- return sha1_to_hex(sha1);
abblen = strlen(abbrev);
if (abblen < 37) {
static char hex[41];
@@ -2573,12 +2610,20 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
printf("%c%c", p->status, inter_name_termination);
}
- if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {
- write_name_quoted(p->one->path, stdout, inter_name_termination);
- write_name_quoted(p->two->path, stdout, line_termination);
+ if (p->status == DIFF_STATUS_COPIED ||
+ p->status == DIFF_STATUS_RENAMED) {
+ const char *name_a, *name_b;
+ name_a = p->one->path;
+ name_b = p->two->path;
+ strip_prefix(opt->prefix_length, &name_a, &name_b);
+ write_name_quoted(name_a, stdout, inter_name_termination);
+ write_name_quoted(name_b, stdout, line_termination);
} else {
- const char *path = p->one->mode ? p->one->path : p->two->path;
- write_name_quoted(path, stdout, line_termination);
+ const char *name_a, *name_b;
+ name_a = p->one->mode ? p->one->path : p->two->path;
+ name_b = NULL;
+ strip_prefix(opt->prefix_length, &name_a, &name_b);
+ write_name_quoted(name_a, stdout, line_termination);
}
}
@@ -2775,8 +2820,13 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
diff_flush_checkdiff(p, opt);
else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
diff_flush_raw(p, opt);
- else if (fmt & DIFF_FORMAT_NAME)
- write_name_quoted(p->two->path, stdout, opt->line_termination);
+ else if (fmt & DIFF_FORMAT_NAME) {
+ const char *name_a, *name_b;
+ name_a = p->two->path;
+ name_b = NULL;
+ strip_prefix(opt->prefix_length, &name_a, &name_b);
+ write_name_quoted(name_a, stdout, opt->line_termination);
+ }
}
static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
@@ -3137,11 +3187,8 @@ static void diffcore_apply_filter(const char *filter)
static int diff_filespec_is_identical(struct diff_filespec *one,
struct diff_filespec *two)
{
- if (S_ISGITLINK(one->mode)) {
- diff_fill_sha1_info(one);
- diff_fill_sha1_info(two);
- return !hashcmp(one->sha1, two->sha1);
- }
+ if (S_ISGITLINK(one->mode))
+ return 0;
if (diff_populate_filespec(one, 0))
return 0;
if (diff_populate_filespec(two, 0))
@@ -3264,6 +3311,11 @@ void diff_addremove(struct diff_options *options,
if (!path) path = "";
sprintf(concatpath, "%s%s", base, path);
+
+ if (options->prefix &&
+ strncmp(concatpath, options->prefix, options->prefix_length))
+ return;
+
one = alloc_filespec(concatpath);
two = alloc_filespec(concatpath);
@@ -3293,6 +3345,11 @@ void diff_change(struct diff_options *options,
}
if (!path) path = "";
sprintf(concatpath, "%s%s", base, path);
+
+ if (options->prefix &&
+ strncmp(concatpath, options->prefix, options->prefix_length))
+ return;
+
one = alloc_filespec(concatpath);
two = alloc_filespec(concatpath);
fill_filespec(one, old_sha1, old_mode);
@@ -3307,6 +3364,11 @@ void diff_unmerge(struct diff_options *options,
unsigned mode, const unsigned char *sha1)
{
struct diff_filespec *one, *two;
+
+ if (options->prefix &&
+ strncmp(path, options->prefix, options->prefix_length))
+ return;
+
one = alloc_filespec(path);
two = alloc_filespec(path);
fill_filespec(one, sha1, mode);
diff --git a/diff.h b/diff.h
index c5d3a4f..9a652e7 100644
--- a/diff.h
+++ b/diff.h
@@ -62,6 +62,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
#define DIFF_OPT_EXIT_WITH_STATUS (1 << 14)
#define DIFF_OPT_REVERSE_DIFF (1 << 15)
#define DIFF_OPT_CHECK_FAILED (1 << 16)
+#define DIFF_OPT_RELATIVE_NAME (1 << 17)
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag)
@@ -85,6 +86,8 @@ struct diff_options {
int dirstat_percent;
int setup;
int abbrev;
+ const char *prefix;
+ int prefix_length;
const char *msg_sep;
const char *stat_sep;
long xdl_opts;
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 3d37725..31941bc 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -468,10 +468,11 @@ void diffcore_rename(struct diff_options *options)
*/
if (rename_limit <= 0 || rename_limit > 32767)
rename_limit = 32767;
- if (num_create > rename_limit && num_src > rename_limit)
- goto cleanup;
- if (num_create * num_src > rename_limit * rename_limit)
+ if ((num_create > rename_limit && num_src > rename_limit) ||
+ (num_create * num_src > rename_limit * rename_limit)) {
+ warning("too many files, skipping inexact rename detection");
goto cleanup;
+ }
mx = xmalloc(sizeof(*mx) * num_create * num_src);
for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
diff --git a/dir.c b/dir.c
index 1f507da..edc458e 100644
--- a/dir.c
+++ b/dir.c
@@ -704,8 +704,7 @@ static struct path_simplify *create_simplify(const char **pathspec)
static void free_simplify(struct path_simplify *simplify)
{
- if (simplify)
- free(simplify);
+ free(simplify);
}
int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
diff --git a/environment.c b/environment.c
index 3527f16..6739a3f 100644
--- a/environment.c
+++ b/environment.c
@@ -37,6 +37,7 @@ const char *excludes_file;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
+enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
/* This is set by setup_git_dir_gently() and/or git_default_config() */
char *git_work_tree_cfg;
diff --git a/git-am.sh b/git-am.sh
index 2ecebc4..a2c6fea 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -2,6 +2,7 @@
#
# Copyright (c) 2005, 2006 Junio C Hamano
+SUBDIRECTORY_OK=Yes
OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
git-am [options] <mbox>|<Maildir>...
@@ -25,6 +26,7 @@ skip skip the current patch"
. git-sh-setup
set_reflog_action am
require_work_tree
+cd_to_toplevel
git var GIT_COMMITTER_IDENT >/dev/null || exit
diff --git a/git-bisect.sh b/git-bisect.sh
index 74715ed..2c32d0b 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -67,16 +67,18 @@ bisect_start() {
die "Bad HEAD - I need a HEAD"
case "$head" in
refs/heads/bisect)
- if [ -s "$GIT_DIR/head-name" ]; then
- branch=`cat "$GIT_DIR/head-name"`
+ if [ -s "$GIT_DIR/BISECT_START" ]; then
+ branch=`cat "$GIT_DIR/BISECT_START"`
else
branch=master
fi
git checkout $branch || exit
;;
refs/heads/*|$_x40)
+ # This error message should only be triggered by cogito usage,
+ # and cogito users should understand it relates to cg-seek.
[ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
- echo "${head#refs/heads/}" >"$GIT_DIR/head-name"
+ echo "${head#refs/heads/}" >"$GIT_DIR/BISECT_START"
;;
*)
die "Bad HEAD - strange symbolic ref"
@@ -353,8 +355,8 @@ bisect_reset() {
return
}
case "$#" in
- 0) if [ -s "$GIT_DIR/head-name" ]; then
- branch=`cat "$GIT_DIR/head-name"`
+ 0) if [ -s "$GIT_DIR/BISECT_START" ]; then
+ branch=`cat "$GIT_DIR/BISECT_START"`
else
branch=master
fi ;;
@@ -365,7 +367,9 @@ bisect_reset() {
usage ;;
esac
if git checkout "$branch"; then
+ # Cleanup head-name if it got left by an old version of git-bisect
rm -f "$GIT_DIR/head-name"
+ rm -f "$GIT_DIR/BISECT_START"
bisect_clean_state
fi
}
diff --git a/git-clone.sh b/git-clone.sh
index 0d686c3..e981122 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -210,11 +210,28 @@ if base=$(get_repo_base "$repo"); then
then
local=yes
fi
+elif test -f "$repo"
+then
+ case "$repo" in /*) ;; *) repo="$PWD/$repo" ;; esac
+fi
+
+# Decide the directory name of the new repository
+if test -n "$2"
+then
+ dir="$2"
+else
+ # Derive one from the repository name
+ # Try using "humanish" part of source repo if user didn't specify one
+ if test -f "$repo"
+ then
+ # Cloning from a bundle
+ dir=$(echo "$repo" | sed -e 's|/*\.bundle$||' -e 's|.*/||g')
+ else
+ dir=$(echo "$repo" |
+ sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
+ fi
fi
-dir="$2"
-# Try using "humanish" part of source repo if user didn't specify one
-[ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
[ -e "$dir" ] && die "destination directory '$dir' already exists."
[ yes = "$bare" ] && unset GIT_WORK_TREE
[ -n "$GIT_WORK_TREE" ] && [ -e "$GIT_WORK_TREE" ] &&
@@ -364,11 +381,17 @@ yes)
fi
;;
*)
- case "$upload_pack" in
- '') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";;
- *) git-fetch-pack --all -k $quiet "$upload_pack" $depth $no_progress "$repo" ;;
- esac >"$GIT_DIR/CLONE_HEAD" ||
+ if [ -f "$repo" ] ; then
+ git bundle unbundle "$repo" > "$GIT_DIR/CLONE_HEAD" ||
+ die "unbundle from '$repo' failed."
+ else
+ case "$upload_pack" in
+ '') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";;
+ *) git-fetch-pack --all -k \
+ $quiet "$upload_pack" $depth $no_progress "$repo" ;;
+ esac >"$GIT_DIR/CLONE_HEAD" ||
die "fetch-pack from '$repo' failed."
+ fi
;;
esac
;;
diff --git a/git-gui/lib/about.tcl b/git-gui/lib/about.tcl
index 47be8eb..241ab89 100644
--- a/git-gui/lib/about.tcl
+++ b/git-gui/lib/about.tcl
@@ -41,7 +41,8 @@ proc do_about {} {
append v "Tcl version $tcl_patchLevel"
append v ", Tk version $tk_patchLevel"
}
- if {[info exists ui_comm_spell]} {
+ if {[info exists ui_comm_spell]
+ && [$ui_comm_spell version] ne {}} {
append v "\n"
append v [$ui_comm_spell version]
}
diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl
index 08a2462..8c27678 100644
--- a/git-gui/lib/error.tcl
+++ b/git-gui/lib/error.tcl
@@ -47,7 +47,7 @@ proc info_popup {msg} {
append title " ([reponame])"
}
tk_messageBox \
- -parent $parent \
+ -parent [_error_parent] \
-icon info \
-type ok \
-title $title \
diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl
index 7f018e4..9be7486 100644
--- a/git-gui/lib/spellcheck.tcl
+++ b/git-gui/lib/spellcheck.tcl
@@ -1,27 +1,31 @@
-# git-gui spellchecking support through aspell
+# git-gui spellchecking support through ispell/aspell
# Copyright (C) 2008 Shawn Pearce
class spellcheck {
-field s_fd {} ; # pipe to aspell
-field s_version ; # aspell version string
-field s_lang ; # current language code
+field s_fd {} ; # pipe to ispell/aspell
+field s_version {} ; # ispell/aspell version string
+field s_lang {} ; # current language code
+field s_prog aspell; # are we actually old ispell?
+field s_failed 0 ; # is $s_prog bogus and not working?
field w_text ; # text widget we are spelling
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_i {} ; # timer registration for _run callbacks
field s_clear 0 ; # did we erase mispelled 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 aspell
+field s_pending [list] ; # [$line $data] sent to ispell/aspell
field s_suggest ; # array, list of suggestions, keyed by misspelling
constructor init {pipe_fd ui_text ui_menu} {
set w_text $ui_text
set w_menu $ui_menu
+ array unset s_suggest
+ bind_button3 $w_text [cb _popup_suggest %X %Y @%x,%y]
_connect $this $pipe_fd
return $this
}
@@ -33,14 +37,53 @@ method _connect {pipe_fd} {
-translation lf
if {[gets $pipe_fd s_version] <= 0} {
- close $pipe_fd
- error [mc "Not connected to aspell"]
+ if {[catch {close $pipe_fd} err]} {
+
+ # Eh? Is this actually ispell choking on aspell options?
+ #
+ if {$s_prog eq {aspell}
+ && [regexp -nocase {^Usage: } $err]
+ && ![catch {
+ set pipe_fd [open [list | $s_prog -v] r]
+ gets $pipe_fd s_version
+ close $pipe_fd
+ }]
+ && $s_version ne {}} {
+ if {{@(#) } eq [string range $s_version 0 4]} {
+ set s_version [string range $s_version 5 end]
+ }
+ set s_failed 1
+ error_popup [strcat \
+ [mc "Unsupported spell checker"] \
+ ":\n\n$s_version"]
+ set s_version {}
+ return
+ }
+
+ regsub -nocase {^Error: } $err {} err
+ if {$s_fd eq {}} {
+ error_popup [strcat [mc "Spell checking is unavailable"] ":\n\n$err"]
+ } else {
+ error_popup [strcat \
+ [mc "Invalid spell checking configuration"] \
+ ":\n\n$err\n\n" \
+ [mc "Reverting dictionary to %s." $s_lang]]
+ }
+ } else {
+ error_popup [mc "Spell checker silently failed on startup"]
+ }
+ return
}
+
if {{@(#) } ne [string range $s_version 0 4]} {
- close $pipe_fd
- error [strcat [mc "Unrecognized aspell version"] ": $s_version"]
+ catch {close $pipe_fd}
+ error_popup [strcat [mc "Unrecognized spell checker"] ":\n\n$s_version"]
+ return
}
set s_version [string range $s_version 5 end]
+ regexp \
+ {International Ispell Version .* \(but really (Aspell .*?)\)$} \
+ $s_version _junk s_version
puts $pipe_fd ! ; # enable terse mode
puts $pipe_fd {$$cr master} ; # fetch the language
@@ -65,7 +108,6 @@ method _connect {pipe_fd} {
$w_text tag conf misspelled \
-foreground red \
-underline 1
- bind_button3 $w_text [cb _popup_suggest %X %Y @%x,%y]
array unset s_suggest
set s_seen [list]
@@ -75,7 +117,7 @@ method _connect {pipe_fd} {
}
method lang {{n {}}} {
- if {$n ne {} && $s_lang ne $n} {
+ if {$n ne {} && $s_lang ne $n && !$s_failed} {
set spell_cmd [list |]
lappend spell_cmd aspell
lappend spell_cmd --master=$n
@@ -88,7 +130,10 @@ method lang {{n {}}} {
}
method version {} {
- return "$s_version, $s_lang"
+ if {$s_version ne {}} {
+ return "$s_version, $s_lang"
+ }
+ return {}
}
method stop {} {
@@ -333,11 +378,11 @@ method _read {} {
fconfigure $s_fd -block 1
if {[eof $s_fd]} {
if {![catch {close $s_fd} err]} {
- set err [mc "unexpected eof from aspell"]
+ set err [mc "Unexpected EOF from spell checker"]
}
catch {after cancel $s_i}
$w_text tag remove misspelled 1.0 end
- error_popup [strcat "Spell Checker Failed" "\n\n" $err]
+ error_popup [strcat [mc "Spell Checker Failed"] "\n\n" $err]
return
}
fconfigure $s_fd -block 0
diff --git a/git-gui/po/de.po b/git-gui/po/de.po
index d7c38f9..e84e1c7 100644
--- a/git-gui/po/de.po
+++ b/git-gui/po/de.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: git-gui\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-02-02 10:14+0100\n"
-"PO-Revision-Date: 2008-02-02 10:18+0100\n"
+"POT-Creation-Date: 2008-02-16 21:24+0100\n"
+"PO-Revision-Date: 2008-02-16 21:52+0100\n"
"Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
"Language-Team: German\n"
"MIME-Version: 1.0\n"
@@ -653,7 +653,7 @@ msgstr "Lokale Zweige"
#: lib/branch_delete.tcl:52
msgid "Delete Only If Merged Into"
-msgstr "Nur löschen, wenn darin zusammengeführt"
+msgstr "Nur löschen, wenn zusammengeführt nach"
#: lib/branch_delete.tcl:54
msgid "Always (Do not perform merge test.)"
@@ -1292,19 +1292,19 @@ msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung »%s« nicht."
#: lib/commit.tcl:221
msgid "Calling pre-commit hook..."
-msgstr ""
+msgstr "Aufrufen der Vor-Eintragen-Kontrolle..."
#: lib/commit.tcl:236
msgid "Commit declined by pre-commit hook."
-msgstr ""
+msgstr "Eintragen abgelehnt durch Vor-Eintragen-Kontrolle (»pre-commit hook«)."
#: lib/commit.tcl:259
msgid "Calling commit-msg hook..."
-msgstr ""
+msgstr "Aufrufen der Versionsbeschreibungs-Kontrolle..."
#: lib/commit.tcl:274
msgid "Commit declined by commit-msg hook."
-msgstr ""
+msgstr "Eintragen abgelehnt durch Versionsbeschreibungs-Kontrolle (»commit-message hook«)."
#: lib/commit.tcl:287
msgid "Committing changes..."
@@ -1389,7 +1389,7 @@ msgstr "Festplattenplatz von komprimierten Objekten"
#: lib/database.tcl:48
msgid "Packed objects waiting for pruning"
-msgstr "Komprimierte Objekte, die zum Entfernen vorgesehen sind"
+msgstr "Komprimierte Objekte, die zum Aufräumen vorgesehen sind"
#: lib/database.tcl:49
msgid "Garbage files"
@@ -1622,10 +1622,10 @@ msgstr "%s von %s"
#: lib/merge.tcl:119
#, tcl-format
-msgid "Merging %s and %s"
-msgstr "Zusammenführen von %s und %s"
+msgid "Merging %s and %s..."
+msgstr "Zusammenführen von %s und %s..."
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
msgid "Merge completed successfully."
msgstr "Zusammenführen erfolgreich abgeschlossen."
@@ -1636,7 +1636,7 @@ msgstr "Zusammenführen fehlgeschlagen. Konfliktauflösung ist notwendig."
#: lib/merge.tcl:158
#, tcl-format
msgid "Merge Into %s"
-msgstr "Zusammenführen in %s"
+msgstr "Zusammenführen in »%s«"
#: lib/merge.tcl:177
msgid "Revision To Merge"
@@ -1741,7 +1741,7 @@ msgstr "Auf Dateiänderungsdatum verlassen"
#: lib/option.tcl:111
msgid "Prune Tracking Branches During Fetch"
-msgstr "Übernahmezweige entfernen während Anforderung"
+msgstr "Übernahmezweige aufräumen während Anforderung"
#: lib/option.tcl:112
msgid "Match Tracking Branches"
@@ -1755,7 +1755,11 @@ msgstr "Anzahl der Kontextzeilen beim Vergleich"
msgid "New Branch Name Template"
msgstr "Namensvorschlag für neue Zweige"
-#: lib/option.tcl:176
+#: lib/option.tcl:191
+msgid "Spelling Dictionary:"
+msgstr "Wörterbuch Rechtschreibprüfung:"
+
+#: lib/option.tcl:215
msgid "Change Font"
msgstr "Schriftart ändern"
@@ -1778,11 +1782,11 @@ msgstr "Optionen konnten nicht gespeichert werden:"
#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
msgid "Delete Remote Branch"
-msgstr "Zweig aus anderem Projektarchiv löschen"
+msgstr "Zweig in anderem Projektarchiv löschen"
#: lib/remote_branch_delete.tcl:47
msgid "From Repository"
-msgstr "Von Projektarchiv"
+msgstr "In Projektarchiv"
#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
msgid "Remote:"
@@ -1790,7 +1794,7 @@ msgstr "Anderes Archiv:"
#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
msgid "Arbitrary URL:"
-msgstr "Kommunikation mit URL:"
+msgstr "Archiv-URL:"
#: lib/remote_branch_delete.tcl:84
msgid "Branches"
@@ -1798,11 +1802,11 @@ msgstr "Zweige"
#: lib/remote_branch_delete.tcl:109
msgid "Delete Only If"
-msgstr "Löschen, falls"
+msgstr "Nur löschen, wenn"
#: lib/remote_branch_delete.tcl:111
msgid "Merged Into:"
-msgstr "Zusammenführen mit:"
+msgstr "Zusammengeführt mit:"
#: lib/remote_branch_delete.tcl:119
msgid "Always (Do not perform merge checks)"
@@ -1864,7 +1868,7 @@ msgstr "»%s« laden..."
#: lib/remote.tcl:165
msgid "Prune from"
-msgstr "Entfernen von"
+msgstr "Aufräumen von"
#: lib/remote.tcl:170
msgid "Fetch from"
@@ -1882,6 +1886,26 @@ msgstr "Fehler beim Schreiben der Verknüpfung:"
msgid "Cannot write icon:"
msgstr "Fehler beim Erstellen des Icons:"
+#: lib/spellcheck.tcl:37
+msgid "Not connected to aspell"
+msgstr "Keine Verbindung zu »aspell«"
+
+#: lib/spellcheck.tcl:41
+msgid "Unrecognized aspell version"
+msgstr "Unbekannte Version von »aspell«"
+
+#: lib/spellcheck.tcl:135
+msgid "No Suggestions"
+msgstr "Keine Vorschläge"
+
+#: lib/spellcheck.tcl:336
+msgid "Unexpected EOF from aspell"
+msgstr "Unerwartetes EOF von »aspell«"
+
+#: lib/spellcheck.tcl:340
+msgid "Spell Checker Failed"
+msgstr "Rechtschreibprüfung fehlgeschlagen"
+
#: lib/status_bar.tcl:83
#, tcl-format
msgid "%s ... %*i of %*i %s (%3i%%)"
@@ -1900,12 +1924,12 @@ msgstr "Neue Änderungen von »%s« holen"
#: lib/transport.tcl:18
#, tcl-format
msgid "remote prune %s"
-msgstr "Entfernen von »%s« aus anderem Archiv"
+msgstr "Aufräumen von »%s«"
#: lib/transport.tcl:19
#, tcl-format
msgid "Pruning tracking branches deleted from %s"
-msgstr "Übernahmezweige entfernen, die in »%s« gelöscht wurden"
+msgstr "Übernahmezweige aufräumen und entfernen, die in »%s« gelöscht wurden"
#: lib/transport.tcl:25 lib/transport.tcl:71
#, tcl-format
@@ -1928,7 +1952,7 @@ msgstr "Zweige versenden"
#: lib/transport.tcl:103
msgid "Source Branches"
-msgstr "Herkunftszweige"
+msgstr "Lokale Zweige"
#: lib/transport.tcl:120
msgid "Destination Repository"
diff --git a/git-gui/po/git-gui.pot b/git-gui/po/git-gui.pot
index 3f139da..2e33284 100644
--- a/git-gui/po/git-gui.pot
+++ b/git-gui/po/git-gui.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-02-02 10:14+0100\n"
+"POT-Creation-Date: 2008-02-16 21:24+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1474,10 +1474,10 @@ msgstr ""
#: lib/merge.tcl:119
#, tcl-format
-msgid "Merging %s and %s"
+msgid "Merging %s and %s..."
msgstr ""
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
msgid "Merge completed successfully."
msgstr ""
@@ -1592,7 +1592,11 @@ msgstr ""
msgid "New Branch Name Template"
msgstr ""
-#: lib/option.tcl:176
+#: lib/option.tcl:191
+msgid "Spelling Dictionary:"
+msgstr ""
+
+#: lib/option.tcl:215
msgid "Change Font"
msgstr ""
@@ -1709,6 +1713,26 @@ msgstr ""
msgid "Cannot write icon:"
msgstr ""
+#: lib/spellcheck.tcl:37
+msgid "Not connected to aspell"
+msgstr ""
+
+#: lib/spellcheck.tcl:41
+msgid "Unrecognized aspell version"
+msgstr ""
+
+#: lib/spellcheck.tcl:135
+msgid "No Suggestions"
+msgstr ""
+
+#: lib/spellcheck.tcl:336
+msgid "Unexpected EOF from aspell"
+msgstr ""
+
+#: lib/spellcheck.tcl:340
+msgid "Spell Checker Failed"
+msgstr ""
+
#: lib/status_bar.tcl:83
#, tcl-format
msgid "%s ... %*i of %*i %s (%3i%%)"
diff --git a/git-gui/po/glossary/de.po b/git-gui/po/glossary/de.po
index 0b33c57..35764d1 100644
--- a/git-gui/po/glossary/de.po
+++ b/git-gui/po/glossary/de.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: git-gui glossary\n"
"POT-Creation-Date: 2008-01-07 21:20+0100\n"
-"PO-Revision-Date: 2008-01-15 20:32+0100\n"
+"PO-Revision-Date: 2008-02-16 21:48+0100\n"
"Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
"Language-Team: German \n"
"MIME-Version: 1.0\n"
@@ -114,7 +114,7 @@ msgstr "Beschreibung (Meldung?, Nachricht?; Source Safe: Kommentar)"
#. "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'."
msgid "prune"
-msgstr "entfernen"
+msgstr "aufräumen (entfernen?)"
#. "Pulling a branch means to fetch it and merge it."
msgid "pull"
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index fb12b03..c2bedd6 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -268,6 +268,10 @@ do_next () {
warn
warn " git commit --amend"
warn
+ warn "Once you are satisfied with your changes, run"
+ warn
+ warn " git rebase --continue"
+ warn
exit 0
;;
squash|s)
diff --git a/git-rebase.sh b/git-rebase.sh
index bdcea0e..6b9af96 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -208,16 +208,15 @@ do
if test -d "$dotest"
then
move_to_original_branch
- rm -r "$dotest"
elif test -d .dotest
then
dotest=.dotest
move_to_original_branch
- rm -r .dotest
else
die "No rebase in progress?"
fi
- git reset --hard ORIG_HEAD
+ git reset --hard $(cat $dotest/orig-head)
+ rm -r "$dotest"
exit
;;
--onto)
diff --git a/git-svn.perl b/git-svn.perl
index 05fb358..9e2faf9 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -3643,6 +3643,7 @@ sub _auth_providers () {
SVN::Client::get_ssl_client_cert_file_provider(),
SVN::Client::get_ssl_client_cert_prompt_provider(
\&Git::SVN::Prompt::ssl_client_cert, 2),
+ SVN::Client::get_ssl_client_cert_pw_file_provider(),
SVN::Client::get_ssl_client_cert_pw_prompt_provider(
\&Git::SVN::Prompt::ssl_client_cert_pw, 2),
SVN::Client::get_username_provider(),
diff --git a/git.c b/git.c
index 0cb8688..9cca81a 100644
--- a/git.c
+++ b/git.c
@@ -87,19 +87,6 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
return handled;
}
-static const char *alias_command;
-static char *alias_string;
-
-static int git_alias_config(const char *var, const char *value)
-{
- if (!prefixcmp(var, "alias.") && !strcmp(var + 6, alias_command)) {
- if (!value)
- return config_error_nonbool(var);
- alias_string = xstrdup(value);
- }
- return 0;
-}
-
static int split_cmdline(char *cmdline, const char ***argv)
{
int src, dst, count = 0, size = 16;
@@ -159,11 +146,13 @@ static int handle_alias(int *argcp, const char ***argv)
const char *subdir;
int count, option_count;
const char** new_argv;
+ const char *alias_command;
+ char *alias_string;
subdir = setup_git_directory_gently(&nongit);
alias_command = (*argv)[0];
- git_config(git_alias_config);
+ alias_string = alias_lookup(alias_command);
if (alias_string) {
if (alias_string[0] == '!') {
if (*argcp > 1) {
@@ -289,6 +278,7 @@ 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", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
{ "checkout-index", cmd_checkout_index,
RUN_SETUP | NEED_WORK_TREE},
{ "check-ref-format", cmd_check_ref_format },
@@ -332,6 +322,8 @@ static void handle_internal_command(int argc, const char **argv)
{ "merge-base", cmd_merge_base, RUN_SETUP },
{ "merge-file", cmd_merge_file },
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
+ { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
{ "name-rev", cmd_name_rev, RUN_SETUP },
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index e8226b1..fc95e2c 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -848,32 +848,73 @@ sub project_in_list {
## ----------------------------------------------------------------------
## HTML aware string manipulation
+# Try to chop given string on a word boundary between position
+# $len and $len+$add_len. If there is no word boundary there,
+# chop at $len+$add_len. Do not chop if chopped part plus ellipsis
+# (marking chopped part) would be longer than given string.
sub chop_str {
my $str = shift;
my $len = shift;
my $add_len = shift || 10;
+ my $where = shift || 'right'; # 'left' | 'center' | 'right'
# allow only $len chars, but don't cut a word if it would fit in $add_len
# if it doesn't fit, cut it if it's still longer than the dots we would add
- $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/;
- my $body = $1;
- my $tail = $2;
- if (length($tail) > 4) {
- $tail = " ...";
- $body =~ s/&[^;]*$//; # remove chopped character entities
+ # remove chopped character entities entirely
+
+ # when chopping in the middle, distribute $len into left and right part
+ # return early if chopping wouldn't make string shorter
+ if ($where eq 'center') {
+ return $str if ($len + 5 >= length($str)); # filler is length 5
+ $len = int($len/2);
+ } else {
+ return $str if ($len + 4 >= length($str)); # filler is length 4
+ }
+
+ # regexps: ending and beginning with word part up to $add_len
+ my $endre = qr/.{$len}\w{0,$add_len}/;
+ my $begre = qr/\w{0,$add_len}.{$len}/;
+
+ if ($where eq 'left') {
+ $str =~ m/^(.*?)($begre)$/;
+ my ($lead, $body) = ($1, $2);
+ if (length($lead) > 4) {
+ $body =~ s/^[^;]*;// if ($lead =~ m/&[^;]*$/);
+ $lead = " ...";
+ }
+ return "$lead$body";
+
+ } elsif ($where eq 'center') {
+ $str =~ m/^($endre)(.*)$/;
+ my ($left, $str) = ($1, $2);
+ $str =~ m/^(.*?)($begre)$/;
+ my ($mid, $right) = ($1, $2);
+ if (length($mid) > 5) {
+ $left =~ s/&[^;]*$//;
+ $right =~ s/^[^;]*;// if ($mid =~ m/&[^;]*$/);
+ $mid = " ... ";
+ }
+ return "$left$mid$right";
+
+ } else {
+ $str =~ m/^($endre)(.*)$/;
+ my $body = $1;
+ my $tail = $2;
+ if (length($tail) > 4) {
+ $body =~ s/&[^;]*$//;
+ $tail = "... ";
+ }
+ return "$body$tail";
}
- return "$body$tail";
}
# takes the same arguments as chop_str, but also wraps a <span> around the
# result with a title attribute if it does get chopped. Additionally, the
# string is HTML-escaped.
sub chop_and_escape_str {
- my $str = shift;
- my $len = shift;
- my $add_len = shift || 10;
+ my ($str) = @_;
- my $chopped = chop_str($str, $len, $add_len);
+ my $chopped = chop_str(@_);
if ($chopped eq $str) {
return esc_html($chopped);
} else {
@@ -3791,11 +3832,11 @@ sub git_search_grep_body {
foreach my $line (@$comment) {
if ($line =~ m/^(.*)($search_regexp)(.*)$/i) {
my ($lead, $match, $trail) = ($1, $2, $3);
- $match = chop_str($match, 70, 5); # in case match is very long
- my $contextlen = int((80 - length($match))/2); # for the remainder
- $contextlen = 30 if ($contextlen > 30); # but not too much
- $lead = chop_str($lead, $contextlen, 10);
- $trail = chop_str($trail, $contextlen, 10);
+ $match = chop_str($match, 70, 5, 'center');
+ my $contextlen = int((80 - length($match))/2);
+ $contextlen = 30 if ($contextlen > 30);
+ $lead = chop_str($lead, $contextlen, 10, 'left');
+ $trail = chop_str($trail, $contextlen, 10, 'right');
$lead = esc_html($lead);
$match = esc_html($match);
diff --git a/hash-object.c b/hash-object.c
index 0a58f3f..61e7160 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -41,6 +41,7 @@ int main(int argc, char **argv)
const char *prefix = NULL;
int prefix_length = -1;
int no_more_flags = 0;
+ int hashstdin = 0;
git_config(git_default_config);
@@ -65,13 +66,20 @@ int main(int argc, char **argv)
else if (!strcmp(argv[i], "--help"))
usage(hash_object_usage);
else if (!strcmp(argv[i], "--stdin")) {
- hash_stdin(type, write_object);
+ if (hashstdin)
+ die("Multiple --stdin arguments are not supported");
+ hashstdin = 1;
}
else
usage(hash_object_usage);
}
else {
const char *arg = argv[i];
+
+ if (hashstdin) {
+ hash_stdin(type, write_object);
+ hashstdin = 0;
+ }
if (0 <= prefix_length)
arg = prefix_filename(prefix, prefix_length,
arg);
@@ -79,5 +87,7 @@ int main(int argc, char **argv)
no_more_flags = 1;
}
}
+ if (hashstdin)
+ hash_stdin(type, write_object);
return 0;
}
diff --git a/help.c b/help.c
index 8143dcd..e57a50e 100644
--- a/help.c
+++ b/help.c
@@ -7,40 +7,49 @@
#include "builtin.h"
#include "exec_cmd.h"
#include "common-cmds.h"
-
-static const char *help_default_format;
-
-static enum help_format {
- man_format,
- info_format,
- web_format,
-} help_format = man_format;
-
-static void parse_help_format(const char *format)
+#include "parse-options.h"
+
+enum help_format {
+ HELP_FORMAT_MAN,
+ HELP_FORMAT_INFO,
+ HELP_FORMAT_WEB,
+};
+
+static int show_all = 0;
+static enum help_format help_format = HELP_FORMAT_MAN;
+static struct option builtin_help_options[] = {
+ OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
+ OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
+ OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
+ HELP_FORMAT_WEB),
+ OPT_SET_INT('i', "info", &help_format, "show info page",
+ HELP_FORMAT_INFO),
+};
+
+static const char * const builtin_help_usage[] = {
+ "git-help [--all] [--man|--web|--info] [command]",
+ NULL
+};
+
+static enum help_format parse_help_format(const char *format)
{
- if (!format) {
- help_format = man_format;
- return;
- }
- if (!strcmp(format, "man")) {
- help_format = man_format;
- return;
- }
- if (!strcmp(format, "info")) {
- help_format = info_format;
- return;
- }
- if (!strcmp(format, "web") || !strcmp(format, "html")) {
- help_format = web_format;
- return;
- }
+ if (!strcmp(format, "man"))
+ return HELP_FORMAT_MAN;
+ if (!strcmp(format, "info"))
+ return HELP_FORMAT_INFO;
+ if (!strcmp(format, "web") || !strcmp(format, "html"))
+ return HELP_FORMAT_WEB;
die("unrecognized help format '%s'", format);
}
static int git_help_config(const char *var, const char *value)
{
- if (!strcmp(var, "help.format"))
- return git_config_string(&help_default_format, var, value);
+ if (!strcmp(var, "help.format")) {
+ if (!value)
+ return config_error_nonbool(var);
+ help_format = parse_help_format(value);
+ return 0;
+ }
return git_default_config(var, value);
}
@@ -201,7 +210,7 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds,
return longest;
}
-static void list_commands(void)
+static unsigned int load_command_list(void)
{
unsigned int longest = 0;
unsigned int len;
@@ -241,6 +250,14 @@ static void list_commands(void)
uniq(&other_cmds);
exclude_cmds(&other_cmds, &main_cmds);
+ return longest;
+}
+
+static void list_commands(void)
+{
+ unsigned int longest = load_command_list();
+ const char *exec_path = git_exec_path();
+
if (main_cmds.cnt) {
printf("available git commands in '%s'\n", exec_path);
printf("----------------------------");
@@ -275,6 +292,22 @@ void list_common_cmds_help(void)
}
}
+static int is_in_cmdlist(struct cmdnames *c, const char *s)
+{
+ int i;
+ for (i = 0; i < c->cnt; i++)
+ if (!strcmp(s, c->names[i]->name))
+ return 1;
+ return 0;
+}
+
+static int is_git_command(const char *s)
+{
+ load_command_list();
+ return is_in_cmdlist(&main_cmds, s) ||
+ is_in_cmdlist(&other_cmds, s);
+}
+
static const char *cmd_to_page(const char *git_cmd)
{
if (!git_cmd)
@@ -362,50 +395,43 @@ int cmd_version(int argc, const char **argv, const char *prefix)
int cmd_help(int argc, const char **argv, const char *prefix)
{
- const char *help_cmd = argv[1];
+ int nongit;
+ const char *alias;
- if (argc < 2) {
- printf("usage: %s\n\n", git_usage_string);
- list_common_cmds_help();
- exit(0);
- }
+ setup_git_directory_gently(&nongit);
+ git_config(git_help_config);
+
+ argc = parse_options(argc, argv, builtin_help_options,
+ builtin_help_usage, 0);
- if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
+ if (show_all) {
printf("usage: %s\n\n", git_usage_string);
list_commands();
+ return 0;
}
- else if (!strcmp(help_cmd, "--web") || !strcmp(help_cmd, "-w")) {
- show_html_page(argc > 2 ? argv[2] : NULL);
- }
-
- else if (!strcmp(help_cmd, "--info") || !strcmp(help_cmd, "-i")) {
- show_info_page(argc > 2 ? argv[2] : NULL);
+ if (!argv[0]) {
+ printf("usage: %s\n\n", git_usage_string);
+ list_common_cmds_help();
+ return 0;
}
- else if (!strcmp(help_cmd, "--man") || !strcmp(help_cmd, "-m")) {
- show_man_page(argc > 2 ? argv[2] : NULL);
+ alias = alias_lookup(argv[0]);
+ if (alias && !is_git_command(argv[0])) {
+ printf("`git %s' is aliased to `%s'\n", argv[0], alias);
+ return 0;
}
- else {
- int nongit;
-
- setup_git_directory_gently(&nongit);
- git_config(git_help_config);
- if (help_default_format)
- parse_help_format(help_default_format);
-
- switch (help_format) {
- case man_format:
- show_man_page(help_cmd);
- break;
- case info_format:
- show_info_page(help_cmd);
- break;
- case web_format:
- show_html_page(help_cmd);
- break;
- }
+ switch (help_format) {
+ case HELP_FORMAT_MAN:
+ show_man_page(argv[0]);
+ break;
+ case HELP_FORMAT_INFO:
+ show_info_page(argv[0]);
+ break;
+ case HELP_FORMAT_WEB:
+ show_html_page(argv[0]);
+ break;
}
return 0;
diff --git a/http-push.c b/http-push.c
index 0beb740..406270f 100644
--- a/http-push.c
+++ b/http-push.c
@@ -664,8 +664,7 @@ static void release_request(struct transfer_request *request)
close(request->local_fileno);
if (request->local_stream)
fclose(request->local_stream);
- if (request->url != NULL)
- free(request->url);
+ free(request->url);
free(request);
}
@@ -1283,10 +1282,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
strbuf_release(&in_buffer);
if (lock->token == NULL || lock->timeout <= 0) {
- if (lock->token != NULL)
- free(lock->token);
- if (lock->owner != NULL)
- free(lock->owner);
+ free(lock->token);
+ free(lock->owner);
free(url);
free(lock);
lock = NULL;
@@ -1344,8 +1341,7 @@ static int unlock_remote(struct remote_lock *lock)
prev->next = prev->next->next;
}
- if (lock->owner != NULL)
- free(lock->owner);
+ free(lock->owner);
free(lock->url);
free(lock->token);
free(lock);
@@ -2035,8 +2031,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
}
free(url);
- if (*symref != NULL)
- free(*symref);
+ free(*symref);
*symref = NULL;
hashclr(sha1);
@@ -2435,8 +2430,7 @@ int main(int argc, char **argv)
}
cleanup:
- if (rewritten_url)
- free(rewritten_url);
+ free(rewritten_url);
if (info_ref_lock)
unlock_remote(info_ref_lock);
free(remote);
diff --git a/imap-send.c b/imap-send.c
index 9025d9a..10cce15 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -472,7 +472,7 @@ v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,
if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {
free( cmd->cmd );
free( cmd );
- if (cb && cb->data)
+ if (cb)
free( cb->data );
return NULL;
}
@@ -858,8 +858,7 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
normal:
if (cmdp->cb.done)
cmdp->cb.done( ctx, cmdp, resp );
- if (cmdp->cb.data)
- free( cmdp->cb.data );
+ free( cmdp->cb.data );
free( cmdp->cmd );
free( cmdp );
if (!tcmd || tcmd == cmdp)
diff --git a/interpolate.c b/interpolate.c
index 6ef53f2..7f03bd9 100644
--- a/interpolate.c
+++ b/interpolate.c
@@ -11,8 +11,7 @@ void interp_set_entry(struct interp *table, int slot, const char *value)
char *oldval = table[slot].value;
char *newval = NULL;
- if (oldval)
- free(oldval);
+ free(oldval);
if (value)
newval = xstrdup(value);
diff --git a/log-tree.c b/log-tree.c
index e9ba6df..608f697 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -137,6 +137,72 @@ static int has_non_ascii(const char *s)
return 0;
}
+void log_write_email_headers(struct rev_info *opt, const char *name,
+ const char **subject_p, const char **extra_headers_p)
+{
+ const char *subject = NULL;
+ const char *extra_headers = opt->extra_headers;
+ if (opt->total > 0) {
+ static char buffer[64];
+ snprintf(buffer, sizeof(buffer),
+ "Subject: [%s %0*d/%d] ",
+ opt->subject_prefix,
+ digits_in_number(opt->total),
+ opt->nr, opt->total);
+ subject = buffer;
+ } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
+ static char buffer[256];
+ snprintf(buffer, sizeof(buffer),
+ "Subject: [%s] ",
+ opt->subject_prefix);
+ subject = buffer;
+ } else {
+ subject = "Subject: ";
+ }
+
+ printf("From %s Mon Sep 17 00:00:00 2001\n", name);
+ if (opt->message_id)
+ printf("Message-Id: <%s>\n", opt->message_id);
+ if (opt->ref_message_id)
+ printf("In-Reply-To: <%s>\nReferences: <%s>\n",
+ opt->ref_message_id, opt->ref_message_id);
+ if (opt->mime_boundary) {
+ static char subject_buffer[1024];
+ static char buffer[1024];
+ snprintf(subject_buffer, sizeof(subject_buffer) - 1,
+ "%s"
+ "MIME-Version: 1.0\n"
+ "Content-Type: multipart/mixed;"
+ " boundary=\"%s%s\"\n"
+ "\n"
+ "This is a multi-part message in MIME "
+ "format.\n"
+ "--%s%s\n"
+ "Content-Type: text/plain; "
+ "charset=UTF-8; format=fixed\n"
+ "Content-Transfer-Encoding: 8bit\n\n",
+ extra_headers ? extra_headers : "",
+ mime_boundary_leader, opt->mime_boundary,
+ mime_boundary_leader, opt->mime_boundary);
+ extra_headers = subject_buffer;
+
+ snprintf(buffer, sizeof(buffer) - 1,
+ "--%s%s\n"
+ "Content-Type: text/x-patch;"
+ " name=\"%s.diff\"\n"
+ "Content-Transfer-Encoding: 8bit\n"
+ "Content-Disposition: %s;"
+ " filename=\"%s.diff\"\n\n",
+ mime_boundary_leader, opt->mime_boundary,
+ name,
+ opt->no_inline ? "attachment" : "inline",
+ name);
+ opt->diffopt.stat_sep = buffer;
+ }
+ *subject_p = subject;
+ *extra_headers_p = extra_headers;
+}
+
void show_log(struct rev_info *opt, const char *sep)
{
struct strbuf msgbuf;
@@ -188,64 +254,8 @@ void show_log(struct rev_info *opt, const char *sep)
*/
if (opt->commit_format == CMIT_FMT_EMAIL) {
- char *sha1 = sha1_to_hex(commit->object.sha1);
- if (opt->total > 0) {
- static char buffer[64];
- snprintf(buffer, sizeof(buffer),
- "Subject: [%s %0*d/%d] ",
- opt->subject_prefix,
- digits_in_number(opt->total),
- opt->nr, opt->total);
- subject = buffer;
- } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
- static char buffer[256];
- snprintf(buffer, sizeof(buffer),
- "Subject: [%s] ",
- opt->subject_prefix);
- subject = buffer;
- } else {
- subject = "Subject: ";
- }
-
- printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
- if (opt->message_id)
- printf("Message-Id: <%s>\n", opt->message_id);
- if (opt->ref_message_id)
- printf("In-Reply-To: <%s>\nReferences: <%s>\n",
- opt->ref_message_id, opt->ref_message_id);
- if (opt->mime_boundary) {
- static char subject_buffer[1024];
- static char buffer[1024];
- snprintf(subject_buffer, sizeof(subject_buffer) - 1,
- "%s"
- "MIME-Version: 1.0\n"
- "Content-Type: multipart/mixed;"
- " boundary=\"%s%s\"\n"
- "\n"
- "This is a multi-part message in MIME "
- "format.\n"
- "--%s%s\n"
- "Content-Type: text/plain; "
- "charset=UTF-8; format=fixed\n"
- "Content-Transfer-Encoding: 8bit\n\n",
- extra_headers ? extra_headers : "",
- mime_boundary_leader, opt->mime_boundary,
- mime_boundary_leader, opt->mime_boundary);
- extra_headers = subject_buffer;
-
- snprintf(buffer, sizeof(buffer) - 1,
- "--%s%s\n"
- "Content-Type: text/x-patch;"
- " name=\"%s.diff\"\n"
- "Content-Transfer-Encoding: 8bit\n"
- "Content-Disposition: %s;"
- " filename=\"%s.diff\"\n\n",
- mime_boundary_leader, opt->mime_boundary,
- sha1,
- opt->no_inline ? "attachment" : "inline",
- sha1);
- opt->diffopt.stat_sep = buffer;
- }
+ log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
+ &subject, &extra_headers);
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
if (opt->commit_format != CMIT_FMT_ONELINE)
diff --git a/log-tree.h b/log-tree.h
index b33f7cd..0cc9344 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -13,5 +13,7 @@ int log_tree_commit(struct rev_info *, struct commit *);
int log_tree_opt_parse(struct rev_info *, const char **, int);
void show_log(struct rev_info *opt, const char *sep);
void show_decorations(struct commit *commit);
+void log_write_email_headers(struct rev_info *opt, const char *name,
+ const char **subject_p, const char **extra_headers_p);
#endif
diff --git a/merge-recursive.h b/merge-recursive.h
new file mode 100644
index 0000000..f37630a
--- /dev/null
+++ b/merge-recursive.h
@@ -0,0 +1,20 @@
+#ifndef MERGE_RECURSIVE_H
+#define MERGE_RECURSIVE_H
+
+int merge_recursive(struct commit *h1,
+ struct commit *h2,
+ const char *branch1,
+ const char *branch2,
+ struct commit_list *ancestors,
+ struct commit **result);
+
+int merge_trees(struct tree *head,
+ struct tree *merge,
+ struct tree *common,
+ const char *branch1,
+ const char *branch2,
+ struct tree **result);
+
+struct tree *write_tree_from_memory(void);
+
+#endif
diff --git a/path.c b/path.c
index 4260952..af27161 100644
--- a/path.c
+++ b/path.c
@@ -311,8 +311,10 @@ const char *make_absolute_path(const char *path)
if (last_slash) {
*last_slash = '\0';
last_elem = xstrdup(last_slash + 1);
- } else
+ } else {
last_elem = xstrdup(buf);
+ *buf = '\0';
+ }
}
if (*buf) {
diff --git a/pretty.c b/pretty.c
index 997f583..703f521 100644
--- a/pretty.c
+++ b/pretty.c
@@ -30,8 +30,7 @@ enum cmit_fmt get_commit_format(const char *arg)
if (*arg == '=')
arg++;
if (!prefixcmp(arg, "format:")) {
- if (user_format)
- free(user_format);
+ free(user_format);
user_format = xstrdup(arg + 7);
return CMIT_FMT_USERFORMAT;
}
@@ -110,9 +109,9 @@ needquote:
strbuf_addstr(sb, "?=");
}
-static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
- const char *line, enum date_mode dmode,
- const char *encoding)
+void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
+ const char *line, enum date_mode dmode,
+ const char *encoding)
{
char *date;
int namelen;
@@ -621,23 +620,23 @@ static void pp_header(enum cmit_fmt fmt,
*/
if (!memcmp(line, "author ", 7)) {
strbuf_grow(sb, linelen + 80);
- add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
+ pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
}
if (!memcmp(line, "committer ", 10) &&
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
strbuf_grow(sb, linelen + 80);
- add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
+ pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
}
}
}
-static void pp_title_line(enum cmit_fmt fmt,
- const char **msg_p,
- struct strbuf *sb,
- const char *subject,
- const char *after_subject,
- const char *encoding,
- int plain_non_ascii)
+void pp_title_line(enum cmit_fmt fmt,
+ const char **msg_p,
+ struct strbuf *sb,
+ const char *subject,
+ const char *after_subject,
+ const char *encoding,
+ int plain_non_ascii)
{
struct strbuf title;
@@ -686,10 +685,10 @@ static void pp_title_line(enum cmit_fmt fmt,
strbuf_release(&title);
}
-static void pp_remainder(enum cmit_fmt fmt,
- const char **msg_p,
- struct strbuf *sb,
- int indent)
+void pp_remainder(enum cmit_fmt fmt,
+ const char **msg_p,
+ struct strbuf *sb,
+ int indent)
{
int first = 1;
for (;;) {
diff --git a/read-cache.c b/read-cache.c
index fee0c80..657f0c5 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1166,6 +1166,16 @@ int discard_index(struct index_state *istate)
return 0;
}
+int unmerged_index(struct index_state *istate)
+{
+ int i;
+ for (i = 0; i < istate->cache_nr; i++) {
+ if (ce_stage(istate->cache[i]))
+ return 1;
+ }
+ return 0;
+}
+
#define WRITE_BUFFER_SIZE 8192
static unsigned char write_buffer[WRITE_BUFFER_SIZE];
static unsigned long write_buffer_len;
diff --git a/receive-pack.c b/receive-pack.c
index 3267495..a971433 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -132,6 +132,7 @@ static int run_hook(const char *hook_name)
break;
}
}
+ close(proc.in);
return hook_status(finish_command(&proc), hook_name);
}
@@ -414,6 +415,7 @@ static const char *unpack(void)
if (start_command(&ip))
return "index-pack fork failed";
pack_lockfile = index_pack_lockfile(ip.out);
+ close(ip.out);
status = finish_command(&ip);
if (!status) {
reprepare_packed_git();
diff --git a/refs.c b/refs.c
index fb33da1..c979fb1 100644
--- a/refs.c
+++ b/refs.c
@@ -157,6 +157,7 @@ static struct cached_refs {
struct ref_list *loose;
struct ref_list *packed;
} cached_refs;
+static struct ref_list *current_ref;
static void free_ref_list(struct ref_list *list)
{
@@ -476,6 +477,7 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
error("%s does not point to a valid object!", entry->name);
return 0;
}
+ current_ref = entry;
return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
}
@@ -485,6 +487,16 @@ int peel_ref(const char *ref, unsigned char *sha1)
unsigned char base[20];
struct object *o;
+ if (current_ref && (current_ref->name == ref
+ || !strcmp(current_ref->name, ref))) {
+ if (current_ref->flag & REF_KNOWS_PEELED) {
+ hashcpy(sha1, current_ref->peeled);
+ return 0;
+ }
+ hashcpy(base, current_ref->sha1);
+ goto fallback;
+ }
+
if (!resolve_ref(ref, base, 1, &flag))
return -1;
@@ -504,7 +516,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
}
}
- /* fallback - callers should not call this for unpacked refs */
+fallback:
o = parse_object(base);
if (o && o->type == OBJ_TAG) {
o = deref_tag(o, ref, 0);
@@ -519,7 +531,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
void *cb_data)
{
- int retval;
+ int retval = 0;
struct ref_list *packed = get_packed_refs();
struct ref_list *loose = get_loose_refs();
@@ -539,15 +551,18 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
}
retval = do_one_ref(base, fn, trim, cb_data, entry);
if (retval)
- return retval;
+ goto end_each;
}
for (packed = packed ? packed : loose; packed; packed = packed->next) {
retval = do_one_ref(base, fn, trim, cb_data, packed);
if (retval)
- return retval;
+ goto end_each;
}
- return 0;
+
+end_each:
+ current_ref = NULL;
+ return retval;
}
int head_ref(each_ref_fn fn, void *cb_data)
diff --git a/remote.c b/remote.c
index 6b56473..7e19372 100644
--- a/remote.c
+++ b/remote.c
@@ -2,123 +2,184 @@
#include "remote.h"
#include "refs.h"
+struct counted_string {
+ size_t len;
+ const char *s;
+};
+struct rewrite {
+ const char *base;
+ size_t baselen;
+ struct counted_string *instead_of;
+ int instead_of_nr;
+ int instead_of_alloc;
+};
+
static struct remote **remotes;
-static int allocated_remotes;
+static int remotes_alloc;
+static int remotes_nr;
static struct branch **branches;
-static int allocated_branches;
+static int branches_alloc;
+static int branches_nr;
static struct branch *current_branch;
static const char *default_remote_name;
+static struct rewrite **rewrite;
+static int rewrite_alloc;
+static int rewrite_nr;
+
#define BUF_SIZE (2048)
static char buffer[BUF_SIZE];
+static const char *alias_url(const char *url)
+{
+ int i, j;
+ char *ret;
+ struct counted_string *longest;
+ int longest_i;
+
+ longest = NULL;
+ longest_i = -1;
+ for (i = 0; i < rewrite_nr; i++) {
+ if (!rewrite[i])
+ continue;
+ for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
+ if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+ (!longest ||
+ longest->len < rewrite[i]->instead_of[j].len)) {
+ longest = &(rewrite[i]->instead_of[j]);
+ longest_i = i;
+ }
+ }
+ }
+ if (!longest)
+ return url;
+
+ ret = malloc(rewrite[longest_i]->baselen +
+ (strlen(url) - longest->len) + 1);
+ strcpy(ret, rewrite[longest_i]->base);
+ strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+ return ret;
+}
+
static void add_push_refspec(struct remote *remote, const char *ref)
{
- int nr = remote->push_refspec_nr + 1;
- remote->push_refspec =
- xrealloc(remote->push_refspec, nr * sizeof(char *));
- remote->push_refspec[nr-1] = ref;
- remote->push_refspec_nr = nr;
+ ALLOC_GROW(remote->push_refspec,
+ remote->push_refspec_nr + 1,
+ remote->push_refspec_alloc);
+ remote->push_refspec[remote->push_refspec_nr++] = ref;
}
static void add_fetch_refspec(struct remote *remote, const char *ref)
{
- int nr = remote->fetch_refspec_nr + 1;
- remote->fetch_refspec =
- xrealloc(remote->fetch_refspec, nr * sizeof(char *));
- remote->fetch_refspec[nr-1] = ref;
- remote->fetch_refspec_nr = nr;
+ ALLOC_GROW(remote->fetch_refspec,
+ remote->fetch_refspec_nr + 1,
+ remote->fetch_refspec_alloc);
+ remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
}
static void add_url(struct remote *remote, const char *url)
{
- int nr = remote->url_nr + 1;
- remote->url =
- xrealloc(remote->url, nr * sizeof(char *));
- remote->url[nr-1] = url;
- remote->url_nr = nr;
+ ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
+ remote->url[remote->url_nr++] = url;
+}
+
+static void add_url_alias(struct remote *remote, const char *url)
+{
+ add_url(remote, alias_url(url));
}
static struct remote *make_remote(const char *name, int len)
{
- int i, empty = -1;
+ struct remote *ret;
+ int i;
- for (i = 0; i < allocated_remotes; i++) {
- if (!remotes[i]) {
- if (empty < 0)
- empty = i;
- } else {
- if (len ? (!strncmp(name, remotes[i]->name, len) &&
- !remotes[i]->name[len]) :
- !strcmp(name, remotes[i]->name))
- return remotes[i];
- }
+ for (i = 0; i < remotes_nr; i++) {
+ if (len ? (!strncmp(name, remotes[i]->name, len) &&
+ !remotes[i]->name[len]) :
+ !strcmp(name, remotes[i]->name))
+ return remotes[i];
}
- if (empty < 0) {
- empty = allocated_remotes;
- allocated_remotes += allocated_remotes ? allocated_remotes : 1;
- remotes = xrealloc(remotes,
- sizeof(*remotes) * allocated_remotes);
- memset(remotes + empty, 0,
- (allocated_remotes - empty) * sizeof(*remotes));
- }
- remotes[empty] = xcalloc(1, sizeof(struct remote));
+ ret = xcalloc(1, sizeof(struct remote));
+ ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
+ remotes[remotes_nr++] = ret;
if (len)
- remotes[empty]->name = xstrndup(name, len);
+ ret->name = xstrndup(name, len);
else
- remotes[empty]->name = xstrdup(name);
- return remotes[empty];
+ ret->name = xstrdup(name);
+ return ret;
}
static void add_merge(struct branch *branch, const char *name)
{
- int nr = branch->merge_nr + 1;
- branch->merge_name =
- xrealloc(branch->merge_name, nr * sizeof(char *));
- branch->merge_name[nr-1] = name;
- branch->merge_nr = nr;
+ ALLOC_GROW(branch->merge_name, branch->merge_nr + 1,
+ branch->merge_alloc);
+ branch->merge_name[branch->merge_nr++] = name;
}
static struct branch *make_branch(const char *name, int len)
{
- int i, empty = -1;
+ struct branch *ret;
+ int i;
char *refname;
- for (i = 0; i < allocated_branches; i++) {
- if (!branches[i]) {
- if (empty < 0)
- empty = i;
- } else {
- if (len ? (!strncmp(name, branches[i]->name, len) &&
- !branches[i]->name[len]) :
- !strcmp(name, branches[i]->name))
- return branches[i];
- }
+ for (i = 0; i < branches_nr; i++) {
+ if (len ? (!strncmp(name, branches[i]->name, len) &&
+ !branches[i]->name[len]) :
+ !strcmp(name, branches[i]->name))
+ return branches[i];
}
- if (empty < 0) {
- empty = allocated_branches;
- allocated_branches += allocated_branches ? allocated_branches : 1;
- branches = xrealloc(branches,
- sizeof(*branches) * allocated_branches);
- memset(branches + empty, 0,
- (allocated_branches - empty) * sizeof(*branches));
- }
- branches[empty] = xcalloc(1, sizeof(struct branch));
+ ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
+ ret = xcalloc(1, sizeof(struct branch));
+ branches[branches_nr++] = ret;
if (len)
- branches[empty]->name = xstrndup(name, len);
+ ret->name = xstrndup(name, len);
else
- branches[empty]->name = xstrdup(name);
+ ret->name = xstrdup(name);
refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
strcpy(refname, "refs/heads/");
- strcpy(refname + strlen("refs/heads/"),
- branches[empty]->name);
- branches[empty]->refname = refname;
+ strcpy(refname + strlen("refs/heads/"), ret->name);
+ ret->refname = refname;
+
+ return ret;
+}
+
+static struct rewrite *make_rewrite(const char *base, int len)
+{
+ struct rewrite *ret;
+ int i;
+
+ for (i = 0; i < rewrite_nr; i++) {
+ if (len
+ ? (len == rewrite[i]->baselen &&
+ !strncmp(base, rewrite[i]->base, len))
+ : !strcmp(base, rewrite[i]->base))
+ return rewrite[i];
+ }
- return branches[empty];
+ ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+ ret = xcalloc(1, sizeof(struct rewrite));
+ rewrite[rewrite_nr++] = ret;
+ if (len) {
+ ret->base = xstrndup(base, len);
+ ret->baselen = len;
+ }
+ else {
+ ret->base = xstrdup(base);
+ ret->baselen = strlen(base);
+ }
+ return ret;
+}
+
+static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
+{
+ ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
+ rewrite->instead_of[rewrite->instead_of_nr].s = instead_of;
+ rewrite->instead_of[rewrite->instead_of_nr].len = strlen(instead_of);
+ rewrite->instead_of_nr++;
}
static void read_remotes_file(struct remote *remote)
@@ -154,7 +215,7 @@ static void read_remotes_file(struct remote *remote)
switch (value_list) {
case 0:
- add_url(remote, xstrdup(s));
+ add_url_alias(remote, xstrdup(s));
break;
case 1:
add_push_refspec(remote, xstrdup(s));
@@ -206,7 +267,7 @@ static void read_branches_file(struct remote *remote)
} else {
branch = "refs/heads/master";
}
- add_url(remote, p);
+ add_url_alias(remote, p);
add_fetch_refspec(remote, branch);
remote->fetch_tags = 1; /* always auto-follow */
}
@@ -236,6 +297,19 @@ static int handle_config(const char *key, const char *value)
}
return 0;
}
+ if (!prefixcmp(key, "url.")) {
+ struct rewrite *rewrite;
+ name = key + 5;
+ subkey = strrchr(name, '.');
+ if (!subkey)
+ return 0;
+ rewrite = make_rewrite(name, subkey - name);
+ if (!strcmp(subkey, ".insteadof")) {
+ if (!value)
+ return config_error_nonbool(key);
+ add_instead_of(rewrite, xstrdup(value));
+ }
+ }
if (prefixcmp(key, "remote."))
return 0;
name = key + 7;
@@ -287,6 +361,18 @@ static int handle_config(const char *key, const char *value)
return 0;
}
+static void alias_all_urls(void)
+{
+ int i, j;
+ for (i = 0; i < remotes_nr; i++) {
+ if (!remotes[i])
+ continue;
+ for (j = 0; j < remotes[i]->url_nr; j++) {
+ remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
+ }
+ }
+}
+
static void read_config(void)
{
unsigned char sha1[20];
@@ -303,6 +389,7 @@ static void read_config(void)
make_branch(head_ref + strlen("refs/heads/"), 0);
}
git_config(handle_config);
+ alias_all_urls();
}
struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
@@ -368,7 +455,7 @@ struct remote *remote_get(const char *name)
read_branches_file(ret);
}
if (!ret->url)
- add_url(ret, name);
+ add_url_alias(ret, name);
if (!ret->url)
return NULL;
ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
@@ -380,7 +467,7 @@ int for_each_remote(each_remote_fn fn, void *priv)
{
int i, result = 0;
read_config();
- for (i = 0; i < allocated_remotes && !result; i++) {
+ for (i = 0; i < remotes_nr && !result; i++) {
struct remote *r = remotes[i];
if (!r)
continue;
@@ -506,8 +593,7 @@ void free_refs(struct ref *ref)
struct ref *next;
while (ref) {
next = ref->next;
- if (ref->peer_ref)
- free(ref->peer_ref);
+ free(ref->peer_ref);
free(ref);
ref = next;
}
@@ -643,9 +729,17 @@ static int match_explicit(struct ref *src, struct ref *dst,
errs = 1;
if (!dst_value) {
+ unsigned char sha1[20];
+ int flag;
+
if (!matched_src)
return errs;
- dst_value = matched_src->name;
+ dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
+ if (!dst_value ||
+ ((flag & REF_ISSYMREF) &&
+ prefixcmp(dst_value, "refs/heads/")))
+ die("%s cannot be resolved to branch.",
+ matched_src->name);
}
switch (count_refspec_match(dst_value, dst, &matched_dst)) {
diff --git a/remote.h b/remote.h
index 86e036d..0f6033f 100644
--- a/remote.h
+++ b/remote.h
@@ -6,14 +6,17 @@ struct remote {
const char **url;
int url_nr;
+ int url_alloc;
const char **push_refspec;
struct refspec *push;
int push_refspec_nr;
+ int push_refspec_alloc;
const char **fetch_refspec;
struct refspec *fetch;
int fetch_refspec_nr;
+ int fetch_refspec_alloc;
/*
* -1 to never fetch tags
@@ -100,6 +103,7 @@ struct branch {
const char **merge_name;
struct refspec **merge;
int merge_nr;
+ int merge_alloc;
};
struct branch *branch_get(const char *name);
diff --git a/revision.c b/revision.c
index d3e8658..0eb6faa 100644
--- a/revision.c
+++ b/revision.c
@@ -633,12 +633,13 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,
return 0;
}
-static void handle_all(struct rev_info *revs, unsigned flags)
+static void handle_refs(struct rev_info *revs, unsigned flags,
+ int (*for_each)(each_ref_fn, void *))
{
struct all_refs_cb cb;
cb.all_revs = revs;
cb.all_flags = flags;
- for_each_ref(handle_one_ref, &cb);
+ for_each(handle_one_ref, &cb);
}
static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -738,6 +739,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->commit_format = CMIT_FMT_DEFAULT;
diff_setup(&revs->diffopt);
+ if (prefix && !revs->diffopt.prefix) {
+ revs->diffopt.prefix = prefix;
+ revs->diffopt.prefix_length = strlen(prefix);
+ }
}
static void add_pending_commit_list(struct rev_info *revs,
@@ -942,6 +947,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
int left = 1;
int all_match = 0;
int regflags = 0;
+ int fixed = 0;
/* First, search for "--" */
seen_dashdash = 0;
@@ -1010,7 +1016,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
continue;
}
if (!strcmp(arg, "--all")) {
- handle_all(revs, flags);
+ handle_refs(revs, flags, for_each_ref);
+ continue;
+ }
+ if (!strcmp(arg, "--branches")) {
+ handle_refs(revs, flags, for_each_branch_ref);
+ continue;
+ }
+ if (!strcmp(arg, "--tags")) {
+ handle_refs(revs, flags, for_each_tag_ref);
+ continue;
+ }
+ if (!strcmp(arg, "--remotes")) {
+ handle_refs(revs, flags, for_each_remote_ref);
continue;
}
if (!strcmp(arg, "--first-parent")) {
@@ -1238,6 +1256,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
regflags |= REG_ICASE;
continue;
}
+ if (!strcmp(arg, "--fixed-strings") ||
+ !strcmp(arg, "-F")) {
+ fixed = 1;
+ continue;
+ }
if (!strcmp(arg, "--all-match")) {
all_match = 1;
continue;
@@ -1293,8 +1316,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
}
}
- if (revs->grep_filter)
+ if (revs->grep_filter) {
revs->grep_filter->regflags |= regflags;
+ revs->grep_filter->fixed = fixed;
+ }
if (show_merge)
prepare_show_merge(revs);
diff --git a/revision.h b/revision.h
index b5f01f8..c8b3b94 100644
--- a/revision.h
+++ b/revision.h
@@ -75,7 +75,7 @@ struct rev_info {
struct log_info *loginfo;
int nr, total;
const char *mime_boundary;
- const char *message_id;
+ char *message_id;
const char *ref_message_id;
const char *add_signoff;
const char *extra_headers;
diff --git a/run-command.c b/run-command.c
index 476d00c..743757c 100644
--- a/run-command.c
+++ b/run-command.c
@@ -20,12 +20,19 @@ int start_command(struct child_process *cmd)
int need_in, need_out, need_err;
int fdin[2], fdout[2], fderr[2];
+ /*
+ * In case of errors we must keep the promise to close FDs
+ * that have been passed in via ->in and ->out.
+ */
+
need_in = !cmd->no_stdin && cmd->in < 0;
if (need_in) {
- if (pipe(fdin) < 0)
+ if (pipe(fdin) < 0) {
+ if (cmd->out > 0)
+ close(cmd->out);
return -ERR_RUN_COMMAND_PIPE;
+ }
cmd->in = fdin[1];
- cmd->close_in = 1;
}
need_out = !cmd->no_stdout
@@ -35,10 +42,11 @@ int start_command(struct child_process *cmd)
if (pipe(fdout) < 0) {
if (need_in)
close_pair(fdin);
+ else if (cmd->in)
+ close(cmd->in);
return -ERR_RUN_COMMAND_PIPE;
}
cmd->out = fdout[0];
- cmd->close_out = 1;
}
need_err = !cmd->no_stderr && cmd->err < 0;
@@ -46,8 +54,12 @@ int start_command(struct child_process *cmd)
if (pipe(fderr) < 0) {
if (need_in)
close_pair(fdin);
+ else if (cmd->in)
+ close(cmd->in);
if (need_out)
close_pair(fdout);
+ else if (cmd->out)
+ close(cmd->out);
return -ERR_RUN_COMMAND_PIPE;
}
cmd->err = fderr[0];
@@ -57,8 +69,12 @@ int start_command(struct child_process *cmd)
if (cmd->pid < 0) {
if (need_in)
close_pair(fdin);
+ else if (cmd->in)
+ close(cmd->in);
if (need_out)
close_pair(fdout);
+ else if (cmd->out)
+ close(cmd->out);
if (need_err)
close_pair(fderr);
return -ERR_RUN_COMMAND_FORK;
@@ -120,7 +136,7 @@ int start_command(struct child_process *cmd)
if (need_out)
close(fdout[1]);
- else if (cmd->out > 1)
+ else if (cmd->out)
close(cmd->out);
if (need_err)
@@ -157,10 +173,6 @@ static int wait_or_whine(pid_t pid)
int finish_command(struct child_process *cmd)
{
- if (cmd->close_in)
- close(cmd->in);
- if (cmd->close_out)
- close(cmd->out);
return wait_or_whine(cmd->pid);
}
diff --git a/run-command.h b/run-command.h
index 1fc781d..debe307 100644
--- a/run-command.h
+++ b/run-command.h
@@ -14,13 +14,29 @@ enum {
struct child_process {
const char **argv;
pid_t pid;
+ /*
+ * Using .in, .out, .err:
+ * - Specify 0 for no redirections (child inherits stdin, stdout,
+ * stderr from parent).
+ * - Specify -1 to have a pipe allocated as follows:
+ * .in: returns the writable pipe end; parent writes to it,
+ * the readable pipe end becomes child's stdin
+ * .out, .err: returns the readable pipe end; parent reads from
+ * it, the writable pipe end becomes child's stdout/stderr
+ * The caller of start_command() must close the returned FDs
+ * after it has completed reading from/writing to it!
+ * - Specify > 0 to set a channel to a particular FD as follows:
+ * .in: a readable FD, becomes child's stdin
+ * .out: a writable FD, becomes child's stdout/stderr
+ * .err > 0 not supported
+ * The specified FD is closed by start_command(), even in case
+ * of errors!
+ */
int in;
int out;
int err;
const char *dir;
const char *const *env;
- unsigned close_in:1;
- unsigned close_out:1;
unsigned no_stdin:1;
unsigned no_stdout:1;
unsigned no_stderr:1;
diff --git a/setup.c b/setup.c
index dc247a8..89c81e5 100644
--- a/setup.c
+++ b/setup.c
@@ -448,8 +448,7 @@ int check_repository_format_version(const char *var, const char *value)
} else if (strcmp(var, "core.worktree") == 0) {
if (!value)
return config_error_nonbool(var);
- if (git_work_tree_cfg)
- free(git_work_tree_cfg);
+ free(git_work_tree_cfg);
git_work_tree_cfg = xstrdup(value);
inside_work_tree = -1;
}
diff --git a/sha1_name.c b/sha1_name.c
index c2805e7..8358ba2 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -192,26 +192,25 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
const char *find_unique_abbrev(const unsigned char *sha1, int len)
{
- int status, is_null;
+ int status, exists;
static char hex[41];
- is_null = is_null_sha1(sha1);
+ exists = has_sha1_file(sha1);
memcpy(hex, sha1_to_hex(sha1), 40);
if (len == 40 || !len)
return hex;
while (len < 40) {
unsigned char sha1_ret[20];
status = get_short_sha1(hex, len, sha1_ret, 1);
- if (!status ||
- (is_null && status != SHORT_NAME_AMBIGUOUS)) {
+ if (exists
+ ? !status
+ : status == SHORT_NAME_NOT_FOUND) {
hex[len] = 0;
return hex;
}
- if (status != SHORT_NAME_AMBIGUOUS)
- return NULL;
len++;
}
- return NULL;
+ return hex;
}
static int ambiguous_path(const char *path, int len)
@@ -625,8 +624,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
if (!parse_object(commit->object.sha1))
continue;
- if (temp_commit_buffer)
- free(temp_commit_buffer);
+ free(temp_commit_buffer);
if (commit->buffer)
p = commit->buffer;
else {
@@ -643,8 +641,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
break;
}
}
- if (temp_commit_buffer)
- free(temp_commit_buffer);
+ free(temp_commit_buffer);
free_commit_list(list);
for (l = backup; l; l = l->next)
clear_commit_marks(l->item, ONELINE_SEEN);
diff --git a/shortlog.h b/shortlog.h
new file mode 100644
index 0000000..31ff491
--- /dev/null
+++ b/shortlog.h
@@ -0,0 +1,26 @@
+#ifndef SHORTLOG_H
+#define SHORTLOG_H
+
+#include "path-list.h"
+
+struct shortlog {
+ struct path_list list;
+ int summary;
+ int wrap_lines;
+ int sort_by_number;
+ int wrap;
+ int in1;
+ int in2;
+
+ char *common_repo_prefix;
+ int email;
+ struct path_list mailmap;
+};
+
+void shortlog_init(struct shortlog *log);
+
+void shortlog_add_commit(struct shortlog *log, struct commit *commit);
+
+void shortlog_output(struct shortlog *log);
+
+#endif
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 92de088..27b54cb 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -304,6 +304,8 @@ test_expect_success 'absolute path works as expected' '
test "$dir" = "$(test-absolute-path $dir2)" &&
file="$dir"/index &&
test "$file" = "$(test-absolute-path $dir2/index)" &&
+ basename=blub &&
+ test "$dir/$basename" = $(cd .git && test-absolute-path $basename) &&
ln -s ../first/file .git/syml &&
sym="$(cd first; pwd -P)"/file &&
test "$sym" = "$(test-absolute-path $dir2/syml)"
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
new file mode 100755
index 0000000..cd088b3
--- /dev/null
+++ b/t/t0050-filesystem.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='Various filesystem issues'
+
+. ./test-lib.sh
+
+auml=`perl -CO -e 'print pack("U",0x00E4)'`
+aumlcdiar=`perl -CO -e 'print pack("U",0x0061).pack("U",0x0308)'`
+
+test_expect_success 'see if we expect ' '
+
+ test_case=test_expect_success
+ test_unicode=test_expect_success
+ mkdir junk &&
+ echo good >junk/CamelCase &&
+ echo bad >junk/camelcase &&
+ if test "$(cat junk/CamelCase)" != good
+ then
+ test_case=test_expect_failure
+ say "will test on a case insensitive filesystem"
+ fi &&
+ rm -fr junk &&
+ mkdir junk &&
+ >junk/"$auml" &&
+ case "$(cd junk && echo *)" in
+ "$aumlcdiar")
+ test_unicode=test_expect_failure
+ say "will test on a unicode corrupting filesystem"
+ ;;
+ *) ;;
+ esac &&
+ rm -fr junk
+'
+
+test_expect_success "setup case tests" '
+
+ touch camelcase &&
+ git add camelcase &&
+ git commit -m "initial" &&
+ git tag initial &&
+ git checkout -b topic &&
+ git mv camelcase tmp &&
+ git mv tmp CamelCase &&
+ git commit -m "rename" &&
+ git checkout -f master
+
+'
+
+$test_case 'rename (case change)' '
+
+ git mv camelcase CamelCase &&
+ git commit -m "rename"
+
+'
+
+$test_case 'merge (case change)' '
+
+ git reset --hard initial &&
+ git merge topic
+
+'
+
+test_expect_success "setup unicode normalization tests" '
+
+ test_create_repo unicode &&
+ cd unicode &&
+ touch "$aumlcdiar" &&
+ git add "$aumlcdiar" &&
+ git commit -m initial
+ git tag initial &&
+ git checkout -b topic &&
+ git mv $aumlcdiar tmp &&
+ git mv tmp "$auml" &&
+ git commit -m rename &&
+ git checkout -f master
+
+'
+
+$test_unicode 'rename (silent unicode normalization)' '
+
+ git mv "$aumlcdiar" "$auml" &&
+ git commit -m rename
+
+'
+
+$test_unicode 'merge (silent unicode normalization)' '
+
+ git reset --hard initial &&
+ git merge topic
+
+'
+
+test_done
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
new file mode 100755
index 0000000..762af5f
--- /dev/null
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='test git rev-parse --parseopt'
+. ./test-lib.sh
+
+cat > expect.err <<EOF
+usage: some-command [options] <args>...
+
+ some-command does foo and bar!
+
+ -h, --help show the help
+ --foo some nifty option --foo
+ --bar ... some cool option --bar with an argument
+
+An option group Header
+ -C [...] option C with an optional argument
+
+Extras
+ --extra1 line above used to cause a segfault but no longer does
+
+EOF
+
+test_expect_success 'test --parseopt help output' '
+ git rev-parse --parseopt -- -h 2> output.err <<EOF
+some-command [options] <args>...
+
+some-command does foo and bar!
+--
+h,help show the help
+
+foo some nifty option --foo
+bar= some cool option --bar with an argument
+
+ An option group Header
+C? option C with an optional argument
+
+Extras
+extra1 line above used to cause a segfault but no longer does
+EOF
+ git diff expect.err output.err
+'
+
+test_done
diff --git a/t/t2008-checkout-subdir.sh b/t/t2008-checkout-subdir.sh
index 4a723dc..3e098ab 100755
--- a/t/t2008-checkout-subdir.sh
+++ b/t/t2008-checkout-subdir.sh
@@ -68,15 +68,15 @@ test_expect_success 'checkout with simple prefix' '
'
test_expect_success 'relative path outside tree should fail' \
- '! git checkout HEAD -- ../../Makefile'
+ 'test_must_fail git checkout HEAD -- ../../Makefile'
test_expect_success 'incorrect relative path to file should fail (1)' \
- '! git checkout HEAD -- ../file0'
+ 'test_must_fail git checkout HEAD -- ../file0'
test_expect_success 'incorrect relative path should fail (2)' \
- '( cd dir1 && ! git checkout HEAD -- ./file0 )'
+ '( cd dir1 && test_must_fail git checkout HEAD -- ./file0 )'
test_expect_success 'incorrect relative path should fail (3)' \
- '( cd dir1 && ! git checkout HEAD -- ../../file0 )'
+ '( cd dir1 && test_must_fail git checkout HEAD -- ../../file0 )'
test_done
diff --git a/t/t2009-checkout-statinfo.sh b/t/t2009-checkout-statinfo.sh
new file mode 100755
index 0000000..f3c2152
--- /dev/null
+++ b/t/t2009-checkout-statinfo.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='checkout should leave clean stat info'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ echo hello >world &&
+ git update-index --add world &&
+ git commit -m initial &&
+ git branch side &&
+ echo goodbye >world &&
+ git update-index --add world &&
+ git commit -m second
+
+'
+
+test_expect_success 'branch switching' '
+
+ git reset --hard &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout master &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout side &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout master &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_expect_success 'path checkout' '
+
+ git reset --hard &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout master world &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout side world &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout master world &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_done
+
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index d21081d..38a90ad 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -15,6 +15,9 @@ test_expect_success \
'echo Hello > A &&
git update-index --add A &&
git-commit -m "Initial commit." &&
+ echo World >> A &&
+ git update-index --add A &&
+ git-commit -m "Second commit." &&
HEAD=$(git rev-parse --verify HEAD)'
test_expect_success \
@@ -171,7 +174,9 @@ test_expect_success 'test overriding tracking setup via --no-track' \
! test "$(git config branch.my2.merge)" = refs/heads/master'
test_expect_success 'no tracking without .fetch entries' \
- 'git branch --track my6 s &&
+ 'git config branch.autosetupmerge true &&
+ git branch my6 s &&
+ git config branch.automsetupmerge false &&
test -z "$(git config branch.my6.remote)" &&
test -z "$(git config branch.my6.merge)"'
@@ -192,6 +197,21 @@ test_expect_success 'test deleting branch without config' \
'git branch my7 s &&
test "$(git branch -d my7 2>&1)" = "Deleted branch my7."'
+test_expect_success 'test --track without .fetch entries' \
+ 'git branch --track my8 &&
+ test "$(git config branch.my8.remote)" &&
+ test "$(git config branch.my8.merge)"'
+
+test_expect_success \
+ 'branch from non-branch HEAD w/autosetupmerge=always' \
+ 'git config branch.autosetupmerge always &&
+ git branch my9 HEAD^ &&
+ git config branch.autosetupmerge false'
+
+test_expect_success \
+ 'branch from non-branch HEAD w/--track causes failure' \
+ '!(git branch --track my10 HEAD^)'
+
# Keep this test last, as it changes the current branch
cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 62e65d7..049aa37 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -61,7 +61,7 @@ test_expect_success 'setup' '
git tag I
'
-echo "#!$SHELL" >fake-editor
+echo "#!$SHELL_PATH" >fake-editor.sh
cat >> fake-editor.sh <<\EOF
case "$1" in
*/COMMIT_EDITMSG)
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
new file mode 100755
index 0000000..3417138
--- /dev/null
+++ b/t/t3407-rebase-abort.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='git rebase --abort tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo a > a &&
+ git add a &&
+ git commit -m a &&
+ git branch to-rebase &&
+
+ echo b > a &&
+ git commit -a -m b &&
+ echo c > a &&
+ git commit -a -m c &&
+
+ git checkout to-rebase &&
+ echo d > a &&
+ git commit -a -m "merge should fail on this" &&
+ echo e > a &&
+ git commit -a -m "merge should fail on this, too" &&
+ git branch pre-rebase
+'
+
+test_expect_success 'rebase --abort' '
+ test_must_fail git rebase master &&
+ git rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
+'
+
+test_expect_success 'rebase --abort after --skip' '
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase
+ rm -rf .dotest
+
+ test_must_fail git rebase master &&
+ test_must_fail git rebase --skip &&
+ test $(git rev-parse HEAD) = $(git rev-parse master) &&
+ git rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
+'
+
+test_expect_success 'rebase --abort after --continue' '
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase
+ rm -rf .dotest
+
+ test_must_fail git rebase master &&
+ echo c > a &&
+ echo d >> a &&
+ git add a &&
+ test_must_fail git rebase --continue &&
+ test $(git rev-parse HEAD) != $(git rev-parse master) &&
+ git rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
+'
+
+test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 9eec754..6b4d1c5 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -245,6 +245,7 @@ format-patch --inline --stdout initial..master
format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
config format.subjectprefix DIFFERENT_PREFIX
format-patch --inline --stdout initial..master^^
+format-patch --stdout --cover-letter -n initial..master^
diff --abbrev initial..side
diff -r initial..side
diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
new file mode 100644
index 0000000..0151453
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
@@ -0,0 +1,100 @@
+$ git format-patch --stdout --cover-letter -n initial..master^
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: C O Mitter <committer@example.com>
+Date: Mon, 26 Jun 2006 00:05:00 +0000
+Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***
+
+*** BLURB HERE ***
+
+A U Thor (2):
+ Second
+ Third
+
+ dir/sub | 4 ++++
+ file0 | 3 +++
+ file1 | 3 +++
+ file2 | 3 ---
+ 4 files changed, 10 insertions(+), 3 deletions(-)
+ create mode 100644 file1
+ delete mode 100644 file2
+
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [DIFFERENT_PREFIX 1/2] Second
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [DIFFERENT_PREFIX 2/2] Third
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+--
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 0a6fe53..16aa99d 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -88,4 +88,117 @@ test_expect_success 'replay did not screw up the log message' '
'
+test_expect_success 'extra headers' '
+
+ git config format.headers "To: R. E. Cipient <rcipient@example.com>
+" &&
+ git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
+" &&
+ git format-patch --stdout master..side > patch2 &&
+ sed -e "/^$/q" patch2 > hdrs2 &&
+ grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs2 &&
+ grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs2
+
+'
+
+test_expect_success 'extra headers without newlines' '
+
+ git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
+ git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
+ git format-patch --stdout master..side >patch3 &&
+ sed -e "/^$/q" patch3 > hdrs3 &&
+ grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs3 &&
+ grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs3
+
+'
+
+test_expect_success 'extra headers with multiple To:s' '
+
+ git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
+ git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
+ git format-patch --stdout master..side > patch4 &&
+ sed -e "/^$/q" patch4 > hdrs4 &&
+ grep "^To: R. E. Cipient <rcipient@example.com>,$" hdrs4 &&
+ grep "^ *S. E. Cipient <scipient@example.com>$" hdrs4
+'
+
+test_expect_success 'additional command line cc' '
+
+ git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
+ git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch5 &&
+ grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch5 &&
+ grep "^ *S. E. Cipient <scipient@example.com>$" patch5
+'
+
+test_expect_success 'multiple files' '
+
+ rm -rf patches/ &&
+ git checkout side &&
+ git format-patch -o patches/ master &&
+ ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
+'
+
+test_expect_success 'thread' '
+
+ rm -rf patches/ &&
+ git checkout side &&
+ git format-patch --thread -o patches/ master &&
+ FIRST_MID=$(grep "Message-Id:" patches/0001-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
+ for i in patches/0002-* patches/0003-*
+ do
+ grep "References: $FIRST_MID" $i &&
+ grep "In-Reply-To: $FIRST_MID" $i
+ done
+'
+
+test_expect_success 'thread in-reply-to' '
+
+ rm -rf patches/ &&
+ git checkout side &&
+ git format-patch --in-reply-to="<test.message>" --thread -o patches/ master &&
+ FIRST_MID="<test.message>" &&
+ for i in patches/*
+ do
+ grep "References: $FIRST_MID" $i &&
+ grep "In-Reply-To: $FIRST_MID" $i
+ done
+'
+
+test_expect_success 'thread cover-letter' '
+
+ rm -rf patches/ &&
+ git checkout side &&
+ git format-patch --cover-letter --thread -o patches/ master &&
+ FIRST_MID=$(grep "Message-Id:" patches/0000-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
+ for i in patches/0001-* patches/0002-* patches/0003-*
+ do
+ grep "References: $FIRST_MID" $i &&
+ grep "In-Reply-To: $FIRST_MID" $i
+ done
+'
+
+test_expect_success 'thread cover-letter in-reply-to' '
+
+ rm -rf patches/ &&
+ git checkout side &&
+ git format-patch --cover-letter --in-reply-to="<test.message>" --thread -o patches/ master &&
+ FIRST_MID="<test.message>" &&
+ for i in patches/*
+ do
+ grep "References: $FIRST_MID" $i &&
+ grep "In-Reply-To: $FIRST_MID" $i
+ done
+'
+
+test_expect_success 'excessive subject' '
+
+ rm -rf patches/ &&
+ git checkout side &&
+ for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file &&
+ git update-index file &&
+ git commit -m "This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole paragraph right at the start as the only thing in the commit message. It had better not become the filename for the patch." &&
+ git format-patch -o patches/ master..side &&
+ ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
+'
+
test_done
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
new file mode 100755
index 0000000..3d2d081
--- /dev/null
+++ b/t/t4027-diff-submodule.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='difference in submodules'
+
+. ./test-lib.sh
+. ../diff-lib.sh
+
+_z40=0000000000000000000000000000000000000000
+test_expect_success setup '
+ test_tick &&
+ test_create_repo sub &&
+ (
+ cd sub &&
+ echo hello >world &&
+ git add world &&
+ git commit -m submodule
+ ) &&
+
+ test_tick &&
+ echo frotz >nitfol &&
+ git add nitfol sub &&
+ git commit -m superproject &&
+
+ (
+ cd sub &&
+ echo goodbye >world &&
+ git add world &&
+ git commit -m "submodule #2"
+ ) &&
+
+ set x $(
+ cd sub &&
+ git rev-list HEAD
+ ) &&
+ echo ":160000 160000 $3 $_z40 M sub" >expect
+'
+
+test_expect_success 'git diff --raw HEAD' '
+ git diff --raw --abbrev=40 HEAD >actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'git diff-index --raw HEAD' '
+ git diff-index --raw HEAD >actual.index &&
+ diff -u expect actual.index
+'
+
+test_expect_success 'git diff-files --raw' '
+ git diff-files --raw >actual.files &&
+ diff -u expect actual.files
+'
+
+test_done
diff --git a/t/t5303-hash-object.sh b/t/t5303-hash-object.sh
new file mode 100755
index 0000000..543c078
--- /dev/null
+++ b/t/t5303-hash-object.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description=git-hash-object
+
+. ./test-lib.sh
+
+test_expect_success \
+ 'git hash-object -w --stdin saves the object' \
+ 'obname=$(echo foo | git hash-object -w --stdin) &&
+ obpath=$(echo $obname | sed -e "s/\(..\)/\1\//") &&
+ test -r .git/objects/"$obpath" &&
+ rm -f .git/objects/"$obpath"'
+
+test_expect_success \
+ 'git hash-object --stdin -w saves the object' \
+ 'obname=$(echo foo | git hash-object --stdin -w) &&
+ obpath=$(echo $obname | sed -e "s/\(..\)/\1\//") &&
+ test -r .git/objects/"$obpath" &&
+ rm -f .git/objects/"$obpath"'
+
+test_expect_success \
+ 'git hash-object --stdin file1 <file0 first operates on file0, then file1' \
+ 'echo foo > file1 &&
+ obname0=$(echo bar | git hash-object --stdin) &&
+ obname1=$(git hash-object file1) &&
+ obname0new=$(echo bar | git hash-object --stdin file1 | sed -n -e 1p) &&
+ obname1new=$(echo bar | git hash-object --stdin file1 | sed -n -e 2p) &&
+ test "$obname0" = "$obname0new" &&
+ test "$obname1" = "$obname1new"'
+
+test_expect_success \
+ 'git hash-object refuses multiple --stdin arguments' \
+ '! git hash-object --stdin --stdin < file1'
+
+test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 636aec2..4fc62f5 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -4,9 +4,6 @@ test_description='git remote porcelain-ish'
. ./test-lib.sh
-GIT_CONFIG=.git/config
-export GIT_CONFIG
-
setup_repository () {
mkdir "$1" && (
cd "$1" &&
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 9d2dc33..793ffc6 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -100,6 +100,23 @@ test_expect_success 'fetch with wildcard' '
)
'
+test_expect_success 'fetch with insteadOf' '
+ mk_empty &&
+ (
+ TRASH=$(pwd) &&
+ cd testrepo &&
+ git config url./$TRASH/.insteadOf trash/
+ git config remote.up.url trash/. &&
+ git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+ git fetch up &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
test_expect_success 'push without wildcard' '
mk_empty &&
@@ -126,6 +143,20 @@ test_expect_success 'push with wildcard' '
)
'
+test_expect_success 'push with insteadOf' '
+ mk_empty &&
+ TRASH=$(pwd) &&
+ git config url./$TRASH/.insteadOf trash/ &&
+ git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
test_expect_success 'push with matching heads' '
mk_test heads/master &&
@@ -271,6 +302,49 @@ test_expect_success 'push with HEAD nonexisting at remote' '
check_push_result $the_commit heads/local
'
+test_expect_success 'push with +HEAD' '
+
+ mk_test heads/master &&
+ git checkout master &&
+ git branch -D local &&
+ git checkout -b local &&
+ git push testrepo master local &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_commit heads/local &&
+
+ # Without force rewinding should fail
+ git reset --hard HEAD^ &&
+ ! git push testrepo HEAD &&
+ check_push_result $the_commit heads/local &&
+
+ # With force rewinding should succeed
+ git push testrepo +HEAD &&
+ check_push_result $the_first_commit heads/local
+
+'
+
+test_expect_success 'push with config remote.*.push = HEAD' '
+
+ mk_test heads/local &&
+ git checkout master &&
+ git branch -f local $the_commit &&
+ (
+ cd testrepo &&
+ git checkout local &&
+ git reset --hard $the_first_commit
+ ) &&
+ git config remote.there.url testrepo &&
+ git config remote.there.push HEAD &&
+ git config branch.master.remote there &&
+ git push &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_first_commit heads/local
+'
+
+# clean up the cruft left with the previous one
+git config --remove-section remote.there
+git config --remove-section branch.master
+
test_expect_success 'push with dry-run' '
mk_test heads/master &&
diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh
index 59a165a..8dfaaa4 100755
--- a/t/t5701-clone-local.sh
+++ b/t/t5701-clone-local.sh
@@ -11,6 +11,11 @@ test_expect_success 'preparing origin repository' '
git clone --bare . x &&
test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true &&
test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true
+ git bundle create b1.bundle --all HEAD &&
+ git bundle create b2.bundle --all &&
+ mkdir dir &&
+ cp b1.bundle dir/b3
+ cp b1.bundle b4
'
test_expect_success 'local clone without .git suffix' '
@@ -71,4 +76,44 @@ test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
git fetch &&
test ! -e .git/refs/remotes/origin/HEAD'
+test_expect_success 'bundle clone without .bundle suffix' '
+ cd "$D" &&
+ git clone dir/b3 &&
+ cd b3 &&
+ git fetch
+'
+
+test_expect_success 'bundle clone with .bundle suffix' '
+ cd "$D" &&
+ git clone b1.bundle &&
+ cd b1 &&
+ git fetch
+'
+
+test_expect_success 'bundle clone from b4' '
+ cd "$D" &&
+ git clone b4 bdl &&
+ cd bdl &&
+ git fetch
+'
+
+test_expect_success 'bundle clone from b4.bundle that does not exist' '
+ cd "$D" &&
+ if git clone b4.bundle bb
+ then
+ echo "Oops, should have failed"
+ false
+ else
+ echo happy
+ fi
+'
+
+test_expect_success 'bundle clone with nonexistent HEAD' '
+ cd "$D" &&
+ git clone b2.bundle b2 &&
+ cd b2 &&
+ git fetch
+ test ! -e .git/refs/heads/master
+'
+
test_done
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index 8641996..79dc58b 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -139,4 +139,24 @@ test_expect_success 'binary files cannot be merged' '
grep "Cannot merge binary files" merge.err
'
+sed -e "s/deerit.$/deerit;/" -e "s/me;$/me./" < new5.txt > new6.txt
+sed -e "s/deerit.$/deerit,/" -e "s/me;$/me,/" < new5.txt > new7.txt
+
+test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
+
+ ! git merge-file -p new6.txt new5.txt new7.txt > output &&
+ test 1 = $(grep ======= < output | wc -l)
+
+'
+
+sed -e 's/deerit./&\n\n\n\n/' -e "s/locavit,/locavit;/" < new6.txt > new8.txt
+sed -e 's/deerit./&\n\n\n\n/' -e "s/locavit,/locavit --/" < new7.txt > new9.txt
+
+test_expect_success 'ZEALOUS_ALNUM' '
+
+ ! git merge-file -p new8.txt new5.txt new9.txt > merge.out &&
+ test 1 = $(grep ======= < merge.out | wc -l)
+
+'
+
test_done
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index 149ea85..23d24d3 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -81,8 +81,8 @@ EOF
test_expect_success "virtual trees were processed" "git diff expect out"
-git reset --hard
test_expect_success 'refuse to merge binary files' '
+ git reset --hard &&
printf "\0" > binary-file &&
git add binary-file &&
git commit -m binary &&
diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
new file mode 100755
index 0000000..35d66e8
--- /dev/null
+++ b/t/t6029-merge-subtree.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='subtree merge strategy'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ s="1 2 3 4 5 6 7 8"
+ for i in $s; do echo $i; done >hello &&
+ git add hello &&
+ git commit -m initial &&
+ git checkout -b side &&
+ echo >>hello world &&
+ git add hello &&
+ git commit -m second &&
+ git checkout master &&
+ for i in mundo $s; do echo $i; done >hello &&
+ git add hello &&
+ git commit -m master
+
+'
+
+test_expect_success 'subtree available and works like recursive' '
+
+ git merge -s subtree side &&
+ for i in mundo $s world; do echo $i; done >expect &&
+ diff -u expect hello
+
+'
+
+test_expect_success 'setup' '
+ mkdir git-gui &&
+ cd git-gui &&
+ git init &&
+ echo git-gui > git-gui.sh &&
+ o1=$(git hash-object git-gui.sh) &&
+ git add git-gui.sh &&
+ git commit -m "initial git-gui" &&
+ cd .. &&
+ mkdir git &&
+ cd git &&
+ git init &&
+ echo git >git.c &&
+ o2=$(git hash-object git.c) &&
+ git add git.c &&
+ git commit -m "initial git"
+'
+
+test_expect_success 'initial merge' '
+ git remote add -f gui ../git-gui &&
+ git merge -s ours --no-commit gui/master &&
+ git read-tree --prefix=git-gui/ -u gui/master &&
+ git commit -m "Merge git-gui as our subdirectory" &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 git-gui/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ git diff -u expected actual
+'
+
+test_expect_success 'merge update' '
+ cd ../git-gui &&
+ echo git-gui2 > git-gui.sh &&
+ o3=$(git hash-object git-gui.sh) &&
+ git add git-gui.sh &&
+ git commit -m "update git-gui" &&
+ cd ../git &&
+ git pull -s subtree gui master &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o3 0 git-gui/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ git diff -u expected actual
+'
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index ec71123..4908e87 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -260,7 +260,7 @@ test_expect_success 'bisect starting with a detached HEAD' '
git checkout master^ &&
HEAD=$(git rev-parse --verify HEAD) &&
git bisect start &&
- test $HEAD = $(cat .git/head-name) &&
+ test $HEAD = $(cat .git/BISECT_START) &&
git bisect reset &&
test $HEAD = $(git rev-parse --verify HEAD)
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index dbf1ace..63915cd 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -83,13 +83,13 @@ test_expect_success "checkout with unrelated dirty tree without -m" '
fill 0 1 2 3 4 5 6 7 8 >same &&
cp same kept
git checkout side >messages &&
- git diff same kept
+ diff -u same kept
(cat > messages.expect <<EOF
M same
EOF
) &&
touch messages.expect &&
- git diff messages.expect messages
+ diff -u messages.expect messages
'
test_expect_success "checkout -m with dirty tree" '
@@ -103,29 +103,22 @@ test_expect_success "checkout -m with dirty tree" '
test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
(cat >expect.messages <<EOF
-Merging side with local
-Merging:
-ab76817 Side M one, D two, A three
-virtual local
-found 1 common ancestor(s):
-7329388 Initial A one, A two
-Auto-merged one
M one
EOF
) &&
- git diff expect.messages messages &&
+ diff -u expect.messages messages &&
fill "M one" "A three" "D two" >expect.master &&
git diff --name-status master >current.master &&
- diff expect.master current.master &&
+ diff -u expect.master current.master &&
fill "M one" >expect.side &&
git diff --name-status side >current.side &&
- diff expect.side current.side &&
+ diff -u expect.side current.side &&
: >expect.index &&
git diff --cached >current.index &&
- diff expect.index current.index
+ diff -u expect.index current.index
'
test_expect_success "checkout -m with dirty tree, renamed" '
@@ -143,7 +136,7 @@ test_expect_success "checkout -m with dirty tree, renamed" '
git checkout -m renamer &&
fill 1 3 4 5 7 8 >expect &&
- diff expect uno &&
+ diff -u expect uno &&
! test -f one &&
git diff --cached >current &&
! test -s current
@@ -168,7 +161,7 @@ test_expect_success 'checkout -m with merge conflict' '
git diff master:one :3:uno |
sed -e "1,/^@@/d" -e "/^ /d" -e "s/^-/d/" -e "s/^+/a/" >current &&
fill d2 aT d7 aS >expect &&
- diff current expect &&
+ diff -u current expect &&
git diff --cached two >current &&
! test -s current
'
@@ -185,7 +178,7 @@ If you want to create a new branch from this checkout, you may do so
HEAD is now at 7329388... Initial A one, A two
EOF
) &&
- git diff messages.expect messages &&
+ diff -u messages.expect messages &&
H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) &&
test "z$H" = "z$M" &&
@@ -286,4 +279,62 @@ test_expect_success 'checkout with ambiguous tag/branch names' '
'
+test_expect_success 'switch branches while in subdirectory' '
+
+ git reset --hard &&
+ git checkout master &&
+
+ mkdir subs &&
+ (
+ cd subs &&
+ git checkout side
+ ) &&
+ ! test -f subs/one &&
+ rm -fr subs
+
+'
+
+test_expect_success 'checkout specific path while in subdirectory' '
+
+ git reset --hard &&
+ git checkout side &&
+ mkdir subs &&
+ >subs/bero &&
+ git add subs/bero &&
+ git commit -m "add subs/bero" &&
+
+ git checkout master &&
+ mkdir -p subs &&
+ (
+ cd subs &&
+ git checkout side -- bero
+ ) &&
+ test -f subs/bero
+
+'
+
+test_expect_success \
+ 'checkout w/--track sets up tracking' '
+ git config branch.autosetupmerge false &&
+ git checkout master &&
+ git checkout --track -b track1 &&
+ test "$(git config branch.track1.remote)" &&
+ test "$(git config branch.track1.merge)"'
+
+test_expect_success \
+ 'checkout w/autosetupmerge=always sets up tracking' '
+ git config branch.autosetupmerge always &&
+ git checkout master &&
+ git checkout -b track2 &&
+ test "$(git config branch.track2.remote)" &&
+ test "$(git config branch.track2.merge)"
+ git config branch.autosetupmerge false'
+
+test_expect_success \
+ 'checkout w/--track from non-branch HEAD fails' '
+ git checkout -b delete-me master &&
+ rm .git/refs/heads/delete-me &&
+ test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
+ !(git checkout --track -b track)'
+
test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 83889c4..68efda4 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -270,6 +270,23 @@ test_expect_code () {
echo >&3 ""
}
+# This is not among top-level (test_expect_success | test_expect_failure)
+# but is a prefix that can be used in the test script, like:
+#
+# test_expect_success 'complain and die' '
+# do something &&
+# do something else &&
+# test_must_fail git checkout ../outerspace
+# '
+#
+# Writing this as "! git checkout ../outerspace" is wrong, because
+# the failure could be due to a segv. We want a controlled failure.
+
+test_must_fail () {
+ "$@"
+ test $? -gt 0 -a $? -le 128
+}
+
# Most tests can use the created repository, but some may need to create more.
# Usage: test_create_repo <directory>
test_create_repo () {
@@ -342,6 +359,8 @@ if ! test -x ../test-chmtime; then
exit 1
fi
+. ../GIT-BUILD-OPTIONS
+
# Test repository
test=trash
rm -fr "$test"
diff --git a/templates/Makefile b/templates/Makefile
index ebd3a62..bda9d13 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -29,10 +29,10 @@ boilerplates.made : $(bpsrc)
case "$$boilerplate" in *~) continue ;; esac && \
dst=`echo "$$boilerplate" | sed -e 's|^this|.|;s|--|/|g'` && \
dir=`expr "$$dst" : '\(.*\)/'` && \
- mkdir -p blt/$$dir && \
+ $(INSTALL) -d -m 755 blt/$$dir && \
case "$$boilerplate" in \
*--) ;; \
- *) cp $$boilerplate blt/$$dst ;; \
+ *) cp -p $$boilerplate blt/$$dst ;; \
esac || exit; \
done && \
date >$@
@@ -48,4 +48,4 @@ clean:
install: all
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(template_dir_SQ)'
(cd blt && $(TAR) cf - .) | \
- (cd '$(DESTDIR_SQ)$(template_dir_SQ)' && $(TAR) xf -)
+ (cd '$(DESTDIR_SQ)$(template_dir_SQ)' && umask 022 && $(TAR) xf -)
diff --git a/thread-utils.c b/thread-utils.c
new file mode 100644
index 0000000..55e7e29
--- /dev/null
+++ b/thread-utils.c
@@ -0,0 +1,48 @@
+#include "cache.h"
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+# include <sys/pstat.h>
+#endif
+
+/*
+ * By doing this in two steps we can at least get
+ * the function to be somewhat coherent, even
+ * with this disgusting nest of #ifdefs.
+ */
+#ifndef _SC_NPROCESSORS_ONLN
+# ifdef _SC_NPROC_ONLN
+# define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
+# elif defined _SC_CRAY_NCPU
+# define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
+# endif
+#endif
+
+int online_cpus(void)
+{
+#ifdef _SC_NPROCESSORS_ONLN
+ long ncpus;
+#endif
+
+#ifdef _WIN32
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+
+ if ((int)info.dwNumberOfProcessors > 0)
+ return (int)info.dwNumberOfProcessors;
+#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+ struct pst_dynamic psd;
+
+ if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))
+ return (int)psd.psd_proc_cnt;
+#endif
+
+#ifdef _SC_NPROCESSORS_ONLN
+ if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)
+ return (int)ncpus;
+#endif
+
+ return 1;
+}
diff --git a/thread-utils.h b/thread-utils.h
new file mode 100644
index 0000000..cce4b77
--- /dev/null
+++ b/thread-utils.h
@@ -0,0 +1,6 @@
+#ifndef THREAD_COMPAT_H
+#define THREAD_COMPAT_H
+
+extern int online_cpus(void);
+
+#endif /* THREAD_COMPAT_H */
diff --git a/transport.c b/transport.c
index 397983d..0a5cf0a 100644
--- a/transport.c
+++ b/transport.c
@@ -622,6 +622,7 @@ static int fetch_refs_via_pack(struct transport *transport,
char *dest = xstrdup(transport->url);
struct fetch_pack_args args;
int i;
+ struct ref *refs_tmp = NULL;
memset(&args, 0, sizeof(args));
args.uploadpack = data->uploadpack;
@@ -634,15 +635,13 @@ static int fetch_refs_via_pack(struct transport *transport,
for (i = 0; i < nr_heads; i++)
origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
- refs = transport_get_remote_refs(transport);
if (!data->conn) {
- struct ref *refs_tmp;
connect_setup(transport);
get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0);
- free_refs(refs_tmp);
}
- refs = fetch_pack(&args, data->fd, data->conn, transport->remote_refs,
+ refs = fetch_pack(&args, data->fd, data->conn,
+ refs_tmp ? refs_tmp : transport->remote_refs,
dest, nr_heads, heads, &transport->pack_lockfile);
close(data->fd[0]);
close(data->fd[1]);
@@ -650,6 +649,8 @@ static int fetch_refs_via_pack(struct transport *transport,
refs = NULL;
data->conn = NULL;
+ free_refs(refs_tmp);
+
for (i = 0; i < nr_heads; i++)
free(origh[i]);
free(origh);
diff --git a/unpack-trees.c b/unpack-trees.c
index 56c1ffb..3e448d8 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -85,6 +85,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
int any_dirs = 0;
char *cache_name;
int ce_stage;
+ int skip_entry = 0;
/* Find the first name in the input. */
@@ -122,13 +123,13 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
#if DBRT_DEBUG > 1
if (first)
- printf("index %s\n", first);
+ fprintf(stderr, "index %s\n", first);
#endif
for (i = 0; i < len; i++) {
if (!posns[i] || posns[i] == df_conflict_list)
continue;
#if DBRT_DEBUG > 1
- printf("%d %s\n", i + 1, posns[i]->name);
+ fprintf(stderr, "%d %s\n", i + 1, posns[i]->name);
#endif
if (!first || entcmp(first, firstdir,
posns[i]->name,
@@ -153,6 +154,8 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
any_files = 1;
src[0] = active_cache[o->pos];
remove = o->pos;
+ if (o->skip_unmerged && ce_stage(src[0]))
+ skip_entry = 1;
}
for (i = 0; i < len; i++) {
@@ -181,6 +184,12 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
continue;
}
+ if (skip_entry) {
+ subposns[i] = df_conflict_list;
+ posns[i] = posns[i]->next;
+ continue;
+ }
+
if (!o->merge)
ce_stage = 0;
else if (i + 1 < o->head_idx)
@@ -205,23 +214,31 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
posns[i] = posns[i]->next;
}
if (any_files) {
- if (o->merge) {
+ if (skip_entry) {
+ o->pos++;
+ while (o->pos < active_nr &&
+ !strcmp(active_cache[o->pos]->name,
+ src[0]->name))
+ o->pos++;
+ } else if (o->merge) {
int ret;
#if DBRT_DEBUG > 1
- printf("%s:\n", first);
+ fprintf(stderr, "%s:\n", first);
for (i = 0; i < src_size; i++) {
- printf(" %d ", i);
+ fprintf(stderr, " %d ", i);
if (src[i])
- printf("%s\n", sha1_to_hex(src[i]->sha1));
+ fprintf(stderr, "%06x %s\n", src[i]->ce_mode, sha1_to_hex(src[i]->sha1));
else
- printf("\n");
+ fprintf(stderr, "\n");
}
#endif
ret = o->fn(src, o, remove);
+ if (ret < 0)
+ return ret;
#if DBRT_DEBUG > 1
- printf("Added %d entries\n", ret);
+ fprintf(stderr, "Added %d entries\n", ret);
#endif
o->pos += ret;
} else {
@@ -286,16 +303,16 @@ static void unlink_entry(char *name, char *last_symlink)
}
static struct checkout state;
-static void check_updates(struct cache_entry **src, int nr,
- struct unpack_trees_options *o)
+static void check_updates(struct unpack_trees_options *o)
{
unsigned cnt = 0, total = 0;
struct progress *progress = NULL;
char last_symlink[PATH_MAX];
+ int i;
if (o->update && o->verbose_update) {
- for (total = cnt = 0; cnt < nr; cnt++) {
- struct cache_entry *ce = src[cnt];
+ for (total = cnt = 0; cnt < active_nr; cnt++) {
+ struct cache_entry *ce = active_cache[cnt];
if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
total++;
}
@@ -306,14 +323,16 @@ static void check_updates(struct cache_entry **src, int nr,
}
*last_symlink = '\0';
- while (nr--) {
- struct cache_entry *ce = *src++;
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
display_progress(progress, ++cnt);
if (ce->ce_flags & CE_REMOVE) {
if (o->update)
unlink_entry(ce->name, last_symlink);
+ remove_cache_entry_at(i);
+ i--;
continue;
}
if (ce->ce_flags & CE_UPDATE) {
@@ -354,23 +373,34 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
posns[i] = create_tree_entry_list(t+i);
if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
- o, &df_conflict_list))
+ o, &df_conflict_list)) {
+ if (o->gently) {
+ discard_cache();
+ read_cache();
+ }
return -1;
+ }
}
- if (o->trivial_merges_only && o->nontrivial_merge)
- die("Merge requires file-level merging");
+ if (o->trivial_merges_only && o->nontrivial_merge) {
+ if (o->gently) {
+ discard_cache();
+ read_cache();
+ }
+ return o->gently ? -1 :
+ error("Merge requires file-level merging");
+ }
- check_updates(active_cache, active_nr, o);
+ check_updates(o);
return 0;
}
/* Here come the merge functions */
-static void reject_merge(struct cache_entry *ce)
+static int reject_merge(struct cache_entry *ce)
{
- die("Entry '%s' would be overwritten by merge. Cannot merge.",
- ce->name);
+ return error("Entry '%s' would be overwritten by merge. Cannot merge.",
+ ce->name);
}
static int same(struct cache_entry *a, struct cache_entry *b)
@@ -388,18 +418,18 @@ static int same(struct cache_entry *a, struct cache_entry *b)
* When a CE gets turned into an unmerged entry, we
* want it to be up-to-date
*/
-static void verify_uptodate(struct cache_entry *ce,
+static int verify_uptodate(struct cache_entry *ce,
struct unpack_trees_options *o)
{
struct stat st;
if (o->index_only || o->reset)
- return;
+ return 0;
if (!lstat(ce->name, &st)) {
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
if (!changed)
- return;
+ return 0;
/*
* NEEDSWORK: the current default policy is to allow
* submodule to be out of sync wrt the supermodule
@@ -408,12 +438,13 @@ static void verify_uptodate(struct cache_entry *ce,
* checked out.
*/
if (S_ISGITLINK(ce->ce_mode))
- return;
+ return 0;
errno = 0;
}
if (errno == ENOENT)
- return;
- die("Entry '%s' not uptodate. Cannot merge.", ce->name);
+ return 0;
+ return o->gently ? -1 :
+ error("Entry '%s' not uptodate. Cannot merge.", ce->name);
}
static void invalidate_ce_path(struct cache_entry *ce)
@@ -479,7 +510,8 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
* ce->name is an entry in the subdirectory.
*/
if (!ce_stage(ce)) {
- verify_uptodate(ce, o);
+ if (verify_uptodate(ce, o))
+ return -1;
ce->ce_flags |= CE_REMOVE;
}
cnt++;
@@ -498,8 +530,9 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
d.exclude_per_dir = o->dir->exclude_per_dir;
i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
if (i)
- die("Updating '%s' would lose untracked files in it",
- ce->name);
+ return o->gently ? -1 :
+ error("Updating '%s' would lose untracked files in it",
+ ce->name);
free(pathbuf);
return cnt;
}
@@ -508,16 +541,16 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
* We do not want to remove or overwrite a working tree file that
* is not tracked, unless it is ignored.
*/
-static void verify_absent(struct cache_entry *ce, const char *action,
- struct unpack_trees_options *o)
+static int verify_absent(struct cache_entry *ce, const char *action,
+ struct unpack_trees_options *o)
{
struct stat st;
if (o->index_only || o->reset || !o->update)
- return;
+ return 0;
if (has_symlink_leading_path(ce->name, NULL))
- return;
+ return 0;
if (!lstat(ce->name, &st)) {
int cnt;
@@ -528,7 +561,7 @@ static void verify_absent(struct cache_entry *ce, const char *action,
* ce->name is explicitly excluded, so it is Ok to
* overwrite it.
*/
- return;
+ return 0;
if (S_ISDIR(st.st_mode)) {
/*
* We are checking out path "foo" and
@@ -557,7 +590,7 @@ static void verify_absent(struct cache_entry *ce, const char *action,
* deleted entries here.
*/
o->pos += cnt;
- return;
+ return 0;
}
/*
@@ -569,12 +602,14 @@ static void verify_absent(struct cache_entry *ce, const char *action,
if (0 <= cnt) {
struct cache_entry *ce = active_cache[cnt];
if (ce->ce_flags & CE_REMOVE)
- return;
+ return 0;
}
- die("Untracked working tree file '%s' "
- "would be %s by merge.", ce->name, action);
+ return o->gently ? -1 :
+ error("Untracked working tree file '%s' "
+ "would be %s by merge.", ce->name, action);
}
+ return 0;
}
static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
@@ -592,12 +627,14 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
if (same(old, merge)) {
copy_cache_entry(merge, old);
} else {
- verify_uptodate(old, o);
+ if (verify_uptodate(old, o))
+ return -1;
invalidate_ce_path(old);
}
}
else {
- verify_absent(merge, "overwritten", o);
+ if (verify_absent(merge, "overwritten", o))
+ return -1;
invalidate_ce_path(merge);
}
@@ -609,10 +646,12 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
struct unpack_trees_options *o)
{
- if (old)
- verify_uptodate(old, o);
- else
- verify_absent(ce, "removed", o);
+ if (old) {
+ if (verify_uptodate(old, o))
+ return -1;
+ } else
+ if (verify_absent(ce, "removed", o))
+ return -1;
ce->ce_flags |= CE_REMOVE;
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
invalidate_ce_path(ce);
@@ -700,16 +739,15 @@ int threeway_merge(struct cache_entry **stages,
/* #14, #14ALT, #2ALT */
if (remote && !df_conflict_head && head_match && !remote_match) {
if (index && !same(index, remote) && !same(index, head))
- reject_merge(index);
+ return o->gently ? -1 : reject_merge(index);
return merged_entry(remote, index, o);
}
/*
* If we have an entry in the index cache, then we want to
* make sure that it matches head.
*/
- if (index && !same(index, head)) {
- reject_merge(index);
- }
+ if (index && !same(index, head))
+ return o->gently ? -1 : reject_merge(index);
if (head) {
/* #5ALT, #15 */
@@ -759,8 +797,10 @@ int threeway_merge(struct cache_entry **stages,
remove_entry(remove);
if (index)
return deleted_entry(index, index, o);
- else if (ce && !head_deleted)
- verify_absent(ce, "removed", o);
+ else if (ce && !head_deleted) {
+ if (verify_absent(ce, "removed", o))
+ return -1;
+ }
return 0;
}
/*
@@ -776,7 +816,8 @@ int threeway_merge(struct cache_entry **stages,
* conflict resolution files.
*/
if (index) {
- verify_uptodate(index, o);
+ if (verify_uptodate(index, o))
+ return -1;
}
remove_entry(remove);
@@ -856,11 +897,11 @@ int twoway_merge(struct cache_entry **src,
/* all other failures */
remove_entry(remove);
if (oldtree)
- reject_merge(oldtree);
+ return o->gently ? -1 : reject_merge(oldtree);
if (current)
- reject_merge(current);
+ return o->gently ? -1 : reject_merge(current);
if (newtree)
- reject_merge(newtree);
+ return o->gently ? -1 : reject_merge(newtree);
return -1;
}
}
@@ -887,7 +928,8 @@ int bind_merge(struct cache_entry **src,
return error("Cannot do a bind merge of %d trees\n",
o->merge_size);
if (a && old)
- die("Entry '%s' overlaps. Cannot bind.", a->name);
+ return o->gently ? -1 :
+ error("Entry '%s' overlaps. Cannot bind.", a->name);
if (!a)
return keep_entry(old, o);
else
diff --git a/unpack-trees.h b/unpack-trees.h
index 197a004..a2df544 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -16,6 +16,8 @@ struct unpack_trees_options {
int trivial_merges_only;
int verbose_update;
int aggressive;
+ int skip_unmerged;
+ int gently;
const char *prefix;
int pos;
struct dir_struct *dir;
diff --git a/ws.c b/ws.c
index 522f646..ba7e834 100644
--- a/ws.c
+++ b/ws.c
@@ -234,7 +234,7 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
* Strip trailing whitespace
*/
if ((ws_rule & WS_TRAILING_SPACE) &&
- (2 < len && isspace(src[len-2]))) {
+ (2 <= len && isspace(src[len-2]))) {
if (src[len - 1] == '\n') {
add_nl_to_tail = 1;
len--;
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 4b8e5cc..bba2364 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -233,8 +233,7 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value)
expression = value;
if (regcomp(&reg->re, expression, 0))
die("Invalid regexp to look for hunk header: %s", expression);
- if (buffer)
- free(buffer);
+ free(buffer);
value = ep + 1;
}
}
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index c00ddaa..413082e 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -53,6 +53,7 @@ extern "C" {
#define XDL_MERGE_MINIMAL 0
#define XDL_MERGE_EAGER 1
#define XDL_MERGE_ZEALOUS 2
+#define XDL_MERGE_ZEALOUS_ALNUM 3
typedef struct s_mmfile {
char *ptr;
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index b83b334..82b3573 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -248,10 +248,76 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
return 0;
}
+static int line_contains_alnum(const char *ptr, long size)
+{
+ while (size--)
+ if (isalnum(*(ptr++)))
+ return 1;
+ return 0;
+}
+
+static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
+{
+ for (; chg; chg--, i++)
+ if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
+ xe->xdf2.recs[i]->size))
+ return 1;
+ return 0;
+}
+
+/*
+ * This function merges m and m->next, marking everything between those hunks
+ * as conflicting, too.
+ */
+static void xdl_merge_two_conflicts(xdmerge_t *m)
+{
+ xdmerge_t *next_m = m->next;
+ m->chg1 = next_m->i1 + next_m->chg1 - m->i1;
+ m->chg2 = next_m->i2 + next_m->chg2 - m->i2;
+ m->next = next_m->next;
+ free(next_m);
+}
+
+/*
+ * If there are less than 3 non-conflicting lines between conflicts,
+ * it appears simpler -- because it takes up less (or as many) lines --
+ * if the lines are moved into the conflicts.
+ */
+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
+ int simplify_if_no_alnum)
+{
+ int result = 0;
+
+ if (!m)
+ return result;
+ for (;;) {
+ xdmerge_t *next_m = m->next;
+ int begin, end;
+
+ if (!next_m)
+ return result;
+
+ begin = m->i1 + m->chg1;
+ end = next_m->i1;
+
+ if (m->mode != 0 || next_m->mode != 0 ||
+ (end - begin > 3 &&
+ (!simplify_if_no_alnum ||
+ lines_contain_alnum(xe1, begin, end - begin)))) {
+ m = next_m;
+ } else {
+ result++;
+ xdl_merge_two_conflicts(m);
+ }
+ }
+}
+
/*
* level == 0: mark all overlapping changes as conflict
* level == 1: mark overlapping changes as conflict only if not identical
* level == 2: analyze non-identical changes for minimal conflict set
+ * level == 3: analyze non-identical changes for minimal conflict set, but
+ * treat hunks not containing any letter or number as conflicting
*
* returns < 0 on error, == 0 for no conflicts, else number of conflicts
*/
@@ -355,7 +421,9 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
if (!changes)
changes = c;
/* refine conflicts */
- if (level > 1 && xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0) {
+ if (level > 1 &&
+ (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+ xdl_simplify_non_conflicts(xe1, changes, level > 2) < 0)) {
xdl_cleanup_merge(changes);
return -1;
}