Merge branch 'jk/remote-helpers-doc' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 25 Feb 2013 16:03:37 +0000 (08:03 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Feb 2013 16:03:37 +0000 (08:03 -0800)
* jk/remote-helpers-doc:
  Rename {git- => git}remote-helpers.txt

51 files changed:
Documentation/RelNotes/1.8.1.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.1.4.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-cvsimport.txt
Documentation/git.txt
Documentation/howto/maintain-git.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
builtin/add.c
builtin/apply.c
builtin/branch.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/help.c
builtin/merge-index.c
builtin/merge-tree.c
contrib/completion/git-completion.bash
git-am.sh
git-p4.py
gitweb/gitweb.perl
gpg-interface.c
help.c
ident.c
imap-send.c
merge-blobs.c [moved from merge-file.c with 94% similarity]
merge-blobs.h [new file with mode: 0644]
merge-file.h [deleted file]
merge-recursive.c
mergetools/p4merge
sequencer.c
t/t1505-rev-parse-last.sh
t/t3404-rebase-interactive.sh
t/t3501-revert-cherry-pick.sh
t/t3506-cherry-pick-ff.sh
t/t3507-cherry-pick-conflict.sh
t/t3508-cherry-pick-many-commits.sh
t/t3510-cherry-pick-sequence.sh
t/t4300-merge-tree.sh
t/t6030-bisect-porcelain.sh
t/t7402-submodule-rebase.sh
t/t7512-status-help.sh
t/t9802-git-p4-filetype.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib.sh
transport.c

diff --git a/Documentation/RelNotes/1.8.1.3.txt b/Documentation/RelNotes/1.8.1.3.txt
new file mode 100644 (file)
index 0000000..681cb35
--- /dev/null
@@ -0,0 +1,47 @@
+Git 1.8.1.3 Release Notes
+=========================
+
+Fixes since v1.8.1.2
+--------------------
+
+ * The attribute mechanism didn't allow limiting attributes to be
+   applied to only a single directory itself with "path/" like the
+   exclude mechanism does.  The fix for this in 1.8.1.2 had
+   performance degradations.
+
+ * Command line completion code was inadvertently made incompatible with
+   older versions of bash by using a newer array notation.
+
+ * Scripts to test bash completion was inherently flaky as it was
+   affected by whatever random things the user may have on $PATH.
+
+ * A fix was added to the build procedure to work around buggy
+   versions of ccache broke the auto-generation of dependencies, which
+   unfortunately is still relevant because some people use ancient
+   distros.
+
+ * We used to stuff "user@" and then append what we read from
+   /etc/mailname to come up with a default e-mail ident, but a bug
+   lost the "user@" part.
+
+ * "git am" did not parse datestamp correctly from Hg generated patch,
+   when it is run in a locale outside C (or en).
+
+ * Attempt to "branch --edit-description" an existing branch, while
+   being on a detached HEAD, errored out.
+
+ * "git cherry-pick" did not replay a root commit to an unborn branch.
+
+ * We forgot to close the file descriptor reading from "gpg" output,
+   killing "git log --show-signature" on a long history.
+
+ * "git rebase --preserve-merges" lost empty merges in recent versions
+   of Git.
+
+ * Rebasing the history of superproject with change in the submodule
+   has been broken since v1.7.12.
+
+ * A failure to push due to non-ff while on an unborn branch
+   dereferenced a NULL pointer when showing an error message.
+
+Also contains various documentation fixes.
diff --git a/Documentation/RelNotes/1.8.1.4.txt b/Documentation/RelNotes/1.8.1.4.txt
new file mode 100644 (file)
index 0000000..22af1d1
--- /dev/null
@@ -0,0 +1,11 @@
+Git 1.8.1.4 Release Notes
+=========================
+
+Fixes since v1.8.1.3
+--------------------
+
+ * "git imap-send" talking over imaps:// did make sure it received a
+   valid certificate from the other end, but did not check if the
+   certificate matched the host it thought it was talking to.
+
+Also contains various documentation fixes.
index e452ff8..dbb2faf 100644 (file)
@@ -168,7 +168,7 @@ advice.*::
                Advice shown when linkgit:git-merge[1] refuses to
                merge to avoid overwriting local changes.
        resolveConflict::
-               Advices shown by various commands when conflicts
+               Advice shown by various commands when conflicts
                prevent the operation from being performed.
        implicitIdentity::
                Advice on how to set your identity configuration when
index fd9e36b..d0cdb07 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
          [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
          [--refresh] [--ignore-errors] [--ignore-missing] [--]
-         [<filepattern>...]
+         [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -49,7 +49,7 @@ commit.
 
 OPTIONS
 -------
-<filepattern>...::
+<pathspec>...::
        Files to add content from.  Fileglobs (e.g. `*.c`) can
        be given to add all matching files.  Also a
        leading directory name (e.g. `dir` to add `dir/file1`
@@ -100,20 +100,20 @@ apply to the index. See EDITING PATCHES below.
 
 -u::
 --update::
-       Only match <filepattern> against already tracked files in
+       Only match <pathspec> against already tracked files in
        the index rather than the working tree. That means that it
        will never stage new files, but that it will stage modified
        new contents of tracked files and that it will remove files
        from the index if the corresponding files in the working tree
        have been removed.
 +
-If no <filepattern> is given, default to "."; in other words,
+If no <pathspec> is given, default to "."; in other words,
 update all tracked files in the current directory and its
 subdirectories.
 
 -A::
 --all::
-       Like `-u`, but match <filepattern> against files in the
+       Like `-u`, but match <pathspec> against files in the
        working tree in addition to the index. That means that it
        will find new files as well as staging modified content and
        removing files that are no longer in the working tree.
index 9d5353e..f059ea9 100644 (file)
@@ -18,6 +18,12 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
+*WARNING:* `git cvsimport` uses cvsps version 2, which is considered
+deprecated; it does not work with cvsps version 3 and later.  If you are
+performing a one-shot import of a CVS repository consider using
+link:http://cvs2svn.tigris.org/cvs2git.html[cvs2git] or
+link:https://github.com/BartMassey/parsecvs[parsecvs].
+
 Imports a CVS repository into git. It will either create a new
 repository, or incrementally import into an existing one.
 
index 2764912..da0115f 100644 (file)
@@ -43,9 +43,12 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.8.1.1/git.html[documentation for release 1.8.1.1]
+* link:v1.8.1.4/git.html[documentation for release 1.8.1.4]
 
 * release notes for
+  link:RelNotes/1.8.1.4.txt[1.8.1.4],
+  link:RelNotes/1.8.1.3.txt[1.8.1.3],
+  link:RelNotes/1.8.1.2.txt[1.8.1.2],
   link:RelNotes/1.8.1.1.txt[1.8.1.1],
   link:RelNotes/1.8.1.txt[1.8.1].
 
index ea6e4a5..816c791 100644 (file)
@@ -10,35 +10,42 @@ Content-type: text/asciidoc
 How to maintain Git
 ===================
 
+Activities
+----------
+
 The maintainer's git time is spent on three activities.
 
- - Communication (60%)
+ - Communication (45%)
 
    Mailing list discussions on general design, fielding user
    questions, diagnosing bug reports; reviewing, commenting on,
    suggesting alternatives to, and rejecting patches.
 
- - Integration (30%)
+ - Integration (50%)
 
    Applying new patches from the contributors while spotting and
    correcting minor mistakes, shuffling the integration and
    testing branches, pushing the results out, cutting the
    releases, and making announcements.
 
- - Own development (10%)
+ - Own development (5%)
 
    Scratching my own itch and sending proposed patch series out.
 
+The Policy
+----------
+
 The policy on Integration is informally mentioned in "A Note
 from the maintainer" message, which is periodically posted to
 this mailing list after each feature release is made.
 
-The policy.
-
  - Feature releases are numbered as vX.Y.Z and are meant to
    contain bugfixes and enhancements in any area, including
    functionality, performance and usability, without regression.
 
+ - One release cycle for a feature release is expected to last for
+   eight to ten weeks.
+
  - Maintenance releases are numbered as vX.Y.Z.W and are meant
    to contain only bugfixes for the corresponding vX.Y.Z feature
    release and earlier maintenance releases vX.Y.Z.V (V < W).
@@ -62,12 +69,15 @@ The policy.
  - 'pu' branch is used to publish other proposed changes that do
    not yet pass the criteria set for 'next'.
 
- - The tips of 'master', 'maint' and 'next' branches will always
-   fast-forward, to allow people to build their own
-   customization on top of them.
+ - The tips of 'master' and 'maint' branches will not be rewound to
+   allow people to build their own customization on top of them.
+   Early in a new development cycle, 'next' is rewound to the tip of
+   'master' once, but otherwise it will not be rewound until the end
+   of the cycle.
 
- - Usually 'master' contains all of 'maint', 'next' contains all
-   of 'master' and 'pu' contains all of 'next'.
+ - Usually 'master' contains all of 'maint' and 'next' contains all
+   of 'master'.  'pu' contains all the topics merged to 'next', but
+   is rebuilt directly on 'master'.
 
  - The tip of 'master' is meant to be more stable than any
    tagged releases, and the users are encouraged to follow it.
@@ -77,14 +87,22 @@ The policy.
    are found before new topics are merged to 'master'.
 
 
+A Typical Git Day
+-----------------
+
 A typical git day for the maintainer implements the above policy
 by doing the following:
 
- - Scan mailing list and #git channel log.  Respond with review
-   comments, suggestions etc.  Kibitz.  Collect potentially
-   usable patches from the mailing list.  Patches about a single
-   topic go to one mailbox (I read my mail in Gnus, and type
-   \C-o to save/append messages in files in mbox format).
+ - Scan mailing list.  Respond with review comments, suggestions
+   etc.  Kibitz.  Collect potentially usable patches from the
+   mailing list.  Patches about a single topic go to one mailbox (I
+   read my mail in Gnus, and type \C-o to save/append messages in
+   files in mbox format).
+
+ - Write his own patches to address issues raised on the list but
+   nobody has stepped up solving.  Send it out just like other
+   contributors do, and pick them up just like patches from other
+   contributors (see above).
 
  - Review the patches in the saved mailboxes.  Edit proposed log
    message for typofixes and clarifications, and add Acks
@@ -100,40 +118,32 @@ by doing the following:
    - Obviously correct fixes that pertain to the tip of 'master'
      are directly applied to 'master'.
 
+   - Other topics are not handled in this step.
+
    This step is done with "git am".
 
      $ git checkout master    ;# or "git checkout maint"
-     $ git am -3 -s mailbox
+     $ git am -sc3 mailbox
      $ make test
 
- - Merge downwards (maint->master):
-
-     $ git checkout master
-     $ git merge maint
-     $ make test
+   In practice, almost no patch directly goes to 'master' or
+   'maint'.
 
  - Review the last issue of "What's cooking" message, review the
-   topics scheduled for merging upwards (topic->master and
-   topic->maint), and merge.
+   topics ready for merging (topic->master and topic->maint).  Use
+   "Meta/cook -w" script (where Meta/ contains a checkout of the
+   'todo' branch) to aid this step.
+
+   And perform the merge.  Use "Meta/Reintegrate -e" script (see
+   later) to aid this step.
+
+     $ Meta/cook -w last-issue-of-whats-cooking.mbox
 
      $ git checkout master    ;# or "git checkout maint"
-     $ git merge ai/topic     ;# or "git merge ai/maint-topic"
+     $ echo ai/topic | Meta/Reintegrate -e ;# "git merge ai/topic"
      $ git log -p ORIG_HEAD.. ;# final review
      $ git diff ORIG_HEAD..   ;# final review
      $ make test              ;# final review
-     $ git branch -d ai/topic ;# or "git branch -d ai/maint-topic"
-
- - Merge downwards (maint->master) if needed:
-
-     $ git checkout master
-     $ git merge maint
-     $ make test
-
- - Merge downwards (master->next) if needed:
-
-     $ git checkout next
-     $ git merge master
-     $ make test
 
  - Handle the remaining patches:
 
@@ -142,9 +152,9 @@ by doing the following:
      and not in 'master') is applied to a new topic branch that
      is forked from the tip of 'master'.  This includes both
      enhancements and unobvious fixes to 'master'.  A topic
-     branch is named as ai/topic where "ai" is typically
-     author's initial and "topic" is a descriptive name of the
-     topic (in other words, "what's the series is about").
+     branch is named as ai/topic where "ai" is two-letter string
+     named after author's initial and "topic" is a descriptive name
+     of the topic (in other words, "what's the series is about").
 
    - An unobvious fix meant for 'maint' is applied to a new
      topic branch that is forked from the tip of 'maint'.  The
@@ -162,7 +172,8 @@ by doing the following:
 
    The above except the "replacement" are all done with:
 
-     $ git am -3 -s mailbox
+     $ git checkout ai/topic ;# or "git checkout -b ai/topic master"
+     $ git am -sc3 mailbox
 
    while patch replacement is often done by:
 
@@ -170,93 +181,170 @@ by doing the following:
 
    then replace some parts with the new patch, and reapplying:
 
+     $ git checkout ai/topic
      $ git reset --hard ai/topic~$n
-     $ git am -3 -s 000*.txt
+     $ git am -sc3 -s 000*.txt
 
    The full test suite is always run for 'maint' and 'master'
    after patch application; for topic branches the tests are run
    as time permits.
 
- - Update "What's cooking" message to review the updates to
-   existing topics, newly added topics and graduated topics.
+ - Merge maint to master as needed:
 
-   This step is helped with Meta/cook script (where Meta/ contains
-   a checkout of the 'todo' branch).
-
- - Merge topics to 'next'.  For each branch whose tip is not
-   merged to 'next', one of three things can happen:
+     $ git checkout master
+     $ git merge maint
+     $ make test
 
  - The commits are all next-worthy; merge the topic to next:
- Merge master to next as needed:
 
      $ git checkout next
-     $ git merge ai/topic     ;# or "git merge ai/maint-topic"
+     $ git merge master
      $ make test
 
+ - Review the last issue of "What's cooking" again and see if topics
+   that are ready to be merged to 'next' are still in good shape
+   (e.g. has there any new issue identified on the list with the
+   series?)
+
+ - Prepare 'jch' branch, which is used to represent somewhere
+   between 'master' and 'pu' and often is slightly ahead of 'next'.
+
+     $ Meta/Reintegrate master..pu >Meta/redo-jch.sh
+
+   The result is a script that lists topics to be merged in order to
+   rebuild 'pu' as the input to Meta/Reintegrate script.  Remove
+   later topics that should not be in 'jch' yet.  Add a line that
+   consists of '### match next' before the name of the first topic
+   in the output that should be in 'jch' but not in 'next' yet.
+
+ - Now we are ready to start merging topics to 'next'.  For each
+   branch whose tip is not merged to 'next', one of three things can
+   happen:
+
+   - The commits are all next-worthy; merge the topic to next;
    - The new parts are of mixed quality, but earlier ones are
-     next-worthy; merge the early parts to next:
+     next-worthy; merge the early parts to next;
+   - Nothing is next-worthy; do not do anything.
+
+   This step is aided with Meta/redo-jch.sh script created earlier.
+   If a topic that was already in 'next' gained a patch, the script
+   would list it as "ai/topic~1".  To include the new patch to the
+   updated 'next', drop the "~1" part; to keep it excluded, do not
+   touch the line.  If a topic that was not in 'next' should be
+   merged to 'next', add it at the end of the list.  Then:
+
+     $ git checkout -B jch master
+     $ Meta/redo-jch.sh -c1
+
+   to rebuild the 'jch' branch from scratch.  "-c1" tells the script
+   to stop merging at the first line that begins with '###'
+   (i.e. the "### match next" line you added earlier).
+
+   At this point, build-test the result.  It may reveal semantic
+   conflicts (e.g. a topic renamed a variable, another added a new
+   reference to the variable under its old name), in which case
+   prepare an appropriate merge-fix first (see appendix), and
+   rebuild the 'jch' branch from scratch, starting at the tip of
+   'master'.
+
+   Then do the same to 'next'
 
      $ git checkout next
-     $ git merge ai/topic~2   ;# the tip two are dubious
-     $ make test
+     $ sh Meta/redo-jch.sh -c1 -e
 
-   - Nothing is next-worthy; do not do anything.
+   The "-e" option allows the merge message that comes from the
+   history of the topic and the comments in the "What's cooking" to
+   be edited.  The resulting tree should match 'jch' as the same set
+   of topics are merged on 'master'; otherwise there is a mismerge.
+   Investigate why and do not proceed until the mismerge is found
+   and rectified.
 
- - [** OBSOLETE **] Optionally rebase topics that do not have any commit
-   in next yet, when they can take advantage of low-level framework
-   change that is merged to 'master' already.
+     $ git diff jch next
 
-     $ git rebase master ai/topic
+   When all is well, clean up the redo-jch.sh script with
 
-   This step is helped with Meta/git-topic.perl script to
-   identify which topic is rebaseable.  There also is a
-   pre-rebase hook to make sure that topics that are already in
-   'next' are not rebased beyond the merged commit.
+     $ sh Meta/redo-jch.sh -u
 
- - [** OBSOLETE **] Rebuild "pu" to merge the tips of topics not in 'next'.
+   This removes topics listed in the script that have already been
+   merged to 'master'.  This may lose '### match next' marker;
+   add it again to the appropriate place when it happens.
 
-     $ git checkout pu
-     $ git reset --hard next
-     $ git merge ai/topic     ;# repeat for all remaining topics
-     $ make test
+ - Rebuild 'pu'.
 
-   This step is helped with Meta/PU script
+     $ Meta/Reintegrate master..pu >Meta/redo-pu.sh
 
- - Push four integration branches to a private repository at
-   k.org and run "make test" on all of them.
+   Edit the result by adding new topics that are not still in 'pu'
+   in the script.  Then
 
- - Push four integration branches to /pub/scm/git/git.git at
-   k.org.  This triggers its post-update hook which:
+     $ git checkout -B pu jch
+     $ sh Meta/redo-pu.sh
 
-    (1) runs "git pull" in $HOME/git-doc/ repository to pull
-        'master' just pushed out;
+   When all is well, clean up the redo-pu.sh script with
 
-    (2) runs "make doc" in $HOME/git-doc/, install the generated
-        documentation in staging areas, which are separate
-        repositories that have html and man branches checked
-        out.
+     $ sh Meta/redo-pu.sh -u
 
-    (3) runs "git commit" in the staging areas, and run "git
-        push" back to /pub/scm/git/git.git/ to update the html
-        and man branches.
+   Double check by running
 
-    (4) installs generated documentation to /pub/software/scm/git/docs/
-        to be viewed from http://www.kernel.org/
+     $ git branch --no-merged pu
 
- - Fetch html and man branches back from k.org, and push four
-   integration branches and the two documentation branches to
-   repo.or.cz and other mirrors.
+   to see there is no unexpected leftover topics.
 
+   At this point, build-test the result for semantic conflicts, and
+   if there are, prepare an appropriate merge-fix first (see
+   appendix), and rebuild the 'pu' branch from scratch, starting at
+   the tip of 'jch'.
+
+ - Update "What's cooking" message to review the updates to
+   existing topics, newly added topics and graduated topics.
+
+   This step is helped with Meta/cook script.
+
+     $ Meta/cook
+
+   This script inspects the history between master..pu, finds tips
+   of topic branches, compares what it found with the current
+   contents in Meta/whats-cooking.txt, and updates that file.
+   Topics not listed in the file but are found in master..pu are
+   added to the "New topics" section, topics listed in the file that
+   are no longer found in master..pu are moved to the "Graduated to
+   master" section, and topics whose commits changed their states
+   (e.g. used to be only in 'pu', now merged to 'next') are updated
+   with change markers "<<" and ">>".
+
+   Look for lines enclosed in "<<" and ">>"; they hold contents from
+   old file that are replaced by this integration round.  After
+   verifying them, remove the old part.  Review the description for
+   each topic and update its doneness and plan as needed.  To review
+   the updated plan, run
+
+     $ Meta/cook -w
+
+   which will pick up comments given to the topics, such as "Will
+   merge to 'next'", etc. (see Meta/cook script to learn what kind
+   of phrases are supported).
+
+ - Compile, test and install all four (five) integration branches;
+   Meta/Dothem script may aid this step.
+
+ - Format documentation if the 'master' branch was updated;
+   Meta/dodoc.sh script may aid this step.
+
+ - Push the integration branches out to public places; Meta/pushall
+   script may aid this step.
+
+Observations
+------------
 
 Some observations to be made.
 
- * Each topic is tested individually, and also together with
-   other topics cooking in 'next'.  Until it matures, none part
-   of it is merged to 'master'.
+ * Each topic is tested individually, and also together with other
+   topics cooking first in 'pu', then in 'jch' and then in 'next'.
+   Until it matures, no part of it is merged to 'master'.
 
  * A topic already in 'next' can get fixes while still in
    'next'.  Such a topic will have many merges to 'next' (in
    other words, "git log --first-parent next" will show many
-   "Merge ai/topic to next" for the same topic.
+   "Merge branch 'ai/topic' to next" for the same topic.
 
  * An unobvious fix for 'maint' is cooked in 'next' and then
    merged to 'master' to make extra sure it is Ok and then
@@ -278,3 +366,80 @@ Some observations to be made.
  * Being in the 'next' branch is not a guarantee for a topic to
    be included in the next feature release.  Being in the
    'master' branch typically is.
+
+
+Appendix
+--------
+
+Preparing a "merge-fix"
+~~~~~~~~~~~~~~~~~~~~~~~
+
+A merge of two topics may not textually conflict but still have
+conflict at the semantic level. A classic example is for one topic
+to rename an variable and all its uses, while another topic adds a
+new use of the variable under its old name. When these two topics
+are merged together, the reference to the variable newly added by
+the latter topic will still use the old name in the result.
+
+The Meta/Reintegrate script that is used by redo-jch and redo-pu
+scripts implements a crude but usable way to work this issue around.
+When the script merges branch $X, it checks if "refs/merge-fix/$X"
+exists, and if so, the effect of it is squashed into the result of
+the mechanical merge.  In other words,
+
+     $ echo $X | Meta/Reintegrate
+
+is roughly equivalent to this sequence:
+
+     $ git merge --rerere-autoupdate $X
+     $ git commit
+     $ git cherry-pick -n refs/merge-fix/$X
+     $ git commit --amend
+
+The goal of this "prepare a merge-fix" step is to come up with a
+commit that can be squashed into a result of mechanical merge to
+correct semantic conflicts.
+
+After finding that the result of merging branch "ai/topic" to an
+integration branch had such a semantic conflict, say pu~4, check the
+problematic merge out on a detached HEAD, edit the working tree to
+fix the semantic conflict, and make a separate commit to record the
+fix-up:
+
+     $ git checkout pu~4
+     $ git show -s --pretty=%s ;# double check
+     Merge branch 'ai/topic' to pu
+     $ edit
+     $ git commit -m 'merge-fix/ai/topic' -a
+
+Then make a reference "refs/merge-fix/ai/topic" to point at this
+result:
+
+     $ git update-ref refs/merge-fix/ai/topic HEAD
+
+Then double check the result by asking Meta/Reintegrate to redo the
+merge:
+
+     $ git checkout pu~5 ;# the parent of the problem merge
+     $ echo ai/topic | Meta/Reintegrate
+     $ git diff pu~4
+
+This time, because you prepared refs/merge-fix/ai/topic, the
+resulting merge should have been tweaked to include the fix for the
+semantic conflict.
+
+Note that this assumes that the order in which conflicting branches
+are merged does not change.  If the reason why merging ai/topic
+branch needs this merge-fix is because another branch merged earlier
+to the integration branch changed the underlying assumption ai/topic
+branch made (e.g. ai/topic branch added a site to refer to a
+variable, while the other branch renamed that variable and adjusted
+existing use sites), and if you changed redo-jch (or redo-pu) script
+to merge ai/topic branch before the other branch, then the above
+merge-fix should not be applied while merging ai/topic, but should
+instead be applied while merging the other branch.  You would need
+to move the fix to apply to the other branch, perhaps like this:
+
+      $ mf=refs/merge-fix
+      $ git update-ref $mf/$the_other_branch $mf/ai/topic
+      $ git update-ref -d $mf/ai/topic
index 1b377dc..52c8523 100644 (file)
@@ -782,7 +782,7 @@ e05db0fd4f31dde7005f075a84f96b360d05984b
 
 Or you could recall that the ... operator selects all commits
 contained reachable from either one reference or the other but not
-both: so
+both; so
 
 -------------------------------------------------
 $ git log origin...master
@@ -931,11 +931,20 @@ The linkgit:git-archive[1] command can create a tar or zip archive from
 any version of a project; for example:
 
 -------------------------------------------------
-$ git archive --format=tar --prefix=project/ HEAD | gzip >latest.tar.gz
+$ git archive -o latest.tar.gz --prefix=project/ HEAD
 -------------------------------------------------
 
-will use HEAD to produce a tar archive in which each filename is
-preceded by "project/".
+will use HEAD to produce a gzipped tar archive in which each filename
+is preceded by `project/`.  The output file format is inferred from
+the output file extension if possible, see linkgit:git-archive[1] for
+details.
+
+Versions of Git older than 1.7.7 don't know about the 'tar.gz' format,
+you'll need to use gzip explicitly:
+
+-------------------------------------------------
+$ git archive --format=tar --prefix=project/ HEAD | gzip >latest.tar.gz
+-------------------------------------------------
 
 If you're releasing a new version of a software project, you may want
 to simultaneously make a changelog to include in the release
@@ -991,9 +1000,16 @@ Developing with git
 Telling git your name
 ---------------------
 
-Before creating any commits, you should introduce yourself to git.  The
-easiest way to do so is to make sure the following lines appear in a
-file named .gitconfig in your home directory:
+Before creating any commits, you should introduce yourself to Git.
+The easiest way to do so is to use linkgit:git-config[1]:
+
+------------------------------------------------
+$ git config --global user.name 'Your Name Comes Here'
+$ git config --global user.email 'you@yourdomain.example.com'
+------------------------------------------------
+
+Which will add the following to a file named `.gitconfig` in your
+home directory:
 
 ------------------------------------------------
 [user]
@@ -1001,8 +1017,9 @@ file named .gitconfig in your home directory:
        email = you@yourdomain.example.com
 ------------------------------------------------
 
-(See the "CONFIGURATION FILE" section of linkgit:git-config[1] for
-details on the configuration file.)
+See the "CONFIGURATION FILE" section of linkgit:git-config[1] for
+details on the configuration file.  The file is plain text, so you can
+also edit it with your favorite editor.
 
 
 [[creating-a-new-repository]]
@@ -1561,18 +1578,12 @@ $ git stash pop
 Ensuring good performance
 -------------------------
 
-On large repositories, git depends on compression to keep the history
-information from taking up too much space on disk or in memory.
-
-This compression is not performed automatically.  Therefore you
-should occasionally run linkgit:git-gc[1]:
-
--------------------------------------------------
-$ git gc
--------------------------------------------------
-
-to recompress the archive.  This can be very time-consuming, so
-you may prefer to run `git gc` when you are not doing other work.
+On large repositories, Git depends on compression to keep the history
+information from taking up too much space on disk or in memory.  Some
+git commands may automatically run linkgit:git-gc[1], so you don't
+have to worry about running it manually.  However, compressing a large
+repository may take a while, so you may want to call `gc` explicitly
+to avoid automatic compression kicking in when it is not convenient.
 
 
 [[ensuring-reliability]]
@@ -1931,11 +1942,11 @@ linkgit:git-daemon[1] man page for details.  (See especially the
 examples section.)
 
 [[exporting-via-http]]
-Exporting a git repository via http
+Exporting a git repository via HTTP
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The git protocol gives better performance and reliability, but on a
-host with a web server set up, http exports may be simpler to set up.
+host with a web server set up, HTTP exports may be simpler to set up.
 
 All you need to do is place the newly created bare git repository in
 a directory that is exported by the web server, and make some
@@ -1961,7 +1972,7 @@ $ git clone http://yourserver.com/~you/proj.git
 (See also
 link:howto/setup-git-server-over-http.txt[setup-git-server-over-http]
 for a slightly more sophisticated setup using WebDAV which also
-allows pushing over http.)
+allows pushing over HTTP.)
 
 [[pushing-changes-to-a-public-repository]]
 Pushing changes to a public repository
@@ -1998,16 +2009,21 @@ will not be updated by the push.  This may lead to unexpected results if
 the branch you push to is the currently checked-out branch!
 
 As with `git fetch`, you may also set up configuration options to
-save typing; so, for example, after
+save typing; so, for example:
+
+-------------------------------------------------
+$ git remote add public-repo ssh://yourserver.com/~you/proj.git
+-------------------------------------------------
+
+adds the following to `.git/config`:
 
 -------------------------------------------------
-$ cat >>.git/config <<EOF
 [remote "public-repo"]
-       url = ssh://yourserver.com/~you/proj.git
-EOF
+       url = yourserver.com:proj.git
+       fetch = +refs/heads/*:refs/remotes/example/*
 -------------------------------------------------
 
-you should be able to perform the above push with just
+which lets you do the same push with just
 
 -------------------------------------------------
 $ git push public-repo master
@@ -2046,6 +2062,13 @@ branch name with a plus sign:
 $ git push ssh://yourserver.com/~you/proj.git +master
 -------------------------------------------------
 
+Note the addition of the `+` sign.  Alternatively, you can use the
+`-f` flag to force the remote update, as in:
+
+-------------------------------------------------
+$ git push -f ssh://yourserver.com/~you/proj.git master
+-------------------------------------------------
+
 Normally whenever a branch head in a public repository is modified, it
 is modified to point to a descendant of the commit that it pointed to
 before.  By forcing a push in this situation, you break that convention.
@@ -2850,48 +2873,34 @@ branch.master.merge=refs/heads/master
 
 If there are other repositories that you also use frequently, you can
 create similar configuration options to save typing; for example,
-after
 
 -------------------------------------------------
-$ git config remote.example.url git://example.com/proj.git
+$ git remote add example git://example.com/proj.git
 -------------------------------------------------
 
-then the following two commands will do the same thing:
+adds the following to `.git/config`:
 
 -------------------------------------------------
-$ git fetch git://example.com/proj.git master:refs/remotes/example/master
-$ git fetch example master:refs/remotes/example/master
+[remote "example"]
+       url = git://example.com/proj.git
+       fetch = +refs/heads/*:refs/remotes/example/*
 -------------------------------------------------
 
-Even better, if you add one more option:
+Also note that the above configuration can be performed by directly
+editing the file `.git/config` instead of using linkgit:git-remote[1].
 
--------------------------------------------------
-$ git config remote.example.fetch master:refs/remotes/example/master
--------------------------------------------------
-
-then the following commands will all do the same thing:
+After configuring the remote, the following three commands will do the
+same thing:
 
 -------------------------------------------------
-$ git fetch git://example.com/proj.git master:refs/remotes/example/master
-$ git fetch example master:refs/remotes/example/master
+$ git fetch git://example.com/proj.git +refs/heads/*:refs/remotes/example/*
+$ git fetch example +refs/heads/*:refs/remotes/example/*
 $ git fetch example
 -------------------------------------------------
 
-You can also add a "+" to force the update each time:
-
--------------------------------------------------
-$ git config remote.example.fetch +master:refs/remotes/example/master
--------------------------------------------------
-
-Don't do this unless you're sure you won't mind "git fetch" possibly
-throwing away commits on 'example/master'.
-
-Also note that all of the above configuration can be performed by
-directly editing the file .git/config instead of using
-linkgit:git-config[1].
-
 See linkgit:git-config[1] for more details on the configuration
-options mentioned above.
+options mentioned above and linkgit:git-fetch[1] for more details on
+the refspec syntax.
 
 
 [[git-concepts]]
@@ -3396,7 +3405,7 @@ $ git log --raw --all
 ------------------------------------------------
 
 and just looked for the sha of the missing object (4b9458b..) in that
-whole thing. It's up to you - git does *have* a lot of information, it is
+whole thing. It's up to you--Git does *have* a lot of information, it is
 just missing one particular blob version.
 
 [[the-index]]
index 2edb996..dcd3595 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.1.2
+DEF_VER=v1.8.1.4
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index 28f34bd..2dc3b61 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -131,8 +131,9 @@ Issues of note:
          use English. Under autoconf the configure script will do this
          automatically if it can't find libintl on the system.
 
-       - Python version 2.6 or later is needed to use the git-p4
-         interface to Perforce.
+       - Python version 2.4 or later (but not 3.x, which is not
+         supported by Perforce) is needed to use the git-p4 interface
+         to Perforce.
 
  - Some platform specific issues are dealt with Makefile rules,
    but depending on your specific installation, you may not
index 05d241b..4bb3cf1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -653,7 +653,7 @@ LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
 LIB_H += mailmap.h
-LIB_H += merge-file.h
+LIB_H += merge-blobs.h
 LIB_H += merge-recursive.h
 LIB_H += mergesort.h
 LIB_H += notes-cache.h
@@ -769,7 +769,7 @@ LIB_OBJS += log-tree.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge.o
-LIB_OBJS += merge-file.o
+LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += mergesort.o
 LIB_OBJS += name-hash.o
@@ -1475,7 +1475,8 @@ endif
 
 ifeq ($(COMPUTE_HEADER_DEPENDENCIES),auto)
 dep_check = $(shell $(CC) $(ALL_CFLAGS) \
-       -c -MF /dev/null -MMD -MP -x c /dev/null -o /dev/null 2>&1; \
+       -c -MF /dev/null -MQ /dev/null -MMD -MP \
+       -x c /dev/null -o /dev/null 2>&1; \
        echo $$?)
 ifeq ($(dep_check),0)
 override COMPUTE_HEADER_DEPENDENCIES = yes
@@ -2329,7 +2330,7 @@ $(dep_dirs):
 
 missing_dep_dirs := $(filter-out $(wildcard $(dep_dirs)),$(dep_dirs))
 dep_file = $(dir $@).depend/$(notdir $@).d
-dep_args = -MF $(dep_file) -MMD -MP
+dep_args = -MF $(dep_file) -MQ $@ -MMD -MP
 ifdef CHECK_HEADER_DEPENDENCIES
 $(error cannot compute header dependencies outside a normal build. \
 Please unset CHECK_HEADER_DEPENDENCIES and try again)
index db86b8c..ad613ea 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.8.1.2.txt
\ No newline at end of file
+Documentation/RelNotes/1.8.1.4.txt
\ No newline at end of file
index e664100..6325947 100644 (file)
@@ -16,7 +16,7 @@
 #include "bulk-checkin.h"
 
 static const char * const builtin_add_usage[] = {
-       N_("git add [options] [--] <filepattern>..."),
+       N_("git add [options] [--] <pathspec>..."),
        NULL
 };
 static int patch_interactive, add_interactive, edit_interactive;
index 6c11e8b..9706ca7 100644 (file)
@@ -3609,7 +3609,6 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
         * worth showing the new sha1 prefix, but until then...
         */
        for (patch = list; patch; patch = patch->next) {
-               const unsigned char *sha1_ptr;
                unsigned char sha1[20];
                struct cache_entry *ce;
                const char *name;
@@ -3617,20 +3616,23 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
                name = patch->old_name ? patch->old_name : patch->new_name;
                if (0 < patch->is_new)
                        continue;
-               else if (get_sha1_blob(patch->old_sha1_prefix, sha1))
-                       /* git diff has no index line for mode/type changes */
-                       if (!patch->lines_added && !patch->lines_deleted) {
-                               if (get_current_sha1(patch->old_name, sha1))
-                                       die("mode change for %s, which is not "
-                                               "in current HEAD", name);
-                               sha1_ptr = sha1;
-                       } else
-                               die("sha1 information is lacking or useless "
-                                       "(%s).", name);
-               else
-                       sha1_ptr = sha1;
 
-               ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
+               if (S_ISGITLINK(patch->old_mode)) {
+                       if (get_sha1_hex(patch->old_sha1_prefix, sha1))
+                               die("submoule change for %s without full index name",
+                                   name);
+               } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) {
+                       ; /* ok */
+               } else if (!patch->lines_added && !patch->lines_deleted) {
+                       /* mode-only change: update the current */
+                       if (get_current_sha1(patch->old_name, sha1))
+                               die("mode change for %s, which is not "
+                                   "in current HEAD", name);
+               } else
+                       die("sha1 information is lacking or useless "
+                           "(%s).", name);
+
+               ce = make_cache_entry(patch->old_mode, sha1, name, 0, 0);
                if (!ce)
                        die(_("make_cache_entry failed for path '%s'"), name);
                if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
index 1ec9c02..947c84b 100644 (file)
@@ -850,11 +850,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                const char *branch_name;
                struct strbuf branch_ref = STRBUF_INIT;
 
-               if (detached)
-                       die("Cannot give description to detached HEAD");
-               if (!argc)
+               if (!argc) {
+                       if (detached)
+                               die("Cannot give description to detached HEAD");
                        branch_name = head;
-               else if (argc == 1)
+               else if (argc == 1)
                        branch_name = argv[0];
                else
                        usage_with_options(builtin_branch_usage, options);
index 69c1cda..f4b760b 100644 (file)
@@ -10,6 +10,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
+#include "refs.h"
 #include "string-list.h"
 #include "quote.h"
 
@@ -20,6 +21,12 @@ static const char *const builtin_clean_usage[] = {
        NULL
 };
 
+static const char *msg_remove = N_("Removing %s\n");
+static const char *msg_would_remove = N_("Would remove %s\n");
+static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
+static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
+static const char *msg_warn_remove_failed = N_("failed to remove %s");
+
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "clean.requireforce"))
@@ -34,11 +41,112 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
+               int dry_run, int quiet, int *dir_gone)
+{
+       DIR *dir;
+       struct strbuf quoted = STRBUF_INIT;
+       struct dirent *e;
+       int res = 0, ret = 0, gone = 1, original_len = path->len, len, i;
+       unsigned char submodule_head[20];
+       struct string_list dels = STRING_LIST_INIT_DUP;
+
+       *dir_gone = 1;
+
+       if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
+                       !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
+               if (!quiet) {
+                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
+                                       quoted.buf);
+               }
+
+               *dir_gone = 0;
+               return 0;
+       }
+
+       dir = opendir(path->buf);
+       if (!dir) {
+               /* an empty dir could be removed even if it is unreadble */
+               res = dry_run ? 0 : rmdir(path->buf);
+               if (res) {
+                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       warning(_(msg_warn_remove_failed), quoted.buf);
+                       *dir_gone = 0;
+               }
+               return res;
+       }
+
+       if (path->buf[original_len - 1] != '/')
+               strbuf_addch(path, '/');
+
+       len = path->len;
+       while ((e = readdir(dir)) != NULL) {
+               struct stat st;
+               if (is_dot_or_dotdot(e->d_name))
+                       continue;
+
+               strbuf_setlen(path, len);
+               strbuf_addstr(path, e->d_name);
+               if (lstat(path->buf, &st))
+                       ; /* fall thru */
+               else if (S_ISDIR(st.st_mode)) {
+                       if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
+                               ret = 1;
+                       if (gone) {
+                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               string_list_append(&dels, quoted.buf);
+                       } else
+                               *dir_gone = 0;
+                       continue;
+               } else {
+                       res = dry_run ? 0 : unlink(path->buf);
+                       if (!res) {
+                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               string_list_append(&dels, quoted.buf);
+                       } else {
+                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               warning(_(msg_warn_remove_failed), quoted.buf);
+                               *dir_gone = 0;
+                               ret = 1;
+                       }
+                       continue;
+               }
+
+               /* path too long, stat fails, or non-directory still exists */
+               *dir_gone = 0;
+               ret = 1;
+               break;
+       }
+       closedir(dir);
+
+       strbuf_setlen(path, original_len);
+
+       if (*dir_gone) {
+               res = dry_run ? 0 : rmdir(path->buf);
+               if (!res)
+                       *dir_gone = 1;
+               else {
+                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       warning(_(msg_warn_remove_failed), quoted.buf);
+                       *dir_gone = 0;
+                       ret = 1;
+               }
+       }
+
+       if (!*dir_gone && !quiet) {
+               for (i = 0; i < dels.nr; i++)
+                       printf(dry_run ?  _(msg_would_remove) : _(msg_remove), dels.items[i].string);
+       }
+       string_list_clear(&dels, 0);
+       return ret;
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
-       int i;
-       int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
-       int ignored_only = 0, config_set = 0, errors = 0;
+       int i, res;
+       int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
+       int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
        int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
        struct strbuf directory = STRBUF_INIT;
        struct dir_struct dir;
@@ -49,7 +157,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        char *seen = NULL;
        struct option options[] = {
                OPT__QUIET(&quiet, N_("do not print names of files removed")),
-               OPT__DRY_RUN(&show_only, N_("dry run")),
+               OPT__DRY_RUN(&dry_run, N_("dry run")),
                OPT__FORCE(&force, N_("force")),
                OPT_BOOLEAN('d', NULL, &remove_directories,
                                N_("remove whole directories")),
@@ -77,7 +185,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        if (ignored && ignored_only)
                die(_("-x and -X cannot be used together"));
 
-       if (!show_only && !force) {
+       if (!dry_run && !force) {
                if (config_set)
                        die(_("clean.requireForce set to true and neither -n nor -f given; "
                                  "refusing to clean"));
@@ -149,38 +257,26 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
                if (S_ISDIR(st.st_mode)) {
                        strbuf_addstr(&directory, ent->name);
-                       qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
-                       if (show_only && (remove_directories ||
-                           (matches == MATCHED_EXACTLY))) {
-                               printf(_("Would remove %s\n"), qname);
-                       } else if (remove_directories ||
-                                  (matches == MATCHED_EXACTLY)) {
-                               if (!quiet)
-                                       printf(_("Removing %s\n"), qname);
-                               if (remove_dir_recursively(&directory,
-                                                          rm_flags) != 0) {
-                                       warning(_("failed to remove %s"), qname);
+                       if (remove_directories || (matches == MATCHED_EXACTLY)) {
+                               if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
                                        errors++;
+                               if (gone && !quiet) {
+                                       qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
+                                       printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
                                }
-                       } else if (show_only) {
-                               printf(_("Would not remove %s\n"), qname);
-                       } else {
-                               printf(_("Not removing %s\n"), qname);
                        }
                        strbuf_reset(&directory);
                } else {
                        if (pathspec && !matches)
                                continue;
-                       qname = quote_path_relative(ent->name, -1, &buf, prefix);
-                       if (show_only) {
-                               printf(_("Would remove %s\n"), qname);
-                               continue;
-                       } else if (!quiet) {
-                               printf(_("Removing %s\n"), qname);
-                       }
-                       if (unlink(ent->name) != 0) {
-                               warning(_("failed to remove %s"), qname);
+                       res = dry_run ? 0 : unlink(ent->name);
+                       if (res) {
+                               qname = quote_path_relative(ent->name, -1, &buf, prefix);
+                               warning(_(msg_warn_remove_failed), qname);
                                errors++;
+                       } else if (!quiet) {
+                               qname = quote_path_relative(ent->name, -1, &buf, prefix);
+                               printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
                        }
                }
        }
index 8d23a62..36ec99d 100644 (file)
@@ -704,6 +704,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (option_origin)
                        die(_("--bare and --origin %s options are incompatible."),
                            option_origin);
+               if (real_git_dir)
+                       die(_("--bare and --separate-git-dir are incompatible."));
                option_no_checkout = 1;
        }
 
index d6dd3df..9668410 100644 (file)
 #include "sequencer.h"
 
 static const char * const builtin_commit_usage[] = {
-       N_("git commit [options] [--] <filepattern>..."),
+       N_("git commit [options] [--] <pathspec>..."),
        NULL
 };
 
 static const char * const builtin_status_usage[] = {
-       N_("git status [options] [--] <filepattern>..."),
+       N_("git status [options] [--] <pathspec>..."),
        NULL
 };
 
index bd86253..6067a61 100644 (file)
@@ -6,7 +6,6 @@
 #include "cache.h"
 #include "builtin.h"
 #include "exec_cmd.h"
-#include "common-cmds.h"
 #include "parse-options.h"
 #include "run-command.h"
 #include "column.h"
@@ -287,23 +286,6 @@ static int git_help_config(const char *var, const char *value, void *cb)
 
 static struct cmdnames main_cmds, other_cmds;
 
-void list_common_cmds_help(void)
-{
-       int i, longest = 0;
-
-       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-               if (longest < strlen(common_cmds[i].name))
-                       longest = strlen(common_cmds[i].name);
-       }
-
-       puts(_("The most commonly used git commands are:"));
-       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-               printf("   %s   ", common_cmds[i].name);
-               mput_char(' ', longest - strlen(common_cmds[i].name));
-               puts(_(common_cmds[i].help));
-       }
-}
-
 static int is_git_command(const char *s)
 {
        return is_in_cmdlist(&main_cmds, s) ||
index 2338832..be5e514 100644 (file)
@@ -42,7 +42,7 @@ static int merge_entry(int pos, const char *path)
        return found;
 }
 
-static void merge_file(const char *path)
+static void merge_one_path(const char *path)
 {
        int pos = cache_name_pos(path, strlen(path));
 
@@ -102,7 +102,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix)
                        }
                        die("git merge-index: unknown option %s", arg);
                }
-               merge_file(arg);
+               merge_one_path(arg);
        }
        if (err && !quiet)
                die("merge program failed");
index 897a563..e0d0b7d 100644 (file)
@@ -3,17 +3,15 @@
 #include "xdiff-interface.h"
 #include "blob.h"
 #include "exec_cmd.h"
-#include "merge-file.h"
+#include "merge-blobs.h"
 
 static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
-static int resolve_directories = 1;
 
 struct merge_list {
        struct merge_list *next;
        struct merge_list *link;        /* other stages for this object */
 
-       unsigned int stage : 2,
-                    flags : 30;
+       unsigned int stage : 2;
        unsigned int mode;
        const char *path;
        struct blob *blob;
@@ -27,7 +25,7 @@ static void add_merge_entry(struct merge_list *entry)
        merge_result_end = &entry->next;
 }
 
-static void merge_trees(struct tree_desc t[3], const char *base);
+static void merge_trees_recursive(struct tree_desc t[3], const char *base, int df_conflict);
 
 static const char *explanation(struct merge_list *entry)
 {
@@ -76,7 +74,7 @@ static void *result(struct merge_list *entry, unsigned long *size)
        their = NULL;
        if (entry)
                their = entry->blob;
-       return merge_file(path, base, our, their, size);
+       return merge_blobs(path, base, our, their, size);
 }
 
 static void *origin(struct merge_list *entry, unsigned long *size)
@@ -174,17 +172,17 @@ static char *traverse_path(const struct traverse_info *info, const struct name_e
        return make_traverse_path(path, info, n);
 }
 
-static void resolve(const struct traverse_info *info, struct name_entry *branch1, struct name_entry *result)
+static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result)
 {
        struct merge_list *orig, *final;
        const char *path;
 
-       /* If it's already branch1, don't bother showing it */
-       if (!branch1)
+       /* If it's already ours, don't bother showing it */
+       if (!ours)
                return;
 
        path = traverse_path(info, result);
-       orig = create_entry(2, branch1->mode, branch1->sha1, path);
+       orig = create_entry(2, ours->mode, ours->sha1, path);
        final = create_entry(0, result->mode, result->sha1, path);
 
        final->link = orig;
@@ -192,34 +190,35 @@ static void resolve(const struct traverse_info *info, struct name_entry *branch1
        add_merge_entry(final);
 }
 
-static int unresolved_directory(const struct traverse_info *info, struct name_entry n[3])
+static void unresolved_directory(const struct traverse_info *info, struct name_entry n[3],
+                                int df_conflict)
 {
        char *newbase;
        struct name_entry *p;
        struct tree_desc t[3];
        void *buf0, *buf1, *buf2;
 
-       if (!resolve_directories)
-               return 0;
-       p = n;
-       if (!p->mode) {
-               p++;
-               if (!p->mode)
-                       p++;
+       for (p = n; p < n + 3; p++) {
+               if (p->mode && S_ISDIR(p->mode))
+                       break;
        }
-       if (!S_ISDIR(p->mode))
-               return 0;
+       if (n + 3 <= p)
+               return; /* there is no tree here */
+
        newbase = traverse_path(info, p);
-       buf0 = fill_tree_descriptor(t+0, n[0].sha1);
-       buf1 = fill_tree_descriptor(t+1, n[1].sha1);
-       buf2 = fill_tree_descriptor(t+2, n[2].sha1);
-       merge_trees(t, newbase);
+
+#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->sha1 : NULL)
+       buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0));
+       buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1));
+       buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2));
+#undef ENTRY_SHA1
+
+       merge_trees_recursive(t, newbase, df_conflict);
 
        free(buf0);
        free(buf1);
        free(buf2);
        free(newbase);
-       return 1;
 }
 
 
@@ -242,18 +241,26 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info
 static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 {
        struct merge_list *entry = NULL;
+       int i;
+       unsigned dirmask = 0, mask = 0;
+
+       for (i = 0; i < 3; i++) {
+               mask |= (1 << 1);
+               if (n[i].mode && S_ISDIR(n[i].mode))
+                       dirmask |= (1 << i);
+       }
 
-       if (unresolved_directory(info, n))
+       unresolved_directory(info, n, dirmask && (dirmask != mask));
+
+       if (dirmask == mask)
                return;
 
-       /*
-        * Do them in reverse order so that the resulting link
-        * list has the stages in order - link_entry adds new
-        * links at the front.
-        */
-       entry = link_entry(3, info, n + 2, entry);
-       entry = link_entry(2, info, n + 1, entry);
-       entry = link_entry(1, info, n + 0, entry);
+       if (n[2].mode && !S_ISDIR(n[2].mode))
+               entry = link_entry(3, info, n + 2, entry);
+       if (n[1].mode && !S_ISDIR(n[1].mode))
+               entry = link_entry(2, info, n + 1, entry);
+       if (n[0].mode && !S_ISDIR(n[0].mode))
+               entry = link_entry(1, info, n + 0, entry);
 
        add_merge_entry(entry);
 }
@@ -292,20 +299,29 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
        /* Same in both? */
        if (same_entry(entry+1, entry+2)) {
                if (entry[0].sha1) {
+                       /* Modified identically */
                        resolve(info, NULL, entry+1);
                        return mask;
                }
+               /* "Both added the same" is left unresolved */
        }
 
        if (same_entry(entry+0, entry+1)) {
                if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
+                       /* We did not touch, they modified -- take theirs */
                        resolve(info, entry+1, entry+2);
                        return mask;
                }
+               /*
+                * If we did not touch a directory but they made it
+                * into a file, we fall through and unresolved()
+                * recurses down.  Likewise for the opposite case.
+                */
        }
 
        if (same_entry(entry+0, entry+2)) {
                if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
+                       /* We modified, they did not touch -- take ours */
                        resolve(info, NULL, entry+1);
                        return mask;
                }
@@ -315,15 +331,21 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
        return mask;
 }
 
-static void merge_trees(struct tree_desc t[3], const char *base)
+static void merge_trees_recursive(struct tree_desc t[3], const char *base, int df_conflict)
 {
        struct traverse_info info;
 
        setup_traverse_info(&info, base);
+       info.data = &df_conflict;
        info.fn = threeway_callback;
        traverse_trees(3, t, &info);
 }
 
+static void merge_trees(struct tree_desc t[3], const char *base)
+{
+       merge_trees_recursive(t, base, 0);
+}
+
 static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
 {
        unsigned char sha1[20];
index 14dd5e7..2186b4b 100644 (file)
@@ -531,10 +531,19 @@ __git_complete_strategy ()
        return 1
 }
 
+__git_commands () {
+       if test -n "${GIT_TESTING_COMMAND_COMPLETION:-}"
+       then
+               printf "%s" "${GIT_TESTING_COMMAND_COMPLETION}"
+       else
+               git help -a|egrep '^  [a-zA-Z0-9]'
+       fi
+}
+
 __git_list_all_commands ()
 {
        local i IFS=" "$'\n'
-       for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
+       for i in $(__git_commands)
        do
                case $i in
                *--*)             : helper pattern;;
@@ -2431,7 +2440,7 @@ if [[ -n ${ZSH_VERSION-} ]]; then
                                --*=*|*.) ;;
                                *) c="$c " ;;
                                esac
-                               array+=("$c")
+                               array[$#array+1]="$c"
                        done
                        compset -P '*[=:]'
                        compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
index c682d34..202130f 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -334,7 +334,7 @@ split_patches () {
                        # Since we cannot guarantee that the commit message is in
                        # git-friendly format, we put no Subject: line and just consume
                        # all of the message as the body
-                       perl -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 }
+                       LANG=C LC_ALL=C perl -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 }
                                if ($subject) { print ; }
                                elsif (/^\# User /) { s/\# User/From:/ ; print ; }
                                elsif (/^\# Date /) {
@@ -664,7 +664,7 @@ do
                        sed -e '1,/^$/d' >"$dotest/msg-clean"
                        echo "$commit" >"$dotest/original-commit"
                        get_author_ident_from_commit "$commit" >"$dotest/author-script"
-                       git diff-tree --root --binary "$commit" >"$dotest/patch"
+                       git diff-tree --root --binary --full-index "$commit" >"$dotest/patch"
                else
                        git mailinfo $keep $no_inbody_headers $scissors $utf8 "$dotest/msg" "$dotest/patch" \
                                <"$dotest/$msgnum" >"$dotest/info" ||
index 551aec9..0682e61 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -12,6 +12,21 @@ import optparse, sys, os, marshal, subprocess, shelve
 import tempfile, getopt, os.path, time, platform
 import re, shutil
 
+try:
+    from subprocess import CalledProcessError
+except ImportError:
+    # from python2.7:subprocess.py
+    # Exception classes used by this module.
+    class CalledProcessError(Exception):
+        """This exception is raised when a process run by check_call() returns
+        a non-zero exit status.  The exit status will be stored in the
+        returncode attribute."""
+        def __init__(self, returncode, cmd):
+            self.returncode = returncode
+            self.cmd = cmd
+        def __str__(self):
+            return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+
 verbose = False
 
 # Only labels/tags matching this will be imported/exported
@@ -152,13 +167,17 @@ def system(cmd):
     expand = isinstance(cmd,basestring)
     if verbose:
         sys.stderr.write("executing %s\n" % str(cmd))
-    subprocess.check_call(cmd, shell=expand)
+    retcode = subprocess.call(cmd, shell=expand)
+    if retcode:
+        raise CalledProcessError(retcode, cmd)
 
 def p4_system(cmd):
     """Specifically invoke p4 as the system command. """
     real_cmd = p4_build_cmd(cmd)
     expand = isinstance(real_cmd, basestring)
-    subprocess.check_call(real_cmd, shell=expand)
+    retcode = subprocess.call(real_cmd, shell=expand)
+    if retcode:
+        raise CalledProcessError(retcode, real_cmd)
 
 def p4_integrate(src, dest):
     p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)])
@@ -742,7 +761,8 @@ def wildcard_encode(path):
     return path
 
 def wildcard_present(path):
-    return path.translate(None, "*#@%") != path
+    m = re.search("[*#@%]", path)
+    return m is not None
 
 class Command:
     def __init__(self):
@@ -3103,7 +3123,9 @@ class P4Clone(P4Sync):
         init_cmd = [ "git", "init" ]
         if self.cloneBare:
             init_cmd.append("--bare")
-        subprocess.check_call(init_cmd)
+        retcode = subprocess.call(init_cmd)
+        if retcode:
+            raise CalledProcessError(retcode, init_cmd)
 
         if not P4Sync.run(self, depotPaths):
             return False
index c6bafe6..1309196 100755 (executable)
@@ -2068,7 +2068,7 @@ sub picon_url {
        if (!$avatar_cache{$email}) {
                my ($user, $domain) = split('@', $email);
                $avatar_cache{$email} =
-                       "http://www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
+                       "//www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
                        "$domain/$user/" .
                        "users+domains+unknown/up/single";
        }
@@ -2083,7 +2083,7 @@ sub gravatar_url {
        my $email = lc shift;
        my $size = shift;
        $avatar_cache{$email} ||=
-               "http://www.gravatar.com/avatar/" .
+               "//www.gravatar.com/avatar/" .
                        Digest::MD5::md5_hex($email) . "?s=";
        return $avatar_cache{$email} . $size;
 }
index 0863c61..5f142f6 100644 (file)
@@ -130,8 +130,10 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
        write_in_full(gpg.in, payload, payload_size);
        close(gpg.in);
 
-       if (gpg_output)
+       if (gpg_output) {
                strbuf_read(gpg_output, gpg.err, 0);
+               close(gpg.err);
+       }
        ret = finish_command(&gpg);
 
        unlink_or_warn(path);
diff --git a/help.c b/help.c
index 2a42ec6..1dfa0b0 100644 (file)
--- a/help.c
+++ b/help.c
@@ -223,6 +223,23 @@ void list_commands(unsigned int colopts,
        }
 }
 
+void list_common_cmds_help(void)
+{
+       int i, longest = 0;
+
+       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+               if (longest < strlen(common_cmds[i].name))
+                       longest = strlen(common_cmds[i].name);
+       }
+
+       puts(_("The most commonly used git commands are:"));
+       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+               printf("   %s   ", common_cmds[i].name);
+               mput_char(' ', longest - strlen(common_cmds[i].name));
+               puts(_(common_cmds[i].help));
+       }
+}
+
 int is_in_cmdlist(struct cmdnames *c, const char *s)
 {
        int i;
diff --git a/ident.c b/ident.c
index ac9672f..1c123e6 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -46,6 +46,7 @@ static void copy_gecos(const struct passwd *w, struct strbuf *name)
 static int add_mailname_host(struct strbuf *buf)
 {
        FILE *mailname;
+       struct strbuf mailnamebuf = STRBUF_INIT;
 
        mailname = fopen("/etc/mailname", "r");
        if (!mailname) {
@@ -54,14 +55,17 @@ static int add_mailname_host(struct strbuf *buf)
                                strerror(errno));
                return -1;
        }
-       if (strbuf_getline(buf, mailname, '\n') == EOF) {
+       if (strbuf_getline(&mailnamebuf, mailname, '\n') == EOF) {
                if (ferror(mailname))
                        warning("cannot read /etc/mailname: %s",
                                strerror(errno));
+               strbuf_release(&mailnamebuf);
                fclose(mailname);
                return -1;
        }
        /* success! */
+       strbuf_addbuf(buf, &mailnamebuf);
+       strbuf_release(&mailnamebuf);
        fclose(mailname);
        return 0;
 }
index d42e471..ef50011 100644 (file)
@@ -31,6 +31,7 @@ typedef void *SSL;
 #else
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#include <openssl/x509v3.h>
 #endif
 
 struct store_conf {
@@ -266,12 +267,64 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
        }
 }
 
+#ifdef NO_OPENSSL
 static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
 {
-#ifdef NO_OPENSSL
        fprintf(stderr, "SSL requested but SSL support not compiled in\n");
        return -1;
+}
+
 #else
+
+static int host_matches(const char *host, const char *pattern)
+{
+       if (pattern[0] == '*' && pattern[1] == '.') {
+               pattern += 2;
+               if (!(host = strchr(host, '.')))
+                       return 0;
+               host++;
+       }
+
+       return *host && *pattern && !strcasecmp(host, pattern);
+}
+
+static int verify_hostname(X509 *cert, const char *hostname)
+{
+       int len;
+       X509_NAME *subj;
+       char cname[1000];
+       int i, found;
+       STACK_OF(GENERAL_NAME) *subj_alt_names;
+
+       /* try the DNS subjectAltNames */
+       found = 0;
+       if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {
+               int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);
+               for (i = 0; !found && i < num_subj_alt_names; i++) {
+                       GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
+                       if (subj_alt_name->type == GEN_DNS &&
+                           strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&
+                           host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))
+                               found = 1;
+               }
+               sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
+       }
+       if (found)
+               return 0;
+
+       /* try the common name */
+       if (!(subj = X509_get_subject_name(cert)))
+               return error("cannot get certificate subject");
+       if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
+               return error("cannot get certificate common name");
+       if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
+               return 0;
+       return error("certificate owner '%s' does not match hostname '%s'",
+                    cname, hostname);
+}
+
+static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+{
 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
        const SSL_METHOD *meth;
 #else
@@ -279,6 +332,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 #endif
        SSL_CTX *ctx;
        int ret;
+       X509 *cert;
 
        SSL_library_init();
        SSL_load_error_strings();
@@ -322,9 +376,18 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
                return -1;
        }
 
+       if (verify) {
+               /* make sure the hostname matches that of the certificate */
+               cert = SSL_get_peer_certificate(sock->ssl);
+               if (!cert)
+                       return error("unable to get peer certificate.");
+               if (verify_hostname(cert, server.host) < 0)
+                       return -1;
+       }
+
        return 0;
-#endif
 }
+#endif
 
 static int socket_read(struct imap_socket *sock, char *buf, int len)
 {
similarity index 94%
rename from merge-file.c
rename to merge-blobs.c
index 7845528..57211bc 100644 (file)
@@ -3,7 +3,7 @@
 #include "xdiff-interface.h"
 #include "ll-merge.h"
 #include "blob.h"
-#include "merge-file.h"
+#include "merge-blobs.h"
 
 static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
 {
@@ -80,7 +80,7 @@ static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
        return xdi_diff(f1, f2, &xpp, &xecfg, &ecb);
 }
 
-void *merge_file(const char *path, struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
+void *merge_blobs(const char *path, struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
 {
        void *res = NULL;
        mmfile_t f1, f2, common;
diff --git a/merge-blobs.h b/merge-blobs.h
new file mode 100644 (file)
index 0000000..62b569e
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef MERGE_BLOBS_H
+#define MERGE_BLOBS_H
+
+#include "blob.h"
+
+extern void *merge_blobs(const char *, struct blob *, struct blob *, struct blob *, unsigned long *);
+
+#endif /* MERGE_BLOBS_H */
diff --git a/merge-file.h b/merge-file.h
deleted file mode 100644 (file)
index 9b3b83a..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef MERGE_FILE_H
-#define MERGE_FILE_H
-
-extern void *merge_file(const char *path, struct blob *base, struct blob *our,
-                       struct blob *their, unsigned long *size);
-
-#endif
index d882060..33ba5dc 100644 (file)
@@ -976,7 +976,7 @@ merge_file_special_markers(struct merge_options *o,
        return mfi;
 }
 
-static struct merge_file_info merge_file(struct merge_options *o,
+static struct merge_file_info merge_file_one(struct merge_options *o,
                                         const char *path,
                                         const unsigned char *o_sha, int o_mode,
                                         const unsigned char *a_sha, int a_mode,
@@ -1166,7 +1166,7 @@ static void conflict_rename_rename_1to2(struct merge_options *o,
                struct merge_file_info mfi;
                struct diff_filespec other;
                struct diff_filespec *add;
-               mfi = merge_file(o, one->path,
+               mfi = merge_file_one(o, one->path,
                                 one->sha1, one->mode,
                                 a->sha1, a->mode,
                                 b->sha1, b->mode,
@@ -1450,7 +1450,7 @@ static int process_renames(struct merge_options *o,
                                       ren1_dst, branch2);
                                if (o->call_depth) {
                                        struct merge_file_info mfi;
-                                       mfi = merge_file(o, ren1_dst, null_sha1, 0,
+                                       mfi = merge_file_one(o, ren1_dst, null_sha1, 0,
                                                         ren1->pair->two->sha1, ren1->pair->two->mode,
                                                         dst_other.sha1, dst_other.mode,
                                                         branch1, branch2);
index 295361a..52f7c8f 100644 (file)
@@ -1,29 +1,21 @@
 diff_cmd () {
+       empty_file=
+
        # p4merge does not like /dev/null
-       rm_local=
-       rm_remote=
        if test "/dev/null" = "$LOCAL"
        then
-               LOCAL="./p4merge-dev-null.LOCAL.$$"
-               >"$LOCAL"
-               rm_local=true
+               LOCAL="$(create_empty_file)"
        fi
        if test "/dev/null" = "$REMOTE"
        then
-               REMOTE="./p4merge-dev-null.REMOTE.$$"
-               >"$REMOTE"
-               rm_remote=true
+               REMOTE="$(create_empty_file)"
        fi
 
        "$merge_tool_path" "$LOCAL" "$REMOTE"
 
-       if test -n "$rm_local"
-       then
-               rm -f "$LOCAL"
-       fi
-       if test -n "$rm_remote"
+       if test -n "$empty_file"
        then
-               rm -f "$REMOTE"
+               rm -f "$empty_file"
        fi
 }
 
@@ -33,3 +25,10 @@ merge_cmd () {
        "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
        check_unchanged
 }
+
+create_empty_file () {
+       empty_file="${TMPDIR:-/tmp}/git-difftool-p4merge-empty-file.$$"
+       >"$empty_file"
+
+       printf "$empty_file"
+}
index 2260490..aef5e8a 100644 (file)
@@ -186,14 +186,15 @@ static int error_dirty_index(struct replay_opts *opts)
        return -1;
 }
 
-static int fast_forward_to(const unsigned char *to, const unsigned char *from)
+static int fast_forward_to(const unsigned char *to, const unsigned char *from,
+                          int unborn)
 {
        struct ref_lock *ref_lock;
 
        read_cache();
        if (checkout_fast_forward(from, to, 1))
                exit(1); /* the callee should have complained already */
-       ref_lock = lock_any_ref_for_update("HEAD", from, 0);
+       ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from, 0);
        return write_ref_sha1(ref_lock, to, "cherry-pick");
 }
 
@@ -390,7 +391,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
-       int res;
+       int res, unborn = 0;
 
        if (opts->no_commit) {
                /*
@@ -402,9 +403,10 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                if (write_cache_as_tree(head, 0, NULL))
                        die (_("Your index file is unmerged."));
        } else {
-               if (get_sha1("HEAD", head))
-                       return error(_("You do not have a valid HEAD"));
-               if (index_differs_from("HEAD", 0))
+               unborn = get_sha1("HEAD", head);
+               if (unborn)
+                       hashcpy(head, EMPTY_TREE_SHA1_BIN);
+               if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0))
                        return error_dirty_index(opts);
        }
        discard_cache();
@@ -435,8 +437,10 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        else
                parent = commit->parents->item;
 
-       if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
-               return fast_forward_to(commit->object.sha1, head);
+       if (opts->allow_ff &&
+           ((parent && !hashcmp(parent->object.sha1, head)) ||
+            (!parent && unborn)))
+            return fast_forward_to(commit->object.sha1, head, unborn);
 
        if (parent && parse_commit(parent) < 0)
                /* TRANSLATORS: The first %s will be "revert" or
index d709ecf..4969edb 100755 (executable)
@@ -32,32 +32,24 @@ test_expect_success 'setup' '
 #
 # and 'side' should be the last branch
 
-test_rev_equivalent () {
-
-       git rev-parse "$1" > expect &&
-       git rev-parse "$2" > output &&
-       test_cmp expect output
-
-}
-
 test_expect_success '@{-1} works' '
-       test_rev_equivalent side @{-1}
+       test_cmp_rev side @{-1}
 '
 
 test_expect_success '@{-1}~2 works' '
-       test_rev_equivalent side~2 @{-1}~2
+       test_cmp_rev side~2 @{-1}~2
 '
 
 test_expect_success '@{-1}^2 works' '
-       test_rev_equivalent side^2 @{-1}^2
+       test_cmp_rev side^2 @{-1}^2
 '
 
 test_expect_success '@{-1}@{1} works' '
-       test_rev_equivalent side@{1} @{-1}@{1}
+       test_cmp_rev side@{1} @{-1}@{1}
 '
 
 test_expect_success '@{-2} works' '
-       test_rev_equivalent master @{-2}
+       test_cmp_rev master @{-2}
 '
 
 test_expect_success '@{-3} fails' '
index 32fdc99..8462be1 100755 (executable)
@@ -29,12 +29,6 @@ Initial setup:
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
-test_cmp_rev () {
-       git rev-parse --verify "$1" >expect.rev &&
-       git rev-parse --verify "$2" >actual.rev &&
-       test_cmp expect.rev actual.rev
-}
-
 set_fake_editor
 
 # WARNING: Modifications to the initial repository can change the SHA ID used
index 34c86e5..6f489e2 100755 (executable)
@@ -100,4 +100,13 @@ test_expect_success 'revert forbidden on dirty working tree' '
 
 '
 
+test_expect_success 'chery-pick on unborn branch' '
+       git checkout --orphan unborn &&
+       git rm --cached -r . &&
+       rm -rf * &&
+       git cherry-pick initial &&
+       git diff --quiet initial &&
+       ! test_cmp_rev initial HEAD
+'
+
 test_done
index 51ca391..373aad6 100755 (executable)
@@ -105,4 +105,12 @@ test_expect_success 'cherry pick a root commit with --ff' '
        test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1"
 '
 
+test_expect_success 'chery-pick --ff on unborn branch' '
+       git checkout --orphan unborn &&
+       git rm --cached -r . &&
+       rm -rf * &&
+       git cherry-pick --ff first &&
+       test_cmp_rev first HEAD
+'
+
 test_done
index c82f721..223b984 100755 (executable)
@@ -11,12 +11,6 @@ test_description='test cherry-pick and revert with conflicts
 
 . ./test-lib.sh
 
-test_cmp_rev () {
-       git rev-parse --verify "$1" >expect.rev &&
-       git rev-parse --verify "$2" >actual.rev &&
-       test_cmp expect.rev actual.rev
-}
-
 pristine_detach () {
        git checkout -f "$1^0" &&
        git read-tree -u --reset HEAD &&
index 340afc7..4e7136b 100755 (executable)
@@ -5,15 +5,11 @@ test_description='test cherry-picking many commits'
 . ./test-lib.sh
 
 check_head_differs_from() {
-       head=$(git rev-parse --verify HEAD) &&
-       arg=$(git rev-parse --verify "$1") &&
-       test "$head" != "$arg"
+       ! test_cmp_rev HEAD "$1"
 }
 
 check_head_equals() {
-       head=$(git rev-parse --verify HEAD) &&
-       arg=$(git rev-parse --verify "$1") &&
-       test "$head" = "$arg"
+       test_cmp_rev HEAD "$1"
 }
 
 test_expect_success setup '
index b5fb527..7b7a89d 100755 (executable)
@@ -24,12 +24,6 @@ pristine_detach () {
        git clean -d -f -f -q -x
 }
 
-test_cmp_rev () {
-       git rev-parse --verify "$1" >expect.rev &&
-       git rev-parse --verify "$2" >actual.rev &&
-       test_cmp expect.rev actual.rev
-}
-
 test_expect_success setup '
        git config advice.detachedhead false &&
        echo unrelated >unrelated &&
index 46c3fe7..d0b2a45 100755 (executable)
@@ -254,4 +254,48 @@ EXPECTED
        test_cmp expected actual
 '
 
+test_expect_success 'turn file to tree' '
+       git reset --hard initial &&
+       rm initial-file &&
+       mkdir initial-file &&
+       test_commit "turn-file-to-tree" "initial-file/ONE" "CCC" &&
+       git merge-tree initial initial turn-file-to-tree >actual &&
+       cat >expect <<-\EOF &&
+       added in remote
+         their  100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 initial-file/ONE
+       @@ -0,0 +1 @@
+       +CCC
+       removed in remote
+         base   100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+         our    100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+       @@ -1 +0,0 @@
+       -initial
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'turn tree to file' '
+       git reset --hard initial &&
+       mkdir dir &&
+       test_commit "add-tree" "dir/path" "AAA" &&
+       test_commit "add-another-tree" "dir/another" "BBB" &&
+       rm -fr dir &&
+       test_commit "make-file" "dir" "CCC" &&
+       git merge-tree add-tree add-another-tree make-file >actual &&
+       cat >expect <<-\EOF &&
+       added in local
+         our    100644 ba629238ca89489f2b350e196ca445e09d8bb834 dir/another
+       removed in remote
+         base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path
+         our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path
+       @@ -1 +0,0 @@
+       -AAA
+       added in remote
+         their  100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 dir
+       @@ -0,0 +1 @@
+       +CCC
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 72e28ee..3e0e15f 100755 (executable)
@@ -676,9 +676,7 @@ test_expect_success 'bisect fails if tree is broken on trial commit' '
 check_same()
 {
        echo "Checking $1 is the same as $2" &&
-       git rev-parse "$1" > expected.same &&
-       git rev-parse "$2" > expected.actual &&
-       test_cmp expected.same expected.actual
+       test_cmp_rev "$1" "$2"
 }
 
 test_expect_success 'bisect: --no-checkout - start commit bad' '
index f919c8d..8e32f19 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2008 Johannes Schindelin
 #
 
-test_description='Test rebasing and stashing with dirty submodules'
+test_description='Test rebasing, stashing, etc. with submodules'
 
 . ./test-lib.sh
 
@@ -20,7 +20,8 @@ test_expect_success setup '
        echo second line >> file &&
        (cd submodule && git pull) &&
        test_tick &&
-       git commit -m file-and-submodule -a
+       git commit -m file-and-submodule -a &&
+       git branch added-submodule
 
 '
 
@@ -89,4 +90,29 @@ test_expect_success 'stash with a dirty submodule' '
 
 '
 
+test_expect_success 'rebasing submodule that should conflict' '
+       git reset --hard &&
+       git checkout added-submodule &&
+       git add submodule &&
+       test_tick &&
+       git commit -m third &&
+       (
+               cd submodule &&
+               git commit --allow-empty -m extra
+       ) &&
+       git add submodule &&
+       test_tick &&
+       git commit -m fourth &&
+
+       test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 &&
+       git ls-files -s submodule >actual &&
+       (
+               cd submodule &&
+               echo "160000 $(git rev-parse HEAD^) 1   submodule" &&
+               echo "160000 $(git rev-parse HEAD^^) 2  submodule" &&
+               echo "160000 $(git rev-parse HEAD) 3    submodule"
+       ) >expect &&
+       test_cmp expect actual
+'
+
 test_done
index b3f6eb9..95d6510 100755 (executable)
@@ -5,7 +5,7 @@
 #                   Grenoble INP Ensimag
 #
 
-test_description='git status advices'
+test_description='git status advice'
 
 . ./test-lib.sh
 
index 21924df..aae1a3f 100755 (executable)
@@ -105,12 +105,13 @@ build_gendouble() {
        cat >gendouble.py <<-\EOF
        import sys
        import struct
-       import array
 
-       s = array.array("c", '\0' * 26)
-       struct.pack_into(">L", s,  0, 0x00051607)  # AppleDouble
-       struct.pack_into(">L", s,  4, 0x00020000)  # version 2
-       s.tofile(sys.stdout)
+       s = struct.pack(">LL18s",
+                       0x00051607,  # AppleDouble
+                       0x00020000,  # version 2
+                       ""           # pad to 26 bytes
+       )
+       sys.stdout.write(s)
        EOF
 }
 
index 3cd53f8..adc1372 100755 (executable)
@@ -13,6 +13,25 @@ complete ()
        return 0
 }
 
+# Be careful when updating this list:
+#
+# (1) The build tree may have build artifact from different branch, or
+#     the user's $PATH may have a random executable that may begin
+#     with "git-check" that are not part of the subcommands this build
+#     will ship, e.g.  "check-ignore".  The tests for completion for
+#     subcommand names tests how "check" is expanded; we limit the
+#     possible candidates to "checkout" and "check-attr" to make sure
+#     "check-attr", which is known by the filter function as a
+#     subcommand to be thrown out, while excluding other random files
+#     that happen to begin with "check" to avoid letting them get in
+#     the way.
+#
+# (2) A test makes sure that common subcommands are included in the
+#     completion for "git <TAB>", and a plumbing is excluded.  "add",
+#     "filter-branch" and "ls-files" are listed for this.
+
+GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files'
+
 . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash"
 
 # We don't need this function to actually join words or do anything special.
@@ -196,7 +215,6 @@ test_expect_success 'general options plus command' '
        test_completion "git --paginate check" "checkout " &&
        test_completion "git --git-dir=foo check" "checkout " &&
        test_completion "git --bare check" "checkout " &&
-       test_completion "git --help des" "describe " &&
        test_completion "git --exec-path=foo check" "checkout " &&
        test_completion "git --html-path check" "checkout " &&
        test_completion "git --no-pager check" "checkout " &&
@@ -207,6 +225,11 @@ test_expect_success 'general options plus command' '
        test_completion "git --no-replace-objects check" "checkout "
 '
 
+test_expect_success 'git --help completion' '
+       test_completion "git --help ad" "add " &&
+       test_completion "git --help core" "core-tutorial "
+'
+
 test_expect_success 'setup for ref completion' '
        echo content >file1 &&
        echo more >file2 &&
index 22a4f8f..fa62d01 100644 (file)
@@ -602,6 +602,13 @@ test_cmp() {
        $GIT_TEST_CMP "$@"
 }
 
+# Tests that its two parameters refer to the same revision
+test_cmp_rev () {
+       git rev-parse --verify "$1" >expect.rev &&
+       git rev-parse --verify "$2" >actual.rev &&
+       test_cmp expect.rev actual.rev
+}
+
 # Print a sequence of numbers or letters in increasing order.  This is
 # similar to GNU seq(1), but the latter might not be available
 # everywhere (and does not do letters).  It may be used like:
index fc42d3a..ea1e4a0 100644 (file)
@@ -86,7 +86,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
                PROVE
                VALGRIND
                UNZIP
-               PERF_AGGREGATING_LATER
+               PERF_
        ));
        my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
        print join("\n", @vars);
index 9932f40..b9306ef 100644 (file)
@@ -741,7 +741,7 @@ void transport_print_push_status(const char *dest, struct ref *refs,
                        n += print_one_push_status(ref, dest, n, porcelain);
                if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD &&
                    *nonfastforward != NON_FF_HEAD) {
-                       if (!strcmp(head, ref->name))
+                       if (head != NULL && !strcmp(head, ref->name))
                                *nonfastforward = NON_FF_HEAD;
                        else
                                *nonfastforward = NON_FF_OTHER;