GIT-CFLAGS
+GIT-GUI-VARS
GIT-VERSION-FILE
git
git-add
git-merge-recursive
git-merge-resolve
git-merge-stupid
+git-merge-subtree
git-mergetool
git-mktag
git-mktree
git-whatchanged
git-write-tree
git-core-*/?*
+gitk-wish
gitweb/gitweb.cgi
test-chmtime
test-date
test-delta
test-dump-cache-tree
+test-match-trees
common-cmds.h
*.tar.gz
*.dsc
*.html
*.1
*.7
+*.made
howto-index.txt
doc.dep
cmds-*.txt
ARTICLES += hooks
ARTICLES += everyday
ARTICLES += git-tools
+ARTICLES += glossary
# with their own formatting rules.
-SP_ARTICLES = glossary howto/revert-branch-rebase user-manual
+SP_ARTICLES = howto/revert-branch-rebase user-manual
DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
+../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+ $(MAKE) -C ../ GIT-VERSION-FILE
+
+-include ../GIT-VERSION-FILE
+
#
# Determine "include::" file references in asciidoc files.
#
cmds-purehelpers.txt \
cmds-foreignscminterface.txt
-$(cmds_txt): cmd-list.perl $(MAN1_TXT)
+$(cmds_txt): cmd-list.made
+
+cmd-list.made: cmd-list.perl $(MAN1_TXT)
perl ./cmd-list.perl
+ date >$@
git.7 git.html: git.txt core-intro.txt
clean:
- rm -f *.xml *.html *.1 *.7 howto-index.txt howto/*.html doc.dep
- rm -f $(cmds_txt)
+ rm -f *.xml *.xml+ *.html *.html+ *.1 *.7 howto-index.txt howto/*.html doc.dep
+ rm -f $(cmds_txt) *.made
%.html : %.txt
- $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf $(ASCIIDOC_EXTRA) $<
+ rm -f $@+ $@
+ $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
+ $(ASCIIDOC_EXTRA) -o - $< | \
+ sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' >$@+
+ mv $@+ $@
%.1 %.7 : %.xml
xmlto -m callouts.xsl man $<
%.xml : %.txt
- $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf $<
+ rm -f $@+ $@
+ $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
+ $(ASCIIDOC_EXTRA) -o - $< | \
+ sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' >$@+
+ mv $@+ $@
user-manual.xml: user-manual.txt user-manual.conf
$(ASCIIDOC) -b docbook -d book $<
XSLT = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
-XSLTOPTS = --nonet --xinclude --stringparam html.stylesheet docbook-xsl.css
+XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
-glossary.html : glossary.txt sort_glossary.pl
- cat $< | \
- perl sort_glossary.pl | \
- $(ASCIIDOC) -b xhtml11 - > glossary.html
-
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
rm -f $@+ $@
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+
quick-install:
sh ./install-doc-quick.sh $(DOC_REF) $(mandir)
+
+.PHONY: .FORCE-GIT-VERSION-FILE
--- /dev/null
+GIT v1.5.0.6 Release Notes
+==========================
+
+Fixes since v1.5.0.5
+--------------------
+
+* Bugfixes
+
+ - a handful small fixes to gitweb.
+
+ - build procedure for user-manual is fixed not to require locally
+ installed stylesheets.
+
+ - "git commit $paths" on paths whose earlier contents were
+ already updated in the index were failing out.
+
+* Documentation
+
+ - user-manual has better cross references.
+
+ - gitweb installation/deployment procedure is now documented.
+
--- /dev/null
+GIT v1.5.0.7 Release Notes
+==========================
+
+Fixes since v1.5.0.6
+--------------------
+
+* Bugfixes
+
+ - git-upload-pack failed to close unused pipe ends, resulting
+ in many zombies to hang around.
+
+ - git-rerere was recording the contents of earlier hunks
+ duplicated in later hunks. This prevented resolving the same
+ conflict when performing the same merge the other way around.
+
+* Documentation
+
+ - a few documentation fixes from Debian package maintainer.
--- /dev/null
+GIT v1.5.1.1 Release Notes
+==========================
+
+Fixes since v1.5.1
+------------------
+
+* Documentation updates
+
+ - The --left-right option of rev-list and friends is documented.
+
+ - The documentation for cvsimport has been majorly improved.
+
+ - "git-show-ref --exclude-existing" was documented.
+
+* Bugfixes
+
+ - The implementation of -p option in "git cvsexportcommit" had
+ the meaning of -C (context reduction) option wrong, and
+ loosened the context requirements when it was told to be
+ strict.
+
+ - "git cvsserver" did not behave like the real cvsserver when
+ client side removed a file from the working tree without
+ doing anything else on the path. In such a case, it should
+ restore it from the checked out revision.
+
+ - "git fsck" issued an alarming error message on detached
+ HEAD. It is not an error since at least 1.5.0.
+
+ - "git send-email" produced of References header of unbounded length;
+ fixed this with line-folding.
+
+ - "git archive" to download from remote site should not
+ require you to be in a git repository, but it incorrectly
+ did.
+
+ - "git apply" ignored -p<n> for "diff --git" formatted
+ patches.
+
+ - "git rerere" recorded a conflict that had one side empty
+ (the other side adds) incorrectly; this made merging in the
+ other direction fail to use previously recorded resolution.
+
+ - t4200 test was broken where "wc -l" pads its output with
+ spaces.
+
+ - "git branch -m old new" to rename branch did not work
+ without a configuration file in ".git/config".
+
+ - The sample hook for notification e-mail was misnamed.
+
+ - gitweb did not show type-changing patch correctly in the
+ blobdiff view.
+
+ - git-svn did not error out with incorrect command line options.
+
+ - git-svn fell into an infinite loop when insanely long commit
+ message was found.
+
+ - git-svn dcommit and rebase was confused by patches that were
+ merged from another branch that is managed by git-svn.
--- /dev/null
+GIT v1.5.1.2 Release Notes (draft)
+==========================
+
+Fixes since v1.5.1.1
+--------------------
+
+* Bugfixes
+
+ - "git diff a/ b/" incorrectly fell in "diff between two
+ filesystem objects" codepath, when the user most likely
+ wanted to limit the extent of output to two tracked
+ directories.
+
+ - git-quiltimport had the same bug as we fixed for
+ git-applymbox in v1.5.1.1 -- it gave an alarming "did not
+ have any patch" message (but did not actually fail and was
+ harmless).
+
+ - various git-svn fixes.
+
+ - Sample update hook incorrectly always refused requests to
+ delete branches through push.
+
+ - git-blame on a very long working tree path had buffer
+ overrun problem.
+
+* Documentation updates
+
+ - Various documentation updates from J. Bruce Fields, Frank
+ Lichtenheld, Alex Riesen and others. Andrew Ruder started a
+ war on undocumented options.
+
+---
+exec >/var/tmp/1
+O=v1.5.1.1-31-g0220f1e
+echo O=`git describe refs/heads/maint`
+git shortlog --no-merges $O..refs/heads/maint
* New commands and options.
- - "git log" and friends take --reverse. This makes output
- that typically goes reverse order in chronological order.
- "git shortlog" usually lists commits in chronological order,
- but with "--reverse", they are shown in reverse
- chronological order.
+ - "git log" and friends take --reverse, which instructs them
+ to give their output in the order opposite from their usual.
+ They typically output from new to old, but with this option
+ their output would read from old to new. "git shortlog"
+ usually lists older commits first, but with this option,
+ they are shown from new to old.
+
+ - "git log --pretty=format:<string>" to allow more flexible
+ custom log output.
- "git diff" learned --ignore-space-at-eol. This is a weaker
form of --ignore-space-change.
- "git diff --no-index pathA pathB" can be used as diff
replacement with git specific enhancements.
- - "git diff --pretty=format:<string>" to allow more flexible
- custom log output.
-
- "git diff --no-index" can read from '-' (standard input).
- "git diff" also learned --exit-code to exit with non-zero
backward incompatible change; it will stay as an option for
now.
+ - "git diff --quiet" is --exit-code with output turned off,
+ meant for scripted use to quickly determine if there is any
+ tree-level difference.
+
+ - Textual patch generation with "git diff" without -w/-b
+ option has been significantly optimized. "git blame" got
+ faster because of the same change.
+
+ - "git log" and "git rev-list" has been optimized
+ significantly when they are used with pathspecs.
+
- "git branch --track" can be used to set up configuration
variables to help it easier to base your work on branches
you track from a remote site.
symlinks on filesystems that do not support them; they are
checked out as regular files instead.
+ - You can name a commit object with its first line of the
+ message. The syntax to use is ':/message text'. E.g.
+
+ $ git show ":/object name: introduce ':/<oneline prefix>' notation"
+
+ means the same thing as:
+
+ $ git show 28a4d940443806412effa246ecc7768a21553ec7
+
+ - "git bisect" learned a new command "run" that takes a script
+ to run after each revision is checked out to determine if it
+ is good or bad, to automate the bisection process.
-* Updated behaviour of existing commands.
+ - "git log" family learned a new traversal option --first-parent,
+ which does what the name suggests.
+
+
+* Updated behavior of existing commands.
+
+ - "git-merge-recursive" used to barf when there are more than
+ one common ancestors for the merge, and merging them had a
+ rename/rename conflict. This has been fixed.
- "git fsck" does not barf on corrupt loose objects.
+ - "git rm" does not remove newly added files without -f.
+
- "git archimport" allows remapping when coming up with git
branch names from arch names.
allow users to explicitly override this heuristic based on
paths.
- - The behaviour of 'git-apply', when run in a subdirectory,
+ - The behavior of 'git-apply', when run in a subdirectory,
without --index nor --cached were inconsistent with that of
the command with these options. This was fixed to match the
- behaviour with --index. A patch that is meant to be applied
+ behavior with --index. A patch that is meant to be applied
with -p1 from the toplevel of the project tree can be
applied with any custom -p<n> option. A patch that is not
relative to the toplevel needs to be applied with -p<n>
the heaviest parts in C.
- "git mailinfo" which splits an e-mail into a patch and the
- metainformation was rewritten, thanks to Don Zickus. It
- handles nested multipart better.
+ meta-information was rewritten, thanks to Don Zickus. It
+ handles nested multipart better. The command was broken for
+ a brief period on 'master' branch since 1.5.0 but the
+ breakage is fixed now.
- send-email learned configurable bcc and chain-reply-to.
- - Using objects from packs is now seriouly optimized by clever
+ - "git remote show $remote" also talks about branches that
+ would be pushed if you run "git push remote".
+
+ - Using objects from packs is now seriously optimized by clever
use of a cache. This should be most noticeable in git-log
family of commands that involve reading many tree objects.
In addition, traversing revisions while filtering changes
* Hooks
- - The sample update hook to show how to send out notification
- e-mail was updated to show only new commits that appeared in
- the repository. Earlier, it showed new commits that appeared
- on the branch.
+ - The part to send out notification e-mails was removed from
+ the sample update hook, as it was not an appropriate place
+ to do so. The proper place to do this is the new post-receive
+ hook. An example hook has been added to contrib/hooks/.
* Others
- git-revert, git-gc and git-cherry-pick are now built-ins.
+Fixes since v1.5.0
+------------------
+
+These are all in v1.5.0.x series.
+
+* Documentation updates
+
+ - Clarifications and corrections to 1.5.0 release notes.
+
+ - The main documentation did not link to git-remote documentation.
+
+ - Clarified introductory text of git-rebase documentation.
+
+ - Converted remaining mentions of update-index on Porcelain
+ documents to git-add/git-rm.
+
+ - Some i18n.* configuration variables were incorrectly
+ described as core.*; fixed.
+
+ - added and clarified core.bare, core.legacyheaders configurations.
+
+ - updated "git-clone --depth" documentation.
+
+ - user-manual updates.
+
+ - Options to 'git remote add' were described insufficiently.
+
+ - Configuration format.suffix was not documented.
+
+ - Other formatting and spelling fixes.
+
+ - user-manual has better cross references.
+
+ - gitweb installation/deployment procedure is now documented.
+
+
+* Bugfixes
+
+ - git-upload-pack closes unused pipe ends; earlier this caused
+ many zombies to hang around.
+
+ - git-rerere was recording the contents of earlier hunks
+ duplicated in later hunks. This prevented resolving the same
+ conflict when performing the same merge the other way around.
+
+ - git-add and git-update-index on a filesystem on which
+ executable bits are unreliable incorrectly reused st_mode
+ bits even when the path changed between symlink and regular
+ file.
+
+ - git-daemon marks the listening sockets with FD_CLOEXEC so
+ that it won't be leaked into the children.
+
+ - segfault from git-blame when the mandatory pathname
+ parameter was missing was fixed; usage() message is given
+ instead.
+
+ - git-rev-list did not read $GIT_DIR/config file, which means
+ that did not honor i18n.logoutputencoding correctly.
+
+ - Automated merge conflict handling when changes to symbolic
+ links conflicted were completely broken. The merge-resolve
+ strategy created a regular file with conflict markers in it
+ in place of the symbolic link. The default strategy,
+ merge-recursive was even more broken. It removed the path
+ that was pointed at by the symbolic link. Both of these
+ problems have been fixed.
+
+ - 'git diff maint master next' did not correctly give combined
+ diff across three trees.
+
+ - 'git fast-import' portability fix for Solaris.
+
+ - 'git show-ref --verify' without arguments did not error out
+ but segfaulted.
+
+ - 'git diff :tracked-file `pwd`/an-untracked-file' gave an extra
+ slashes after a/ and b/.
+
+ - 'git format-patch' produced too long filenames if the commit
+ message had too long line at the beginning.
+
+ - Running 'make all' and then without changing anything
+ running 'make install' still rebuilt some files. This
+ was inconvenient when building as yourself and then
+ installing as root (especially problematic when the source
+ directory is on NFS and root is mapped to nobody).
+
+ - 'git-rerere' failed to deal with two unconflicted paths that
+ sorted next to each other.
+
+ - 'git-rerere' attempted to open(2) a symlink and failed if
+ there was a conflict. Since a conflicting change to a
+ symlink would not benefit from rerere anyway, the command
+ now ignores conflicting changes to symlinks.
+
+ - 'git-repack' did not like to pass more than 64 arguments
+ internally to underlying 'rev-list' logic, which made it
+ impossible to repack after accumulating many (small) packs
+ in the repository.
+
+ - 'git-diff' to review the combined diff during a conflicted
+ merge were not reading the working tree version correctly
+ when changes to a symbolic link conflicted. It should have
+ read the data using readlink(2) but read from the regular
+ file the symbolic link pointed at.
+
+ - 'git-remote' did not like period in a remote's name.
+
+ - 'git.el' honors the commit coding system from the configuration.
+
+ - 'blameview' in contrib/ correctly digs deeper when a line is
+ clicked.
+
+ - 'http-push' correctly makes sure the remote side has leading
+ path. Earlier it started in the middle of the path, and
+ incorrectly.
+
+ - 'git-merge' did not exit with non-zero status when the
+ working tree was dirty and cannot fast forward. It does
+ now.
+
+ - 'cvsexportcommit' does not lose yet-to-be-used message file.
+
+ - int-vs-size_t typefix when running combined diff on files
+ over 2GB long.
+
+ - 'git apply --whitespace=strip' should not touch unmodified
+ lines.
+
+ - 'git-mailinfo' choke when a logical header line was too long.
+
+ - 'git show A..B' did not error out. Negative ref ("not A" in
+ this example) does not make sense for the purpose of the
+ command, so now it errors out.
+
+ - 'git fmt-merge-msg --file' without file parameter did not
+ correctly error out.
+
+ - 'git archimport' barfed upon encountering a commit without
+ summary.
+
+ - 'git index-pack' did not protect itself from getting a short
+ read out of pread(2).
+
+ - 'git http-push' had a few buffer overruns.
+
+ - Build dependency fixes to rebuild fetch.o when other headers
+ change.
+
+ - git.el does not add duplicate sign-off lines.
+
+ - git-commit shows the full stat of the resulting commit, not
+ just about the files in the current directory, when run from
+ a subdirectory.
+
+ - "git-checkout -m '@{8 hours ago}'" had a funny failure from
+ eval; fixed.
+
+ - git-merge (hence git-pull) did not refuse fast-forwarding
+ when the working tree had local changes that would have
+ conflicted with it.
+
+ - a handful small fixes to gitweb.
+
+ - build procedure for user-manual is fixed not to require locally
+ installed stylesheets.
+
+ - "git commit $paths" on paths whose earlier contents were
+ already updated in the index were failing out.
+
---
-exec >/var/tmp/1
-O=v1.5.0.5-446-g5d86501
-echo O=`git describe master`
-git shortlog --no-merges $O..master ^maint
+* Tweaks
-# Local Variables:
-# mode: text
-# End:
+ - sliding mmap() inefficiently mmaped the same region of a
+ packfile with an access pattern that used objects in the
+ reverse order. This has been made more efficient.
--- /dev/null
+GIT v1.5.2 Release Notes (draft)
+========================
+
+Updates since v1.5.1
+--------------------
+
+* New commands and options.
+
+ - "git bisect start" can optionally take a single bad commit and
+ zero or more good commits on the command line.
+
+ - "git shortlog" can optionally be told to wrap its output.
+
+ - "subtree" merge strategy allows another project to be merged in as
+ your subdirectory.
+
+ - "git format-patch" learned a new --subject-prefix=<string>
+ option, to override the built-in "[PATCH]".
+
+* Updated behavior of existing commands.
+
+ - "git diff --stat" shows size of preimage and postimage blobs
+ for binary contents. Earlier it only said "Bin".
+
+ - "git lost-found" shows stuff that are unreachable except
+ from reflogs.
+
+ - "git checkout branch^0" now detaches HEAD at the tip commit
+ on the named branch, instead of just switching to the
+ branch (use "git checkout branch" to switch to the branch,
+ as before).
+
+ - "git bisect next" can be used after giving only a bad commit
+ without giving a good one (this starts bisection half-way to
+ the root commit). We used to refuse to operate without a
+ good and a bad commit.
+
+ - "git push", when pushing into more than one repository, does
+ not stop at the first error.
+
+ - "git archive" does not insist you to give --format parameter
+ anymore; it defaults to "tar".
+
+* Builds
+
+ - git-p4import has never been installed; now there is an
+ installation option to do so.
+
+ - gitk and git-gui can be configured out.
+
+ - Generated documentation pages automatically get version
+ information from GIT_VERSION
+
+ - Parallel build with "make -j" descending into subdirectory
+ was fixed.
+
+* Performance Tweaks
+
+ - optimized "git-rev-list --bisect" (hence "git-bisect").
+
+ - optimized "git-add $path" in a large directory, most of
+ whose contents are ignored.
+
+
+Fixes since v1.5.1
+------------------
+
+The following are all in v1.5.1.x series, unless otherwise noted.
+
+* Documentation updates
+
+ - Various documentation updates from J. Bruce Fields, Frank
+ Lichtenheld, Alex Riesen and others. Andrew Ruder started a
+ war on undocumented options.
+
+* Bugfixes
+
+ - "git diff a/ b/" incorrectly fell in "diff between two
+ filesystem objects" codepath, when the user most likely
+ wanted to limit the extent of output to two tracked
+ directories.
+
+ - git-quiltimport had the same bug as we fixed for
+ git-applymbox in v1.5.1.1 -- it gave an alarming "did not
+ have any patch" message (but did not actually fail and was
+ harmless).
+
+ - various git-svn fixes.
+
+ - Sample update hook incorrectly always refused requests to
+ delete branches through push.
+
+ - git-blame on a very long working tree path had buffer
+ overrun problem.
+
+ - Switching branches with "git checkout" refused to work when
+ a path changes from a file to a directory between the
+ current branch and the new branch, in order not to lose
+ possible local changes in the directory that is being turned
+ into a file with the switch. We now allow such a branch
+ switch after making sure that there is no locally modified
+ file nor un-ignored file in the directory. This has not
+ been backported to 1.5.1.x series, as it is rather an
+ intrusive change.
+
+ - Merging branches that have a file in one and a directory in
+ another at the same path used to get quite confused. We
+ handle such a case a bit more carefully, even though that is
+ still left as a conflict for the user to sort out. This
+ will not be backported to 1.5.1.x series, as it is rather an
+ intrusive change.
+
+* Performance Tweaks
+
+--
+exec >/var/tmp/1
+O=v1.5.1.1-158-g86da9de
+echo O=`git describe refs/heads/master`
+git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
- provide additional information (which is unsuitable for
the commit message) between the "---" and the diffstat
- send the patch to the list _and_ the maintainer
+ - if you change, add, or remove a command line option or
+ make some other user interface change, the associated
+ documentation should be updated as well.
Long version:
{title#}</example>
endif::backend-docbook[]
+ifdef::doctype-manpage[]
+ifdef::backend-docbook[]
+[header]
+template::[header-declarations]
+<refentry>
+<refmeta>
+<refentrytitle>{mantitle}</refentrytitle>
+<manvolnum>{manvolnum}</manvolnum>
+<refmiscinfo class="source">Git</refmiscinfo>
+<refmiscinfo class="version">@@GIT_VERSION@@</refmiscinfo>
+<refmiscinfo class="manual">Git Manual</refmiscinfo>
+</refmeta>
+<refnamediv>
+ <refname>{manname}</refname>
+ <refpurpose>{manpurpose}</refpurpose>
+</refnamediv>
+endif::backend-docbook[]
+endif::doctype-manpage[]
+
ifdef::backend-xhtml11[]
[gitlink-inlinemacro]
<a href="{target}.html">{target}{0?({0})}</a>
--- /dev/null
+-b::
+ Show blank SHA-1 for boundary commits. This can also
+ be controlled via the `blame.blankboundary` config option.
+
+--root::
+ Do not treat root commits as boundaries. This can also be
+ controlled via the `blame.showroot` config option.
+
+--show-stats::
+ Include additional statistics at the end of blame output.
+
+-L n,m::
+ Annotate only the specified line range (lines count from 1).
+
+-l::
+ Show long rev (Default: off).
+
+-t::
+ Show raw timestamp (Default: off).
+
+-S <revs-file>::
+ Use revs from revs-file instead of calling gitlink:git-rev-list[1].
+
+-p, --porcelain::
+ Show in a format designed for machine consumption.
+
+--incremental::
+ Show the result incrementally in a format designed for
+ machine consumption.
+
+--contents <file>::
+ When <rev> is not specified, the command annotates the
+ changes starting backwards from the working tree copy.
+ This flag makes the command pretend as if the working
+ tree copy has the contents of he named file (specify
+ `-` to make the command read from the standard input).
+
+-M|<num>|::
+ Detect moving lines in the file as well. When a commit
+ moves a block of lines in a file (e.g. the original file
+ has A and then B, and the commit changes it to B and
+ then A), traditional 'blame' algorithm typically blames
+ the lines that were moved up (i.e. B) to the parent and
+ assigns blame to the lines that were moved down (i.e. A)
+ to the child commit. With this option, both groups of lines
+ are blamed on the parent.
+
+ <num> is optional but it is the lower bound on the number of
+ alphanumeric characters that git must detect as moving
+ within a file for it to associate those lines with the parent
+ commit.
+
+-C|<num>|::
+ In addition to `-M`, detect lines copied from other
+ files that were modified in the same commit. This is
+ useful when you reorganize your program and move code
+ around across files. When this option is given twice,
+ the command looks for copies from all other files in the
+ parent for the commit that creates the file in addition.
+
+ <num> is optional but it is the lower bound on the number of
+ alphanumeric characters that git must detect as moving
+ between files for it to associate those lines with the parent
+ commit.
+
+-h, --help::
+ Show help message.
-#
+#!/usr/bin/perl -w
+
+use File::Compare qw(compare);
sub format_one {
my ($out, $name) = @_;
my ($state, $description);
+ $state = 0;
open I, '<', "$name.txt" or die "No such file $name.txt";
while (<I>) {
if (/^NAME$/) {
format_one(\*O, $_);
}
close O;
- rename "$out+", "$out";
+
+ if (-f "$out" && compare("$out", "$out+") == 0) {
+ unlink "$out+";
+ }
+ else {
+ print STDERR "$out\n";
+ rename "$out+", "$out";
+ }
}
__DATA__
the working copy are ignored; useful on broken filesystems like FAT.
See gitlink:git-update-index[1]. True by default.
+core.autocrlf::
+ If true, makes git convert `CRLF` at the end of lines in text files to
+ `LF` when reading from the filesystem, and convert in reverse when
+ writing to the filesystem. The variable can be set to
+ 'input', in which case the conversion happens only while
+ reading from the filesystem but files are written out with
+ `LF` at the end of lines. Currently, which paths to consider
+ "text" (i.e. be subjected to the autocrlf mechanism) is
+ decided purely based on the contents.
+
core.symlinks::
If false, symbolic links are checked out as small plain files that
contain the link text. gitlink:git-update-index[1] and
The default is 15 days. See gitlink:git-rerere[1].
gitcvs.enabled::
- Whether the cvs pserver interface is enabled for this repository.
+ Whether the cvs server interface is enabled for this repository.
See gitlink:git-cvsserver[1].
gitcvs.logfile::
- Path to a log file where the cvs pserver interface well... logs
+ Path to a log file where the cvs server interface well... logs
various stuff. See gitlink:git-cvsserver[1].
+gitcvs.allbinary::
+ If true, all files are sent to the client in mode '-kb'. This
+ causes the client to treat all files as binary files which suppresses
+ any newline munging it otherwise might do. A work-around for the
+ fact that there is no way yet to set single files to mode '-kb'.
+
gitcvs.dbname::
Database used by git-cvsserver to cache revision information
derived from the git repository. The exact meaning depends on the
http.noEPSV::
A boolean which disables using of EPSV ftp command by curl.
- This can helpful with some "poor" ftp servers which doesn't
+ This can helpful with some "poor" ftp servers which don't
support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
environment variable. Default is false (curl will use EPSV).
That is, it exits with 1 if there were differences and
0 means no differences.
+--quiet::
+ Disable all output of the program. Implies --exit-code.
+
For more detailed explanation on these common options, see also
link:diffcore.html[diffcore documentation].
The list of mailbox files to read patches from. If you do not
supply this argument, reads from the standard input.
---signoff::
+-s, --signoff::
Add `Signed-off-by:` line to the commit message, using
the committer identity of yourself.
---dotest=<dir>::
+-d=<dir>, --dotest=<dir>::
Instead of `.dotest` directory, use <dir> as a working
area to store extracted patches.
---keep::
+-k, --keep::
Pass `-k` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
---utf8::
+-u, --utf8::
Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
The proposed commit log message taken from the e-mail
are re-coded into UTF-8 encoding (configuration variable
default. You could use `--no-utf8` to override this.
--no-utf8::
- Do not pass `-u` flag to `git-mailinfo` (see
+ Pass `-n` flag to `git-mailinfo` (see
gitlink:git-mailinfo[1]).
---binary::
+-b, --binary::
Pass `--allow-binary-replacement` flag to `git-apply`
(see gitlink:git-apply[1]).
---3way::
+-3, --3way::
When the patch does not apply cleanly, fall back on
3-way merge, if the patch records the identity of blobs
it is supposed to apply to, and we have those blobs
the patch.
-C<n>, -p<n>::
- These flag are passed to the `git-apply` program that applies
+ These flags are passed to the `git-apply` program that applies
the patch.
---interactive::
+-i, --interactive::
Run interactively, just like git-applymbox.
---resolved::
+-r, --resolved::
After a patch failure (e.g. attempting to apply
conflicting patch), the user has applied it by hand and
the index file stores the result of the application.
extracted from the e-mail message and the current index
file, and continue.
+--resolvemsg=<msg>::
+ When a patch failure occurs, <msg> will be printed
+ to the screen before exiting. This overrides the
+ standard message informing you to use `--resolved`
+ or `--skip` to handle the failure. This is solely
+ for internal use between `git-rebase` and `git-am`.
+
DISCUSSION
----------
+The commit author name is taken from the "From: " line of the
+message, and commit author time is taken from the "Date: " line
+of the message. The "Subject: " line is used as the title of
+the commit, after stripping common prefix "[PATCH <anything>]".
+It is supposed to describe what the commit is about concisely as
+a one line text.
+
+The body of the message (iow, after a blank line that terminates
+RFC2822 headers) can begin with "Subject: " and "From: " lines
+that are different from those of the mail header, to override
+the values of these fields.
+
+The commit message is formed by the title taken from the
+"Subject: ", a blank line and the body of the message up to
+where the patch begins. Excess whitespaces at the end of the
+lines are automatically stripped.
+
+The patch is expected to be inline, directly following the
+message. Any line that is of form:
+
+* three-dashes and end-of-line, or
+* a line that begins with "diff -", or
+* a line that begins with "Index: "
+
+is taken as the beginning of a patch, and the commit log message
+is terminated before the first occurrence of such a line.
+
When initially invoking it, you give it names of the mailboxes
to crunch. Upon seeing the first patch that does not apply, it
aborts in the middle, just like 'git-applymbox' does. You can
OPTIONS
-------
--l, --long::
- Show long rev (Defaults off).
-
--t, --time::
- Show raw timestamp (Defaults off).
-
--r, --rename::
- Follow renames (Defaults on).
-
--S, --rev-file <revs-file>::
- Use revs from revs-file instead of calling git-rev-list.
-
--h, --help::
- Show help message.
+include::blame-options.txt[]
SEE ALSO
--------
SYNOPSIS
--------
[verse]
-'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply]
- [--no-add] [--index-info] [--allow-binary-replacement | --binary]
- [-R | --reverse] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof]
- [--whitespace=<nowarn|warn|error|error-all|strip>] [--exclude=PATH]
- [--cached] [--verbose] [<patch>...]
+'git-apply' [--stat] [--numstat] [--summary] [--check] [--index]
+ [--apply] [--no-add] [--index-info] [-R | --reverse]
+ [--allow-binary-replacement | --binary] [--reject] [-z]
+ [-pNUM] [-CNUM] [--inaccurate-eof] [--cached]
+ [--whitespace=<nowarn|warn|error|error-all|strip>]
+ [--exclude=PATH] [--verbose] [<patch>...]
DESCRIPTION
-----------
correctly. This option adds support for applying such patches by
working around this bug.
---verbose::
+-v, --verbose::
Report progress to stderr. By default, only a message about the
current patch being applied will be printed. This option will cause
additional information to be reported.
and the current tree.
-u::
- The commit log message, author name and author email are
- taken from the e-mail, and after minimally decoding MIME
- transfer encoding, re-coded in UTF-8 by transliterating
- them. This used to be optional but now it is the default.
+ Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
+ The proposed commit log message taken from the e-mail
+ are re-coded into UTF-8 encoding (configuration variable
+ `i18n.commitencoding` can be used to specify project's
+ preferred encoding if it is not UTF-8). This used to be
+ optional but now it is the default.
+
Note that the patch is always used as-is without charset
conversion, even with this flag.
+-n::
+ Pass `-n` flag to `git-mailinfo` (see
+ gitlink:git-mailinfo[1]).
+
-c .dotest/<num>::
When the patch contained in an e-mail does not cleanly
apply, the command exits with an error message. The
-------
--format=<fmt>::
- Format of the resulting archive: 'tar', 'zip'...
+ Format of the resulting archive: 'tar', 'zip'... The default
+ is 'tar'.
--list::
Show all available formats.
DESCRIPTION
-----------
-The command takes various subcommands, and different options
-depending on the subcommand:
+The command takes various subcommands, and different options depending
+on the subcommand:
- git bisect start [<paths>...]
+ git bisect start [<bad> [<good>...]] [--] [<paths>...]
git bisect bad <rev>
git bisect good <rev>
git bisect reset [<branch>]
git bisect visualize
git bisect replay <logfile>
git bisect log
+ git bisect run <cmd>...
-This command uses 'git-rev-list --bisect' option to help drive
-the binary search process to find which change introduced a bug,
-given an old "good" commit object name and a later "bad" commit
-object name.
+This command uses 'git-rev-list --bisect' option to help drive the
+binary search process to find which change introduced a bug, given an
+old "good" commit object name and a later "bad" commit object name.
+
+Basic bisect commands: start, bad, good
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The way you use it is:
------------------------------------------------
$ git bisect start
-$ git bisect bad # Current version is bad
-$ git bisect good v2.6.13-rc2 # v2.6.13-rc2 was the last version
- # tested that was good
+$ git bisect bad # Current version is bad
+$ git bisect good v2.6.13-rc2 # v2.6.13-rc2 was the last version
+ # tested that was good
------------------------------------------------
-When you give at least one bad and one good versions, it will
-bisect the revision tree and say something like:
+When you give at least one bad and one good versions, it will bisect
+the revision tree and say something like:
------------------------------------------------
Bisecting: 675 revisions left to test after this
------------------------------------------------
-and check out the state in the middle. Now, compile that kernel, and boot
-it. Now, let's say that this booted kernel works fine, then just do
+and check out the state in the middle. Now, compile that kernel, and
+boot it. Now, let's say that this booted kernel works fine, then just
+do
------------------------------------------------
$ git bisect good # this one is good
Bisecting: 337 revisions left to test after this
------------------------------------------------
-and you continue along, compiling that one, testing it, and depending on
-whether it is good or bad, you say "git bisect good" or "git bisect bad",
-and ask for the next bisection.
+and you continue along, compiling that one, testing it, and depending
+on whether it is good or bad, you say "git bisect good" or "git bisect
+bad", and ask for the next bisection.
+
+Until you have no more left, and you'll have been left with the first
+bad kernel rev in "refs/bisect/bad".
-Until you have no more left, and you'll have been left with the first bad
-kernel rev in "refs/bisect/bad".
+Bisect reset
+~~~~~~~~~~~~
Oh, and then after you want to reset to the original head, do a
$ git bisect reset
------------------------------------------------
-to get back to the master branch, instead of being in one of the bisection
-branches ("git bisect start" will do that for you too, actually: it will
-reset the bisection state, and before it does that it checks that you're
-not using some old bisection branch).
+to get back to the master branch, instead of being in one of the
+bisection branches ("git bisect start" will do that for you too,
+actually: it will reset the bisection state, and before it does that
+it checks that you're not using some old bisection branch).
+
+Bisect visualize
+~~~~~~~~~~~~~~~~
During the bisection process, you can say
to see the currently remaining suspects in `gitk`.
-The good/bad input is logged, and `git bisect
-log` shows what you have done so far. You can truncate its
-output somewhere and save it in a file, and run
+Bisect log and bisect replay
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The good/bad input is logged, and
+
+------------
+$ git bisect log
+------------
+
+shows what you have done so far. You can truncate its output somewhere
+and save it in a file, and run
------------
$ git bisect replay that-file
if you find later you made a mistake telling good/bad about a
revision.
-If in a middle of bisect session, you know what the bisect
-suggested to try next is not a good one to test (e.g. the change
-the commit introduces is known not to work in your environment
-and you know it does not have anything to do with the bug you
-are chasing), you may want to find a near-by commit and try that
-instead. It goes something like this:
+Avoiding to test a commit
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If in a middle of bisect session, you know what the bisect suggested
+to try next is not a good one to test (e.g. the change the commit
+introduces is known not to work in your environment and you know it
+does not have anything to do with the bug you are chasing), you may
+want to find a near-by commit and try that instead.
+
+It goes something like this:
------------
$ git bisect good/bad # previous round was good/bad.
# was suggested
------------
-Then compile and test the one you chose to try. After that,
-tell bisect what the result was as usual.
+Then compile and test the one you chose to try. After that, tell
+bisect what the result was as usual.
+
+Cutting down bisection by giving more parameters to bisect start
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can further cut down the number of trials if you know what part of
+the tree is involved in the problem you are tracking down, by giving
+paths parameters when you say `bisect start`, like this:
+
+------------
+$ git bisect start -- arch/i386 include/asm-i386
+------------
+
+If you know beforehand more than one good commits, you can narrow the
+bisect space down without doing the whole tree checkout every time you
+give good commits. You give the bad revision immediately after `start`
+and then you give all the good revisions you have:
+
+------------
+$ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 --
+ # v2.6.20-rc6 is bad
+ # v2.6.20-rc4 and v2.6.20-rc1 are good
+------------
+
+Bisect run
+~~~~~~~~~~
-You can further cut down the number of trials if you know what
-part of the tree is involved in the problem you are tracking
-down, by giving paths parameters when you say `bisect start`,
-like this:
+If you have a script that can tell if the current source code is good
+or bad, you can automatically bisect using:
------------
-$ git bisect start arch/i386 include/asm-i386
+$ git bisect run my_script
------------
+Note that the "run" script (`my_script` in the above example) should
+exit with code 0 in case the current source code is good and with a
+code between 1 and 127 (included) in case the current source code is
+bad.
+
+Any other exit code will abort the automatic bisect process. (A
+program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
+the value is chopped with "& 0377".)
+
+You may often find that during bisect you want to have near-constant
+tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
+"revision that does not have this commit needs this patch applied to
+work around other problem this bisection is not interested in")
+applied to the revision being tested.
+
+To cope with such a situation, after the inner git-bisect finds the
+next revision to test, with the "run" script, you can apply that tweak
+before compiling, run the real test, and after the test decides if the
+revision (possibly with the needed tweaks) passed the test, rewind the
+tree to the pristine state. Finally the "run" script can exit with
+the status of the real test to let "git bisect run" command loop to
+know the outcome.
Author
------
SYNOPSIS
--------
[verse]
-'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] [-S <revs-file>]
- [-M] [-C] [-C] [--since=<date>] [<rev> | --contents <file>] [--] <file>
+'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m]
+ [-S <revs-file>] [-M] [-C] [-C] [--since=<date>]
+ [<rev> | --contents <file>] [--] <file>
DESCRIPTION
-----------
OPTIONS
-------
--c, --compatibility::
- Use the same output mode as gitlink:git-annotate[1] (Default: off).
-
--L n,m::
- Annotate only the specified line range (lines count from 1).
-
--l, --long::
- Show long rev (Default: off).
+include::blame-options.txt[]
--t, --time::
- Show raw timestamp (Default: off).
+-c::
+ Use the same output mode as gitlink:git-annotate[1] (Default: off).
--S, --rev-file <revs-file>::
- Use revs from revs-file instead of calling gitlink:git-rev-list[1].
+--score-debug::
+ Include debugging information related to the movement of
+ lines between files (see `-C`) and lines moved within a
+ file (see `-M`). The first number listed is the score.
+ This is the number of alphanumeric characters detected
+ to be moved between or within files. This must be above
+ a certain threshold for git-blame to consider those lines
+ of code to have been moved.
-f, --show-name::
Show filename in the original commit. By default
-n, --show-number::
Show line number in the original commit (Default: off).
--p, --porcelain::
- Show in a format designed for machine consumption.
-
---incremental::
- Show the result incrementally in a format designed for
- machine consumption.
-
---contents <file>::
- When <rev> is not specified, the command annotates the
- changes starting backwards from the working tree copy.
- This flag makes the command pretend as if the working
- tree copy has the contents of he named file (specify
- `-` to make the command read from the standard input).
-
--M::
- Detect moving lines in the file as well. When a commit
- moves a block of lines in a file (e.g. the original file
- has A and then B, and the commit changes it to B and
- then A), traditional 'blame' algorithm typically blames
- the lines that were moved up (i.e. B) to the parent and
- assigns blame to the lines that were moved down (i.e. A)
- to the child commit. With this option, both groups of
- lines are blamed on the parent.
-
--C::
- In addition to `-M`, detect lines copied from other
- files that were modified in the same commit. This is
- useful when you reorganize your program and move code
- around across files. When this option is given twice,
- the command looks for copies from all other files in the
- parent for the commit that creates the file in addition.
-
--h, --help::
- Show help message.
-
-
THE PORCELAIN FORMAT
--------------------
When <paths> are given, this command does *not* switch
branches. It updates the named paths in the working tree from
-the index file (i.e. it runs `git-checkout-index -f -u`), or a
-named commit. In
-this case, `-f` and `-b` options are meaningless and giving
+the index file (i.e. it runs `git-checkout-index -f -u`), or
+from a named commit. In
+this case, the `-f` and `-b` options are meaningless and giving
either of them results in an error. <tree-ish> argument can be
used to specify a specific tree-ish (i.e. commit, tag or tree)
to update the index for the given paths before updating the
Quiet, supress feedback messages.
-f::
- Force a re-read of everything.
+ Proceed even if the index or the working tree differs
+ from HEAD. This is used to throw away local changes.
-b::
Create a new branch named <new_branch> and start it at
--track::
When -b is given and a branch is created off a remote branch,
- setup so that git-pull will automatically retrieve data from
- the remote branch.
+ set up configuration so that git-pull will automatically
+ retrieve data from the remote branch. Set the
+ branch.autosetupmerge configuration variable to true if you
+ want git-checkout and git-branch to always behave as if
+ '--track' were given.
--no-track::
When -b is given and a branch is created off a remote branch,
- force that git-pull will automatically retrieve data from
- the remote branch independent of the configuration settings.
+ set up configuration so that git-pull will not retrieve data
+ from the remote branch, ignoring the branch.autosetupmerge
+ configuration variable.
-l::
Create the new branch's ref log. This activates recording of
SYNOPSIS
--------
[verse]
-'git-cvsimport' [-o <branch-for-HEAD>] [-h] [-v] [-d <CVSROOT>] [-s <subst>]
- [-p <options-for-cvsps>] [-C <git_repository>] [-i] [-P <file>]
- [-m] [-M regex] [<CVS_module>]
+'git-cvsimport' [-o <branch-for-HEAD>] [-h] [-v] [-d <CVSROOT>]
+ [-A <author-conv-file>] [-p <options-for-cvsps>] [-P <file>]
+ [-C <git_repository>] [-z <fuzz>] [-i] [-k] [-u] [-s <subst>]
+ [-a] [-m] [-M <regex>] [-S <regex>] [-L <commitlimit>]
+ [<CVS_module>]
DESCRIPTION
OPTIONS
-------
+-v::
+ Verbosity: let 'cvsimport' report what it is doing.
+
-d <CVSROOT>::
The root of the CVS archive. May be local (a simple path) or remote;
currently, only the :local:, :ext: and :pserver: access methods
- are supported.
+ are supported. If not given, git-cvsimport will try to read it
+ from `CVS/Root`. If no such file exists, it checks for the
+ `CVSROOT` environment variable.
+
+<CVS_module>::
+ The CVS module you want to import. Relative to <CVSROOT>.
+ If not given, git-cvsimport tries to read it from
+ `CVS/Repository`.
-C <target-dir>::
The git repository to import to. If the directory doesn't
exist, it will be created. Default is the current directory.
+-o <branch-for-HEAD>::
+ The 'HEAD' branch from CVS is imported to the 'origin' branch within
+ the git repository, as 'HEAD' already has a special meaning for git.
+ Use this option if you want to import into a different branch.
++
+Use '-o master' for continuing an import that was initially done by
+the old cvs2git tool.
+
-i::
Import-only: don't perform a checkout after importing. This option
ensures the working directory and index remain untouched and will
not create them if they do not exist.
-k::
- Kill keywords: will extract files with -kk from the CVS archive
+ Kill keywords: will extract files with '-kk' from the CVS archive
to avoid noisy changesets. Highly recommended, but off by default
to preserve compatibility with early imported trees.
-u::
Convert underscores in tag and branch names to dots.
--o <branch-for-HEAD>::
- The 'HEAD' branch from CVS is imported to the 'origin' branch within
- the git repository, as 'HEAD' already has a special meaning for git.
- Use this option if you want to import into a different branch.
-+
-Use '-o master' for continuing an import that was initially done by
-the old cvs2git tool.
+-s <subst>::
+ Substitute the character "/" in branch names with <subst>
-p <options-for-cvsps>::
Additional options for cvsps.
+
If you need to pass multiple options, separate them with a comma.
+-z <fuzz>::
+ Pass the timestamp fuzz factor to cvsps, in seconds. If unset,
+ cvsps defaults to 300s.
+
-P <cvsps-output-file>::
Instead of calling cvsps, read the provided cvsps output file. Useful
for debugging or when cvsps is being handled outside cvsimport.
-M <regex>::
Attempt to detect merges based on the commit message with a custom
- regex. It can be used with -m to also see the default regexes.
+ regex. It can be used with '-m' to also see the default regexes.
You must escape forward slashes.
--v::
- Verbosity: let 'cvsimport' report what it is doing.
-
-<CVS_module>::
- The CVS module you want to import. Relative to <CVSROOT>.
-
--h::
- Print a short usage message and exit.
-
--z <fuzz>::
- Pass the timestamp fuzz factor to cvsps, in seconds. If unset,
- cvsps defaults to 300s.
-
--s <subst>::
- Substitute the character "/" in branch names with <subst>
+-S <regex>::
+ Skip paths matching the regex.
-a::
Import all commits, including recent ones. cvsimport by default
skips commits that have a timestamp less than 10 minutes ago.
--S <regex>::
- Skip paths matching the regex.
-
-L <limit>::
Limit the number of commits imported. Workaround for cases where
cvsimport leaks memory.
their GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL set properly
all along.
+
-For convenience, this data is saved to $GIT_DIR/cvs-authors
-each time the -A option is provided and read from that same
+For convenience, this data is saved to `$GIT_DIR/cvs-authors`
+each time the '-A' option is provided and read from that same
file each time git-cvsimport is run.
+
It is not recommended to use this feature if you intend to
export changes back to CVS again later with
gitlink:git-cvsexportcommit[1].
+-h::
+ Print a short usage message and exit.
+
OUTPUT
------
If '-v' is specified, the script reports what it is doing.
Protocol notes: If you are using anonymous access via pserver, just select that.
Those using SSH access should choose the 'ext' protocol, and configure 'ext'
access on the Preferences->Team->CVS->ExtConnection pane. Set CVS_SERVER to
-'git-cvsserver'. Not that password support is not good when using 'ext',
+'git-cvsserver'. Note that password support is not good when using 'ext',
you will definitely want to have SSH keys setup.
Alternatively, you can just use the non-standard extssh protocol that Eclipse
offer. In that case CVS_SERVER is ignored, and you will have to replace
-the cvs utility on the server with git-cvsserver or manipulate your .bashrc
+the cvs utility on the server with git-cvsserver or manipulate your `.bashrc`
so that calling 'cvs' effectively calls git-cvsserver.
Clients known to work
Legacy monitoring operations are not supported (edit, watch and related).
Exports and tagging (tags and branches) are not supported at this stage.
-The server will set the -k mode to binary when relevant. In proper GIT
-tradition, the contents of the files are always respected.
-No keyword expansion or newline munging is supported.
+The server should set the '-k' mode to binary when relevant, however,
+this is not really implemented yet. For now, you can force the server
+to set '-kb' for all files by setting the `gitcvs.allbinary` config
+variable. In proper GIT tradition, the contents of the files are
+always respected. No keyword expansion or newline munging is supported.
Dependencies
------------
--------
[verse]
'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--thread]
- [--attach[=<boundary>] | --inline[=<boundary>]]
- [-s | --signoff] [<common diff options>] [--start-number <n>]
- [--in-reply-to=Message-Id] [--suffix=.<sfx>]
- [--ignore-if-in-upstream]
- <since>[..<until>]
+ [--attach[=<boundary>] | --inline[=<boundary>]]
+ [-s | --signoff] [<common diff options>] [--start-number <n>]
+ [--in-reply-to=Message-Id] [--suffix=.<sfx>]
+ [--ignore-if-in-upstream]
+ [--subject-prefix=Subject-Prefix]
+ <since>[..<until>]
DESCRIPTION
-----------
patches being generated, and any patch that matches is
ignored.
+--subject-prefix=<Subject-Prefix>::
+ Instead of the standard '[PATCH]' prefix in the subject
+ line, instead use '[<Subject-Prefix>]'. This
+ allows for useful naming of a patch series, and can be
+ combined with the --numbered option.
+
--suffix=.<sfx>::
Instead of using `.patch` as the suffix for generated
filenames, use specifed suffix. A common alternative is
SYNOPSIS
--------
[verse]
-'git-fsck' [--tags] [--root] [--unreachable] [--cache]
+'git-fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
[--full] [--strict] [<object>*]
DESCRIPTION
Consider any object recorded in the index also as a head node for
an unreachability trace.
+--no-reflogs::
+ Do not consider commits that are referenced only by an
+ entry in a reflog to be reachable. This option is meant
+ only to search for commits that used to be in a ref, but
+ now aren't, but are still in that corresponding reflog.
+
--full::
Check not just objects in GIT_OBJECT_DIRECTORY
($GIT_DIR/objects), but also the ones found in alternate
and <until>, see "SPECIFYING REVISIONS" section in
gitlink:git-rev-parse[1].
+--first-parent::
+ Follow only the first parent commit upon seeing a merge
+ commit. This option gives a better overview of the
+ evolution of a particular branch.
+
-p::
Show the change the commit introduces in a patch form.
+-g, \--walk-reflogs::
+ Show commits as they were recorded in the reflog. The log contains
+ a record about how the tip of a reference was changed.
+ See also gitlink:git-reflog[1].
+
<paths>...::
Show only commits that affect the specified paths.
DESCRIPTION
-----------
Finds dangling commits and tags from the object database, and
-creates refs to them in .git/lost-found/ directory. Commits and
-tags that dereference to commits go to .git/lost-found/commit
-and others are stored in .git/lost-found/other directory.
+creates refs to them in the .git/lost-found/ directory. Commits and
+tags that dereference to commits are stored in .git/lost-found/commit,
+and other objects are stored in .git/lost-found/other.
OUTPUT
------
-One line description from the commit and tag found along with
-their object name are printed on the standard output.
-
+Prints to standard output the object names and one-line descriptions
+of any commits or tags found.
EXAMPLE
-------
-Suppose you run 'git tag -f' and mistyped the tag to overwrite.
+Suppose you run 'git tag -f' and mistype the tag to overwrite.
The ref to your tag is overwritten, but until you run 'git
-prune', it is still there.
+prune', the tag itself is still there.
------------
$ git lost-found
...
------------
-Also you can use gitk to browse how they relate to each other
-and existing (probably old) tags.
+Also you can use gitk to browse how any tags found relate to each
+other.
------------
$ gitk $(cd .git/lost-found/commit && echo ??*)
------------
-After making sure that it is the object you are looking for, you
-can reconnect it to your regular .git/refs hierarchy.
+After making sure you know which the object is the tag you are looking
+for, you can reconnect it to your regular .git/refs hierarchy.
------------
$ git cat-file -t 1ef2b196
Use the merge resolution program specified by <tool>.
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, emerge, and vimdiff.
-
- If a merge resolution program is not specified, 'git mergetool'
- will use the configuration variable merge.tool. If the
- configuration variable merge.tool is not set, 'git mergetool'
- will pick a suitable default.
++
+If a merge resolution program is not specified, 'git mergetool'
+will use the configuration variable merge.tool. If the
+configuration variable merge.tool is not set, 'git mergetool'
+will pick a suitable default.
Author
------
SYNOPSIS
--------
-'git-read-tree' (<tree-ish> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
+'git-read-tree' (<tree-ish> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
DESCRIPTION
file (usually '.gitignore') and allows such an untracked
but explicitly ignored file to be overwritten.
+--index-output=<file>::
+ Instead of writing the results out to `$GIT_INDEX_FILE`,
+ write the resulting index in the named file. While the
+ command is operating, the original index file is locked
+ with the same mechanism as usual. The file must allow
+ to be rename(2)ed into from a temporary file that is
+ created next to the usual index file; typically this
+ means it needs to be on the same filesystem as the index
+ file itself, and you need write permission to the
+ directories the index file and index output file are
+ located in.
+
<tree-ish#>::
The id of the tree object(s) to be read/merged.
[ \--stdin ]
[ \--topo-order ]
[ \--parents ]
+ [ \--left-right ]
+ [ \--cherry-pick ]
[ \--encoding[=<encoding>] ]
[ \--(author|committer|grep)=<pattern> ]
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
[ \--pretty | \--header ]
[ \--bisect ]
+ [ \--bisect-vars ]
[ \--merge ]
[ \--reverse ]
[ \--walk-reflogs ]
Print the parents of the commit.
+--left-right::
+
+ Mark which side of a symmetric diff a commit is reachable from.
+ Commits from the left side are prefixed with `<` and those from
+ the right with `>`. If combined with `--boundary`, those
+ commits are prefixed with `-`.
++
+For example, if you have this topology:
++
+-----------------------------------------------------------------------
+ y---b---b branch B
+ / \ /
+ / .
+ / / \
+ o---x---a---a branch A
+-----------------------------------------------------------------------
++
+you would get an output line this:
++
+-----------------------------------------------------------------------
+ $ git rev-list --left-right --boundary --pretty=oneline A...B
+
+ >bbbbbbb... 3rd on b
+ >bbbbbbb... 2nd on b
+ <aaaaaaa... 3rd on a
+ <aaaaaaa... 2nd on a
+ -yyyyyyy... 1st on b
+ -xxxxxxx... 1st on a
+-----------------------------------------------------------------------
+
Diff Formatting
~~~~~~~~~~~~~~~
In addition to the '<commit>' listed on the command
line, read them from the standard input.
+--cherry-pick::
+
+ Omit any commit that introduces the same change as
+ another commit on the "other side" when the set of
+ commits are limited with symmetric difference.
++
+For example, if you have two branches, `A` and `B`, a usual way
+to list all commits on only one side of them is with
+`--left-right`, like the example above in the description of
+that option. It however shows the commits that were cherry-picked
+from the other branch (for example, "3rd on b" may be cherry-picked
+from branch A). With this option, such pairs of commits are
+excluded from the output.
+
-g, --walk-reflogs::
Instead of walking the commit ancestry chain, walk
generate and test new 'midpoint's until the commit chain is of length
one.
+--bisect-vars::
+
+This calculates the same as `--bisect`, but outputs text ready
+to be eval'ed by the shell. These lines will assign the name of
+the midpoint revision to the variable `bisect_rev`, and the
+expected number of commits to be tested after `bisect_rev` is
+tested to `bisect_nr`, the expected number of commits to be
+tested if `bisect_rev` turns out to be good to `bisect_good`,
+the expected number of commits to be tested if `bisect_rev`
+turns out to be bad to `bisect_bad`, and the number of commits
+we are bisecting right now to `bisect_all`.
+
--
Commit Ordering
Here are a handful examples:
- D A B D
- D F A B C D F
- ^A G B D
- ^A F B C F
- G...I C D F G I
- ^B G I C D F G I
- F^@ A B C
- F^! H D F H
+ D G H D
+ D F G H I J D F
+ ^G D H D
+ ^D B E I J F B
+ B...C G H D E B C
+ ^D B C E I J F B C
+ C^@ I J F
+ F^! D G H D F
Author
------
SYNOPSIS
--------
-'git-rm' [-f] [-n] [-r] [--cached] [--] <file>...
+'git-rm' [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
DESCRIPTION
-----------
the paths only from the index, leaving working tree
files.
+\--ignore-unmatch::
+ Exit with a zero status even if no files matched.
+
+\--quiet::
+ git-rm normally outputs one line (in the form of an "rm" command)
+ for each file removed. This option suppresses that output.
+
DISCUSSION
----------
[verse]
'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
[-s|--hash] [--abbrev] [--tags] [--heads] [--] <pattern>...
+'git-show-ref' --exclude-existing[=pattern]
DESCRIPTION
-----------
dereferenced into object IDs. Additionally, it can be used to test whether a
particular ref exists.
+The --exclude-existing form is a filter that does the inverse, it shows the
+refs from stdin that don't exist in the local repository.
+
Use of this utility is encouraged in favor of directly accessing files under
in the `.git` directory.
Do not print any results to stdout. When combined with '--verify' this
can be used to silently check if a reference exists.
+--exclude-existing, --exclude-existing=pattern::
+
+ Make git-show-ref act as a filter that reads refs from stdin of the
+ form "^(?:<anything>\s)?<refname>(?:\^\{\})?$" and performs the
+ following actions on each:
+ (1) strip "^{}" at the end of line if any;
+ (2) ignore if pattern is provided and does not head-match refname;
+ (3) warn if refname is not a well-formed refname and skip;
+ (4) ignore if refname is a ref that exists in the local repository;
+ (5) otherwise output the line.
+
+
<pattern>::
Show references matching one or more patterns.
SVN access is done by the SVN::Perl module.
git-svnimport assumes that SVN repositories are organized into one
-"trunk" directory where the main development happens, "branch/FOO"
+"trunk" directory where the main development happens, "branches/FOO"
directories for branches, and "/tags/FOO" directories for tags.
Other subdirectories are ignored.
You are reading the documentation for the latest version of git.
Documentation for older releases are available here:
-* link:v1.5.0.5/git.html[documentation for release 1.5.0.5]
+* link:RelNotes-1.5.1.txt[release notes for 1.5.1]
-* link:v1.5.0.5/RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
+* link:v1.5.0.7/git.html[documentation for release 1.5.0.7]
-* link:v1.5.0.3/RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
+* link:RelNotes-1.5.0.7.txt[release notes for 1.5.0.7]
-* link:v1.5.0.2/RelNotes-1.5.0.2.txt[release notes for 1.5.0.2]
+* link:RelNotes-1.5.0.6.txt[release notes for 1.5.0.6]
-* link:v1.5.0.1/RelNotes-1.5.0.1.txt[release notes for 1.5.0.1]
+* link:RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
-* link:v1.5.0/RelNotes-1.5.0.txt[release notes for 1.5.0]
+* link:RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
+
+* link:RelNotes-1.5.0.2.txt[release notes for 1.5.0.2]
+
+* link:RelNotes-1.5.0.1.txt[release notes for 1.5.0.1]
+
+* link:RelNotes-1.5.0.txt[release notes for 1.5.0]
* link:v1.4.4.4/git.html[documentation for release 1.4.4.4]
-alternate object database::
- Via the alternates mechanism, a repository can inherit part of its
- object database from another object database, which is called
- "alternate".
-
-bare repository::
- A bare repository is normally an appropriately named
- directory with a `.git` suffix that does not have a
- locally checked-out copy of any of the files under revision
- control. That is, all of the `git` administrative and
- control files that would normally be present in the
- hidden `.git` sub-directory are directly present in
- the `repository.git` directory instead, and no other files
- are present and checked out. Usually publishers of public
- repositories make bare repositories available.
-
-blob object::
- Untyped object, e.g. the contents of a file.
-
-branch::
- A non-cyclical graph of revisions, i.e. the complete history of
- a particular revision, which is called the branch head. The
- branch heads are stored in `$GIT_DIR/refs/heads/`.
-
-cache::
- Obsolete for: index.
-
-chain::
- A list of objects, where each object in the list contains a
- reference to its successor (for example, the successor of a commit
- could be one of its parents).
-
-changeset::
- BitKeeper/cvsps speak for "commit". Since git does not store
- changes, but states, it really does not make sense to use
- the term "changesets" with git.
-
-checkout::
- The action of updating the working tree to a revision which was
- stored in the object database.
-
-cherry-picking::
- In SCM jargon, "cherry pick" means to choose a subset of
- changes out of a series of changes (typically commits)
- and record them as a new series of changes on top of
- different codebase. In GIT, this is performed by
- "git cherry-pick" command to extract the change
- introduced by an existing commit and to record it based
- on the tip of the current branch as a new commit.
-
-clean::
- A working tree is clean, if it corresponds to the revision
- referenced by the current head. Also see "dirty".
-
-commit::
- As a verb: The action of storing the current state of the index in the
- object database. The result is a revision.
- As a noun: Short hand for commit object.
-
-commit object::
- An object which contains the information about a particular
- revision, such as parents, committer, author, date and the
- tree object which corresponds to the top directory of the
- stored revision.
-
-core git::
- Fundamental data structures and utilities of git. Exposes only
- limited source code management tools.
-
-DAG::
- Directed acyclic graph. The commit objects form a directed acyclic
- graph, because they have parents (directed), and the graph of commit
- objects is acyclic (there is no chain which begins and ends with the
- same object).
-
-dangling object::
- An unreachable object which is not reachable even from other
- unreachable objects; a dangling object has no references to it
- from any reference or object in the repository.
-
-dircache::
+GIT Glossary
+============
+
+[[def_alternate_object_database]]alternate object database::
+ Via the alternates mechanism, a <<def_repository,repository>> can
+ inherit part of its <<def_object_database,object database>> from another
+ <<def_object_database,object database>>, which is called "alternate".
+
+[[def_bare_repository]]bare repository::
+ A <<def_bare_repository,bare repository>> is normally an appropriately
+ named <<def_directory,directory>> with a `.git` suffix that does not
+ have a locally checked-out copy of any of the files under
+ <<def_revision,revision>> control. That is, all of the `git`
+ administrative and control files that would normally be present in the
+ hidden `.git` sub-directory are directly present in the
+ `repository.git` directory instead,
+ and no other files are present and checked out. Usually publishers of
+ public repositories make bare repositories available.
+
+[[def_blob_object]]blob object::
+ Untyped <<def_object,object>>, e.g. the contents of a file.
+
+[[def_branch]]branch::
+ A non-cyclical graph of revisions, i.e. the complete history of a
+ particular <<def_revision,revision>>, which is called the
+ branch <<def_head,head>>. The heads
+ are stored in `$GIT_DIR/refs/heads/`.
+
+[[def_cache]]cache::
+ Obsolete for: <<def_index,index>>.
+
+[[def_chain]]chain::
+ A list of objects, where each <<def_object,object>> in the list contains
+ a reference to its successor (for example, the successor of a
+ <<def_commit,commit>> could be one of its parents).
+
+[[def_changeset]]changeset::
+ BitKeeper/cvsps speak for "<<def_commit,commit>>". Since git does not
+ store changes, but states, it really does not make sense to use the term
+ "changesets" with git.
+
+[[def_checkout]]checkout::
+ The action of updating the <<def_working_tree,working tree>> to a
+ <<def_revision,revision>> which was stored in the
+ <<def_object_database,object database>>.
+
+[[def_cherry-picking]]cherry-picking::
+ In <<def_SCM,SCM>> jargon, "cherry pick" means to choose a subset of
+ changes out of a series of changes (typically commits) and record them
+ as a new series of changes on top of different codebase. In GIT, this is
+ performed by "git cherry-pick" command to extract the change introduced
+ by an existing <<def_commit,commit>> and to record it based on the tip
+ of the current <<def_branch,branch>> as a new <<def_commit,commit>>.
+
+[[def_clean]]clean::
+ A <<def_working_tree,working tree>> is <<def_clean,clean>>, if it
+ corresponds to the <<def_revision,revision>> referenced by the current
+ <<def_head,head>>. Also see "<<def_dirty,dirty>>".
+
+[[def_commit]]commit::
+ As a verb: The action of storing the current state of the
+ <<def_index,index>> in the <<def_object_database,object database>>. The
+ result is a <<def_revision,revision>>. As a noun: Short hand for
+ <<def_commit_object,commit object>>.
+
+[[def_commit_object]]commit object::
+ An <<def_object,object>> which contains the information about a
+ particular <<def_revision,revision>>, such as parents, committer,
+ author, date and the <<def_tree_object,tree object>> which corresponds
+ to the top <<def_directory,directory>> of the stored
+ <<def_revision,revision>>.
+
+[[def_core_git]]core git::
+ Fundamental data structures and utilities of git. Exposes only limited
+ source code management tools.
+
+[[def_DAG]]DAG::
+ Directed acyclic graph. The <<def_commit,commit>> objects form a
+ directed acyclic graph, because they have parents (directed), and the
+ graph of <<def_commit,commit>> objects is acyclic (there is no
+ <<def_chain,chain>> which begins and ends with the same
+ <<def_object,object>>).
+
+[[def_dangling_object]]dangling object::
+ An <<def_unreachable_object,unreachable object>> which is not
+ <<def_reachable,reachable>> even from other unreachable objects; a
+ <<def_dangling_object,dangling object>> has no references to it from any
+ reference or <<def_object,object>> in the <<def_repository,repository>>.
+
+[[def_dircache]]dircache::
You are *waaaaay* behind.
-dirty::
- A working tree is said to be dirty if it contains modifications
- which have not been committed to the current branch.
-
-directory::
+[[def_directory]]directory::
The list you get with "ls" :-)
-ent::
- Favorite synonym to "tree-ish" by some total geeks. See
+[[def_dirty]]dirty::
+ A <<def_working_tree,working tree>> is said to be <<def_dirty,dirty>> if
+ it contains modifications which have not been committed to the current
+ <<def_branch,branch>>.
+
+[[def_ent]]ent::
+ Favorite synonym to "<<def_tree-ish,tree-ish>>" by some total geeks. See
`http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
- explanation. Avoid this term, not to confuse people.
-
-fast forward::
- A fast-forward is a special type of merge where you have
- a revision and you are "merging" another branch's changes
- that happen to be a descendant of what you have.
- In such these cases, you do not make a new merge commit but
- instead just update to his revision. This will happen
- frequently on a tracking branch of a remote repository.
-
-fetch::
- Fetching a branch means to get the branch's head ref from a
- remote repository, to find out which objects are missing from
- the local object database, and to get them, too.
-
-file system::
- Linus Torvalds originally designed git to be a user space file
- system, i.e. the infrastructure to hold files and directories.
- That ensured the efficiency and speed of git.
-
-git archive::
- Synonym for repository (for arch people).
-
-grafts::
- Grafts enables two otherwise different lines of development to be
- joined together by recording fake ancestry information for commits.
- This way you can make git pretend the set of parents a commit
- has is different from what was recorded when the commit was created.
- Configured via the `.git/info/grafts` file.
-
-hash::
- In git's context, synonym to object name.
-
-head::
- The top of a branch. It contains a ref to the corresponding
- commit object.
-
-head ref::
- A ref pointing to a head. Often, this is abbreviated to "head".
- Head refs are stored in `$GIT_DIR/refs/heads/`.
-
-hook::
- During the normal execution of several git commands,
- call-outs are made to optional scripts that allow
- a developer to add functionality or checking.
- Typically, the hooks allow for a command to be pre-verified
- and potentially aborted, and allow for a post-notification
- after the operation is done.
- The hook scripts are found in the `$GIT_DIR/hooks/` directory,
- and are enabled by simply making them executable.
-
-index::
- A collection of files with stat information, whose contents are
- stored as objects. The index is a stored version of your working
- tree. Truth be told, it can also contain a second, and even a third
- version of a working tree, which are used when merging.
-
-index entry::
- The information regarding a particular file, stored in the index.
- An index entry can be unmerged, if a merge was started, but not
- yet finished (i.e. if the index contains multiple versions of
- that file).
-
-master::
- The default development branch. Whenever you create a git
- repository, a branch named "master" is created, and becomes
- the active branch. In most cases, this contains the local
+ explanation. Avoid this term, not to confuse people.
+
+[[def_fast_forward]]fast forward::
+ A fast-forward is a special type of <<def_merge,merge>> where you have a
+ <<def_revision,revision>> and you are "merging" another
+ <<def_branch,branch>>'s changes that happen to be a descendant of what
+ you have. In such these cases, you do not make a new <<def_merge,merge>>
+ <<def_commit,commit>> but instead just update to his
+ <<def_revision,revision>>. This will happen frequently on a
+ <<def_tracking_branch,tracking branch>> of a remote
+ <<def_repository,repository>>.
+
+[[def_fetch]]fetch::
+ Fetching a <<def_branch,branch>> means to get the
+ <<def_branch,branch>>'s <<def_head_ref,head ref>> from a remote
+ <<def_repository,repository>>, to find out which objects are missing
+ from the local <<def_object_database,object database>>, and to get them,
+ too.
+
+[[def_file_system]]file system::
+ Linus Torvalds originally designed git to be a user space file system,
+ i.e. the infrastructure to hold files and directories. That ensured the
+ efficiency and speed of git.
+
+[[def_git_archive]]git archive::
+ Synonym for <<def_repository,repository>> (for arch people).
+
+[[def_grafts]]grafts::
+ Grafts enables two otherwise different lines of development to be joined
+ together by recording fake ancestry information for commits. This way
+ you can make git pretend the set of parents a <<def_commit,commit>> has
+ is different from what was recorded when the <<def_commit,commit>> was
+ created. Configured via the `.git/info/grafts` file.
+
+[[def_hash]]hash::
+ In git's context, synonym to <<def_object_name,object name>>.
+
+[[def_head]]head::
+ The top of a <<def_branch,branch>>. It contains a <<def_ref,ref>> to the
+ corresponding <<def_commit_object,commit object>>.
+
+[[def_head_ref]]head ref::
+ A <<def_ref,ref>> pointing to a <<def_head,head>>. Often, this is
+ abbreviated to "<<def_head,head>>". Head refs are stored in
+ `$GIT_DIR/refs/heads/`.
+
+[[def_hook]]hook::
+ During the normal execution of several git commands, call-outs are made
+ to optional scripts that allow a developer to add functionality or
+ checking. Typically, the hooks allow for a command to be pre-verified
+ and potentially aborted, and allow for a post-notification after the
+ operation is done. The <<def_hook,hook>> scripts are found in the
+ `$GIT_DIR/hooks/` <<def_directory,directory>>, and are enabled by simply
+ making them executable.
+
+[[def_index]]index::
+ A collection of files with stat information, whose contents are stored
+ as objects. The <<def_index,index>> is a stored version of your working
+ <<def_tree,tree>>. Truth be told, it can also contain a second, and even
+ a third version of a <<def_working_tree,working tree>>, which are used
+ when merging.
+
+[[def_index_entry]]index entry::
+ The information regarding a particular file, stored in the
+ <<def_index,index>>. An <<def_index_entry,index entry>> can be unmerged,
+ if a <<def_merge,merge>> was started, but not yet finished (i.e. if the
+ <<def_index,index>> contains multiple versions of that file).
+
+[[def_master]]master::
+ The default development <<def_branch,branch>>. Whenever you create a git
+ <<def_repository,repository>>, a <<def_branch,branch>> named
+ "<<def_master,master>>" is created, and becomes the active
+ <<def_branch,branch>>. In most cases, this contains the local
development, though that is purely conventional and not required.
-merge::
- To merge branches means to try to accumulate the changes since a
- common ancestor and apply them to the first branch. An automatic
- merge uses heuristics to accomplish that. Evidently, an automatic
- merge can fail.
-
-object::
- The unit of storage in git. It is uniquely identified by
- the SHA1 of its contents. Consequently, an object can not
- be changed.
-
-object database::
- Stores a set of "objects", and an individual object is identified
- by its object name. The objects usually live in `$GIT_DIR/objects/`.
-
-object identifier::
- Synonym for object name.
-
-object name::
- The unique identifier of an object. The hash of the object's contents
- using the Secure Hash Algorithm 1 and usually represented by the 40
- character hexadecimal encoding of the hash of the object (possibly
- followed by a white space).
-
-object type::
- One of the identifiers "commit","tree","tag" and "blob" describing
- the type of an object.
-
-octopus::
- To merge more than two branches. Also denotes an intelligent
- predator.
-
-origin::
- The default upstream repository. Most projects have at
- least one upstream project which they track. By default
- 'origin' is used for that purpose. New upstream updates
+[[def_merge]]merge::
+ To <<def_merge,merge>> branches means to try to accumulate the changes
+ since a common ancestor and apply them to the first
+ <<def_branch,branch>>. An automatic <<def_merge,merge>> uses heuristics
+ to accomplish that. Evidently, an automatic <<def_merge,merge>> can
+ fail.
+
+[[def_object]]object::
+ The unit of storage in git. It is uniquely identified by the
+ <<def_SHA1,SHA1>> of its contents. Consequently, an
+ <<def_object,object>> can not be changed.
+
+[[def_object_database]]object database::
+ Stores a set of "objects", and an individual <<def_object,object>> is
+ identified by its <<def_object_name,object name>>. The objects usually
+ live in `$GIT_DIR/objects/`.
+
+[[def_object_identifier]]object identifier::
+ Synonym for <<def_object_name,object name>>.
+
+[[def_object_name]]object name::
+ The unique identifier of an <<def_object,object>>. The <<def_hash,hash>>
+ of the <<def_object,object>>'s contents using the Secure Hash Algorithm
+ 1 and usually represented by the 40 character hexadecimal encoding of
+ the <<def_hash,hash>> of the <<def_object,object>> (possibly followed by
+ a white space).
+
+[[def_object_type]]object type::
+ One of the identifiers
+ "<<def_commit,commit>>","<<def_tree,tree>>","<<def_tag,tag>>" or "<<def_blob_object,blob>>"
+ describing the type of an <<def_object,object>>.
+
+[[def_octopus]]octopus::
+ To <<def_merge,merge>> more than two branches. Also denotes an
+ intelligent predator.
+
+[[def_origin]]origin::
+ The default upstream <<def_repository,repository>>. Most projects have
+ at least one upstream project which they track. By default
+ '<<def_origin,origin>>' is used for that purpose. New upstream updates
will be fetched into remote tracking branches named
origin/name-of-upstream-branch, which you can see using
- "git branch -r".
+ "git <<def_branch,branch>> -r".
-pack::
- A set of objects which have been compressed into one file (to save
- space or to transmit them efficiently).
+[[def_pack]]pack::
+ A set of objects which have been compressed into one file (to save space
+ or to transmit them efficiently).
-pack index::
+[[def_pack_index]]pack index::
The list of identifiers, and other information, of the objects in a
- pack, to assist in efficiently accessing the contents of a pack.
-
-parent::
- A commit object contains a (possibly empty) list of the logical
- predecessor(s) in the line of development, i.e. its parents.
-
-pickaxe::
- The term pickaxe refers to an option to the diffcore routines
- that help select changes that add or delete a given text string.
- With the --pickaxe-all option, it can be used to view the
- full changeset that introduced or removed, say, a particular
- line of text. See gitlink:git-diff[1].
-
-plumbing::
- Cute name for core git.
-
-porcelain::
- Cute name for programs and program suites depending on core git,
- presenting a high level access to core git. Porcelains expose
- more of a SCM interface than the plumbing.
-
-pull::
- Pulling a branch means to fetch it and merge it.
-
-push::
- Pushing a branch means to get the branch's head ref from a remote
- repository, find out if it is an ancestor to the branch's local
- head ref is a direct, and in that case, putting all objects, which
- are reachable from the local head ref, and which are missing from
- the remote repository, into the remote object database, and updating
- the remote head ref. If the remote head is not an ancestor to the
- local head, the push fails.
-
-reachable::
- All of the ancestors of a given commit are said to be reachable from
- that commit. More generally, one object is reachable from another if
- we can reach the one from the other by a chain that follows tags to
- whatever they tag, commits to their parents or trees, and trees to the
- trees or blobs that they contain.
-
-rebase::
- To clean a branch by starting from the head of the main line of
- development ("master"), and reapply the (possibly cherry-picked)
- changes from that branch.
-
-ref::
- A 40-byte hex representation of a SHA1 or a name that denotes
- a particular object. These may be stored in `$GIT_DIR/refs/`.
-
-refspec::
- A refspec is used by fetch and push to describe the mapping
- between remote ref and local ref. They are combined with
- a colon in the format <src>:<dst>, preceded by an optional
- plus sign, +. For example:
- `git fetch $URL refs/heads/master:refs/heads/origin`
- means "grab the master branch head from the $URL and store
- it as my origin branch head".
- And `git push $URL refs/heads/master:refs/heads/to-upstream`
- means "publish my master branch head as to-upstream branch
- at $URL". See also gitlink:git-push[1]
-
-repository::
- A collection of refs together with an object database containing
- all objects, which are reachable from the refs, possibly accompanied
- by meta data from one or more porcelains. A repository can
- share an object database with other repositories.
-
-resolve::
- The action of fixing up manually what a failed automatic merge
- left behind.
-
-revision::
- A particular state of files and directories which was stored in
- the object database. It is referenced by a commit object.
-
-rewind::
- To throw away part of the development, i.e. to assign the head to
- an earlier revision.
-
-SCM::
+ <<def_pack,pack>>, to assist in efficiently accessing the contents of a
+ <<def_pack,pack>>.
+
+[[def_parent]]parent::
+ A <<def_commit_object,commit object>> contains a (possibly empty) list
+ of the logical predecessor(s) in the line of development, i.e. its
+ parents.
+
+[[def_pickaxe]]pickaxe::
+ The term <<def_pickaxe,pickaxe>> refers to an option to the diffcore
+ routines that help select changes that add or delete a given text
+ string. With the --pickaxe-all option, it can be used to view the full
+ <<def_changeset,changeset>> that introduced or removed, say, a
+ particular line of text. See gitlink:git-diff[1].
+
+[[def_plumbing]]plumbing::
+ Cute name for <<def_core_git,core git>>.
+
+[[def_porcelain]]porcelain::
+ Cute name for programs and program suites depending on
+ <<def_core_git,core git>>, presenting a high level access to
+ <<def_core_git,core git>>. Porcelains expose more of a <<def_SCM,SCM>>
+ interface than the <<def_plumbing,plumbing>>.
+
+[[def_pull]]pull::
+ Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
+ <<def_merge,merge>> it.
+
+[[def_push]]push::
+ Pushing a <<def_branch,branch>> means to get the <<def_branch,branch>>'s
+ <<def_head_ref,head ref>> from a remote <<def_repository,repository>>,
+ find out if it is an ancestor to the <<def_branch,branch>>'s local
+ <<def_head_ref,head ref>> is a direct, and in that case, putting all
+ objects, which are <<def_reachable,reachable>> from the local
+ <<def_head_ref,head ref>>, and which are missing from the remote
+ <<def_repository,repository>>, into the remote
+ <<def_object_database,object database>>, and updating the remote
+ <<def_head_ref,head ref>>. If the remote <<def_head,head>> is not an
+ ancestor to the local <<def_head,head>>, the <<def_push,push>> fails.
+
+[[def_reachable]]reachable::
+ All of the ancestors of a given <<def_commit,commit>> are said to be
+ <<def_reachable,reachable>> from that <<def_commit,commit>>. More
+ generally, one <<def_object,object>> is <<def_reachable,reachable>> from
+ another if we can reach the one from the other by a <<def_chain,chain>>
+ that follows <<def_tag,tags>> to whatever they tag,
+ <<def_commit_object,commits>> to their parents or trees, and
+ <<def_tree_object,trees>> to the trees or <<def_blob_object,blobs>>
+ that they contain.
+
+[[def_rebase]]rebase::
+ To reapply a series of changes from a <<def_branch,branch>> to a
+ different base, and reset the <<def_head,head>> of that branch
+ to the result.
+
+[[def_ref]]ref::
+ A 40-byte hex representation of a <<def_SHA1,SHA1>> or a name that
+ denotes a particular <<def_object,object>>. These may be stored in
+ `$GIT_DIR/refs/`.
+
+[[def_refspec]]refspec::
+ A <<def_refspec,refspec>> is used by <<def_fetch,fetch>> and
+ <<def_push,push>> to describe the mapping between remote <<def_ref,ref>>
+ and local <<def_ref,ref>>. They are combined with a colon in the format
+ <src>:<dst>, preceded by an optional plus sign, +. For example: `git
+ fetch $URL refs/heads/master:refs/heads/origin` means
+ "grab the master <<def_branch,branch>> <<def_head,head>>
+ from the $URL and store it as my origin
+ <<def_branch,branch>> <<def_head,head>>". And `git <<def_push,push>>
+ $URL refs/heads/master:refs/heads/to-upstream` means
+ "publish my master <<def_branch,branch>>
+ <<def_head,head>> as to-upstream <<def_branch,branch>> at $URL". See
+ also gitlink:git-push[1]
+
+[[def_repository]]repository::
+ A collection of refs together with an <<def_object_database,object
+ database>> containing all objects which are <<def_reachable,reachable>>
+ from the refs, possibly accompanied by meta data from one or more
+ porcelains. A <<def_repository,repository>> can share an
+ <<def_object_database,object database>> with other repositories.
+
+[[def_resolve]]resolve::
+ The action of fixing up manually what a failed automatic
+ <<def_merge,merge>> left behind.
+
+[[def_revision]]revision::
+ A particular state of files and directories which was stored in the
+ <<def_object_database,object database>>. It is referenced by a
+ <<def_commit_object,commit object>>.
+
+[[def_rewind]]rewind::
+ To throw away part of the development, i.e. to assign the
+ <<def_head,head>> to an earlier <<def_revision,revision>>.
+
+[[def_SCM]]SCM::
Source code management (tool).
-SHA1::
- Synonym for object name.
-
-shallow repository::
- A shallow repository has an incomplete history some of
- whose commits have parents cauterized away (in other
- words, git is told to pretend that these commits do not
- have the parents, even though they are recorded in the
- commit object). This is sometimes useful when you are
- interested only in the recent history of a project even
- though the real history recorded in the upstream is
- much larger. A shallow repository is created by giving
- `--depth` option to gitlink:git-clone[1], and its
- history can be later deepened with gitlink:git-fetch[1].
-
-symref::
- Symbolic reference: instead of containing the SHA1 id itself, it
- is of the format 'ref: refs/some/thing' and when referenced, it
- recursively dereferences to this reference. 'HEAD' is a prime
- example of a symref. Symbolic references are manipulated with
- the gitlink:git-symbolic-ref[1] command.
-
-topic branch::
- A regular git branch that is used by a developer to
- identify a conceptual line of development. Since branches
- are very easy and inexpensive, it is often desirable to
- have several small branches that each contain very well
- defined concepts or small incremental yet related changes.
-
-tracking branch::
- A regular git branch that is used to follow changes from
- another repository. A tracking branch should not contain
- direct modifications or have local commits made to it.
- A tracking branch can usually be identified as the
- right-hand-side ref in a Pull: refspec.
-
-tree object::
- An object containing a list of file names and modes along with refs
- to the associated blob and/or tree objects. A tree is equivalent
- to a directory.
-
-tree::
- Either a working tree, or a tree object together with the
- dependent blob and tree objects (i.e. a stored representation
- of a working tree).
-
-tree-ish::
- A ref pointing to either a commit object, a tree object, or a
- tag object pointing to a tag or commit or tree object.
-
-tag object::
- An object containing a ref pointing to another object, which can
- contain a message just like a commit object. It can also
- contain a (PGP) signature, in which case it is called a "signed
- tag object".
-
-tag::
- A ref pointing to a tag or commit object. In contrast to a head,
- a tag is not changed by a commit. Tags (not tag objects) are
- stored in `$GIT_DIR/refs/tags/`. A git tag has nothing to do with
- a Lisp tag (which is called object type in git's context).
- A tag is most typically used to mark a particular point in the
- commit ancestry chain.
-
-unmerged index::
- An index which contains unmerged index entries.
-
-unreachable object::
- An object which is not reachable from a branch, tag, or any
- other reference.
-
-working tree::
- The set of files and directories currently being worked on,
- i.e. you can work in your working tree without using git at all.
-
+[[def_SHA1]]SHA1::
+ Synonym for <<def_object_name,object name>>.
+
+[[def_shallow_repository]]shallow repository::
+ A <<def_shallow_repository,shallow repository>> has an incomplete
+ history some of whose commits have parents cauterized away (in other
+ words, git is told to pretend that these commits do not have the
+ parents, even though they are recorded in the <<def_commit_object,commit
+ object>>). This is sometimes useful when you are interested only in the
+ recent history of a project even though the real history recorded in the
+ upstream is much larger. A <<def_shallow_repository,shallow repository>>
+ is created by giving the `--depth` option to gitlink:git-clone[1], and
+ its history can be later deepened with gitlink:git-fetch[1].
+
+[[def_symref]]symref::
+ Symbolic reference: instead of containing the <<def_SHA1,SHA1>> id
+ itself, it is of the format 'ref: refs/some/thing' and when
+ referenced, it recursively dereferences to this reference. 'HEAD' is a
+ prime example of a <<def_symref,symref>>. Symbolic references are
+ manipulated with the gitlink:git-symbolic-ref[1] command.
+
+[[def_tag]]tag::
+ A <<def_ref,ref>> pointing to a <<def_tag,tag>> or
+ <<def_commit_object,commit object>>. In contrast to a <<def_head,head>>,
+ a tag is not changed by a <<def_commit,commit>>. Tags (not
+ <<def_tag_object,tag objects>>) are stored in `$GIT_DIR/refs/tags/`. A
+ git tag has nothing to do with a Lisp tag (which would be
+ called an <<def_object_type,object type>> in git's context). A
+ tag is most typically used to mark a particular point in the
+ <<def_commit,commit>> ancestry <<def_chain,chain>>.
+
+[[def_tag_object]]tag object::
+ An <<def_object,object>> containing a <<def_ref,ref>> pointing to
+ another <<def_object,object>>, which can contain a message just like a
+ <<def_commit_object,commit object>>. It can also contain a (PGP)
+ signature, in which case it is called a "signed <<def_tag_object,tag
+ object>>".
+
+[[def_topic_branch]]topic branch::
+ A regular git <<def_branch,branch>> that is used by a developer to
+ identify a conceptual line of development. Since branches are very easy
+ and inexpensive, it is often desirable to have several small branches
+ that each contain very well defined concepts or small incremental yet
+ related changes.
+
+[[def_tracking_branch]]tracking branch::
+ A regular git <<def_branch,branch>> that is used to follow changes from
+ another <<def_repository,repository>>. A <<def_tracking_branch,tracking
+ branch>> should not contain direct modifications or have local commits
+ made to it. A <<def_tracking_branch,tracking branch>> can usually be
+ identified as the right-hand-side <<def_ref,ref>> in a Pull:
+ <<def_refspec,refspec>>.
+
+[[def_tree]]tree::
+ Either a <<def_working_tree,working tree>>, or a <<def_tree_object,tree
+ object>> together with the dependent blob and <<def_tree,tree>> objects
+ (i.e. a stored representation of a <<def_working_tree,working tree>>).
+
+[[def_tree_object]]tree object::
+ An <<def_object,object>> containing a list of file names and modes along
+ with refs to the associated blob and/or tree objects. A
+ <<def_tree,tree>> is equivalent to a <<def_directory,directory>>.
+
+[[def_tree-ish]]tree-ish::
+ A <<def_ref,ref>> pointing to either a <<def_commit_object,commit
+ object>>, a <<def_tree_object,tree object>>, or a <<def_tag_object,tag
+ object>> pointing to a <<def_tag,tag>> or <<def_commit,commit>> or
+ <<def_tree_object,tree object>>.
+
+[[def_unmerged_index]]unmerged index::
+ An <<def_index,index>> which contains unmerged
+ <<def_index_entry,index entries>>.
+
+[[def_unreachable_object]]unreachable object::
+ An <<def_object,object>> which is not <<def_reachable,reachable>> from a
+ <<def_branch,branch>>, <<def_tag,tag>>, or any other reference.
+
+[[def_working_tree]]working tree::
+ The set of files and directories currently being worked on, i.e. you can
+ work in your <<def_working_tree,working tree>> without using git at all.
- '%Cgreen': switch color to green
- '%Cblue': switch color to blue
- '%Creset': reset color
+- '%m': left, right or boundary mark
- '%n': newline
+++ /dev/null
-#!/usr/bin/perl
-
-%terms=();
-
-while(<>) {
- if(/^(\S.*)::$/) {
- my $term=$1;
- if(defined($terms{$term})) {
- die "$1 defined twice\n";
- }
- $terms{$term}="";
- LOOP: while(<>) {
- if(/^$/) {
- last LOOP;
- }
- if(/^ \S/) {
- $terms{$term}.=$_;
- } else {
- die "Error 1: $_";
- }
- }
- }
-}
-
-sub format_tab_80 ($) {
- my $text=$_[0];
- my $result="";
- $text=~s/\s+/ /g;
- $text=~s/^\s+//;
- while($text=~/^(.{1,72})(|\s+(\S.*)?)$/) {
- $result.=" ".$1."\n";
- $text=$3;
- }
- return $result;
-}
-
-sub no_spaces ($) {
- my $result=$_[0];
- $result=~tr/ /_/;
- return $result;
-}
-
-print 'GIT Glossary
-============
-
-This list is sorted alphabetically:
-
-';
-
-@keys=sort {uc($a) cmp uc($b)} keys %terms;
-$pattern='(\b(?<!link:git-)'.join('\b|\b(?<!-)',reverse @keys).'\b)';
-foreach $key (@keys) {
- $terms{$key}=~s/$pattern/sprintf "<<ref_".no_spaces($1).",$1>>";/eg;
- print '[[ref_'.no_spaces($key).']]'.$key."::\n"
- .format_tab_80($terms{$key})."\n";
-}
-
-print '
-
-Author
-------
-Written by Johannes Schindelin <Johannes.Schindelin@gmx.de> and
-the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the link:git.html[git] suite
-';
-
which looks like this:
(undeltified representation)
- n-byte type and length (4-bit type, (n-1)*7+4-bit length)
+ n-byte type and length (3-bit type, (n-1)*7+4-bit length)
compressed data
(deltified representation)
- n-byte type and length (4-bit type, (n-1)*7+4-bit length)
+ n-byte type and length (3-bit type, (n-1)*7+4-bit length)
20-byte base object name
compressed delta data
Pack file entry: <+
packed object header:
- 1-byte type (upper 4-bit)
+ 1-byte size extension bit (MSB)
+ type (next 3 bit)
size0 (lower 4-bit)
n-byte sizeN (as long as MSB is set, each 7-bit)
size0..sizeN form 4+7+7+..+7 bit integer, size0
- is the most significant part.
+ is the least significant part, and sizeN is the
+ most significant part.
packed object data:
If it is not DELTA, then deflated bytes (the size above
is the size before compression).
origin/master
origin/next
...
-$ git branch checkout -b masterwork origin/master
+$ git checkout -b masterwork origin/master
-----------------------------------------------
Fetch a branch from a different repository, and give it a new
------------------------------------------------
$ cat >~/.gitconfig <<\EOF
[user]
-name = Your Name Comes Here
-email = you@yourdomain.example.com
+ name = Your Name Comes Here
+ email = you@yourdomain.example.com
EOF
------------------------------------------------
-----------------------------------------------
$ git format-patch origin..HEAD # format a patch for each commit
# in HEAD but not in origin
-$ git-am mbox # import patches from the mailbox "mbox"
+$ git am mbox # import patches from the mailbox "mbox"
-----------------------------------------------
Fetch a branch in a different git repository, then merge into the
collection of interrelated snapshots (versions) of the project's
contents.
-A single git repository may contain multiple branches. Each branch
-is a bookmark referencing a particular point in the project history.
-The gitlink:git-branch[1] command shows you the list of branches:
+A single git repository may contain multiple branches. It keeps track
+of them by keeping a list of <<def_head,heads>> which reference the
+latest version on each branch; the gitlink:git-branch[1] command shows
+you the list of branch heads:
------------------------------------------------
$ git branch
* master
------------------------------------------------
-A freshly cloned repository contains a single branch, named "master",
-and the working directory contains the version of the project
-referred to by the master branch.
+A freshly cloned repository contains a single branch head, by default
+named "master", with the working directory initialized to the state of
+the project referred to by that branch head.
-Most projects also use tags. Tags, like branches, are references
-into the project's history, and can be listed using the
+Most projects also use <<def_tag,tags>>. Tags, like heads, are
+references into the project's history, and can be listed using the
gitlink:git-tag[1] command:
------------------------------------------------
------------------------------------------------
Tags are expected to always point at the same version of a project,
-while branches are expected to advance as development progresses.
+while heads are expected to advance as development progresses.
-Create a new branch pointing to one of these versions and check it
+Create a new branch head pointing to one of these versions and check it
out using gitlink:git-checkout[1]:
------------------------------------------------
$ git reset --hard v2.6.17
------------------------------------------------
-Note that if the current branch was your only reference to a
+Note that if the current branch head was your only reference to a
particular point in history, then resetting that branch may leave you
-with no way to find the history it used to point to; so use this
-command carefully.
+with no way to find the history it used to point to; so use this command
+carefully.
Understanding History: Commits
------------------------------
Understanding history: What is a branch?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Though we've been using the word "branch" to mean a kind of reference
-to a particular commit, the word branch is also commonly used to
-refer to the line of commits leading up to that point. In the
-example above, git may think of the branch named "A" as just a
-pointer to one particular commit, but we may refer informally to the
-line of three commits leading up to that point as all being part of
+When we need to be precise, we will use the word "branch" to mean a line
+of development, and "branch head" (or just "head") to mean a reference
+to the most recent commit on a branch. In the example above, the branch
+head named "A" is a pointer to one particular commit, but we refer to
+the line of three commits leading up to that point as all being part of
"branch A".
-If we need to make it clear that we're just talking about the most
-recent commit on the branch, we may refer to that commit as the
-"head" of the branch.
+However, when no confusion will result, we often just use the term
+"branch" both for branches and for branch heads.
Manipulating branches
---------------------
create a new branch <new> referencing <start-point>, and
check it out.
-It is also useful to know that the special symbol "HEAD" can always
-be used to refer to the current branch.
+The special symbol "HEAD" can always be used to refer to the current
+branch. In fact, git uses a file named "HEAD" in the .git directory to
+remember which branch is current:
+
+------------------------------------------------
+$ cat .git/HEAD
+ref: refs/heads/master
+------------------------------------------------
+
+[[detached-head]]
+Examining an old version without creating a new branch
+------------------------------------------------------
+
+The git-checkout command normally expects a branch head, but will also
+accept an arbitrary commit; for example, you can check out the commit
+referenced by a tag:
+
+------------------------------------------------
+$ git checkout v2.6.17
+Note: moving to "v2.6.17" which isn't a local branch
+If you want to create a new branch from this checkout, you may do so
+(now or later) by using -b with the checkout command again. Example:
+ git checkout -b <new_branch_name>
+HEAD is now at 427abfa... Linux v2.6.17
+------------------------------------------------
+
+The HEAD then refers to the SHA1 of the commit instead of to a branch,
+and git branch shows that you are no longer on a branch:
+
+------------------------------------------------
+$ cat .git/HEAD
+427abfa28afedffadfca9dd8b067eb6d36bac53f
+git branch
+* (no branch)
+ master
+------------------------------------------------
+
+In this case we say that the HEAD is "detached".
+
+This can be an easy way to check out a particular version without having
+to make up a name for a new branch. However, keep in mind that when you
+switch away from the (for example, by checking out something else), you
+can lose track of what the HEAD used to point to.
Examining branches from a remote repository
-------------------------------------------
-------------------------------------------------
$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
-$ git fetch
+$ git fetch linux-nfs
* refs/remotes/linux-nfs/master: storing branch 'master' ...
commit: bf81b46
-------------------------------------------------
run
-------------------------------------------------
-$ git bisect-visualize
+$ git bisect visualize
-------------------------------------------------
which will run gitk and label the commit it chose with a marker that
running
-------------------------------------------------
-$ git-tag stable-1 1b2e1d63ff
+$ git tag stable-1 1b2e1d63ff
-------------------------------------------------
You can use stable-1 to refer to the commit 1b2e1d63ff.
descendants:
-------------------------------------------------
-$ git name-rev e05db0fd
+$ git name-rev --tags e05db0fd
e05db0fd tags/v1.5.0-rc1^0~23
-------------------------------------------------
-------------------------------------------------
$ git describe e05db0fd
-v1.5.0-rc0-ge05db0f
+v1.5.0-rc0-260-ge05db0f
-------------------------------------------------
but that may sometimes help you guess which tags might come after the
-------------------------------------------------
[[how-to-make-a-commit]]
-how to make a commit
+How to make a commit
--------------------
Creating a new commit takes three steps:
$ git status # a brief per-file summary of the above.
-------------------------------------------------
-creating good commit messages
+Creating good commit messages
-----------------------------
Though not required, it's a good idea to begin the commit message
the first line on the Subject line and the rest of the commit in the
body.
-how to merge
+How to merge
------------
You can rejoin two diverging branches of development using
git-diff will (by default) no longer show diffs for that file.
[[undoing-a-merge]]
-undoing a merge
+Undoing a merge
---------------
If you get stuck and decide to just give up and throw the whole mess
then you can just pull changes from each other's repositories
directly; note that all of the commands (gitlink:git-clone[1],
git-fetch[1], git-pull[1], etc.) that accept a URL as an argument
-will also accept a local file patch; so, for example, you can
+will also accept a local directory name; so, for example, you can
use
-------------------------------------------------
The gitweb cgi script provides users an easy way to browse your
project's files and history without having to install git; see the file
-gitweb/README in the git source tree for instructions on setting it up.
+gitweb/INSTALL in the git source tree for instructions on setting it up.
Examples
--------
(Either gitk or git-log may be useful for finding the commit.)
-Then check out a new branch at that commit, edit it, and rebase the rest of
-the series on top of it:
+Then check out that commit, edit it, and rebase the rest of the series
+on top of it (note that we could check out the commit on a temporary
+branch, but instead we're using a <<detached-head,detached head>>):
-------------------------------------------------
-$ git checkout -b TMP bad
+$ git checkout bad
$ # make changes here and update the index
$ git commit --amend
-$ git rebase --onto TMP bad mywork
+$ git rebase --onto HEAD bad mywork
-------------------------------------------------
-When you're done, you'll be left with mywork checked out, with the top patches
-on mywork reapplied on top of the modified commit you created in TMP. You can
+When you're done, you'll be left with mywork checked out, with the top
+patches on mywork reapplied on top of your modified commit. You can
then clean up with
-------------------------------------------------
-$ git branch -d TMP
$ git tag -d bad
-------------------------------------------------
Git internals
=============
-There are two object abstractions: the "object database", and the
-"current directory cache" aka "index".
+Git depends on two fundamental abstractions: the "object database", and
+the "current directory cache" aka "index".
The Object Database
-------------------
determined at object creation time, and which identifies the format of
the object (i.e. how it is used, and how it can refer to other
objects). There are currently four different object types: "blob",
-"tree", "commit" and "tag".
+"tree", "commit", and "tag".
-A "blob" object cannot refer to any other object, and is, like the type
-implies, a pure storage object containing some user data. It is used to
-actually store the file data, i.e. a blob object is associated with some
-particular version of some file.
+A <<def_blob_object,"blob" object>> cannot refer to any other object,
+and is, as the name implies, a pure storage object containing some
+user data. It is used to actually store the file data, i.e. a blob
+object is associated with some particular version of some file.
-A "tree" object is an object that ties one or more "blob" objects into a
-directory structure. In addition, a tree object can refer to other tree
-objects, thus creating a directory hierarchy.
+A <<def_tree_object,"tree" object>> is an object that ties one or more
+"blob" objects into a directory structure. In addition, a tree object
+can refer to other tree objects, thus creating a directory hierarchy.
-A "commit" object ties such directory hierarchies together into
-a DAG of revisions - each "commit" is associated with exactly one tree
-(the directory hierarchy at the time of the commit). In addition, a
-"commit" refers to one or more "parent" commit objects that describe the
-history of how we arrived at that directory hierarchy.
+A <<def_commit_object,"commit" object>> ties such directory hierarchies
+together into a <<def_DAG,directed acyclic graph>> of revisions - each
+"commit" is associated with exactly one tree (the directory hierarchy at
+the time of the commit). In addition, a "commit" refers to one or more
+"parent" commit objects that describe the history of how we arrived at
+that directory hierarchy.
As a special case, a commit object with no parents is called the "root"
object, and is the point of an initial project commit. Each project
just going to confuse people. So aim for the notion of "one root object
per project", even if git itself does not enforce that.
-A "tag" object symbolically identifies and can be used to sign other
-objects. It contains the identifier and type of another object, a
-symbolic name (of course!) and, optionally, a signature.
+A <<def_tag_object,"tag" object>> symbolically identifies and can be
+used to sign other objects. It contains the identifier and type of
+another object, a symbolic name (of course!) and, optionally, a
+signature.
Regardless of object type, all objects share the following
characteristics: they are all deflated with zlib, and have a header
contrast, running "git prune" while somebody is actively changing the
repository is a *BAD* idea).
-Glossary of git terms
-=====================
-
include::glossary.txt[]
Notes and todo list for this manual
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.1-rc1.GIT
+DEF_VER=v1.5.1.1.GIT
LF='
'
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
# MakeMaker (e.g. using ActiveState under Cygwin).
#
+# Define WITH_P4IMPORT to build and install Python git-p4import script.
+#
+# Define NO_TCLTK if you do not want Tcl/Tk GUI.
+#
+# The TCLTK_PATH variable governs the location of the Tck/Tk interpreter.
+# If not set it defaults to the bare 'wish'. If it is set to the empty
+# string then NO_TCLTK will be forced (this is used by configure script).
+#
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
@$(SHELL_PATH) ./GIT-VERSION-GEN
TAR = tar
INSTALL = install
RPMBUILD = rpmbuild
+TCLTK_PATH = wish
# sparse is architecture-neutral, which means that we need to tell it
# explicitly what architecture to check for. Fix this up for yours..
git-svnimport.perl git-cvsexportcommit.perl \
git-send-email.perl git-svn.perl
+SCRIPT_PYTHON = \
+ git-p4import.py
+
+ifdef WITH_P4IMPORT
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
+ $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
git-status git-instaweb
+else
+SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
+ $(patsubst %.perl,%,$(SCRIPT_PERL)) \
+ git-status git-instaweb
+endif
+
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \
# 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
+ifndef NO_TCLTK
+OTHER_PROGRAMS += gitk-wish
+endif
+
# Backward compatibility -- to be removed after 1.0
PROGRAMS += git-ssh-pull$X git-ssh-push$X
ifndef PERL_PATH
PERL_PATH = /usr/bin/perl
endif
+ifndef PYTHON_PATH
+ PYTHON_PATH = /usr/local/bin/python
+endif
export PERL_PATH
diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
- utf8.h reflog-walk.h
+ utf8.h reflog-walk.h patch-ids.h decorate.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o \
interpolate.o \
lockfile.o \
+ patch-ids.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
reachable.o reflog-walk.o \
quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
revision.o pager.o tree-walk.o xdiff-interface.o \
- write_or_die.o trace.o list-objects.o grep.o \
+ write_or_die.o trace.o list-objects.o grep.o match-trees.o \
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
+ convert.o decorate.o
BUILTIN_OBJS = \
builtin-add.o \
ifeq ($(uname_S),Darwin)
NEEDS_SSL_WITH_CRYPTO = YesPlease
NEEDS_LIBICONV = YesPlease
+ OLD_ICONV = UnfortunatelyYes
NO_STRLCPY = YesPlease
endif
ifeq ($(uname_S),SunOS)
export NO_PERL_MAKEMAKER
endif
-QUIET_SUBDIR0 = $(MAKE) -C # space to separate -C and subdir
+ifeq ($(TCLTK_PATH),)
+NO_TCLTK=NoThanks
+endif
+
+QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
QUIET_SUBDIR1 =
ifneq ($(findstring $(MAKEFLAGS),w),w)
QUIET_LINK = @echo ' ' LINK $@;
QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
QUIET_GEN = @echo ' ' GEN $@;
- QUIET_SUBDIR0 = @subdir=
+ QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
$(MAKE) $(PRINT_DIR) -C $$subdir
export V
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
+TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
LIBS = $(GITLIBS) $(EXTLIBS)
### Build rules
-all:: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
+all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS)
ifneq (,$X)
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$p';)
endif
all::
- $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) all
+ifndef NO_TCLTK
+ $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) TCLTK_PATH='$(TCLTK_PATH_SQ)' all
+endif
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
strip: $(PROGRAMS) git$X
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
+gitk-wish: gitk GIT-GUI-VARS
+ $(QUIET_GEN)rm -f $@ $@+ && \
+ sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \
+ chmod +x $@+ && \
+ mv -f $@+ $@
+
git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
$(QUIET_LINK)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
$(ALL_CFLAGS) -o $@ $(filter %.c,$^) \
help.o: common-cmds.h
+git-merge-subtree$X: git-merge-recursive$X
+ $(QUIET_BUILT_IN)rm -f $@ && ln git-merge-recursive$X $@
+
$(BUILT_INS): git$X
$(QUIET_BUILT_IN)rm -f $@ && ln git$X $@
-common-cmds.h: Documentation/git-*.txt
+common-cmds.h: $(wildcard Documentation/git-*.txt)
$(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
+$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
+ rm -f $@ $@+
+ sed -e '1s|#!.*/python|#!$(PYTHON_PATH_SQ)|' \
+ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+ $@.py >$@+
+ chmod +x $@+
+ mv $@+ $@
+
perl/perl.mak: GIT-CFLAGS
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
echo "$$FLAGS" >GIT-CFLAGS; \
fi
+### Detect Tck/Tk interpreter path changes
+ifndef NO_TCLTK
+TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
+
+GIT-GUI-VARS: .FORCE-GIT-GUI-VARS
+ @VARS='$(TRACK_VARS)'; \
+ if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
+ echo 1>&2 " * new Tcl/Tk interpreter location"; \
+ echo "$$VARS" >$@; \
+ fi
+
+.PHONY: .FORCE-GIT-GUI-VARS
+endif
+
### Testing rules
# GNU make supports exporting all variables by "export" without parameters.
test-sha1$X: test-sha1.o $(GITLIBS)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+test-match-trees$X: test-match-trees.o $(GITLIBS)
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+
test-chmtime$X: test-chmtime.c
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(bindir_SQ)'
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
- $(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
+ $(INSTALL) git$X '$(DESTDIR_SQ)$(bindir_SQ)'
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
$(MAKE) -C perl prefix='$(prefix_SQ)' install
+ifndef NO_TCLTK
+ $(INSTALL) gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
$(MAKE) -C git-gui install
+endif
if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
then \
ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
clean:
rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
- $(LIB_FILE) $(XDIFF_LIB)
+ test-chmtime$X $(LIB_FILE) $(XDIFF_LIB)
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
rm -rf autom4te.cache
rm -f gitweb/gitweb.cgi
$(MAKE) -C Documentation/ clean
$(MAKE) -C perl clean
- $(MAKE) -C git-gui clean
$(MAKE) -C templates/ clean
$(MAKE) -C t/ clean
- rm -f GIT-VERSION-FILE GIT-CFLAGS
+ifndef NO_TCLTK
+ rm -f gitk-wish
+ $(MAKE) -C git-gui clean
+endif
+ rm -f GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
.PHONY: all install clean strip
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags .FORCE-GIT-CFLAGS
git-merge-octopus | git-merge-ours | git-merge-recursive | \
git-merge-resolve | git-merge-stupid | \
git-add--interactive | git-fsck-objects | git-init-db | \
- git-repo-config | \
+ git-repo-config | git-fetch--tool | \
git-ssh-pull | git-ssh-push ) continue ;; \
esac ; \
test -f "Documentation/$$v.txt" || \
to the list, send an email with just "subscribe git" in the body to
majordomo@vger.kernel.org. The mailing list archives are available at
http://marc.theaimsgroup.com/?l=git and other archival sites.
+
+The messages titled "A note from the maintainer", "What's in
+git.git (stable)" and "What's cooking in git.git (topics)" and
+the discussion following them on the mailing list give a good
+reference for project status, development direction and
+remaining tasks.
-Documentation/RelNotes-1.5.1.txt
\ No newline at end of file
+Documentation/RelNotes-1.5.2.txt
\ No newline at end of file
}
}
-int decode_85(char *dst, char *buffer, int len)
+int decode_85(char *dst, const char *buffer, int len)
{
prep_base85();
return 0;
}
-void encode_85(char *buf, unsigned char *data, int bytes)
+void encode_85(char *buf, const unsigned char *data, int bytes)
{
prep_base85();
}
/* Read the directory and prune it */
- read_directory(dir, path, base, baselen);
+ read_directory(dir, path, base, baselen, pathspec);
if (pathspec)
prune_directory(dir, pathspec, baselen);
}
git_config(git_add_config);
- newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
+ newfd = hold_locked_index(&lock_file, 1);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
}
for (i = 0; i < dir.nr; i++)
- add_file_to_index(dir.entries[i]->name, verbose);
+ add_file_to_cache(dir.entries[i]->name, verbose);
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- close(newfd) || commit_lock_file(&lock_file))
+ close(newfd) || commit_locked_index(&lock_file))
die("Unable to write new index file");
}
static int p_value = 1;
static int p_value_known;
static int check_index;
-static int write_index;
+static int update_index;
static int cached;
static int diffstat;
static int numstat;
static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
{
if (!orig_name && !isnull)
- return find_name(line, NULL, 1, TERM_TAB);
+ return find_name(line, NULL, p_value, TERM_TAB);
if (orig_name) {
int len;
len = strlen(name);
if (isnull)
die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
- another = find_name(line, NULL, 1, TERM_TAB);
+ another = find_name(line, NULL, p_value, TERM_TAB);
if (!another || memcmp(another, name, len))
die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
free(another);
static void remove_file(struct patch *patch, int rmdir_empty)
{
- if (write_index) {
+ if (update_index) {
if (remove_file_from_cache(patch->old_name) < 0)
die("unable to remove %s from index", patch->old_name);
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
int namelen = strlen(path);
unsigned ce_size = cache_entry_size(namelen);
- if (!write_index)
+ if (!update_index)
return;
ce = xcalloc(1, ce_size);
static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
{
- int fd;
+ int fd, converted;
char *nbuf;
unsigned long nsize;
* terminated.
*/
return symlink(buf, path);
+
+ fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
+ if (fd < 0)
+ return -1;
+
nsize = size;
nbuf = (char *) buf;
- if (convert_to_working_tree(path, &nbuf, &nsize)) {
- free((char *) buf);
+ converted = convert_to_working_tree(path, &nbuf, &nsize);
+ if (converted) {
buf = nbuf;
size = nsize;
}
-
- fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
- if (fd < 0)
- return -1;
while (size) {
int written = xwrite(fd, buf, size);
if (written < 0)
}
if (close(fd) < 0)
die("closing file %s: %s", path, strerror(errno));
+ if (converted)
+ free(nbuf);
return 0;
}
if (whitespace_error && (new_whitespace == error_on_whitespace))
apply = 0;
- write_index = check_index && apply;
- if (write_index && newfd < 0)
- newfd = hold_lock_file_for_update(&lock_file,
- get_index_file(), 1);
+ update_index = check_index && apply;
+ if (update_index && newfd < 0)
+ newfd = hold_locked_index(&lock_file, 1);
+
if (check_index) {
if (read_cache() < 0)
die("unable to read index file");
whitespace_error == 1 ? "s" : "");
}
- if (write_index) {
+ if (update_index) {
if (write_cache(newfd, active_cache, active_nr) ||
- close(newfd) || commit_lock_file(&lock_file))
+ close(newfd) || commit_locked_index(&lock_file))
die("Unable to write new index file");
}
{
const char *extra_argv[MAX_EXTRA_ARGS];
int extra_argc = 0;
- const char *format = NULL; /* might want to default to "tar" */
+ const char *format = "tar";
const char *base = "";
int verbose = 0;
int i;
/* We need at least one parameter -- tree-ish */
if (argc - 1 < i)
usage(archive_usage);
- if (!format)
- die("You must specify an archive format");
if (init_archiver(format, ar) < 0)
die("Unknown archive format '%s'", format);
memset(&ar, 0, sizeof(ar));
tree_idx = parse_archive_args(argc, argv, &ar);
+ if (prefix == NULL)
+ prefix = setup_git_directory();
argv += tree_idx;
parse_treeish_arg(argv, &ar.args, prefix);
static char blame_usage[] =
"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
-" -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
+" -c Use the same output mode as git-annotate (Default: off)\n"
" -b Show blank SHA-1 for boundary commits (Default: off)\n"
-" -l, --long Show long commit SHA1 (Default: off)\n"
+" -l Show long commit SHA1 (Default: off)\n"
" --root Do not treat root commits as boundaries (Default: off)\n"
-" -t, --time Show raw timestamp (Default: off)\n"
+" -t Show raw timestamp (Default: off)\n"
" -f, --show-name Show original filename (Default: auto)\n"
" -n, --show-number Show original linenumber (Default: off)\n"
" -p, --porcelain Show in a format designed for machine consumption\n"
commit->buffer = xmalloc(400);
ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
- sprintf(commit->buffer,
+ snprintf(commit->buffer, 400,
"tree 0000000000000000000000000000000000000000\n"
"parent %s\n"
"author %s\n"
{
char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
unsigned char sha1[20];
+ char oldsection[PATH_MAX], newsection[PATH_MAX];
if (!oldname)
die("cannot rename the current branch while not on any.");
/* no need to pass logmsg here as HEAD didn't really move */
if (!strcmp(oldname, head) && create_symref("HEAD", newref, NULL))
die("Branch renamed to %s, but HEAD is not updated!", newname);
+
+ snprintf(oldsection, sizeof(oldsection), "branch.%s", oldref + 11);
+ snprintf(newsection, sizeof(newsection), "branch.%s", newref + 11);
+ if (git_config_rename_section(oldsection, newsection) < 0)
+ die("Branch is renamed, but update of config-file failed");
}
int cmd_branch(int argc, const char **argv, const char *prefix)
if (buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
if (get_sha1_hex(buffer + offset, sha1)) {
- warn("unrecognized header: %s", buffer);
+ warning("unrecognized header: %s", buffer);
continue;
}
delim = buffer[40 + offset];
* from getting output.
*/
if (!(e->item->flags & SHOWN)) {
- warn("ref '%s' is excluded by the rev-list options",
+ warning("ref '%s' is excluded by the rev-list options",
e->name);
continue;
}
if (!strcmp(arg, "-u") || !strcmp(arg, "--index")) {
state.refresh_cache = 1;
if (newfd < 0)
- newfd = hold_lock_file_for_update
- (&lock_file, get_index_file(), 1);
- if (newfd < 0)
- die("cannot open index.lock file.");
+ newfd = hold_locked_index(&lock_file, 1);
continue;
}
if (!strcmp(arg, "-z")) {
if (0 <= newfd &&
(write_cache(newfd, active_cache, active_nr) ||
- close(newfd) || commit_lock_file(&lock_file)))
+ close(newfd) || commit_locked_index(&lock_file)))
die("Unable to write new index file");
return 0;
}
static int show_root;
static int show_tags;
static int show_unreachable;
+static int include_reflogs = 1;
static int check_full;
static int check_strict;
static int keep_cache_objects;
return 0;
}
-static int fsck_sha1(unsigned char *sha1)
+static int fsck_sha1(const unsigned char *sha1)
{
struct object *obj = parse_object(sha1);
if (!obj) {
static void get_default_heads(void)
{
for_each_ref(fsck_handle_ref, NULL);
- for_each_reflog(fsck_handle_reflog, NULL);
+ if (include_reflogs)
+ for_each_reflog(fsck_handle_reflog, NULL);
/*
* Not having any default heads isn't really fatal, but
* "show_unreachable" flag.
*/
if (!default_refs) {
- error("No default references");
+ fprintf(stderr, "notice: No default references\n");
show_unreachable = 0;
}
}
{
unsigned char sha1[20];
int flag;
- const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag);
-
- if (!head_points_at || !(flag & REF_ISSYMREF))
- return error("HEAD is not a symbolic ref");
- if (prefixcmp(head_points_at, "refs/heads/"))
+ int null_is_error = 0;
+ const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
+
+ if (!head_points_at)
+ return error("Invalid HEAD");
+ if (!strcmp(head_points_at, "HEAD"))
+ /* detached HEAD */
+ null_is_error = 1;
+ else if (prefixcmp(head_points_at, "refs/heads/"))
return error("HEAD points to something strange (%s)",
head_points_at);
- if (is_null_sha1(sha1))
- return error("HEAD: not a valid git pointer");
+ if (is_null_sha1(sha1)) {
+ if (null_is_error)
+ return error("HEAD: detached HEAD points at nothing");
+ fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
+ head_points_at + 11);
+ }
return 0;
}
keep_cache_objects = 1;
continue;
}
+ if (!strcmp(arg, "--no-reflogs")) {
+ include_reflogs = 0;
+ continue;
+ }
if (!strcmp(arg, "--full")) {
check_full = 1;
continue;
for (p = packed_git; p; p = p->next) {
uint32_t i, num = num_packed_objects(p);
- for (i = 0; i < num; i++) {
- unsigned char sha1[20];
- nth_packed_object_sha1(p, i, sha1);
- fsck_sha1(sha1);
- }
+ for (i = 0; i < num; i++)
+ fsck_sha1(nth_packed_object_sha1(p, i));
}
}
static const char emsg_missing_argument[] =
"option requires an argument -%s";
-static int strtoul_ui(char const *s, unsigned int *result)
-{
- unsigned long ul;
- char *p;
-
- errno = 0;
- ul = strtoul(s, &p, 10);
- if (errno || *p || p == s || (unsigned int) ul != ul)
- return -1;
- *result = ul;
- return 0;
-}
-
int cmd_grep(int argc, const char **argv, const char *prefix)
{
int hit = 0;
scan = arg + 1;
break;
}
- if (strtoul_ui(scan, &num))
+ if (strtoul_ui(scan, 10, &num))
die(emsg_invalid_context_len, scan);
switch (arg[1]) {
case 'A':
#include "builtin.h"
#include "tag.h"
#include "reflog-walk.h"
+#include "patch-ids.h"
+#include "refs.h"
static int default_show_root = 1;
/* this is in builtin-diff.c */
void add_head(struct rev_info *revs);
+static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+{
+ int plen = strlen(prefix);
+ int nlen = strlen(name);
+ struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
+ memcpy(res->name, prefix, plen);
+ memcpy(res->name + plen, name, nlen + 1);
+ res->next = add_decoration(&name_decoration, obj, res);
+}
+
+static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct object *obj = parse_object(sha1);
+ if (!obj)
+ return 0;
+ add_name_decoration("", refname, obj);
+ while (obj->type == OBJ_TAG) {
+ obj = ((struct tag *)obj)->tagged;
+ if (!obj)
+ break;
+ add_name_decoration("tag: ", refname, obj);
+ }
+ return 0;
+}
+
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev)
{
int i;
+ int decorate = 0;
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
git_log_output_encoding = xstrdup(arg);
else
git_log_output_encoding = "";
- }
- else
+ } else if (!strcmp(arg, "--decorate")) {
+ if (!decorate)
+ for_each_ref(add_ref_decoration, NULL);
+ decorate = 1;
+ } else
die("unrecognized argument: %s", arg);
}
}
}
-static int get_patch_id(struct commit *commit, struct diff_options *options,
- unsigned char *sha1)
-{
- if (commit->parents)
- diff_tree_sha1(commit->parents->item->object.sha1,
- commit->object.sha1, "", options);
- else
- diff_root_tree_sha1(commit->object.sha1, "", options);
- diffcore_std(options);
- return diff_flush_patch_id(options, sha1);
-}
-
-static void get_patch_ids(struct rev_info *rev, struct diff_options *options, const char *prefix)
+static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
{
struct rev_info check_rev;
struct commit *commit;
struct object *o1, *o2;
unsigned flags1, flags2;
- unsigned char sha1[20];
if (rev->pending.nr != 2)
die("Need exactly one range.");
if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
die("Not a range.");
- diff_setup(options);
- options->recursive = 1;
- if (diff_setup_done(options) < 0)
- die("diff_setup_done failed");
+ init_patch_ids(ids);
/* given a range a..b get all patch ids for b..a */
init_revisions(&check_rev, prefix);
if (commit->parents && commit->parents->next)
continue;
- if (!get_patch_id(commit, options, sha1))
- created_object(sha1, xcalloc(1, sizeof(struct object)));
+ add_commit_patch_id(commit, ids);
}
/* reset for next revision walk */
int numbered = 0;
int start_number = -1;
int keep_subject = 0;
+ int subject_prefix = 0;
int ignore_if_in_upstream = 0;
int thread = 0;
const char *in_reply_to = NULL;
- struct diff_options patch_id_opts;
+ struct patch_ids ids;
char *add_signoff = NULL;
char message_id[1024];
char ref_message_id[1024];
if (i == argc)
die("Need a Message-Id for --in-reply-to");
in_reply_to = argv[i];
- }
- else if (!prefixcmp(argv[i], "--suffix="))
+ } else if (!prefixcmp(argv[i], "--subject-prefix=")) {
+ subject_prefix = 1;
+ rev.subject_prefix = argv[i] + 17;
+ } else if (!prefixcmp(argv[i], "--suffix="))
fmt_patch_suffix = argv[i] + 9;
else
argv[j++] = argv[i];
start_number = 1;
if (numbered && keep_subject)
die ("-n and -k are mutually exclusive.");
+ if (keep_subject && subject_prefix)
+ die ("--subject-prefix and -k are mutually exclusive.");
argc = setup_revisions(argc, argv, &rev, "HEAD");
if (argc > 1)
}
if (ignore_if_in_upstream)
- get_patch_ids(&rev, &patch_id_opts, prefix);
+ get_patch_ids(&rev, &ids, prefix);
if (!use_stdout)
realstdout = fdopen(dup(1), "w");
prepare_revision_walk(&rev);
while ((commit = get_revision(&rev)) != NULL) {
- unsigned char sha1[20];
-
/* ignore merges */
if (commit->parents && commit->parents->next)
continue;
if (ignore_if_in_upstream &&
- !get_patch_id(commit, &patch_id_opts, sha1) &&
- lookup_object(sha1))
+ has_commit_patch_id(commit, &ids))
continue;
nr++;
fclose(stdout);
}
free(list);
+ if (ignore_if_in_upstream)
+ free_patch_ids(&ids);
return 0;
}
int cmd_cherry(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
- struct diff_options patch_id_opts;
+ struct patch_ids ids;
struct commit *commit;
struct commit_list *list = NULL;
const char *upstream;
return 0;
}
- get_patch_ids(&revs, &patch_id_opts, prefix);
+ get_patch_ids(&revs, &ids, prefix);
if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
die("Unknown commit %s", limit);
}
while (list) {
- unsigned char sha1[20];
char sign = '+';
commit = list->item;
- if (!get_patch_id(commit, &patch_id_opts, sha1) &&
- lookup_object(sha1))
+ if (has_commit_patch_id(commit, &ids))
sign = '-';
if (verbose) {
list = list->next;
}
+ free_patch_ids(&ids);
return 0;
}
if (baselen)
path = base = prefix;
- read_directory(dir, path, base, baselen);
+ read_directory(dir, path, base, baselen, pathspec);
if (show_others)
show_other_files(dir);
if (show_killed)
"From","Subject","Date",
};
-static int check_header(char *line, char **hdr_data)
+static int check_header(char *line, char **hdr_data, int overwrite)
{
int i;
/* search for the interesting parts */
for (i = 0; header[i]; i++) {
int len = strlen(header[i]);
- if (!hdr_data[i] &&
+ if ((!hdr_data[i] || overwrite) &&
!strncasecmp(line, header[i], len) &&
line[len] == ':' && isspace(line[len + 1])) {
/* Unwrap inline B and Q encoding, and optionally
static int handle_boundary(void)
{
+ char newline[]="\n";
again:
if (!memcmp(line+content_top->boundary_len, "--", 2)) {
/* we hit an end boundary */
"can't recover\n");
exit(1);
}
- handle_filter("\n");
+ handle_filter(newline);
/* skip to the next boundary */
if (!find_boundary())
/* slurp in this section's info */
while (read_one_header_line(line, sizeof(line), fin))
- check_header(line, p_hdr_data);
+ check_header(line, p_hdr_data, 0);
/* eat the blank line after section info */
return (fgets(line, sizeof(line), fin) != NULL);
if (!*cp)
return 0;
}
- if ((still_looking = check_header(cp, s_hdr_data)) != 0)
+ if ((still_looking = check_header(cp, s_hdr_data, 0)) != 0)
return 0;
}
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset);
+
if (patchbreak(line)) {
fclose(cmitmsg);
cmitmsg = NULL;
return;
}
- /* Unwrap transfer encoding and optionally
- * normalize the log message to UTF-8.
- */
+ /* Unwrap transfer encoding */
decode_transfer_encoding(line);
- if (metainfo_charset)
- convert_to_utf8(line, charset);
switch (transfer_encoding) {
case TE_BASE64:
/* process the email header */
while (read_one_header_line(line, sizeof(line), fin))
- check_header(line, p_hdr_data);
+ check_header(line, p_hdr_data, 1);
handle_body();
handle_info();
git_config(git_default_config);
- newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
+ newfd = hold_locked_index(&lock_file, 1);
if (read_cache() < 0)
die("index file corrupt");
for (i = 0; i < added.nr; i++) {
const char *path = added.items[i].path;
- add_file_to_index(path, verbose);
+ add_file_to_cache(path, verbose);
}
for (i = 0; i < deleted.nr; i++) {
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
close(newfd) ||
- commit_lock_file(&lock_file))
+ commit_locked_index(&lock_file))
die("Unable to write new index file");
}
}
off_t ofs)
{
struct revindex_entry *entry = find_packed_object(p, ofs);
- return ((unsigned char *)p->index_data) + 4 * 256 + 24 * entry->nr + 4;
+ return nth_packed_object_sha1(p, entry->nr);
}
static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
static int do_push(const char *repo)
{
const char *uri[MAX_URI];
- int i, n;
+ int i, n, errs;
int common_argc;
const char **argv;
int argc;
argv[argc++] = receivepack;
common_argc = argc;
+ errs = 0;
for (i = 0; i < n; i++) {
int err;
int dest_argc = common_argc;
err = run_command_v_opt(argv, RUN_GIT_CMD);
if (!err)
continue;
+
+ error("failed to push to '%s'", uri[i]);
switch (err) {
case -ERR_RUN_COMMAND_FORK:
- die("unable to fork for %s", sender);
+ error("unable to fork for %s", sender);
case -ERR_RUN_COMMAND_EXEC:
- die("unable to exec %s", sender);
+ error("unable to exec %s", sender);
+ break;
case -ERR_RUN_COMMAND_WAITPID:
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
- die("%s died with strange error", sender);
- default:
- return -err;
+ error("%s died with strange error", sender);
}
+ errs++;
}
- return 0;
+ return !!errs;
}
int cmd_push(int argc, const char **argv, const char *prefix)
}
-static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] <sha1> [<sha2> [<sha3>]])";
+static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])";
static struct lock_file lock_file;
setup_git_directory();
git_config(git_default_config);
- newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
+ newfd = hold_locked_index(&lock_file, 1);
git_config(git_default_config);
continue;
}
+ if (!prefixcmp(arg, "--index-output=")) {
+ set_alternate_index_output(arg + 15);
+ continue;
+ }
+
/* "--prefix=<subdirectory>/" means keep the current index
* entries and put the entries from the tree under the
* given subdirectory.
if (opts.dir)
die("more than one --exclude-per-directory are given.");
- dir = calloc(1, sizeof(*opts.dir));
+ dir = xcalloc(1, sizeof(*opts.dir));
dir->show_ignored = 1;
dir->exclude_per_dir = arg + 24;
opts.dir = dir;
if (0 <= pos)
die("file '%.*s' already exists.",
pfxlen-1, opts.prefix);
+ opts.pos = -1 - pos;
}
if (opts.merge) {
}
if (write_cache(newfd, active_cache, active_nr) ||
- close(newfd) || commit_lock_file(&lock_file))
+ close(newfd) || commit_locked_index(&lock_file))
die("unable to write new index file");
return 0;
}
buffer->nr += len;
}
+static void clear_buffer(struct buffer *buffer)
+{
+ free(buffer->ptr);
+ buffer->ptr = NULL;
+ buffer->nr = buffer->alloc = 0;
+}
+
static int handle_file(const char *path,
unsigned char *sha1, const char *output)
{
else if (!prefixcmp(buf, "======="))
hunk = 2;
else if (!prefixcmp(buf, ">>>>>>> ")) {
+ int one_is_longer = (one->nr > two->nr);
+ int common_len = one_is_longer ? two->nr : one->nr;
+ int cmp = memcmp(one->ptr, two->ptr, common_len);
+
hunk_no++;
hunk = 0;
- if (memcmp(one->ptr, two->ptr, one->nr < two->nr ?
- one->nr : two->nr) > 0) {
+ if ((cmp > 0) || ((cmp == 0) && one_is_longer)) {
struct buffer *swap = one;
one = two;
two = swap;
SHA1_Update(&ctx, two->ptr, two->nr);
SHA1_Update(&ctx, "\0", 1);
}
+ clear_buffer(one);
+ clear_buffer(two);
} else if (hunk == 1)
append_line(one, buf);
else if (hunk == 2)
" --header | --pretty\n"
" --abbrev=nr | --no-abbrev\n"
" --abbrev-commit\n"
+" --left-right\n"
" special purpose:\n"
-" --bisect"
+" --bisect\n"
+" --bisect-vars"
;
static struct rev_info revs;
}
}
-static struct commit_list *find_bisection(struct commit_list *list)
+#define DEBUG_BISECT 0
+
+static inline int weight(struct commit_list *elem)
{
- int nr, closest;
- struct commit_list *p, *best;
+ return *((int*)(elem->item->util));
+}
- nr = 0;
- p = list;
- while (p) {
- if (!revs.prune_fn || (p->item->object.flags & TREECHANGE))
- nr++;
- p = p->next;
+static inline void weight_set(struct commit_list *elem, int weight)
+{
+ *((int*)(elem->item->util)) = weight;
+}
+
+static int count_interesting_parents(struct commit *commit)
+{
+ struct commit_list *p;
+ int count;
+
+ for (count = 0, p = commit->parents; p; p = p->next) {
+ if (p->item->object.flags & UNINTERESTING)
+ continue;
+ count++;
}
- closest = 0;
- best = list;
+ return count;
+}
+
+static inline int halfway(struct commit_list *p, int distance, int nr)
+{
+ /*
+ * Don't short-cut something we are not going to return!
+ */
+ if (revs.prune_fn && !(p->item->object.flags & TREECHANGE))
+ return 0;
+ if (DEBUG_BISECT)
+ return 0;
+ /*
+ * 2 and 3 are halfway of 5.
+ * 3 is halfway of 6 but 2 and 4 are not.
+ */
+ distance *= 2;
+ switch (distance - nr) {
+ case -1: case 0: case 1:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+#if !DEBUG_BISECT
+#define show_list(a,b,c,d) do { ; } while (0)
+#else
+static void show_list(const char *debug, int counted, int nr,
+ struct commit_list *list)
+{
+ struct commit_list *p;
+
+ fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
for (p = list; p; p = p->next) {
- int distance;
+ struct commit_list *pp;
+ struct commit *commit = p->item;
+ unsigned flags = commit->object.flags;
+ enum object_type type;
+ unsigned long size;
+ char *buf = read_sha1_file(commit->object.sha1, &type, &size);
+ char *ep, *sp;
+
+ fprintf(stderr, "%c%c%c ",
+ (flags & TREECHANGE) ? 'T' : ' ',
+ (flags & UNINTERESTING) ? 'U' : ' ',
+ (flags & COUNTED) ? 'C' : ' ');
+ if (commit->util)
+ fprintf(stderr, "%3d", weight(p));
+ else
+ fprintf(stderr, "---");
+ fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
+ for (pp = commit->parents; pp; pp = pp->next)
+ fprintf(stderr, " %.*s", 8,
+ sha1_to_hex(pp->item->object.sha1));
+
+ sp = strstr(buf, "\n\n");
+ if (sp) {
+ sp += 2;
+ for (ep = sp; *ep && *ep != '\n'; ep++)
+ ;
+ fprintf(stderr, " %.*s", (int)(ep - sp), sp);
+ }
+ fprintf(stderr, "\n");
+ }
+}
+#endif /* DEBUG_BISECT */
+
+/*
+ * zero or positive weight is the number of interesting commits it can
+ * reach, including itself. Especially, weight = 0 means it does not
+ * reach any tree-changing commits (e.g. just above uninteresting one
+ * but traversal is with pathspec).
+ *
+ * weight = -1 means it has one parent and its distance is yet to
+ * be computed.
+ *
+ * weight = -2 means it has more than one parent and its distance is
+ * unknown. After running count_distance() first, they will get zero
+ * or positive distance.
+ */
+
+static struct commit_list *find_bisection(struct commit_list *list,
+ int *reaches, int *all)
+{
+ int n, nr, on_list, counted, distance;
+ struct commit_list *p, *best, *next, *last;
+ int *weights;
+
+ show_list("bisection 2 entry", 0, 0, list);
- if (revs.prune_fn && !(p->item->object.flags & TREECHANGE))
+ /*
+ * Count the number of total and tree-changing items on the
+ * list, while reversing the list.
+ */
+ for (nr = on_list = 0, last = NULL, p = list;
+ p;
+ p = next) {
+ unsigned flags = p->item->object.flags;
+
+ next = p->next;
+ if (flags & UNINTERESTING)
continue;
+ p->next = last;
+ last = p;
+ if (!revs.prune_fn || (flags & TREECHANGE))
+ nr++;
+ on_list++;
+ }
+ list = last;
+ show_list("bisection 2 sorted", 0, nr, list);
+
+ *all = nr;
+ weights = xcalloc(on_list, sizeof(int*));
+ counted = 0;
+
+ for (n = 0, p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ unsigned flags = commit->object.flags;
+
+ p->item->util = &weights[n++];
+ switch (count_interesting_parents(commit)) {
+ case 0:
+ if (!revs.prune_fn || (flags & TREECHANGE)) {
+ weight_set(p, 1);
+ counted++;
+ show_list("bisection 2 count one",
+ counted, nr, list);
+ }
+ /*
+ * otherwise, it is known not to reach any
+ * tree-changing commit and gets weight 0.
+ */
+ break;
+ case 1:
+ weight_set(p, -1);
+ break;
+ default:
+ weight_set(p, -2);
+ break;
+ }
+ }
+ show_list("bisection 2 initialize", counted, nr, list);
+
+ /*
+ * If you have only one parent in the resulting set
+ * then you can reach one commit more than that parent
+ * can reach. So we do not have to run the expensive
+ * count_distance() for single strand of pearls.
+ *
+ * However, if you have more than one parents, you cannot
+ * just add their distance and one for yourself, since
+ * they usually reach the same ancestor and you would
+ * end up counting them twice that way.
+ *
+ * So we will first count distance of merges the usual
+ * way, and then fill the blanks using cheaper algorithm.
+ */
+ for (p = list; p; p = p->next) {
+ if (p->item->object.flags & UNINTERESTING)
+ continue;
+ n = weight(p);
+ if (n != -2)
+ continue;
distance = count_distance(p);
clear_distance(list);
+ weight_set(p, distance);
+
+ /* Does it happen to be at exactly half-way? */
+ if (halfway(p, distance, nr)) {
+ p->next = NULL;
+ *reaches = distance;
+ free(weights);
+ return p;
+ }
+ counted++;
+ }
+
+ show_list("bisection 2 count_distance", counted, nr, list);
+
+ while (counted < nr) {
+ for (p = list; p; p = p->next) {
+ struct commit_list *q;
+ unsigned flags = p->item->object.flags;
+
+ if (0 <= weight(p))
+ continue;
+ for (q = p->item->parents; q; q = q->next) {
+ if (q->item->object.flags & UNINTERESTING)
+ continue;
+ if (0 <= weight(q))
+ break;
+ }
+ if (!q)
+ continue;
+
+ /*
+ * weight for p is unknown but q is known.
+ * add one for p itself if p is to be counted,
+ * otherwise inherit it from q directly.
+ */
+ if (!revs.prune_fn || (flags & TREECHANGE)) {
+ weight_set(p, weight(q)+1);
+ counted++;
+ show_list("bisection 2 count one",
+ counted, nr, list);
+ }
+ else
+ weight_set(p, weight(q));
+
+ /* Does it happen to be at exactly half-way? */
+ distance = weight(p);
+ if (halfway(p, distance, nr)) {
+ p->next = NULL;
+ *reaches = distance;
+ free(weights);
+ return p;
+ }
+ }
+ }
+
+ show_list("bisection 2 counted all", counted, nr, list);
+
+ /* Then find the best one */
+ counted = -1;
+ best = list;
+ for (p = list; p; p = p->next) {
+ unsigned flags = p->item->object.flags;
+
+ if (revs.prune_fn && !(flags & TREECHANGE))
+ continue;
+ distance = weight(p);
if (nr - distance < distance)
distance = nr - distance;
- if (distance > closest) {
+ if (distance > counted) {
best = p;
- closest = distance;
+ counted = distance;
+ *reaches = weight(p);
}
}
if (best)
best->next = NULL;
+ free(weights);
return best;
}
struct commit_list *list;
int i;
int read_from_stdin = 0;
+ int bisect_show_vars = 0;
git_config(git_default_config);
init_revisions(&revs, prefix);
bisect_list = 1;
continue;
}
+ if (!strcmp(arg, "--bisect-vars")) {
+ bisect_list = 1;
+ bisect_show_vars = 1;
+ continue;
+ }
if (!strcmp(arg, "--stdin")) {
if (read_from_stdin++)
die("--stdin given twice?");
if (revs.tree_objects)
mark_edges_uninteresting(revs.commits, &revs, show_edge);
- if (bisect_list)
- revs.commits = find_bisection(revs.commits);
+ if (bisect_list) {
+ int reaches = reaches, all = all;
+
+ revs.commits = find_bisection(revs.commits, &reaches, &all);
+ if (bisect_show_vars) {
+ int cnt;
+ if (!revs.commits)
+ return 1;
+ /*
+ * revs.commits can reach "reaches" commits among
+ * "all" commits. If it is good, then there are
+ * (all-reaches) commits left to be bisected.
+ * On the other hand, if it is bad, then the set
+ * to bisect is "reaches".
+ * A bisect set of size N has (N-1) commits further
+ * to test, as we already know one bad one.
+ */
+ cnt = all-reaches;
+ if (cnt < reaches)
+ cnt = reaches;
+ printf("bisect_rev=%s\n"
+ "bisect_nr=%d\n"
+ "bisect_good=%d\n"
+ "bisect_bad=%d\n"
+ "bisect_all=%d\n",
+ sha1_to_hex(revs.commits->item->object.sha1),
+ cnt - 1,
+ all - reaches - 1,
+ reaches - 1,
+ all);
+ return 0;
+ }
+ }
traverse_commit_list(&revs, show_commit, show_object);
oneline = get_oneline(message);
if (action == REVERT) {
+ char *oneline_body = strchr(oneline, ' ');
+
base = commit;
next = commit->parents->item;
- add_to_msg("Revert ");
- add_to_msg(find_unique_abbrev(commit->object.sha1,
- DEFAULT_ABBREV));
- add_to_msg(oneline);
- add_to_msg("\nThis reverts commit ");
+ add_to_msg("Revert \"");
+ add_to_msg(oneline_body + 1);
+ add_to_msg("\"\n\nThis reverts commit ");
add_to_msg(sha1_to_hex(commit->object.sha1));
add_to_msg(".\n");
} else {
#include "tree-walk.h"
static const char builtin_rm_usage[] =
-"git-rm [-f] [-n] [-r] [--cached] [--] <file>...";
+"git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...";
static struct {
int nr, alloc;
if (ce_match_stat(ce, &st, 0))
errs = error("'%s' has local modifications "
"(hint: try -f)", ce->name);
- if (no_head)
- continue;
- /*
- * It is Ok to remove a newly added path, as long as
- * it is cache-clean.
- */
- if (get_tree_entry(head, name, sha1, &mode))
- continue;
- /*
- * Otherwise make sure the version from the HEAD
- * matches the index.
- */
- if (ce->ce_mode != create_ce_mode(mode) ||
- hashcmp(ce->sha1, sha1))
+ if (no_head
+ || get_tree_entry(head, name, sha1, &mode)
+ || ce->ce_mode != create_ce_mode(mode)
+ || hashcmp(ce->sha1, sha1))
errs = error("'%s' has changes staged in the index "
"(hint: try -f)", name);
}
int cmd_rm(int argc, const char **argv, const char *prefix)
{
int i, newfd;
- int show_only = 0, force = 0, index_only = 0, recursive = 0;
+ int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
+ int ignore_unmatch = 0;
const char **pathspec;
char *seen;
git_config(git_default_config);
- newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
+ newfd = hold_locked_index(&lock_file, 1);
if (read_cache() < 0)
die("index file corrupt");
force = 1;
else if (!strcmp(arg, "-r"))
recursive = 1;
+ else if (!strcmp(arg, "--quiet"))
+ quiet = 1;
+ else if (!strcmp(arg, "--ignore-unmatch"))
+ ignore_unmatch = 1;
else
usage(builtin_rm_usage);
}
if (pathspec) {
const char *match;
+ int seen_any = 0;
for (i = 0; (match = pathspec[i]) != NULL ; i++) {
- if (!seen[i])
- die("pathspec '%s' did not match any files",
- match);
+ if (!seen[i]) {
+ if (!ignore_unmatch) {
+ die("pathspec '%s' did not match any files",
+ match);
+ }
+ }
+ else {
+ seen_any = 1;
+ }
if (!recursive && seen[i] == MATCHED_RECURSIVELY)
die("not removing '%s' recursively without -r",
*match ? match : ".");
}
+
+ if (! seen_any)
+ exit(0);
}
/*
* must match; but the file can already been removed, since
* this sequence is a natural "novice" way:
*
- * rm F; git fm F
+ * rm F; git rm F
*
* Further, if HEAD commit exists, "diff-index --cached" must
* report no changes unless forced.
*/
for (i = 0; i < list.nr; i++) {
const char *path = list.name[i];
- printf("rm '%s'\n", path);
+ if (!quiet)
+ printf("rm '%s'\n", path);
if (remove_file_from_cache(path))
die("git-rm: unable to remove %s", path);
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- close(newfd) || commit_lock_file(&lock_file))
+ close(newfd) || commit_locked_index(&lock_file))
die("Unable to write new index file");
}
#include "diff.h"
#include "path-list.h"
#include "revision.h"
+#include "utf8.h"
static const char shortlog_usage[] =
"git-shortlog [-n] [-s] [<commit-id>... ]";
}
+static int parse_uint(char const **arg, int comma)
+{
+ unsigned long ul;
+ int ret;
+ char *endp;
+
+ ul = strtoul(*arg, &endp, 10);
+ if (endp != *arg && *endp && *endp != comma)
+ return -1;
+ ret = (int) ul;
+ if (ret != ul)
+ return -1;
+ *arg = endp;
+ if (**arg)
+ (*arg)++;
+ return ret;
+}
+
+static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
+#define DEFAULT_WRAPLEN 76
+#define DEFAULT_INDENT1 6
+#define DEFAULT_INDENT2 9
+
+static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
+{
+ arg += 2; /* skip -w */
+
+ *wrap = parse_uint(&arg, ',');
+ if (*wrap < 0)
+ die(wrap_arg_usage);
+ *in1 = parse_uint(&arg, ',');
+ if (*in1 < 0)
+ die(wrap_arg_usage);
+ *in2 = parse_uint(&arg, '\0');
+ if (*in2 < 0)
+ die(wrap_arg_usage);
+
+ if (!*wrap)
+ *wrap = DEFAULT_WRAPLEN;
+ if (!*in1)
+ *in1 = DEFAULT_INDENT1;
+ if (!*in2)
+ *in2 = DEFAULT_INDENT2;
+ if (*wrap &&
+ ((*in1 && *wrap <= *in1) ||
+ (*in2 && *wrap <= *in2)))
+ die(wrap_arg_usage);
+}
+
int cmd_shortlog(int argc, const char **argv, const char *prefix)
{
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;
/* since -n is a shadowed rev argument, parse our args first */
while (argc > 1) {
else if (!strcmp(argv[1], "-s") ||
!strcmp(argv[1], "--summary"))
summary = 1;
+ else if (!prefixcmp(argv[1], "-w")) {
+ wrap_lines = 1;
+ parse_wrap_args(argv[1], &in1, &in2, &wrap);
+ }
else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
usage(shortlog_usage);
else
printf("%s: %d\n", list.items[i].path, onelines->nr);
} else {
printf("%s (%d):\n", list.items[i].path, onelines->nr);
- for (j = onelines->nr - 1; j >= 0; j--)
- printf(" %s\n", onelines->items[j].path);
- printf("\n");
+ 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)
+ putchar('\n');
+ }
+ else
+ printf(" %s\n", msg);
+ }
+ putchar('\n');
}
onelines->strdup_paths = 1;
return -1;
}
-static int add_file_to_cache(const char *path)
+static int process_file(const char *path)
{
int size, namelen, option, status;
struct cache_entry *ce;
report("remove '%s'", path);
goto free_return;
}
- if (add_file_to_cache(p))
+ if (process_file(p))
die("Unable to process file %s", path);
report("add '%s'", path);
free_return:
char *path_name;
unsigned char sha1[20];
unsigned int mode;
+ unsigned long ul;
int stage;
/* This reads lines formatted in one of three formats:
if (buf.eof)
break;
- mode = strtoul(buf.buf, &ptr, 8);
- if (ptr == buf.buf || *ptr != ' ')
+ errno = 0;
+ ul = strtoul(buf.buf, &ptr, 8);
+ if (ptr == buf.buf || *ptr != ' '
+ || errno || (unsigned int) ul != ul)
goto bad_line;
+ mode = ul;
tab = strchr(ptr, '\t');
if (!tab || tab - ptr < 41)
/* We can't free this memory, it becomes part of a linked list parsed atexit() */
lock_file = xcalloc(1, sizeof(struct lock_file));
- newfd = hold_lock_file_for_update(lock_file, get_index_file(), 0);
+ newfd = hold_locked_index(lock_file, 0);
if (newfd < 0)
lock_error = errno;
if (i+3 >= argc)
die("git-update-index: --cacheinfo <mode> <sha1> <path>");
- if ((sscanf(argv[i+1], "%o", &mode) != 1) ||
+ if ((strtoul_ui(argv[i+1], 8, &mode) != 1) ||
get_sha1_hex(argv[i+2], sha1) ||
add_cacheinfo(mode, sha1, argv[i+3], 0))
die("git-update-index: --cacheinfo"
get_index_file(), strerror(lock_error));
}
if (write_cache(newfd, active_cache, active_nr) ||
- close(newfd) || commit_lock_file(lock_file))
+ close(newfd) || commit_locked_index(lock_file))
die("Unable to write new index file");
}
/* We can't free this memory, it becomes part of a linked list parsed atexit() */
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
- newfd = hold_lock_file_for_update(lock_file, get_index_file(), 0);
+ newfd = hold_locked_index(lock_file, 1);
entries = read_cache();
if (entries < 0)
extern struct cache_entry **active_cache;
extern unsigned int active_nr, active_alloc, active_cache_changed;
extern struct cache_tree *active_cache_tree;
-extern int cache_errno;
enum object_type {
OBJ_BAD = -1,
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern int remove_cache_entry_at(int pos);
extern int remove_file_from_cache(const char *path);
-extern int add_file_to_index(const char *path, int verbose);
+extern int add_file_to_cache(const char *path, int verbose);
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
};
extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
extern int commit_lock_file(struct lock_file *);
+
+extern int hold_locked_index(struct lock_file *, int);
+extern int commit_locked_index(struct lock_file *);
+extern void set_alternate_index_output(const char *);
+
extern void rollback_lock_file(struct lock_file *);
-extern int delete_ref(const char *, unsigned char *sha1);
+extern int delete_ref(const char *, const unsigned char *sha1);
/* Environment bits from configuration mechanism */
extern int use_legacy_headers;
extern void unuse_pack(struct pack_window **);
extern struct packed_git *add_packed_git(const char *, int, int);
extern uint32_t num_packed_objects(const struct packed_git *p);
-extern int nth_packed_object_sha1(const struct packed_git *, uint32_t, unsigned char*);
+extern const unsigned char *nth_packed_object_sha1(const struct packed_git *, uint32_t);
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
extern int pager_use_color;
/* base85 */
-int decode_85(char *dst, char *line, int linelen);
-void encode_85(char *buf, unsigned char *data, int bytes);
+int decode_85(char *dst, const char *line, int linelen);
+void encode_85(char *buf, const unsigned char *data, int bytes);
/* alloc.c */
struct blob;
extern int convert_to_git(const char *path, char **bufp, unsigned long *sizep);
extern int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep);
+/* match-trees.c */
+void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
+
#endif /* CACHE_H */
#include "pkt-line.h"
#include "utf8.h"
#include "interpolate.h"
+#include "diff.h"
+#include "revision.h"
int save_commit_buffer = 1;
static char *replace_encoding_header(char *buf, const char *encoding)
{
char *encoding_header = strstr(buf, "\nencoding ");
+ char *header_end = strstr(buf, "\n\n");
char *end_of_encoding_header;
int encoding_header_pos;
int encoding_header_len;
int need_len;
int buflen = strlen(buf) + 1;
- if (!encoding_header)
- return buf; /* should not happen but be defensive */
+ if (!header_end)
+ header_end = buf + buflen;
+ if (!encoding_header || encoding_header >= header_end)
+ return buf;
encoding_header++;
end_of_encoding_header = strchr(encoding_header, '\n');
if (!end_of_encoding_header)
if (msg + start == ep)
return;
- table[5].value = xstrndup(msg + start, ep - msg + start);
+ table[5].value = xstrndup(msg + start, ep - (msg + start));
/* parse tz */
for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
{ "%Cgreen" }, /* green */
{ "%Cblue" }, /* blue */
{ "%Creset" }, /* reset color */
- { "%n" } /* newline */
+ { "%n" }, /* newline */
+ { "%m" }, /* left/right/bottom */
};
enum interp_index {
IHASH = 0, IHASH_ABBREV,
ISUBJECT,
IBODY,
IRED, IGREEN, IBLUE, IRESET_COLOR,
- INEWLINE
+ INEWLINE,
+ ILEFT_RIGHT,
};
struct commit_list *p;
char parents[1024];
int i;
enum { HEADER, SUBJECT, BODY } state;
- if (INEWLINE + 1 != ARRAY_SIZE(table))
+ if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
die("invalid interp table!");
/* these are independent of the commit */
interp_set_entry(table, ITREE_ABBREV,
find_unique_abbrev(commit->tree->object.sha1,
DEFAULT_ABBREV));
+ interp_set_entry(table, ILEFT_RIGHT,
+ (commit->object.flags & BOUNDARY)
+ ? "-"
+ : (commit->object.flags & SYMMETRIC_LEFT)
+ ? "<"
+ : ">");
+
+ parents[1] = 0;
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
- i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
+ i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
sha1_to_hex(p->item->object.sha1));
- interp_set_entry(table, IPARENTS, parents);
+ interp_set_entry(table, IPARENTS, parents + 1);
+
+ parents[1] = 0;
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
- i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
+ i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
find_unique_abbrev(p->item->object.sha1,
DEFAULT_ABBREV));
- interp_set_entry(table, IPARENTS_ABBREV, parents);
+ interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
int eol;
fill_person(table + ICOMMITTER_NAME,
msg + i + 10, eol - i - 10);
else if (!prefixcmp(msg + i, "encoding "))
- table[IENCODING].value = xstrndup(msg + i, eol - i);
+ table[IENCODING].value =
+ xstrndup(msg + i + 9, eol - i - 9);
i = eol;
}
if (msg[i])
#include "object.h"
#include "tree.h"
+#include "decorate.h"
struct commit_list {
struct commit *item;
extern int save_commit_buffer;
extern const char *commit_type;
+/* While we can decorate any object with a name, it's only used for commits.. */
+extern struct decoration name_decoration;
+struct name_decoration {
+ struct name_decoration *next;
+ char name[1];
+};
+
struct commit *lookup_commit(const unsigned char *sha1);
struct commit *lookup_commit_reference(const unsigned char *sha1);
struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
}
if (!(config_file = fopen(config_filename, "rb"))) {
- ret = error("Could not open config file!");
- goto out;
+ /* no config file means nothing to rename, no error */
+ goto unlock_and_out;
}
while (fgets(buf, sizeof(buf), config_file)) {
}
}
fclose(config_file);
+ unlock_and_out:
if (close(out_fd) || commit_lock_file(lock) < 0)
ret = error("Cannot commit config file!");
out:
AR = @AR@
TAR = @TAR@
#INSTALL = @INSTALL@ # needs install-sh or install.sh in sources
+TCLTK_PATH = @TCLTK_PATH@
prefix = @prefix@
exec_prefix = @exec_prefix@
# Define PERL_PATH to provide path to Perl.
GIT_ARG_SET_PATH(perl)
#
+# Declare the with-tcltk/without-tcltk options.
+AC_ARG_WITH(tcltk,
+AS_HELP_STRING([--with-tcltk],[use Tcl/Tk GUI (default is YES)])
+AS_HELP_STRING([],[ARG is the full path to the Tcl/Tk interpreter.])
+AS_HELP_STRING([],[Bare --with-tcltk will make the GUI part only if])
+AS_HELP_STRING([],[Tcl/Tk interpreter will be found in a system.]),\
+GIT_PARSE_WITH(tcltk))
+#
## Checks for programs.
#AC_PROG_INSTALL # needs install-sh or install.sh in sources
AC_CHECK_TOOL(AR, ar, :)
AC_CHECK_PROGS(TAR, [gtar tar])
+# TCLTK_PATH will be set to some value if we want Tcl/Tk
+# or will be empty otherwise.
+if test -z "$NO_TCLTK"; then
+ if test "$with_tcltk" = ""; then
+ # No Tcl/Tk switches given. Do not check for Tcl/Tk, use bare 'wish'.
+ TCLTK_PATH=wish
+ AC_SUBST(TCLTK_PATH)
+ elif test "$with_tcltk" = "yes"; then
+ # Tcl/Tk check requested.
+ AC_CHECK_PROGS(TCLTK_PATH, [wish], )
+ else
+ AC_MSG_RESULT([Using Tcl/Tk interpreter $with_tcltk])
+ TCLTK_PATH="$with_tcltk"
+ AC_SUBST(TCLTK_PATH)
+ fi
+fi
## Checks for libraries.
AC_MSG_NOTICE([CHECKS for libraries])
if (colon) {
*colon = 0;
port = colon + 1;
+ if (!*port)
+ port = "<none>";
}
memset(&hints, 0, sizeof(hints));
gai = getaddrinfo(host, port, &hints, &ai);
if (gai)
- die("Unable to look up %s (%s)", host, gai_strerror(gai));
+ die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai));
for (ai0 = ai; ai; ai = ai->ai_next) {
sockfd = socket(ai->ai_family,
all: $(ELC)
install: all
- $(INSTALL) -d $(emacsdir)
- $(INSTALL_ELC) $(ELC) $(emacsdir)
+ $(INSTALL) -d $(DESTDIR)$(emacsdir)
+ $(INSTALL_ELC) $(ELC) $(DESTDIR)$(emacsdir)
%.elc: %.el
$(EMACS) -batch -f batch-byte-compile $<
;; License: GPL
;; Keywords: git, version control, release management
;;
-;; Compatibility: Emacs21
-
+;; Compatibility: Emacs21, Emacs22 and EmacsCVS
+;; Git 1.5 and up
;; This file is *NOT* part of GNU Emacs.
;; This file is distributed under the same terms as GNU Emacs.
;;; Compatibility:
;;
-;; It requires GNU Emacs 21. If you'are using Emacs 20, try
-;; changing this:
+;; It requires GNU Emacs 21 or later and Git 1.5.0 and up
+;;
+;; If you'are using Emacs 20, try changing this:
;;
;; (overlay-put ovl 'face (list :background
;; (cdr (assq 'color (cddddr info)))))
;;
;;; Code:
-(require 'cl) ; to use `push', `pop'
-
-(defun color-scale (l)
- (let* ((colors ())
- r g b)
- (setq r l)
- (while r
- (setq g l)
- (while g
- (setq b l)
- (while b
- (push (concat "#" (car r) (car g) (car b)) colors)
- (pop b))
- (pop g))
- (pop r))
- colors))
+(eval-when-compile (require 'cl)) ; to use `push', `pop'
+
+
+(defun git-blame-color-scale (&rest elements)
+ "Given a list, returns a list of triples formed with each
+elements of the list.
+
+a b => bbb bba bab baa abb aba aaa aab"
+ (let (result)
+ (dolist (a elements)
+ (dolist (b elements)
+ (dolist (c elements)
+ (setq result (cons (format "#%s%s%s" a b c) result)))))
+ result))
+
+;; (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c") =>
+;; ("#3c3c3c" "#3c3c14" "#3c3c34" "#3c3c2c" "#3c3c1c" "#3c3c24"
+;; "#3c3c04" "#3c3c0c" "#3c143c" "#3c1414" "#3c1434" "#3c142c" ...)
+
+(defmacro git-blame-random-pop (l)
+ "Select a random element from L and returns it. Also remove
+selected element from l."
+ ;; only works on lists with unique elements
+ `(let ((e (elt ,l (random (length ,l)))))
+ (setq ,l (remove e ,l))
+ e))
(defvar git-blame-dark-colors
- (color-scale '("0c" "04" "24" "1c" "2c" "34" "14" "3c")))
+ (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c")
+ "*List of colors (format #RGB) to use in a dark environment.
+
+To check out the list, evaluate (list-colors-display git-blame-dark-colors).")
(defvar git-blame-light-colors
- (color-scale '("c4" "d4" "cc" "dc" "f4" "e4" "fc" "ec")))
+ (git-blame-color-scale "c4" "d4" "cc" "dc" "f4" "e4" "fc" "ec")
+ "*List of colors (format #RGB) to use in a light environment.
+
+To check out the list, evaluate (list-colors-display git-blame-light-colors).")
-(defvar git-blame-ancient-color "dark green")
+(defvar git-blame-colors '()
+ "Colors used by git-blame. The list is built once when activating git-blame
+minor mode.")
+
+(defvar git-blame-ancient-color "dark green"
+ "*Color to be used for ancient commit.")
(defvar git-blame-autoupdate t
"*Automatically update the blame display while editing")
"A queue of update requests")
(make-variable-buffer-local 'git-blame-update-queue)
+;; FIXME: docstrings
+(defvar git-blame-file nil)
+(defvar git-blame-current nil)
+
(defvar git-blame-mode nil)
(make-variable-buffer-local 'git-blame-mode)
-(unless (assq 'git-blame-mode minor-mode-alist)
- (setq minor-mode-alist
- (cons (list 'git-blame-mode " blame")
- minor-mode-alist)))
+
+(defvar git-blame-mode-line-string " blame"
+ "String to display on the mode line when git-blame is active.")
+
+(or (assq 'git-blame-mode minor-mode-alist)
+ (setq minor-mode-alist
+ (cons '(git-blame-mode git-blame-mode-line-string) minor-mode-alist)))
;;;###autoload
(defun git-blame-mode (&optional arg)
- "Minor mode for displaying Git blame"
+ "Toggle minor mode for displaying Git blame
+
+With prefix ARG, turn the mode on if ARG is positive."
(interactive "P")
- (if arg
- (setq git-blame-mode (eq arg 1))
- (setq git-blame-mode (not git-blame-mode)))
+ (cond
+ ((null arg)
+ (if git-blame-mode (git-blame-mode-off) (git-blame-mode-on)))
+ ((> (prefix-numeric-value arg) 0) (git-blame-mode-on))
+ (t (git-blame-mode-off))))
+
+(defun git-blame-mode-on ()
+ "Turn on git-blame mode.
+
+See also function `git-blame-mode'."
(make-local-variable 'git-blame-colors)
(if git-blame-autoupdate
(add-hook 'after-change-functions 'git-blame-after-change nil t)
(remove-hook 'after-change-functions 'git-blame-after-change t))
(git-blame-cleanup)
- (if git-blame-mode
- (progn
- (let ((bgmode (cdr (assoc 'background-mode (frame-parameters)))))
- (if (eq bgmode 'dark)
- (setq git-blame-colors git-blame-dark-colors)
- (setq git-blame-colors git-blame-light-colors)))
- (setq git-blame-cache (make-hash-table :test 'equal))
- (git-blame-run))
- (cancel-timer git-blame-idle-timer)))
+ (let ((bgmode (cdr (assoc 'background-mode (frame-parameters)))))
+ (if (eq bgmode 'dark)
+ (setq git-blame-colors git-blame-dark-colors)
+ (setq git-blame-colors git-blame-light-colors)))
+ (setq git-blame-cache (make-hash-table :test 'equal))
+ (setq git-blame-mode t)
+ (git-blame-run))
+
+(defun git-blame-mode-off ()
+ "Turn off git-blame mode.
+
+See also function `git-blame-mode'."
+ (git-blame-cleanup)
+ (if git-blame-idle-timer (cancel-timer git-blame-idle-timer))
+ (setq git-blame-mode nil))
;;;###autoload
(defun git-reblame ()
"Recalculate all blame information in the current buffer"
- (unless git-blame-mode
- (error "git-blame is not active"))
(interactive)
+ (unless git-blame-mode
+ (error "Git-blame is not active"))
+
(git-blame-cleanup)
(git-blame-run))
(t
nil)))
-
(defun git-blame-new-commit (hash src-line res-line num-lines)
(save-excursion
(set-buffer git-blame-file)
(inhibit-point-motion-hooks t)
(inhibit-modification-hooks t))
(when (not info)
- (let ((color (pop git-blame-colors)))
- (unless color
- (setq color git-blame-ancient-color))
+ ;; Assign a random color to each new commit info
+ ;; Take care not to select the same color multiple times
+ (let ((color (if git-blame-colors
+ (git-blame-random-pop git-blame-colors)
+ git-blame-ancient-color)))
(setq info (list hash src-line res-line num-lines
(git-describe-commit hash)
(cons 'color color))))
(locale-charset-to-coding-system repo-config))
'utf-8)))
+(defun git-get-logoutput-coding-system ()
+ "Return the coding system used for git-log output."
+ (let ((repo-config (or (git-config "i18n.logoutputencoding")
+ (git-config "i18n.commitencoding"))))
+ (or git-commits-coding-system
+ (and repo-config
+ (fboundp 'locale-charset-to-coding-system)
+ (locale-charset-to-coding-system repo-config))
+ 'utf-8)))
+
(defun git-escape-file-name (name)
"Escape a file name if necessary."
(if (string-match "[\n\t\"\\]" name)
(push (match-string 0) heads))))
(nreverse heads)))
+(defun git-get-commit-description (commit)
+ "Get a one-line description of COMMIT."
+ (let ((coding-system-for-read (git-get-logoutput-coding-system)))
+ (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit)))
+ (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr))
+ (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr))
+ descr))))
+
;;;; File info structure
;;;; ------------------------------------------------------------
"Refresh the ewoc header and footer."
(let ((branch (git-symbolic-ref "HEAD"))
(head (if (git-empty-db-p) "Nothing committed yet"
- (substring (git-rev-parse "HEAD") 0 10)))
+ (git-get-commit-description "HEAD")))
(merge-heads (git-get-merge-heads)))
(ewoc-set-hf status
(format "Directory: %s\nBranch: %s\nHead: %s%s\n"
head
(if merge-heads
(concat "\nMerging: "
- (mapconcat (lambda (str) (substring str 0 10)) merge-heads " "))
+ (mapconcat (lambda (str) (git-get-commit-description str)) merge-heads "\n "))
""))
(if (ewoc-nth status 0) "" " No changes."))))
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Andy Parkins
+#
+# An example hook script to mail out commit update information. This hook sends emails
+# listing new revisions to the repository introduced by the change being reported. The
+# rule is that (for branch updates) each commit will appear on one email and one email
+# only.
+#
+# This hook is stored in the contrib/hooks directory. Your distribution will have put
+# this somewhere standard. You should make this script executable then link to it in
+# the repository you would like to use it in. For example, on debian the hook is stored
+# in /usr/share/doc/git-core/contrib/hooks/post-receive-email:
+#
+# chmod a+x post-receive-email
+# cd /path/to/your/repository.git
+# ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive
+#
+# This hook script assumes it is enabled on the central repository of a project, with
+# all users pushing only to it and not between each other. It will still work if you
+# don't operate in that style, but it would become possible for the email to be from
+# someone other than the person doing the push.
+#
+# Config
+# ------
+# hooks.mailinglist
+# This is the list that all pushes will go to; leave it blank to not send
+# emails for every ref update.
+# hooks.announcelist
+# This is the list that all pushes of annotated tags will go to. Leave it
+# blank to default to the mailinglist field. The announce emails lists the
+# short log summary of the changes since the last annotated tag.
+# hook.envelopesender
+# If set then the -f option is passed to sendmail to allow the envelope sender
+# address to be set
+#
+# Notes
+# -----
+# All emails have their subjects prefixed with "[SCM]" to aid filtering.
+# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
+# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
+# give information for debugging.
+#
+
+# ---------------------------- Functions
+
+#
+# Top level email generation function. This decides what type of update
+# this is and calls the appropriate body-generation routine after outputting
+# the common header
+#
+# Note this function doesn't actually generate any email output, that is taken
+# care of by the functions it calls:
+# - generate_email_header
+# - generate_create_XXXX_email
+# - generate_update_XXXX_email
+# - generate_delete_XXXX_email
+# - generate_email_footer
+#
+generate_email()
+{
+ # --- Arguments
+ oldrev=$(git rev-parse $1)
+ newrev=$(git rev-parse $2)
+ refname="$3"
+
+ # --- Interpret
+ # 0000->1234 (create)
+ # 1234->2345 (update)
+ # 2345->0000 (delete)
+ if expr "$oldrev" : '0*$' >/dev/null
+ then
+ change_type="create"
+ else
+ if expr "$newrev" : '0*$' >/dev/null
+ then
+ change_type="delete"
+ else
+ change_type="update"
+ fi
+ fi
+
+ # --- Get the revision types
+ newrev_type=$(git cat-file -t $newrev 2> /dev/null)
+ oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null)
+ case "$change_type" in
+ create|update)
+ rev="$newrev"
+ rev_type="$newrev_type"
+ ;;
+ delete)
+ rev="$oldrev"
+ rev_type="$oldrev_type"
+ ;;
+ esac
+
+ # The revision type tells us what type the commit is, combined with
+ # the location of the ref we can decide between
+ # - working branch
+ # - tracking branch
+ # - unannoted tag
+ # - annotated tag
+ case "$refname","$rev_type" in
+ refs/tags/*,commit)
+ # un-annotated tag
+ refname_type="tag"
+ short_refname=${refname##refs/tags/}
+ ;;
+ refs/tags/*,tag)
+ # annotated tag
+ refname_type="annotated tag"
+ short_refname=${refname##refs/tags/}
+ # change recipients
+ if [ -n "$announcerecipients" ]; then
+ recipients="$announcerecipients"
+ fi
+ ;;
+ refs/heads/*,commit)
+ # branch
+ refname_type="branch"
+ short_refname=${refname##refs/heads/}
+ ;;
+ refs/remotes/*,commit)
+ # tracking branch
+ refname_type="tracking branch"
+ short_refname=${refname##refs/remotes/}
+ echo >&2 "*** Push-update of tracking branch, $refname"
+ echo >&2 "*** - no email generated."
+ exit 0
+ ;;
+ *)
+ # Anything else (is there anything else?)
+ echo >&2 "*** Unknown type of update to $refname ($rev_type)"
+ echo >&2 "*** - no email generated"
+ exit 1
+ ;;
+ esac
+
+ # Check if we've got anyone to send to
+ if [ -z "$recipients" ]; then
+ echo >&2 "*** hooks.recipients is not set so no email will be sent"
+ echo >&2 "*** for $refname update $oldrev->$newrev"
+ exit 0
+ fi
+
+ # Email parameters
+ # The committer will be obtained from the latest existing rev; so
+ # for a deletion it will be the oldrev, for the others, then newrev
+ committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" |
+ sed -ne 's/\(.*\) </"\1" </p')
+ # The email subject will contain the best description of the ref
+ # that we can build from the parameters
+ describe=$(git describe $rev 2>/dev/null)
+ if [ -z "$describe" ]; then
+ describe=$rev
+ fi
+
+ generate_email_header
+
+ # Call the correct body generation function
+ fn_name=general
+ case "$refname_type" in
+ "tracking branch"|branch)
+ fn_name=branch
+ ;;
+ "annotated tag")
+ fn_name=atag
+ ;;
+ esac
+ generate_${change_type}_${fn_name}_email
+
+ generate_email_footer
+}
+
+generate_email_header()
+{
+ # --- Email (all stdout will be the email)
+ # Generate header
+ cat <<-EOF
+ From: $committer
+ To: $recipients
+ Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
+ X-Git-Refname: $refname
+ X-Git-Reftype: $refname_type
+ X-Git-Oldrev: $oldrev
+ X-Git-Newrev: $newrev
+
+ This is an automated email from the git hooks/post-receive script. It was
+ generated because a ref change was pushed to the repository containing
+ the project "$projectdesc".
+
+ The $refname_type, $short_refname has been ${change_type}d
+ EOF
+}
+
+generate_email_footer()
+{
+ cat <<-EOF
+
+
+ hooks/post-receive
+ --
+ $projectdesc
+ EOF
+}
+
+# --------------- Branches
+
+#
+# Called for the creation of a branch
+#
+generate_create_branch_email()
+{
+ # This is a new branch and so oldrev is not valid
+ echo " at $newrev ($newrev_type)"
+ echo ""
+
+ echo $LOGBEGIN
+ # This shows all log entries that are not already covered by
+ # another ref - i.e. commits that are now accessible from this
+ # ref that were previously not accessible (see generate_update_branch_email
+ # for the explanation of this command)
+ git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
+ git rev-list --pretty --stdin $newrev
+ echo $LOGEND
+}
+
+#
+# Called for the change of a pre-existing branch
+#
+generate_update_branch_email()
+{
+ # Consider this:
+ # 1 --- 2 --- O --- X --- 3 --- 4 --- N
+ #
+ # O is $oldrev for $refname
+ # N is $newrev for $refname
+ # X is a revision pointed to by some other ref, for which we may
+ # assume that an email has already been generated.
+ # In this case we want to issue an email containing only revisions
+ # 3, 4, and N. Given (almost) by
+ #
+ # git-rev-list N ^O --not --all
+ #
+ # The reason for the "almost", is that the "--not --all" will take
+ # precedence over the "N", and effectively will translate to
+ #
+ # git-rev-list N ^O ^X ^N
+ #
+ # So, we need to build up the list more carefully. git-rev-parse will
+ # generate a list of revs that may be fed into git-rev-list. We can get
+ # it to make the "--not --all" part and then filter out the "^N" with:
+ #
+ # git-rev-parse --not --all | grep -v N
+ #
+ # Then, using the --stdin switch to git-rev-list we have effectively
+ # manufactured
+ #
+ # git-rev-list N ^O ^X
+ #
+ # This leaves a problem when someone else updates the repository
+ # while this script is running. Their new value of the ref we're working
+ # on would be included in the "--not --all" output; and as our $newrev
+ # would be an ancestor of that commit, it would exclude all of our
+ # commits. What we really want is to exclude the current value of
+ # $refname from the --not list, rather than N itself. So:
+ #
+ # git-rev-parse --not --all | grep -v $(git-rev-parse $refname)
+ #
+ # Get's us to something pretty safe (apart from the small time between
+ # refname being read, and git-rev-parse running - for that, I give up)
+ #
+ #
+ # Next problem, consider this:
+ # * --- B --- * --- O ($oldrev)
+ # \
+ # * --- X --- * --- N ($newrev)
+ #
+ # That is to say, there is no guarantee that oldrev is a strict subset of
+ # newrev (it would have required a --force, but that's allowed). So, we
+ # can't simply say rev-list $oldrev..$newrev. Instead we find the common
+ # base of the two revs and list from there.
+ #
+ # As above, we need to take into account the presence of X; if another
+ # branch is already in the repository and points at some of the revisions
+ # that we are about to output - we don't want them. The solution is as
+ # before: git-rev-parse output filtered.
+ #
+ # Finally, tags:
+ # 1 --- 2 --- O --- T --- 3 --- 4 --- N
+ #
+ # Tags pushed into the repository generate nice shortlog emails that
+ # summarise the commits between them and the previous tag. However,
+ # those emails don't include the full commit messages that we output
+ # for a branch update. Therefore we still want to output revisions
+ # that have been output on a tag email.
+ #
+ # Luckily, git-rev-parse includes just the tool. Instead of using "--all"
+ # we use "--branches"; this has the added benefit that "remotes/" will
+ # be ignored as well.
+
+ # List all of the revisions that were removed by this update, in a fast forward
+ # update, this list will be empty, because rev-list O ^N is empty. For a non
+ # fast forward, O ^N is the list of removed revisions
+ fastforward=""
+ rev=""
+ for rev in $(git rev-list $newrev..$oldrev)
+ do
+ revtype=$(git cat-file -t "$rev")
+ echo " discards $rev ($revtype)"
+ done
+ if [ -z "$rev" ]; then
+ fast_forward=1
+ fi
+
+ # List all the revisions from baserev to newrev in a kind of
+ # "table-of-contents"; note this list can include revisions that have
+ # already had notification emails and is present to show the full detail
+ # of the change from rolling back the old revision to the base revision and
+ # then forward to the new revision
+ for rev in $(git rev-list $oldrev..$newrev)
+ do
+ revtype=$(git cat-file -t "$rev")
+ echo " via $rev ($revtype)"
+ done
+
+ if [ -z "$fastforward" ]; then
+ echo " from $oldrev ($oldrev_type)"
+ else
+ echo ""
+ echo "This update added new revisions after undoing old revisions. That is to"
+ echo "say, the old revision is not a strict subset of the new revision. This"
+ echo "situation occurs when you --force push a change and generate a"
+ echo "repository containing something like this:"
+ echo ""
+ echo " * -- * -- B -- O -- O -- O ($oldrev)"
+ echo " \\"
+ echo " N -- N -- N ($newrev)"
+ echo ""
+ echo "When this happens we assume that you've already had alert emails for all"
+ echo "of the O revisions, and so we here report only the revisions in the N"
+ echo "branch from the common base, B."
+ fi
+
+ echo ""
+ echo "Those revisions listed above that are new to this repository have"
+ echo "not appeared on any other notification email; so we list those"
+ echo "revisions in full, below."
+
+ echo ""
+ echo $LOGBEGIN
+ git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
+ git rev-list --pretty --stdin $oldrev..$newrev
+
+ # XXX: Need a way of detecting whether git rev-list actually outputted
+ # anything, so that we can issue a "no new revisions added by this
+ # update" message
+
+ echo $LOGEND
+
+ # The diffstat is shown from the old revision to the new revision. This
+ # is to show the truth of what happened in this change. There's no point
+ # showing the stat from the base to the new revision because the base
+ # is effectively a random revision at this point - the user will be
+ # interested in what this revision changed - including the undoing of
+ # previous revisions in the case of non-fast forward updates.
+ echo ""
+ echo "Summary of changes:"
+ git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev
+}
+
+#
+# Called for the deletion of a branch
+#
+generate_delete_branch_email()
+{
+ echo " was $oldrev"
+ echo ""
+ echo $LOGEND
+ git show -s --pretty=oneline $oldrev
+ echo $LOGEND
+}
+
+# --------------- Annotated tags
+
+#
+# Called for the creation of an annotated tag
+#
+generate_create_atag_email()
+{
+ echo " at $newrev ($newrev_type)"
+
+ generate_atag_email
+}
+
+#
+# Called for the update of an annotated tag (this is probably a rare event
+# and may not even be allowed)
+#
+generate_update_atag_email()
+{
+ echo " to $newrev ($newrev_type)"
+ echo " from $oldrev (which is now obsolete)"
+
+ generate_atag_email
+}
+
+#
+# Called when an annotated tag is created or changed
+#
+generate_atag_email()
+{
+ # Use git-for-each-ref to pull out the individual fields from the tag
+ eval $(git for-each-ref --shell --format='
+ tagobject=%(*objectname)
+ tagtype=%(*objecttype)
+ tagger=%(taggername)
+ tagged=%(taggerdate)' $refname
+ )
+
+ echo " tagging $tagobject ($tagtype)"
+ case "$tagtype" in
+ commit)
+ # If the tagged object is a commit, then we assume this is a
+ # release, and so we calculate which tag this tag is replacing
+ prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
+
+ if [ -n "$prevtag" ]; then
+ echo " replaces $prevtag"
+ fi
+ ;;
+ *)
+ echo " length $(git cat-file -s $tagobject) bytes"
+ ;;
+ esac
+ echo " tagged by $tagger"
+ echo " on $tagged"
+
+ echo ""
+ echo $LOGBEGIN
+
+ # Show the content of the tag message; this might contain a change log
+ # or release notes so is worth displaying.
+ git cat-file tag $newrev | sed -e '1,/^$/d'
+
+ echo ""
+ case "$tagtype" in
+ commit)
+ # Only commit tags make sense to have rev-list operations performed
+ # on them
+ if [ -n "$prevtag" ]; then
+ # Show changes since the previous release
+ git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
+ else
+ # No previous tag, show all the changes since time began
+ git rev-list --pretty=short $newrev | git shortlog
+ fi
+ ;;
+ *)
+ # XXX: Is there anything useful we can do for non-commit objects?
+ ;;
+ esac
+
+ echo $LOGEND
+}
+
+#
+# Called for the deletion of an annotated tag
+#
+generate_delete_atag_email()
+{
+ echo " was $oldrev"
+ echo ""
+ echo $LOGEND
+ git show -s --pretty=oneline $oldrev
+ echo $LOGEND
+}
+
+# --------------- General references
+
+#
+# Called when any other type of reference is created (most likely a
+# non-annotated tag)
+#
+generate_create_general_email()
+{
+ echo " at $newrev ($newrev_type)"
+
+ generate_general_email
+}
+
+#
+# Called when any other type of reference is updated (most likely a
+# non-annotated tag)
+#
+generate_update_general_email()
+{
+ echo " to $newrev ($newrev_type)"
+ echo " from $oldrev"
+
+ generate_general_email
+}
+
+#
+# Called for creation or update of any other type of reference
+#
+generate_general_email()
+{
+ # Unannotated tags are more about marking a point than releasing a version;
+ # therefore we don't do the shortlog summary that we do for annotated tags
+ # above - we simply show that the point has been marked, and print the log
+ # message for the marked point for reference purposes
+ #
+ # Note this section also catches any other reference type (although there
+ # aren't any) and deals with them in the same way.
+
+ echo ""
+ if [ "$newrev_type" = "commit" ]; then
+ echo $LOGBEGIN
+ git show --no-color --root -s $newrev
+ echo $LOGEND
+ else
+ # What can we do here? The tag marks an object that is not a commit,
+ # so there is no log for us to display. It's probably not wise to
+ # output git-cat-file as it could be a binary blob. We'll just say how
+ # big it is
+ echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
+ fi
+}
+
+#
+# Called for the deletion of any other type of reference
+#
+generate_delete_general_email()
+{
+ echo " was $oldrev"
+ echo ""
+ echo $LOGEND
+ git show -s --pretty=oneline $oldrev
+ echo $LOGEND
+}
+
+# ---------------------------- main()
+
+# --- Constants
+EMAILPREFIX="[SCM] "
+LOGBEGIN="- Log -----------------------------------------------------------------"
+LOGEND="-----------------------------------------------------------------------"
+
+# --- Config
+# Set GIT_DIR either from the working directory, or from the environment
+# variable.
+GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
+if [ -z "$GIT_DIR" ]; then
+ echo >&2 "fatal: post-receive: GIT_DIR not set"
+ exit 1
+fi
+
+projectdesc=$(sed -e '1p' "$GIT_DIR/description")
+# Check if the description is unchanged from it's default, and shorten it to a
+# more manageable length if it is
+if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
+then
+ projectdesc="UNNAMED PROJECT"
+fi
+
+recipients=$(git repo-config hooks.mailinglist)
+announcerecipients=$(git repo-config hooks.announcelist)
+envelopesender=$(git-repo-config hooks.envelopesender)
+
+# --- Main loop
+# Allow dual mode: run from the command line just like the update hook, or if
+# no arguments are given then run as a hook script
+if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
+ # Output to the terminal in command line mode - if someone wanted to
+ # resend an email; they could redirect the output to sendmail themselves
+ PAGER= generate_email $2 $3 $1
+else
+ if [ -n "$envelopesender" ]; then
+ envelopesender="-f '$envelopesender'"
+ fi
+
+ while read oldrev newrev refname
+ do
+ generate_email $oldrev $newrev $refname |
+ /usr/sbin/sendmail -t $envelopesender
+ done
+fi
--- /dev/null
+#!/bin/sh
+
+usage () {
+ echo "usage:" $@
+ exit 127
+}
+
+die () {
+ echo $@
+ exit 128
+}
+
+if test $# -lt 2 || test $# -gt 3
+then
+ usage "$0 <repository> <new_workdir> [<branch>]"
+fi
+
+orig_git=$1
+new_workdir=$2
+branch=$3
+
+# want to make sure that what is pointed to has a .git directory ...
+test -d "$orig_git/.git" || die "\"$orig_git\" is not a git repository!"
+
+# don't link to a workdir
+if test -L "$orig_git/.git/config"
+then
+ die "\"$orig_git\" is a working directory only, please specify" \
+ "a complete repository."
+fi
+
+# make sure the the links use full paths
+orig_git=$(cd "$orig_git"; pwd)
+
+# create the workdir
+mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
+
+# create the links to the original repo. explictly exclude index, HEAD and
+# logs/HEAD from the list since they are purely related to the current working
+# directory, and should not be shared.
+for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache
+do
+ case $x in
+ */*)
+ mkdir -p "$(dirname "$new_workdir/.git/$x")"
+ ;;
+ esac
+ ln -s "$orig_git/.git/$x" "$new_workdir/.git/$x"
+done
+
+# now setup the workdir
+cd "$new_workdir"
+# copy the HEAD from the original repository as a default branch
+cp "$orig_git/.git/HEAD" .git/HEAD
+# checkout the branch (either the same as HEAD from the original repository, or
+# the one that was asked for)
+git checkout -f $branch
unsigned int mode;
char *slash, *origpath;
- if (!path || sscanf(buffer, "%o", &mode) != 1)
+ if (!path || strtoul_ui(buffer, 8, &mode) != 1)
die("bad tree conversion");
mode = convert_mode(mode);
path++;
--- /dev/null
+/*
+ * decorate.c - decorate a git object with some arbitrary
+ * data.
+ */
+#include "cache.h"
+#include "object.h"
+#include "decorate.h"
+
+static unsigned int hash_obj(struct object *obj, unsigned int n)
+{
+ unsigned int hash = *(unsigned int *)obj->sha1;
+ return hash % n;
+}
+
+static void *insert_decoration(struct decoration *n, struct object *base, void *decoration)
+{
+ int size = n->size;
+ struct object_decoration *hash = n->hash;
+ int j = hash_obj(base, size);
+
+ while (hash[j].base) {
+ if (hash[j].base == base) {
+ void *old = hash[j].decoration;
+ hash[j].decoration = decoration;
+ return old;
+ }
+ j++;
+ if (++j >= size)
+ j = 0;
+ }
+ hash[j].base = base;
+ hash[j].decoration = decoration;
+ n->nr++;
+ return NULL;
+}
+
+static void grow_decoration(struct decoration *n)
+{
+ int i;
+ int old_size = n->size;
+ struct object_decoration *old_hash;
+
+ old_size = n->size;
+ old_hash = n->hash;
+
+ n->size = (old_size + 1000) * 3 / 2;
+ n->hash = xcalloc(n->size, sizeof(struct object_decoration));
+ n->nr = 0;
+
+ for (i = 0; i < old_size; i++) {
+ struct object *base = old_hash[i].base;
+ void *decoration = old_hash[i].decoration;
+
+ if (!base)
+ continue;
+ insert_decoration(n, base, decoration);
+ }
+ free(old_hash);
+}
+
+/* Add a decoration pointer, return any old one */
+void *add_decoration(struct decoration *n, struct object *obj, void *decoration)
+{
+ int nr = n->nr + 1;
+
+ if (nr > n->size * 2 / 3)
+ grow_decoration(n);
+ return insert_decoration(n, obj, decoration);
+}
+
+/* Lookup a decoration pointer */
+void *lookup_decoration(struct decoration *n, struct object *obj)
+{
+ int j;
+
+ /* nothing to lookup */
+ if (!n->size)
+ return NULL;
+ j = hash_obj(obj, n->size);
+ for (;;) {
+ struct object_decoration *ref = n->hash + j;
+ if (ref->base == obj)
+ return ref->decoration;
+ if (!ref->base)
+ return NULL;
+ if (++j == n->size)
+ j = 0;
+ }
+}
--- /dev/null
+#ifndef DECORATE_H
+#define DECORATE_H
+
+struct object_decoration {
+ struct object *base;
+ void *decoration;
+};
+
+struct decoration {
+ const char *name;
+ unsigned int size, nr;
+ struct object_decoration *hash;
+};
+
+extern void *add_decoration(struct decoration *n, struct object *obj, void *decoration);
+extern void *lookup_decoration(struct decoration *n, struct object *obj);
+
+#endif
}
}
+/*
+ * Does the path name a blob in the working tree, or a directory
+ * in the working tree?
+ */
static int is_in_index(const char *path)
{
- int len = strlen(path);
- int pos = cache_name_pos(path, len);
- char c;
-
- if (pos < 0)
- return 0;
- if (strncmp(active_cache[pos]->name, path, len))
- return 0;
- c = active_cache[pos]->name[len];
- return c == '\0' || c == '/';
+ int len, pos;
+ struct cache_entry *ce;
+
+ len = strlen(path);
+ while (path[len-1] == '/')
+ len--;
+ if (!len)
+ return 1; /* "." */
+ pos = cache_name_pos(path, len);
+ if (0 <= pos)
+ return 1;
+ pos = -1 - pos;
+ while (pos < active_nr) {
+ ce = active_cache[pos++];
+ if (ce_namelen(ce) <= len ||
+ strncmp(ce->name, path, len) ||
+ (ce->name[len] > '/'))
+ break; /* path cannot be a prefix */
+ if (ce->name[len] == '/')
+ return 1;
+ }
+ return 0;
}
static int handle_diff_files_args(struct rev_info *revs,
if (data->files[i]->is_binary) {
show_name(prefix, name, len, reset, set);
- printf(" Bin\n");
+ printf(" Bin ");
+ printf("%s%d%s", del_c, deleted, reset);
+ printf(" -> ");
+ printf("%s%d%s", add_c, added, reset);
+ printf(" bytes");
+ printf("\n");
goto free_diffstat_file;
}
else if (data->files[i]->is_unmerged) {
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
- if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
+ if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
data->is_binary = 1;
- else {
+ data->added = mf2.size;
+ data->deleted = mf1.size;
+ } else {
/* Crazy xdl interfaces.. */
xpparam_t xpp;
xdemitconf_t xecfg;
#include "cache.h"
#include "dir.h"
+struct path_simplify {
+ int len;
+ const char *path;
+};
+
int common_prefix(const char **pathspec)
{
const char *path, *slash, *next;
return !strncmp(active_cache[pos]->name, dirname, len);
}
+/*
+ * This is an inexact early pruning of any recursive directory
+ * reading - if the path cannot possibly be in the pathspec,
+ * return true, and we'll skip it early.
+ */
+static int simplify_away(const char *path, int pathlen, const struct path_simplify *simplify)
+{
+ if (simplify) {
+ for (;;) {
+ const char *match = simplify->path;
+ int len = simplify->len;
+
+ if (!match)
+ break;
+ if (len > pathlen)
+ len = pathlen;
+ if (!memcmp(path, match, len))
+ return 0;
+ simplify++;
+ }
+ return 1;
+ }
+ return 0;
+}
+
/*
* Read a directory tree. We currently ignore anything but
* directories, regular files and symlinks. That's because git
* Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
-static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only)
+static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
{
DIR *fdir = opendir(path);
int contents = 0;
continue;
len = strlen(de->d_name);
memcpy(fullname + baselen, de->d_name, len+1);
+ if (simplify_away(fullname, baselen + len, simplify))
+ continue;
if (excluded(dir, fullname) != dir->show_ignored) {
if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
continue;
if (dir->hide_empty_directories &&
!read_directory_recursive(dir,
fullname, fullname,
- baselen + len, 1))
+ baselen + len, 1, simplify))
continue;
break;
}
contents += read_directory_recursive(dir,
- fullname, fullname, baselen + len, 0);
+ fullname, fullname, baselen + len, 0, simplify);
continue;
case DT_REG:
case DT_LNK:
e2->name, e2->len);
}
-int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen)
+/*
+ * Return the length of the "simple" part of a path match limiter.
+ */
+static int simple_length(const char *match)
{
+ const char special[256] = {
+ [0] = 1, ['?'] = 1,
+ ['\\'] = 1, ['*'] = 1,
+ ['['] = 1
+ };
+ int len = -1;
+
+ for (;;) {
+ unsigned char c = *match++;
+ len++;
+ if (special[c])
+ return len;
+ }
+}
+
+static struct path_simplify *create_simplify(const char **pathspec)
+{
+ int nr, alloc = 0;
+ struct path_simplify *simplify = NULL;
+
+ if (!pathspec)
+ return NULL;
+
+ for (nr = 0 ; ; nr++) {
+ const char *match;
+ if (nr >= alloc) {
+ alloc = alloc_nr(alloc);
+ simplify = xrealloc(simplify, alloc * sizeof(*simplify));
+ }
+ match = *pathspec++;
+ if (!match)
+ break;
+ simplify[nr].path = match;
+ simplify[nr].len = simple_length(match);
+ }
+ simplify[nr].path = NULL;
+ simplify[nr].len = 0;
+ return simplify;
+}
+
+static void free_simplify(struct path_simplify *simplify)
+{
+ if (simplify)
+ free(simplify);
+}
+
+int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
+{
+ struct path_simplify *simplify = create_simplify(pathspec);
+
/*
* Make sure to do the per-directory exclude for all the
* directories leading up to our base.
}
}
- read_directory_recursive(dir, path, base, baselen, 0);
+ read_directory_recursive(dir, path, base, baselen, 0, simplify);
+ free_simplify(simplify);
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
return dir->nr;
}
#define MATCHED_EXACTLY 3
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
-extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);
+extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
extern int push_exclude_per_directory(struct dir_struct *, const char *, int);
extern void pop_exclude_per_directory(struct dir_struct *, int);
int pack_fd;
snprintf(tmpfile, sizeof(tmpfile),
- "%s/pack_XXXXXX", get_object_directory());
+ "%s/tmp_pack_XXXXXX", get_object_directory());
pack_fd = mkstemp(tmpfile);
if (pack_fd < 0)
die("Can't create %s: %s", tmpfile, strerror(errno));
}
snprintf(tmpfile, sizeof(tmpfile),
- "%s/index_XXXXXX", get_object_directory());
+ "%s/tmp_idx_XXXXXX", get_object_directory());
idx_fd = mkstemp(tmpfile);
if (idx_fd < 0)
die("Can't create %s: %s", tmpfile, strerror(errno));
if (!in_merge_bases(old_cmit, &new_cmit, 1)) {
unlock_ref(lock);
- warn("Not updating %s"
+ warning("Not updating %s"
" (new tip %s does not contain %s)",
b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
return -1;
# trust what the user has in the index file and the
# working tree.
resolved=
- changed="$(git-diff-index --cached --name-only HEAD)"
- if test '' = "$changed"
- then
+ git-diff-index --quiet --cached HEAD && {
echo "No changes - did you forget to use 'git add'?"
stop_here_user_resolve $this
- fi
+ }
unmerged=$(git-ls-files -u)
if test -n "$unmerged"
then
then
# Applying the patch to an earlier tree and merging the
# result may have produced the same tree as ours.
- changed="$(git-diff-index --cached --name-only HEAD)"
- if test '' = "$changed"
- then
- echo No changes -- Patch already applied.
- go_next
- continue
- fi
+ git-diff-index --quiet --cached HEAD && {
+ echo No changes -- Patch already applied.
+ go_next
+ continue
+ }
# clear apply_status -- we have successfully merged.
apply_status=0
fi
#!/bin/sh
-USAGE='[start|bad|good|next|reset|visualize|replay|log]'
-LONG_USAGE='git bisect start [<pathspec>] reset bisect state and start bisection.
-git bisect bad [<rev>] mark <rev> a known-bad revision.
-git bisect good [<rev>...] mark <rev>... known-good revisions.
-git bisect next find next bisection to test and check it out.
-git bisect reset [<branch>] finish bisection search and go back to branch.
-git bisect visualize show bisect status in gitk.
-git bisect replay <logfile> replay bisection log
-git bisect log show bisect log.'
+USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
+LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
+ reset bisect state and start bisection.
+git bisect bad [<rev>]
+ mark <rev> a known-bad revision.
+git bisect good [<rev>...]
+ mark <rev>... known-good revisions.
+git bisect next
+ find next bisection to test and check it out.
+git bisect reset [<branch>]
+ finish bisection search and go back to branch.
+git bisect visualize
+ show bisect status in gitk.
+git bisect replay <logfile>
+ replay bisection log.
+git bisect log
+ show bisect log.
+git bisect run <cmd>...
+ use <cmd>... to automatically bisect.'
. git-sh-setup
require_work_tree
head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
die "Bad HEAD - I need a symbolic ref"
case "$head" in
- refs/heads/bisect*)
+ refs/heads/bisect)
if [ -s "$GIT_DIR/head-name" ]; then
branch=`cat "$GIT_DIR/head-name"`
else
#
# Get rid of any old bisect state
#
- rm -f "$GIT_DIR/refs/heads/bisect"
- rm -rf "$GIT_DIR/refs/bisect/"
+ bisect_clean_state
mkdir "$GIT_DIR/refs/bisect"
- {
- printf "git-bisect start"
- sq "$@"
- } >"$GIT_DIR/BISECT_LOG"
+
+ #
+ # Check for one bad and then some good revisions.
+ #
+ has_double_dash=0
+ for arg; do
+ case "$arg" in --) has_double_dash=1; break ;; esac
+ done
+ orig_args=$(sq "$@")
+ bad_seen=0
+ while [ $# -gt 0 ]; do
+ arg="$1"
+ case "$arg" in
+ --)
+ shift
+ break
+ ;;
+ *)
+ rev=$(git-rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
+ test $has_double_dash -eq 1 &&
+ die "'$arg' does not appear to be a valid revision"
+ break
+ }
+ if [ $bad_seen -eq 0 ]; then
+ bad_seen=1
+ bisect_write_bad "$rev"
+ else
+ bisect_write_good "$rev"
+ fi
+ shift
+ ;;
+ esac
+ done
+
sq "$@" >"$GIT_DIR/BISECT_NAMES"
+ echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
+ bisect_auto_next
}
bisect_bad() {
0)
rev=$(git-rev-parse --verify HEAD) ;;
1)
- rev=$(git-rev-parse --verify "$1") ;;
+ rev=$(git-rev-parse --verify "$1^{commit}") ;;
*)
usage ;;
esac || exit
- echo "$rev" >"$GIT_DIR/refs/bisect/bad"
- echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+ bisect_write_bad "$rev"
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
bisect_auto_next
}
+bisect_write_bad() {
+ rev="$1"
+ echo "$rev" >"$GIT_DIR/refs/bisect/bad"
+ echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+}
+
bisect_good() {
bisect_autostart
case "$#" in
esac
for rev in $revs
do
- rev=$(git-rev-parse --verify "$rev") || exit
- echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
- echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+ rev=$(git-rev-parse --verify "$rev^{commit}") || exit
+ bisect_write_good "$rev"
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
+
done
bisect_auto_next
}
+bisect_write_good() {
+ rev="$1"
+ echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
+ echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+}
+
bisect_next_check() {
- next_ok=no
- test -f "$GIT_DIR/refs/bisect/bad" &&
- case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in
- refs/bisect/good-\*) ;;
- *) next_ok=yes ;;
- esac
- case "$next_ok,$1" in
- no,) false ;;
- no,fail)
- echo >&2 'You need to give me at least one good and one bad revisions.'
- exit 1 ;;
+ missing_good= missing_bad=
+ git show-ref -q --verify refs/bisect/bad || missing_bad=t
+ test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t
+
+ case "$missing_good,$missing_bad,$1" in
+ ,,*)
+ : have both good and bad - ok
+ ;;
+ *,)
+ # do not have both but not asked to fail - just report.
+ false
+ ;;
+ t,,good)
+ # have bad but not good. we could bisect although
+ # this is less optimum.
+ echo >&2 'Warning: bisecting only with a bad commit.'
+ if test -t 0
+ then
+ printf >&2 'Are you sure [Y/n]? '
+ case "$(read yesno)" in [Nn]*) exit 1 ;; esac
+ fi
+ : bisect without good...
+ ;;
*)
- true ;;
+ THEN=''
+ test -d "$GIT_DIR/refs/bisect" || {
+ echo >&2 'You need to start by "git bisect start".'
+ THEN='then '
+ }
+ echo >&2 'You '$THEN'need to give me at least one good' \
+ 'and one bad revisions.'
+ echo >&2 '(You can use "git bisect bad" and' \
+ '"git bisect good" for that.)'
+ exit 1 ;;
esac
}
bisect_next() {
case "$#" in 0) ;; *) usage ;; esac
bisect_autostart
- bisect_next_check fail
+ bisect_next_check good
+
bad=$(git-rev-parse --verify refs/bisect/bad) &&
- good=$(git-rev-parse --sq --revs-only --not \
- $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
- rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
- if [ -z "$rev" ]; then
- echo "$bad was both good and bad"
- exit 1
+ good=$(git for-each-ref --format='^%(objectname)' \
+ "refs/bisect/good-*" | tr '[\012]' ' ') &&
+ eval="git-rev-list --bisect-vars $good $bad --" &&
+ eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
+ eval=$(eval "$eval") &&
+ eval "$eval" || exit
+
+ if [ -z "$bisect_rev" ]; then
+ echo "$bad was both good and bad"
+ exit 1
fi
- if [ "$rev" = "$bad" ]; then
- echo "$rev is first bad commit"
- git-diff-tree --pretty $rev
- exit 0
+ if [ "$bisect_rev" = "$bad" ]; then
+ echo "$bisect_rev is first bad commit"
+ git-diff-tree --pretty $bisect_rev
+ exit 0
fi
- nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
- echo "Bisecting: $nr revisions left to test after this"
- echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
+
+ echo "Bisecting: $bisect_nr revisions left to test after this"
+ echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
git checkout -q new-bisect || exit
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
- git-show-branch "$rev"
+ git-show-branch "$bisect_rev"
}
bisect_visualize() {
else
branch=master
fi ;;
- 1) test -f "$GIT_DIR/refs/heads/$1" || {
+ 1) git-show-ref --verify --quiet -- "refs/heads/$1" || {
echo >&2 "$1 does not seem to be a valid branch"
exit 1
}
usage ;;
esac
if git checkout "$branch"; then
- rm -fr "$GIT_DIR/refs/bisect"
- rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
- rm -f "$GIT_DIR/BISECT_LOG"
- rm -f "$GIT_DIR/BISECT_NAMES"
+ rm -f "$GIT_DIR/head-name"
+ bisect_clean_state
fi
}
+bisect_clean_state() {
+ rm -fr "$GIT_DIR/refs/bisect"
+ rm -f "$GIT_DIR/refs/heads/bisect"
+ rm -f "$GIT_DIR/BISECT_LOG"
+ rm -f "$GIT_DIR/BISECT_NAMES"
+ rm -f "$GIT_DIR/BISECT_RUN"
+}
+
bisect_replay () {
test -r "$1" || {
echo >&2 "cannot read $1 for replaying"
bisect_auto_next
}
+bisect_run () {
+ bisect_next_check fail
+
+ while true
+ do
+ echo "running $@"
+ "$@"
+ res=$?
+
+ # Check for really bad run error.
+ if [ $res -lt 0 -o $res -ge 128 ]; then
+ echo >&2 "bisect run failed:"
+ echo >&2 "exit code $res from '$@' is < 0 or >= 128"
+ exit $res
+ fi
+
+ # Use "bisect_good" or "bisect_bad"
+ # depending on run success or failure.
+ if [ $res -gt 0 ]; then
+ next_bisect='bisect_bad'
+ else
+ next_bisect='bisect_good'
+ fi
+
+ # We have to use a subshell because bisect_good or
+ # bisect_bad functions can exit.
+ ( $next_bisect > "$GIT_DIR/BISECT_RUN" )
+ res=$?
+
+ cat "$GIT_DIR/BISECT_RUN"
+
+ if [ $res -ne 0 ]; then
+ echo >&2 "bisect run failed:"
+ echo >&2 "$next_bisect exited with error code $res"
+ exit $res
+ fi
+
+ if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
+ echo "bisect run success"
+ exit 0;
+ fi
+
+ done
+}
+
+
case "$#" in
0)
usage ;;
bisect_replay "$@" ;;
log)
cat "$GIT_DIR/BISECT_LOG" ;;
+ run)
+ bisect_run "$@" ;;
*)
usage ;;
esac
detached=
detach_warn=
-if test -z "$branch$newbranch" && test "$new" != "$old"
+describe_detached_head () {
+ test -n "$quiet" || {
+ printf >&2 "$1 "
+ GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2"
+ }
+}
+
+if test -z "$branch$newbranch" && test "$new_name" != "$old_name"
then
detached="$new"
if test -n "$oldbranch" && test -z "$quiet"
(now or later) by using -b with the checkout command again. Example:
git checkout -b <new_branch_name>"
fi
-elif test -z "$oldbranch" && test -z "$quiet"
+elif test -z "$oldbranch" && test "$new" != "$old"
then
- echo >&2 "Previous HEAD position was $old"
+ describe_detached_head 'Previous HEAD position was' "$old"
fi
if [ "X$old" = X ]
then
echo >&2 "$detach_warn"
fi
+ describe_detached_head 'HEAD is now at' HEAD
fi
rm -f "$GIT_DIR/MERGE_HEAD"
else
else
tname=$name
fi
- git-http-fetch $v -a -w "$tname" "$name" "$1/" || exit 1
+ git-http-fetch $v -a -w "$tname" "$name" "$1" || exit 1
done <"$clone_tmp/refs"
rm -fr "$clone_tmp"
http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" ||
# the same way.
if test -z "$initial_commit"
then
- cp "$THIS_INDEX" "$TMP_INDEX"
- GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
+ GIT_INDEX_FILE="$THIS_INDEX" \
+ git-read-tree --index-output="$TMP_INDEX" -i -m HEAD
else
rm -f "$TMP_INDEX"
fi || exit
fi
if test -z "$quiet"
then
+ commit=`git-diff-tree --always --shortstat --pretty="format:%h: %s"\
+ --summary --root HEAD --`
echo "Created${initial_commit:+ initial} commit $commit"
- git-diff-tree --shortstat --summary --root --no-commit-id HEAD --
fi
fi
extern void usage(const char *err) NORETURN;
extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern void warn(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void set_usage_routine(void (*routine)(const char *err) NORETURN);
extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
return strncmp(str, prefix, strlen(prefix));
}
+static inline int strtoul_ui(char const *s, int base, unsigned int *result)
+{
+ unsigned long ul;
+ char *p;
+
+ errno = 0;
+ ul = strtoul(s, &p, base);
+ if (errno || *p || p == s || (unsigned int) ul != ul)
+ return -1;
+ *result = ul;
+ return 0;
+}
+
#endif
`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
## apply non-binary changes
-my $fuzz = $opt_p ? 0 : 2;
+
+# In pedantic mode require all lines of context to match. In normal
+# mode, be compatible with diff/patch: assume 3 lines of context and
+# require at least one line match, i.e. ignore at most 2 lines of
+# context, like diff/patch do by default.
+my $context = $opt_p ? '' : '-C1';
print "Checking if patch will apply\n";
my @stat;
-open APPLY, "GIT_DIR= git-apply -C$fuzz --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
+open APPLY, "GIT_DIR= git-apply $context --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
@stat=<APPLY>;
close APPLY || die "Cannot patch";
my (@bfiles,@files,@afiles,@dfiles);
}
print "Applying\n";
-`GIT_DIR= git-apply -C$fuzz --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+`GIT_DIR= git-apply $context --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
print "Patch applied successfully. Adding new files and directories to CVS\n";
my $dirtypatch = 0;
our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a);
my (%conv_author_name, %conv_author_email);
-sub usage() {
+sub usage(;$) {
+ my $msg = shift;
+ print(STDERR "Error: $msg\n") if $msg;
print STDERR <<END;
Usage: ${\basename $0} # fetch/update GIT from CVS
[-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
- [-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] [-i] [-k] [-u]
- [-s subst] [-a] [-m] [-M regex] [-S regex] [CVS_module]
+ [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
+ [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
+ [CVS_module]
END
exit(1);
}
getopts($opts) or usage();
usage if $opt_h;
-@ARGV <= 1 or usage();
+@ARGV <= 1 or usage("You can't specify more than one CVS module");
if ($opt_d) {
$ENV{"CVSROOT"} = $opt_d;
} elsif ($ENV{"CVSROOT"}) {
$opt_d = $ENV{"CVSROOT"};
} else {
- die "CVSROOT needs to be set";
+ usage("CVSROOT needs to be set");
}
$opt_o ||= "origin";
$opt_s ||= "-";
chomp $cvs_tree;
close $f;
} else {
- usage();
+ usage("CVS module has to be specified");
}
our @mergerx = ();
if ( defined ( $wrev )
and defined($meta->{revision})
and $wrev == $meta->{revision}
+ and defined($state->{entries}{$filename}{modified_hash})
and not exists ( $state->{opt}{C} ) )
{
$log->info("Tell the client the file is modified");
shallow_depth=
no_progress=
test -t 1 || no_progress=--no-progress
+quiet=
while case "$#" in 0) break ;; esac
do
case "$1" in
--update-head-o|--update-head-ok)
update_head_ok=t
;;
+ -q|--q|--qu|--qui|--quie|--quiet)
+ quiet=--quiet
+ ;;
-v|--verbose)
verbose=Yes
;;
git-bundle unbundle "$remote" $rref ||
echo failed "$remote"
else
- git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \
- "$remote" $rref ||
+ git-fetch-pack --thin $exec $keep $shallow_depth \
+ $quiet $no_progress "$remote" $rref ||
echo failed "$remote"
fi
) |
expr "z$head" : "z$_x40\$" >/dev/null ||
die "No such ref $remote_name at $remote"
echo >&2 "Fetching $remote_name from $remote using $proto"
- git-http-fetch -v -a "$head" "$remote/" || exit
+ case "$quiet" in '') v=-v ;; *) v= ;; esac
+ git-http-fetch $v -a "$head" "$remote" || exit
;;
rsync://*)
test -n "$shallow_depth" &&
rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
head=$(git-rev-parse --verify TMP_HEAD)
rm -f "$TMP_HEAD"
+ case "$quiet" in '') v=-v ;; *) v= ;; esac
test "$rsync_slurped_objects" || {
- rsync -av --ignore-existing --exclude info \
+ rsync -a $v --ignore-existing --exclude info \
"$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit
# Look at objects/info/alternates for rsync -- http will
QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
endif
+TCLTK_PATH ?= wish
+
ifeq ($(findstring $(MAKEFLAGS),s),s)
QUIET_GEN =
QUIET_BUILT_IN =
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
$(QUIET_GEN)rm -f $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+ -e 's|^exec wish "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' \
-e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \
$@.sh >$@+ && \
chmod +x $@+ && \
if {[reponame] ne {}} {
append title " ([reponame])"
}
+ option add *Dialog.msg.font font_ui
+ option add *Button.font font_ui
set cmd [list tk_messageBox \
-icon error \
-type ok \
if {[reponame] ne {}} {
append title " ([reponame])"
}
+ option add *Dialog.msg.font font_ui
+ option add *Button.font font_ui
set cmd [list tk_messageBox \
-icon warning \
-type ok \
if {[reponame] ne {}} {
append title " ([reponame])"
}
+ option add *Dialog.msg.font font_ui
+ option add *Button.font font_ui
tk_messageBox \
-parent $parent \
-icon info \
if {[reponame] ne {}} {
append title " ([reponame])"
}
+ option add *Dialog.msg.font font_ui
+ option add *Button.font font_ui
return [tk_messageBox \
-parent . \
-icon question \
[short_path $path] has no changes.
-The modification date of this file was updated
-by another application, but the content within
-the file was not changed.
+The modification date of this file was updated by another application, but the content within the file was not changed.
-A rescan will be automatically started to find
-other files which may have the same state."
+A rescan will be automatically started to find other files which may have the same state."
clear_diff
display_file $path __
if {[llength $PARENT] == 0} {
error_popup {There is nothing to amend.
-You are about to create the initial commit.
-There is no commit before this to amend.
+You are about to create the initial commit. There is no commit before this to amend.
}
return
}
if {$curType eq {merge}} {
error_popup {Cannot amend while merging.
-You are currently in the middle of a merge that
-has not been fully completed. You cannot amend
-the prior commit unless you first abort the
-current merge activity.
+You are currently in the middle of a merge that has not been fully completed. You cannot amend the prior commit unless you first abort the current merge activity.
}
return
}
} elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
info_popup {Last scanned state does not match repository state.
-Another Git program has modified this repository
-since the last scan. A rescan must be performed
-before another commit can be created.
+Another Git program has modified this repository since the last scan. A rescan must be performed before another commit can be created.
The rescan will be automatically started now.
}
U? {
error_popup "Unmerged files cannot be committed.
-File [short_path $path] has merge conflicts.
-You must resolve them and add the file before committing.
+File [short_path $path] has merge conflicts. You must resolve them and add the file before committing.
"
unlock_index
return
if {$tree_id eq $old_tree} {
info_popup {No changes to commit.
-No files were modified by this commit and it
-was not a merge commit.
+No files were modified by this commit and it was not a merge commit.
A rescan will be automatically started now.
}
-value head \
-variable create_branch_revtype \
-font font_ui
- eval tk_optionMenu $w.from.head_m create_branch_head $all_heads
+ set lbranchm [eval tk_optionMenu $w.from.head_m create_branch_head \
+ $all_heads]
+ $lbranchm configure -font font_ui
+ $w.from.head_m configure -font font_ui
grid $w.from.head_r $w.from.head_m -sticky w
set all_trackings [all_tracking_branches]
if {$all_trackings ne {}} {
-value tracking \
-variable create_branch_revtype \
-font font_ui
- eval tk_optionMenu $w.from.tracking_m \
+ set tbranchm [eval tk_optionMenu $w.from.tracking_m \
create_branch_trackinghead \
- $all_trackings
+ $all_trackings]
+ $tbranchm configure -font font_ui
+ $w.from.tracking_m configure -font font_ui
grid $w.from.tracking_r $w.from.tracking_m -sticky w
}
set all_tags [load_all_tags]
-value tag \
-variable create_branch_revtype \
-font font_ui
- eval tk_optionMenu $w.from.tag_m \
+ set tagsm [eval tk_optionMenu $w.from.tag_m \
create_branch_tag \
- $all_tags
+ $all_tags]
+ $tagsm configure -font font_ui
+ $w.from.tag_m configure -font font_ui
grid $w.from.tag_r $w.from.tag_m -sticky w
}
radiobutton $w.from.exp_r \
-value head \
-variable delete_branch_checktype \
-font font_ui
- eval tk_optionMenu $w.validate.head_m delete_branch_head $all_heads
+ set mergedlocalm [eval tk_optionMenu $w.validate.head_m \
+ delete_branch_head \
+ $all_heads]
+ $mergedlocalm configure -font font_ui
+ $w.validate.head_m configure -font font_ui
grid $w.validate.head_r $w.validate.head_m -sticky w
set all_trackings [all_tracking_branches]
if {$all_trackings ne {}} {
-value tracking \
-variable delete_branch_checktype \
-font font_ui
- eval tk_optionMenu $w.validate.tracking_m \
+ set mergedtrackm [eval tk_optionMenu $w.validate.tracking_m \
delete_branch_trackinghead \
- $all_trackings
+ $all_trackings]
+ $mergedtrackm configure -font font_ui
+ $w.validate.tracking_m configure -font font_ui
grid $w.validate.tracking_r $w.validate.tracking_m -sticky w
}
radiobutton $w.validate.always_r \
} elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
info_popup {Last scanned state does not match repository state.
-Another Git program has modified this repository
-since the last scan. A rescan must be performed
-before the current branch can be changed.
+Another Git program has modified this repository since the last scan. A rescan must be performed before the current branch can be changed.
The rescan will be automatically started now.
}
if {[catch {git symbolic-ref HEAD "refs/heads/$new_branch"} err]} {
error_popup "Failed to set current branch.
-This working directory is only partially switched.
-We successfully updated your files, but failed to
-update an internal Git file.
+This working directory is only partially switched. We successfully updated your files, but failed to update an internal Git file.
-This should not have occurred. [appname] will now
-close and give up.
+This should not have occurred. [appname] will now close and give up.
$err"
do_quit
frame $w.buttons
button $w.buttons.create -text Push \
-font font_ui \
+ -default active \
-command [list start_push_anywhere_action $w]
pack $w.buttons.create -side right
button $w.buttons.cancel -text {Cancel} \
-font font_ui \
+ -default normal \
-command [list destroy $w]
pack $w.buttons.cancel -side right -padx 5
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
-value remote \
-variable push_urltype \
-font font_ui
- eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes
+ set remmenu [eval tk_optionMenu $w.dest.remote_m push_remote \
+ $all_remotes]
+ $remmenu configure -font font_ui
+ $w.dest.remote_m configure -font font_ui
grid $w.dest.remote_r $w.dest.remote_m -sticky w
if {[lsearch -sorted -exact $all_remotes origin] != -1} {
set push_remote origin
set push_thin 0
set push_tags 0
- bind $w <Visibility> "grab $w"
+ bind $w <Visibility> "grab $w; focus $w.buttons.create"
bind $w <Key-Escape> "destroy $w"
+ bind $w <Key-Return> [list start_push_anywhere_action $w]
wm title $w "[appname] ([reponame]): Push"
tkwait window $w
}
if {[string match amend* $commit_type]} {
info_popup {Cannot merge while amending.
-You must finish amending this commit before
-starting any type of merge.
+You must finish amending this commit before starting any type of merge.
}
return 0
}
if {$commit_type ne $curType || $HEAD ne $curHEAD} {
info_popup {Last scanned state does not match repository state.
-Another Git program has modified this repository
-since the last scan. A rescan must be performed
-before a merge can be performed.
+Another Git program has modified this repository since the last scan. A rescan must be performed before a merge can be performed.
The rescan will be automatically started now.
}
File [short_path $path] has merge conflicts.
-You must resolve them, add the file, and commit to
-complete the current merge. Only then can you
-begin another merge.
+You must resolve them, add the file, and commit to complete the current merge. Only then can you begin another merge.
"
unlock_index
return 0
File [short_path $path] is modified.
-You should complete the current commit before
-starting a merge. Doing so will help you abort
-a failed merge, should the need arise.
+You should complete the current commit before starting a merge. Doing so will help you abort a failed merge, should the need arise.
"
unlock_index
return 0
Your merge of $revcnt branches has failed.
-There are file-level conflicts between the
-branches which must be resolved manually.
+There are file-level conflicts between the branches which must be resolved manually.
The working directory will now be reset.
-You can attempt this merge again
-by merging only one branch at a time." $w
+You can attempt this merge again by merging only one branch at a time." $w
set fd [open "| git read-tree --reset -u HEAD" r]
fconfigure $fd -blocking 0 -translation binary
if {[ask_popup "Abort $op?
-Aborting the current $op will cause
-*ALL* uncommitted changes to be lost.
+Aborting the current $op will cause *ALL* uncommitted changes to be lost.
Continue with aborting the current $op?"] eq {yes}} {
set fd [open "| git read-tree --reset -u HEAD" r]
proc blame_incremental_status {w} {
global blame_status blame_data
+ set have $blame_data($w,blame_lines)
+ set total $blame_data($w,total_lines)
+ set pdone 0
+ if {$total} {set pdone [expr {100 * $have / $total}]}
+
set blame_status($w) [format \
"Loading annotations... %i of %i lines annotated (%2i%%)" \
- $blame_data($w,blame_lines) \
- $blame_data($w,total_lines) \
- [expr {100 * $blame_data($w,blame_lines)
- / $blame_data($w,total_lines)}]]
+ $have $total $pdone]
}
proc blame_click {w w_cmit w_line w_file cur_w pos} {
if {[winfo exists $w]} {
$w.m.s conf -background green -text {Success}
$w.ok conf -state normal
+ focus $w.ok
}
} else {
if {![winfo exists $w]} {
}
$w.m.s conf -background red -text {Error: Command Failed}
$w.ok conf -state normal
+ focus $w.ok
}
array unset console_cr $w
frame $w.buttons -border 1
button $w.buttons.close -text Close \
-font font_ui \
+ -default active \
-command [list destroy $w]
button $w.buttons.gc -text {Compress Database} \
-font font_ui \
+ -default normal \
-command "destroy $w;do_gc"
pack $w.buttons.close -side right
pack $w.buttons.gc -side left
}
pack $w.stat -pady 10 -padx 10
- bind $w <Visibility> "grab $w; focus $w"
+ bind $w <Visibility> "grab $w; focus $w.buttons.close"
bind $w <Key-Escape> [list destroy $w]
bind $w <Key-Return> [list destroy $w]
wm title $w "[appname] ([reponame]): Database Statistics"
frame $w.buttons
button $w.buttons.close -text {Close} \
-font font_ui \
+ -default active \
-command [list destroy $w]
pack $w.buttons.close -side right
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
clipboard append -format STRING -type STRING -- \[$w.vers cget -text\]
"
- bind $w <Visibility> "grab $w; focus $w"
+ bind $w <Visibility> "grab $w; focus $w.buttons.close"
bind $w <Key-Escape> "destroy $w"
+ bind $w <Key-Return> "destroy $w"
bind_button3 $w.vers "tk_popup $w.ctxm %X %Y; grab $w; focus $w"
wm title $w "About [appname]"
tkwait window $w
frame $w.buttons
button $w.buttons.restore -text {Restore Defaults} \
-font font_ui \
+ -default normal \
-command do_restore_defaults
pack $w.buttons.restore -side left
button $w.buttons.save -text Save \
-font font_ui \
+ -default active \
-command [list do_save_config $w]
pack $w.buttons.save -side right
button $w.buttons.cancel -text {Cancel} \
-font font_ui \
+ -default normal \
-command [list destroy $w]
pack $w.buttons.cancel -side right -padx 5
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
frame $w.global.$name
label $w.global.$name.l -text "$text:" -font font_ui
pack $w.global.$name.l -side left -anchor w -fill x
- eval tk_optionMenu $w.global.$name.family \
+ set fontmenu [eval tk_optionMenu $w.global.$name.family \
global_config_new(gui.$font^^family) \
- $all_fonts
+ $all_fonts]
+ $w.global.$name.family configure -font font_ui
+ $fontmenu configure -font font_ui
spinbox $w.global.$name.size \
-textvariable global_config_new(gui.$font^^size) \
-from 2 -to 80 -increment 1 \
pack $w.global.$name -side top -anchor w -fill x
}
- bind $w <Visibility> "grab $w; focus $w"
+ bind $w <Visibility> "grab $w; focus $w.buttons.save"
bind $w <Key-Escape> "destroy $w"
+ bind $w <Key-Return> [list do_save_config $w]
wm title $w "[appname] ([reponame]): Options"
tkwait window $w
}
# -- Menu Bar
#
menu .mbar -tearoff 0
-.mbar add cascade -label Repository -menu .mbar.repository
-.mbar add cascade -label Edit -menu .mbar.edit
+.mbar add cascade -label Repository -menu .mbar.repository -font font_ui
+.mbar add cascade -label Edit -menu .mbar.edit -font font_ui
if {[is_enabled branch]} {
- .mbar add cascade -label Branch -menu .mbar.branch
+ .mbar add cascade -label Branch -menu .mbar.branch -font font_ui
}
if {[is_enabled multicommit] || [is_enabled singlecommit]} {
- .mbar add cascade -label Commit -menu .mbar.commit
+ .mbar add cascade -label Commit -menu .mbar.commit -font font_ui
}
if {[is_enabled transport]} {
- .mbar add cascade -label Merge -menu .mbar.merge
- .mbar add cascade -label Fetch -menu .mbar.fetch
- .mbar add cascade -label Push -menu .mbar.push
+ .mbar add cascade -label Merge -menu .mbar.merge -font font_ui
+ .mbar add cascade -label Fetch -menu .mbar.fetch -font font_ui
+ .mbar add cascade -label Push -menu .mbar.push -font font_ui
}
. configure -menu .mbar
# -- Help Menu
#
-.mbar add cascade -label Help -menu .mbar.help
+.mbar add cascade -label Help -menu .mbar.help -font font_ui
menu .mbar.help
if {![is_MacOSX]} {
set file_lists($ui_index) [list]
set file_lists($ui_workdir) [list]
-wm title . "[appname] ([file normalize [file dirname [gitdir]]])"
+wm title . "[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
focus -force $ui_comm
# -- Warn the user about environmental problems. Cygwin's Tcl
if {[ask_popup \
"This repository currently has $objects_current loose objects.
-To maintain optimal performance it is strongly
-recommended that you compress the database
-when more than $object_limit loose objects exist.
+To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
Compress the database now?"] eq yes} {
do_gc
laf="$GIT_DIR/lost-found"
rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
-git fsck --full |
+git fsck --full --no-reflogs |
while read dangling type sha1
do
case "$dangling" in
# because the current index is what we will be committing as the
# merge result.
-test "$(git-diff-index --cached --name-status HEAD)" = "" || exit 2
+git-diff-index --quiet --cached HEAD || exit 2
exit 0
LF='
'
-all_strategies='recur recursive octopus resolve stupid ours'
+all_strategies='recur recursive octopus resolve stupid ours subtree'
default_twohead_strategies='recursive'
default_octopus_strategies='octopus'
-no_trivial_merge_strategies='ours'
+no_trivial_merge_strategies='ours subtree'
use_strategies=
index_merge=t
git-show-ref -q --verify "refs/heads/$truname" 2>/dev/null
then
echo "$rh branch '$truname' (early part) of ."
+ elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
+ then
+ sed -e 's/ not-for-merge / /' -e 1q \
+ "$GIT_DIR/FETCH_HEAD"
else
echo "$rh commit '$remote'"
fi
require_work_tree
# Returns true if the mode reflects a symlink
-function is_symlink () {
+is_symlink () {
test "$1" = 120000
}
-function local_present () {
+local_present () {
test -n "$local_mode"
}
-function remote_present () {
+remote_present () {
test -n "$remote_mode"
}
-function base_present () {
+base_present () {
test -n "$base_mode"
}
fi
}
-function describe_file () {
+describe_file () {
mode="$1"
branch="$2"
file="$3"
- echo -n " "
+ printf " {%s}: " "$branch"
if test -z "$mode"; then
- echo -n "'$path' was deleted"
+ echo "deleted"
elif is_symlink "$mode" ; then
- echo -n "'$path' is a symlink containing '"
- cat "$file"
- echo -n "'"
+ echo "a symbolic link -> '$(cat "$file")'"
else
if base_present; then
- echo -n "'$path' was created"
+ echo "modified"
else
- echo -n "'$path' was modified"
+ echo "created"
fi
fi
- echo " in the $branch branch"
}
resolve_symlink_merge () {
- while /bin/true; do
- echo -n "Use (r)emote or (l)ocal, or (a)bort? "
+ while true; do
+ printf "Use (l)ocal or (r)emote, or (a)bort? "
read ans
case "$ans" in
[lL]*)
cleanup_temp_files --save-backup
return
;;
- [rR]*)
+ [rR]*)
git-checkout-index -f --stage=3 -- "$path"
git-add -- "$path"
cleanup_temp_files --save-backup
return
;;
- [qQ]*)
+ [aA]*)
exit 1
;;
esac
}
resolve_deleted_merge () {
- while /bin/true; do
- echo -n "Use (m)odified or (d)eleted file, or (a)bort? "
+ while true; do
+ if base_present; then
+ printf "Use (m)odified or (d)eleted file, or (a)bort? "
+ else
+ printf "Use (c)reated or (d)eleted file, or (a)bort? "
+ fi
read ans
case "$ans" in
- [mM]*)
+ [mMcC]*)
git-add -- "$path"
cleanup_temp_files --save-backup
return
;;
- [dD]*)
- git-rm -- "$path"
+ [dD]*)
+ git-rm -- "$path" > /dev/null
cleanup_temp_files
return
;;
- [qQ]*)
+ [aA]*)
exit 1
;;
esac
done
}
-merge_file () {
- path="$1"
+check_unchanged () {
+ if test "$path" -nt "$BACKUP" ; then
+ status=0;
+ else
+ while true; do
+ echo "$path seems unchanged."
+ printf "Was the merge successful? [y/n] "
+ read answer < /dev/tty
+ case "$answer" in
+ y*|Y*) status=0; break ;;
+ n*|N*) status=1; break ;;
+ esac
+ done
+ fi
+}
- if test ! -f "$path" ; then
- echo "$path: file not found"
- exit 1
+save_backup () {
+ if test "$status" -eq 0; then
+ mv -- "$BACKUP" "$path.orig"
+ fi
+}
+
+remove_backup () {
+ if test "$status" -eq 0; then
+ rm "$BACKUP"
fi
+}
+
+merge_file () {
+ path="$1"
f=`git-ls-files -u -- "$path"`
if test -z "$f" ; then
- echo "$path: file does not need merging"
+ if test ! -f "$path" ; then
+ echo "$path: file not found"
+ else
+ echo "$path: file does not need merging"
+ fi
exit 1
fi
remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null
if test -z "$local_mode" -o -z "$remote_mode"; then
- echo "Deleted merge conflict for $path:"
+ echo "Deleted merge conflict for '$path':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_deleted_merge
fi
if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
- echo "Symlink merge conflict for $path:"
+ echo "Symbolic link merge conflict for '$path':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_symlink_merge
return
fi
- echo "Normal merge conflict for $path:"
+ echo "Normal merge conflict for '$path':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
- echo -n "Hit return to start merge resolution tool ($merge_tool): "
+ printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
read ans
case "$merge_tool" in
-o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
fi
status=$?
- if test "$status" -eq 0; then
- rm "$BACKUP"
- fi
+ remove_backup
;;
tkdiff)
if base_present ; then
tkdiff -o "$path" -- "$LOCAL" "$REMOTE"
fi
status=$?
- if test "$status" -eq 0; then
- mv -- "$BACKUP" "$path.orig"
- fi
+ save_backup
;;
meld|vimdiff)
touch "$BACKUP"
$merge_tool -- "$LOCAL" "$path" "$REMOTE"
- if test "$path" -nt "$BACKUP" ; then
- status=0;
- else
- while true; do
- echo "$path seems unchanged."
- echo -n "Was the merge successful? [y/n] "
- read answer < /dev/tty
- case "$answer" in
- y*|Y*) status=0; break ;;
- n*|N*) status=1; break ;;
- esac
- done
- fi
- if test "$status" -eq 0; then
- mv -- "$BACKUP" "$path.orig"
- fi
+ check_unchanged
+ save_backup
;;
xxdiff)
touch "$BACKUP"
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$path" -- "$LOCAL" "$REMOTE"
fi
- if test "$path" -nt "$BACKUP" ; then
- status=0;
+ check_unchanged
+ save_backup
+ ;;
+ opendiff)
+ touch "$BACKUP"
+ if base_present; then
+ opendiff "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
else
- while true; do
- echo "$path seems unchanged."
- echo -n "Was the merge successful? [y/n] "
- read answer < /dev/tty
- case "$answer" in
- y*|Y*) status=0; break ;;
- n*|N*) status=1; break ;;
- esac
- done
- fi
- if test "$status" -eq 0; then
- mv -- "$BACKUP" "$path.orig"
+ opendiff "$LOCAL" "$REMOTE" -merge "$path" | cat
fi
+ check_unchanged
+ save_backup
;;
emerge)
if base_present ; then
emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$path"
fi
status=$?
- if test "$status" -eq 0; then
- mv -- "$BACKUP" "$path.orig"
- fi
+ save_backup
;;
esac
if test "$status" -ne 0; then
if test -z "$merge_tool"; then
merge_tool=`git-config merge.tool`
case "$merge_tool" in
- kdiff3 | tkdiff | xxdiff | meld | emerge | vimdiff)
+ kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | "")
;; # happy
*)
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
merge_tool=xxdiff
elif type meld >/dev/null 2>&1 && test -n "$DISPLAY"; then
merge_tool=meld
+ elif type opendiff >/dev/null 2>&1; then
+ merge_tool=opendiff
elif type emacs >/dev/null 2>&1; then
merge_tool=emerge
elif type vimdiff >/dev/null 2>&1; then
fi
case "$merge_tool" in
- kdiff3|tkdiff|meld|xxdiff|vimdiff)
+ kdiff3|tkdiff|meld|xxdiff|vimdiff|opendiff)
if ! type "$merge_tool" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available"
exit 1
echo Merging the files: $files
git ls-files -u | sed -e 's/^[^ ]* //' | sort -u | while read i
do
- echo ""
+ printf "\n"
merge_file "$i" < /dev/tty > /dev/tty
done
else
while test $# -gt 0; do
- echo ""
+ printf "\n"
merge_file "$1"
shift
done
for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
echo $patch_name
(cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3
- test -s $dotest/patch || {
+ test -s .dotest/patch || {
echo "Patch is empty. Was is split wrong?"
- stop_here $this
+ exit 1
}
# Parse the author information
if [ -z "$dry_run" ] ; then
git-apply --index -C1 "$tmp_patch" &&
tree=$(git-write-tree) &&
- commit=$((echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) &&
+ commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) &&
git-update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
fi
done
die "$RESOLVEMSG"
fi
- if test -n "`git-diff-index HEAD`"
+ if ! git-diff-index --quiet HEAD
then
if ! git-commit -C "`cat $dotest/current`"
then
do
case "$1" in
--continue)
- diff=$(git-diff-files)
- case "$diff" in
- ?*) echo "You must edit all merge conflicts and then"
+ git-diff-files --quiet || {
+ echo "You must edit all merge conflicts and then"
echo "mark them as resolved using git update-index"
exit 1
- ;;
- esac
+ }
if test -d "$dotest"
then
prev_head="`cat $dotest/prev_head`"
upstream=`git rev-parse --verify "${upstream_name}^0"` ||
die "invalid upstream $upstream_name"
+# Make sure the branch to rebase onto is valid.
+onto_name=${newbase-"$upstream_name"}
+onto=$(git-rev-parse --verify "${onto_name}^0") || exit
+
# If a hook exists, give it a chance to interrupt
if test -x "$GIT_DIR/hooks/pre-rebase"
then
esac
branch=$(git-rev-parse --verify "${branch_name}^0") || exit
-# Make sure the branch to rebase onto is valid.
-onto_name=${newbase-"$upstream_name"}
-onto=$(git-rev-parse --verify "${onto_name}^0") || exit
-
# Now we are rebasing commits $upstream..$branch on top of $onto
# Check if we are already based on $onto, but this should be
echo "$prev_head" > "$dotest/prev_head"
msgnum=0
-for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
- | @@PERL@@ -e 'print reverse <>'`
+for cmt in `git-rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
do
msgnum=$(($msgnum + 1))
echo "$cmt" > "$dotest/cmt.$msgnum"
$hash->{$name}{'FETCH'} ||= [];
push @{$hash->{$name}{'FETCH'}}, $value;
}
+ elsif ($what eq 'push') {
+ $hash->{$name}{'PUSH'} ||= [];
+ push @{$hash->{$name}{'PUSH'}}, $value;
+ }
if (!exists $hash->{$name}{'SOURCE'}) {
$hash->{$name}{'SOURCE'} = 'config';
}
}
}
elsif (/^Push:\s*(.*)$/) {
- ; # later
+ $it->{'PUSH'} ||= [];
+ push @{$it->{'PUSH'}}, $1;
}
elsif (/^Pull:\s*(.*)$/) {
$it->{'FETCH'} ||= [];
if ($info->{'LS_REMOTE'}) {
show_mapping($name, $info);
}
+ if ($info->{'PUSH'}) {
+ my @pushed = map {
+ s|^refs/heads/||;
+ s|:refs/heads/|:|;
+ $_;
+ } @{$info->{'PUSH'}};
+ print " Local branch(es) pushed with 'git push'\n";
+ print " @pushed\n";
+ }
}
sub add_remote {
my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
$from = "\"$name\"$addr";
}
+ my $ccline = "";
+ if ($cc ne '') {
+ $ccline = "\nCc: $cc";
+ }
my $header = "From: $from
-To: $to
-Cc: $cc
+To: $to${ccline}
Subject: $subject
Date: $date
Message-Id: $message_id
if ($chain_reply_to || !defined $reply_to || length($reply_to) == 0) {
$reply_to = $message_id;
if (length $references > 0) {
- $references .= " $message_id";
+ $references .= "\n $message_id";
} else {
$references = "$message_id";
}
use IO::File qw//;
use File::Basename qw/dirname basename/;
use File::Path qw/mkpath/;
-use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/;
+use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
use IPC::Open3;
use Git;
my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
read_repo_config(\%opts);
+Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
'minimize-connections' => \$Git::SVN::Migration::_minimize,
'id|i=s' => \$Git::SVN::default_ref_id,
'svn-remote|remote|R=s' => sub {
$Git::SVN::no_reuse_existing = 1;
$Git::SVN::default_repo_id = $_[1] });
-exit 1 if (!$rv && $cmd ne 'log');
+exit 1 if (!$rv && $cmd && $cmd ne 'log');
usage(0) if $_help;
version() if $_version;
next if /^multi-/; # don't show deprecated commands
print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n";
foreach (keys %{$cmd{$_}->[2]}) {
+ # mixed-case options are for .git/config only
+ next if /[A-Z]/ && /^[a-z]+$/i;
# prints out arguments as they should be passed:
my $x = s#[:=]s$## ? '<arg>' : s#[:=]i$## ? '<num>' : '';
print $fd ' ' x 21, join(', ', map { length $_ > 1 ?
my $head = shift;
$head ||= 'HEAD';
my @refs;
- my ($url, $rev, $uuid) = working_head_info($head, \@refs);
- my $c = $refs[-1];
- unless (defined $url && defined $rev && defined $uuid) {
+ my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
+ unless ($gs) {
die "Unable to determine upstream SVN information from ",
"$head history\n";
}
- my $gs = Git::SVN->find_by_url($url);
+ my $c = $refs[-1];
my $last_rev;
foreach my $d (@refs) {
if (!verify_ref("$d~1")) {
sub cmd_rebase {
command_noisy(qw/update-index --refresh/);
- my $url = (working_head_info('HEAD'))[0];
- if (!defined $url) {
+ my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+ unless ($gs) {
die "Unable to determine upstream SVN information from ",
"working tree history\n";
}
-
- my $gs = Git::SVN->find_by_url($url);
if (command(qw/diff-index HEAD --/)) {
print STDERR "Cannot rebase with uncommited changes:\n";
command_noisy('status');
}
sub cmd_show_ignore {
- my $url = (::working_head_info('HEAD'))[0];
- my $gs = Git::SVN->find_by_url($url) || Git::SVN->new;
+ my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+ $gs ||= Git::SVN->new;
my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
$gs->traverse_ignore(\*STDOUT, $gs->{path}, $r);
}
sub working_head_info {
my ($head, $refs) = @_;
- my ($url, $rev, $uuid);
my ($fh, $ctx) = command_output_pipe('rev-list', $head);
while (<$fh>) {
chomp;
- ($url, $rev, $uuid) = cmt_metadata($_);
- last if (defined $url && defined $rev && defined $uuid);
+ my ($url, $rev, $uuid) = cmt_metadata($_);
+ if (defined $url && defined $rev) {
+ if (my $gs = Git::SVN->find_by_url($url)) {
+ my $c = $gs->rev_db_get($rev);
+ if ($c && $c eq $_) {
+ close $fh; # break the pipe
+ return ($url, $rev, $uuid, $gs);
+ }
+ }
+ }
unshift @$refs, $_ if $refs;
}
- close $fh; # break the pipe
- ($url, $rev, $uuid);
+ command_close_pipe($fh, $ctx);
+ (undef, undef, undef, undef);
}
package Git::SVN;
my ($self) = @_;
my $repos_root = $self->ra->{repos_root};
return $self->{path} if ($self->{url} eq $repos_root);
- die "BUG: rel_path failed! repos_root: $repos_root, Ra URL: ",
- $self->ra->{url}, " path: $self->{path}, URL: $self->{url}\n";
+ my $url = $self->{url} .
+ (length $self->{path} ? "/$self->{path}" : $self->{path});
+ $url =~ s!^\Q$repos_root\E(?:/+|$)!!g;
+ $url;
}
sub traverse_ignore {
}
my ($r0, $parent) = $gs->find_rev_before($r, 1);
if (!defined $r0 || !defined $parent) {
- $gs->fetch(0, $r);
+ my ($base, $head) = parse_revision_argument(0, $r);
+ if ($base <= $r) {
+ $gs->fetch($base, $r);
+ }
($r0, $parent) = $gs->last_rev_commit;
}
if (defined $r0 && defined $parent) {
use vars qw/@ISA $config_dir $_log_window_size/;
use strict;
use warnings;
-my ($can_do_switch);
-my $RA;
+my ($can_do_switch, %ignored_err, $RA);
BEGIN {
# enforce temporary pool usage for some simple functions
# 175007 - http(s):// (this repo required authorization, too...)
# More codes may be discovered later...
if ($errno == 175007 || $errno == 175002 || $errno == 160013) {
- warn "W: Ignoring error from SVN, path probably ",
- "does not exist: ($errno): ",
- $err->expanded_message,"\n";
+ my $err_key = $err->expanded_message;
+ # revision numbers change every time, filter them out
+ $err_key =~ s/\d+/\0/g;
+ $err_key = "$errno\0$err_key";
+ unless ($ignored_err{$err_key}) {
+ warn "W: Ignoring error from SVN, path probably ",
+ "does not exist: ($errno): ",
+ $err->expanded_message,"\n";
+ $ignored_err{$err_key} = 1;
+ }
return;
}
die "Error from SVN, ($errno): ", $err->expanded_message,"\n";
sub cmt_showable {
my ($c) = @_;
return 1 if defined $c->{r};
+
+ # big commit message got truncated by the 16k pretty buffer in rev-list
if ($c->{l} && $c->{l}->[-1] eq "...\n" &&
$c->{a_raw} =~ /\@([a-f\d\-]+)>$/) {
+ @{$c->{l}} = ();
my @log = command(qw/cat-file commit/, $c->{c});
- shift @log while ($log[0] ne "\n");
+
+ # shift off the headers
+ shift @log while ($log[0] ne '');
shift @log;
- @{$c->{l}} = grep !/^git-svn-id: /, @log;
+
+ # TODO: make $c->{l} not have a trailing newline in the future
+ @{$c->{l}} = map { "$_\n" } grep !/^git-svn-id: /, @log;
(undef, $c->{r}, undef) = ::extract_metadata(
(grep(/^git-svn-id: /, @log))[-1]);
last;
}
- my $url = (::working_head_info($head))[0];
- my $gs = Git::SVN->find_by_url($url) || Git::SVN->_new;
+ my ($url, $rev, $uuid, $gs) = ::working_head_info($head);
+ $gs ||= Git::SVN->_new;
my @cmd = (qw/log --abbrev-commit --pretty=raw --default/,
$gs->refname);
push @cmd, '-r' unless $non_recursive;
setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
(*argv)++;
(*argc)--;
+ handled++;
} else if (!prefixcmp(cmd, "--git-dir=")) {
setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
} else if (!strcmp(cmd, "--bare")) {
int src, dst, count = 0, size = 16;
char quoted = 0;
- *argv = malloc(sizeof(char*) * size);
+ *argv = xmalloc(sizeof(char*) * size);
/* split alias_string */
(*argv)[count++] = cmdline;
{ "add", cmd_add, RUN_SETUP | NOT_BARE },
{ "annotate", cmd_annotate, USE_PAGER },
{ "apply", cmd_apply },
- { "archive", cmd_archive, RUN_SETUP },
+ { "archive", cmd_archive },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP },
{ "bundle", cmd_bundle },
# Pass --without docs to rpmbuild if you don't want the documentation
+
+%define python_path /usr/bin/python
+
Name: git
Version: @@VERSION@@
Release: 1%{?dist}
Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, git-gui, perl-Git
+Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, git-gui, git-p4, perl-Git
%description
Git is a fast, scalable, distributed revision control system with an
%description arch
Git tools for importing Arch repositories.
+%package p4
+Summary: Git tools for importing Perforce repositories
+Group: Development/Tools
+Requires: git-core = %{version}-%{release}, python
+%description p4
+Git tools for importing Perforce repositories.
+
%package email
Summary: Git tools for sending email
Group: Development/Tools
%setup -q
%build
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease \
- prefix=%{_prefix} all %{!?_without_docs: doc}
+make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_P4IMPORT=YesPlease \
+ prefix=%{_prefix} PYTHON_PATH=%{python_path} all %{!?_without_docs: doc}
%install
rm -rf $RPM_BUILD_ROOT
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
- WITH_OWN_SUBPROCESS_PY=YesPlease \
- prefix=%{_prefix} mandir=%{_mandir} INSTALLDIRS=vendor \
- install %{!?_without_docs: install-doc}
+ WITH_P4IMPORT=YesPlease prefix=%{_prefix} mandir=%{_mandir} \
+ PYTHON_PATH=%{python_path} \
+ INSTALLDIRS=vendor install %{!?_without_docs: install-doc}
find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
-(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
+(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "p4import|archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
%if %{!?_without_docs:1}0
-(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
+(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "p4import|archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
%else
rm -rf $RPM_BUILD_ROOT%{_mandir}
%endif
%{!?_without_docs: %{_mandir}/man1/git-archimport.1*}
%{!?_without_docs: %doc Documentation/git-archimport.html }
+%files p4
+%defattr(-,root,root)
+%doc Documentation/git-p4import.txt
+%{_bindir}/git-p4import
+%{!?_without_docs: %{_mandir}/man1/git-p4import.1*}
+%{!?_without_docs: %doc Documentation/git-p4import.html }
+
%files email
%defattr(-,root,root)
%doc Documentation/*email*.txt
%{!?_without_docs: %doc Documentation/*.html }
%changelog
+* Tue Mar 27 2007 Eygene Ryabinkin <rea-git@codelabs.ru>
+- Added the git-p4 package: Perforce import stuff.
+
* Mon Feb 13 2007 Nicolas Pitre <nico@cam.org>
- Update core package description (Git isn't as stupid as it used to be)
frame .bright.mode
radiobutton .bright.mode.patch -text "Patch" \
-command reselectline -variable cmitmode -value "patch"
+ .bright.mode.patch configure -font $uifont
radiobutton .bright.mode.tree -text "Tree" \
-command reselectline -variable cmitmode -value "tree"
+ .bright.mode.tree configure -font $uifont
grid .bright.mode.patch .bright.mode.tree -sticky ew
pack .bright.mode -side top -fill x
set cflist .bright.cfiles
}
proc about {} {
+ global uifont
set w .about
if {[winfo exists $w]} {
raise $w
Copyright © 2005-2006 Paul Mackerras
Use and redistribute under the terms of the GNU General Public License} \
- -justify center -aspect 400
- pack $w.m -side top -fill x -padx 20 -pady 20
- button $w.ok -text Close -command "destroy $w"
+ -justify center -aspect 400 -border 2 -bg white -relief groove
+ pack $w.m -side top -fill x -padx 2 -pady 2
+ $w.m configure -font $uifont
+ button $w.ok -text Close -command "destroy $w" -default active
pack $w.ok -side bottom
+ $w.ok configure -font $uifont
+ bind $w <Visibility> "focus $w.ok"
+ bind $w <Key-Escape> "destroy $w"
+ bind $w <Key-Return> "destroy $w"
}
proc keys {} {
+ global uifont
set w .keys
if {[winfo exists $w]} {
raise $w
<Ctrl-minus> Decrease font size
<F5> Update
} \
- -justify left -bg white -border 2 -relief sunken
- pack $w.m -side top -fill both
- button $w.ok -text Close -command "destroy $w"
+ -justify left -bg white -border 2 -relief groove
+ pack $w.m -side top -fill both -padx 2 -pady 2
+ $w.m configure -font $uifont
+ button $w.ok -text Close -command "destroy $w" -default active
pack $w.ok -side bottom
+ $w.ok configure -font $uifont
+ bind $w <Visibility> "focus $w.ok"
+ bind $w <Key-Escape> "destroy $w"
+ bind $w <Key-Return> "destroy $w"
}
# Procedures for manipulating the file list window at the
toplevel $top
wm title $top $title
label $top.nl -text "Name" -font $uifont
- entry $top.name -width 20 -textvariable newviewname($n)
+ entry $top.name -width 20 -textvariable newviewname($n) -font $uifont
grid $top.nl $top.name -sticky w -pady 5
- checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
+ checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
+ -font $uifont
grid $top.perm - -pady 5 -sticky w
message $top.al -aspect 1000 -font $uifont \
-text "Commits to include (arguments to git rev-list):"
grid $top.al - -sticky w -pady 5
entry $top.args -width 50 -textvariable newviewargs($n) \
- -background white
+ -background white -font $uifont
grid $top.args - -sticky ew -padx 5
message $top.l -aspect 1000 -font $uifont \
-text "Enter files and directories to include, one per line:"
grid $top.l - -sticky w
- text $top.t -width 40 -height 10 -background white
+ text $top.t -width 40 -height 10 -background white -font $uifont
if {[info exists viewfiles($n)]} {
foreach f $viewfiles($n) {
$top.t insert end $f
}
grid $top.t - -sticky ew -padx 5
frame $top.buts
- button $top.buts.ok -text "OK" -command [list newviewok $top $n]
- button $top.buts.can -text "Cancel" -command [list destroy $top]
+ button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
+ -font $uifont
+ button $top.buts.can -text "Cancel" -command [list destroy $top] \
+ -font $uifont
grid $top.buts.ok $top.buts.can
grid columnconfigure $top.buts 0 -weight 1 -uniform a
grid columnconfigure $top.buts 1 -weight 1 -uniform a
} else {
set gdtargs [list "-S$highlight_files"]
}
- set cmd [concat | git-diff-tree -r -s --stdin $gdtargs]
+ set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
set filehighlight [open $cmd r+]
fconfigure $filehighlight -blocking 0
fileevent $filehighlight readable readfhighlight
}
if {[eof $filehighlight]} {
# strange...
- puts "oops, git-diff-tree died"
+ puts "oops, git diff-tree died"
catch {close $filehighlight}
unset filehighlight
}
global maxwidth maxgraphpct diffopts
global oldprefs prefstop showneartags
global bgcolor fgcolor ctext diffcolors
+ global uifont
set top .gitkprefs
set prefstop $top
toplevel $top
wm title $top "Gitk preferences"
label $top.ldisp -text "Commit list display options"
+ $top.ldisp configure -font $uifont
grid $top.ldisp - -sticky w -pady 10
label $top.spacer -text " "
label $top.maxwidthl -text "Maximum graph width (lines)" \
grid x $top.maxpctl $top.maxpct -sticky w
label $top.ddisp -text "Diff display options"
+ $top.ddisp configure -font $uifont
grid $top.ddisp - -sticky w -pady 10
label $top.diffoptl -text "Options for diff program" \
-font optionfont
grid x $top.ntag -sticky w
label $top.cdisp -text "Colors: press to choose"
+ $top.cdisp configure -font $uifont
grid $top.cdisp - -sticky w -pady 10
label $top.bg -padx 40 -relief sunk -background $bgcolor
button $top.bgbut -text "Background" -font optionfont \
grid x $top.hunksepbut $top.hunksep -sticky w
frame $top.buts
- button $top.buts.ok -text "OK" -command prefsok
- button $top.buts.can -text "Cancel" -command prefscan
+ button $top.buts.ok -text "OK" -command prefsok -default active
+ $top.buts.ok configure -font $uifont
+ button $top.buts.can -text "Cancel" -command prefscan -default normal
+ $top.buts.can configure -font $uifont
grid $top.buts.ok $top.buts.can
grid columnconfigure $top.buts 0 -weight 1 -uniform a
grid columnconfigure $top.buts 1 -weight 1 -uniform a
grid $top.buts - - -pady 10 -sticky ew
+ bind $top <Visibility> "focus $top.buts.ok"
}
proc choosecolor {v vi w x cmd} {
--- /dev/null
+GIT web Interface (gitweb) Installation
+=======================================
+
+First you have to generate gitweb.cgi from gitweb.perl using
+"make gitweb/gitweb.cgi", then copy appropriate files (gitweb.cgi,
+gitweb.css, git-logo.png and git-favicon.png) to their destination.
+For example if git was (or is) installed with /usr prefix, you can do
+
+ $ make prefix=/usr gitweb/gitweb.cgi ;# as yourself
+ # cp gitweb/git* /var/www/cgi-bin/ ;# as root
+
+Alternatively you can use autoconf generated ./configure script to
+set up path to git binaries (via config.mak.autogen), so you can write
+instead
+
+ $ make configure ;# as yourself
+ $ ./configure --prefix=/usr ;# as yourself
+ $ make gitweb/gitweb.cgi ;# as yourself
+ # cp gitweb/git* /var/www/cgi-bin/ ;# as root
+
+The above example assumes that your web server is configured to run
+[executable] files in /var/www/cgi-bin/ as server scripts (as CGI
+scripts).
+
+
+Build time configuration
+------------------------
+
+See also "How to configure gitweb for your local system" in README
+file for gitweb (in gitweb/README).
+
+- There are many configuration variables which affects building of
+ gitweb.cgi; see "default configuration for gitweb" section in main
+ (top dir) Makefile, and instructions for building gitweb/gitweb.cgi
+ target.
+
+ One of most important is where to find git wrapper binary. Gitweb
+ tries to find git wrapper at $(bindir)/git, so you have to set $bindir
+ when building gitweb.cgi, or $prefix from which $bindir is derived. If
+ you build and install gitweb together with the rest of git suite,
+ there should be no problems. Otherwise, if git was for example
+ installed from a binary package, you have to set $prefix (or $bindir)
+ accordingly.
+
+- Another important issue is where are git repositories you want to make
+ available to gitweb. By default gitweb search for repositories under
+ /pub/git; if you want to have projects somewhere else, like /home/git,
+ use GITWEB_PROJECTROOT build configuration variable.
+
+ By default all git repositories under projectroot are visible and
+ available to gitweb. List of projects is generated by default by
+ scanning the projectroot directory for git repositories. This can be
+ changed (configured) as described in "Gitweb repositories" section
+ below.
+
+ Note that gitweb deals directly with object database, and does not
+ need working directory; the name of the project is the name of its
+ repository object database, usually projectname.git for bare
+ repositories. If you want to provide gitweb access to non-bare (live)
+ repository, you can make projectname.git symbolic link under
+ projectroot linking to projectname/.git (but it is just
+ a suggestion).
+
+- You can control where gitweb tries to find its main CSS style file,
+ its favicon and logo with GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
+ build configuration variables. By default gitweb tries to find them
+ in the same directory as gitweb.cgi script.
+
+Build example
+~~~~~~~~~~~~~
+
+- To install gitweb to /var/www/cgi-bin/gitweb/ when git wrapper
+ is installed at /usr/local/bin/git and the repositories (projects)
+ we want to display are under /home/local/scm, you can do
+
+ make GITWEB_PROJECTROOT="/home/local/scm" \
+ GITWEB_CSS="/gitweb/gitweb.css" \
+ GITWEB_LOGO="/gitweb/git-logo.png" \
+ GITWEB_FAVICON="/gitweb/git-favicon.png" \
+ bindir=/usr/local/bin \
+ gitweb/gitweb.cgi
+
+ cp -fv ~/git/gitweb/gitweb.{cgi,css} \
+ ~/git/gitweb/git-{favicon,logo}.png \
+ /var/www/cgi-bin/gitweb/
+
+
+Gitweb config file
+------------------
+
+See also "Runtime gitweb configuration" section in README file
+for gitweb (in gitweb/README).
+
+- You can configure gitweb further using gitweb configuration file;
+ by default it is file named gitweb_config.perl in the same place as
+ gitweb.cgi script. You can control default place for config file
+ using GITWEB_CONFIG build configuration variable, and you can set it
+ using GITWEB_CONFIG environmental variable.
+
+- Gitweb config file is [fragment] of perl code. You can set variables
+ using "our $variable = value"; text from "#" character until the end
+ of a line is ignored. See perlsyn(1) for details.
+
+ See the top of gitweb.perl file for examples of customizable options.
+
+Config file example
+~~~~~~~~~~~~~~~~~~~
+
+To enable blame, pickaxe search, and snapshot support, while allowing
+individual projects to turn them off, put the following in your
+GITWEB_CONFIG file:
+
+ $feature{'blame'}{'default'} = [1];
+ $feature{'blame'}{'override'} = 1;
+
+ $feature{'pickaxe'}{'default'} = [1];
+ $feature{'pickaxe'}{'override'} = 1;
+
+ $feature{'snapshot'}{'default'} = ['x-gzip', 'gz', 'gzip'];
+ $feature{'snapshot'}{'override'} = 1;
+
+
+Gitweb repositories
+-------------------
+
+- By default all git repositories under projectroot are visible and
+ available to gitweb. List of projects is generated by default by
+ scanning the projectroot directory for git repositories (for object
+ databases to be more exact).
+
+ You can provide pre-generated list of [visible] repositories,
+ together with information about their owners (the project ownership
+ is taken from owner of repository directory otherwise), by setting
+ GITWEB_LIST build configuration variable (or $projects_list variable
+ in gitweb config file) to point to a plain file.
+
+ Each line of projects list file should consist of url-encoded path
+ to project repository database (relative to projectroot) separated
+ by space from url-encoded project owner; spaces in both project path
+ and project owner have to be encoded as either '%20' or '+'.
+
+ You can generate projects list index file using project_index action
+ (the 'TXT' link on projects list page) directly from gitweb.
+
+- By default even if project is not visible on projects list page, you
+ can view it nevertheless by hand-crafting gitweb URL. You can set
+ GITWEB_STRICT_EXPORT build configuration variable (or $strict_export
+ variable in gitweb config file) to only allow viewing of
+ repositories also shown on the overview page.
+
+- Alternatively, you can configure gitweb to only list and allow
+ viewing of the explicitly exported repositories, via
+ GITWEB_EXPORT_OK build configuration variable (or $export_ok
+ variable in gitweb config file). If it evaluates to true, gitweb
+ show repository only if this file exists in its object database
+ (if directory has the magic file $export_ok).
+
+Generating projects list using gitweb
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We assume that GITWEB_CONFIG has its default Makefile value, namely
+gitweb_config.perl. Put the following in gitweb_make_index.perl file:
+
+ $GITWEB_CONFIG = "gitweb_config.perl";
+ do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
+
+ $projects_list = $projectroot;
+
+Then create the following script to get list of project in the format
+suitable for GITWEB_LIST build configuration variable (or
+$projects_list variable in gitweb config):
+
+ #!/bin/sh
+
+ export GITWEB_CONFIG="gitweb_make_index.perl"
+ export GATEWAY_INTERFACE="CGI/1.1"
+ export HTTP_ACCEPT="*/*"
+ export REQUEST_METHOD="GET"
+ export QUERY_STRING="a=project_index"
+
+ perl -- /var/www/cgi-bin/gitweb.cgi
+
+
+Requirements
+------------
+
+ - Core git tools
+ - Perl
+ - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
+ - web server
+
+
+Example web server configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See also "Webserver configuration" section in README file for gitweb
+(in gitweb/README).
+
+
+- Apache2, gitweb installed as CGI script,
+ under /var/www/cgi-bin/
+
+ ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
+
+ <Directory "/var/www/cgi-bin">
+ Options Indexes FollowSymlinks ExecCGI
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+ </Directory>
+
+- Apache2, gitweb installed as mod_perl legacy script,
+ under /var/www/perl/
+
+ Alias /perl "/var/www/perl"
+
+ <Directory "/var/www/perl">
+ SetHandler perl-script
+ PerlResponseHandler ModPerl::Registry
+ PerlOptions +ParseHeaders
+ Options Indexes FollowSymlinks +ExecCGI
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+ </Directory>
font-style: italic;
}
-div.page_body span.signoff {
+span.signoff {
color: #888888;
}
binmode STDOUT, ':utf8';
BEGIN {
- CGI->compile() if $ENV{MOD_PERL};
+ CGI->compile() if $ENV{'MOD_PERL'};
}
our $cgi = new CGI;
# source of projects list
our $projects_list = "++GITWEB_LIST++";
+# default order of projects list
+# valid values are none, project, descr, owner, and age
+our $default_projects_order = "project";
+
# show repository only if this file exists
# (only effective if this variable evaluates to true)
our $export_ok = "++GITWEB_EXPORT_OK++";
# projects matching $projname/*.git will not be shown in the main
# projects list, instead a '+' mark will be added to $projname
# there and a 'forks' view will be enabled for the project, listing
- # all the forks. This feature is supported only if project list
- # is taken from a directory, not file.
+ # all the forks. If project list is taken from a file, forks have
+ # to be listed after the main project.
# To enable system wide have in $GITWEB_CONFIG
# $feature{'forks'}{'default'} = [1];
$filter ||= '';
$filter =~ s/\.git$//;
+ my ($check_forks) = gitweb_check_feature('forks');
+
if (-d $projects_list) {
# search in directory
my $dir = $projects_list . ($filter ? "/$filter" : '');
$dir =~ s!/+$!!;
my $pfxlen = length("$dir");
- my ($check_forks) = gitweb_check_feature('forks');
-
File::Find::find({
follow_fast => 1, # follow symbolic links
dangling_symlinks => 0, # ignore dangling symlinks, silently
# 'git%2Fgit.git Linus+Torvalds'
# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
# 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
+ my %paths;
open my ($fd), $projects_list or return;
+ PROJECT:
while (my $line = <$fd>) {
chomp $line;
my ($path, $owner) = split ' ', $line;
# looking for forks;
my $pfx = substr($path, 0, length($filter));
if ($pfx ne $filter) {
- next;
+ next PROJECT;
}
my $sfx = substr($path, length($filter));
if ($sfx !~ /^\/.*\.git$/) {
- next;
+ next PROJECT;
+ }
+ } elsif ($check_forks) {
+ PATH:
+ foreach my $filter (keys %paths) {
+ # looking for forks;
+ my $pfx = substr($path, 0, length($filter));
+ if ($pfx ne $filter) {
+ next PATH;
+ }
+ my $sfx = substr($path, length($filter));
+ if ($sfx !~ /^\/.*\.git$/) {
+ next PATH;
+ }
+ # is a fork, don't include it in
+ # the list
+ next PROJECT;
}
}
if (check_export_ok("$projectroot/$path")) {
path => $path,
owner => to_utf8($owner),
};
- push @list, $pr
+ push @list, $pr;
+ (my $forks_path = $path) =~ s/\.git$//;
+ $paths{$forks_path}++;
}
}
close $fd;
}
- @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
return @list;
}
$cgi->hidden(-name => "a") . "\n" .
$cgi->hidden(-name => "h") . "\n" .
$cgi->popup_menu(-name => 'st', -default => 'commit',
- -values => ['commit', 'author', 'committer', 'pickaxe']) .
+ -values => ['commit', 'author', 'committer', 'pickaxe']) .
$cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
" search:\n",
$cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
my %arg = map { $_ => {action=>$_} } @navs;
if (defined $head) {
for (qw(commit commitdiff)) {
- $arg{$_}{hash} = $head;
+ $arg{$_}{'hash'} = $head;
}
if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) {
for (qw(shortlog log)) {
- $arg{$_}{hash} = $head;
+ $arg{$_}{'hash'} = $head;
}
}
}
- $arg{tree}{hash} = $treehead if defined $treehead;
- $arg{tree}{hash_base} = $treebase if defined $treebase;
+ $arg{'tree'}{'hash'} = $treehead if defined $treehead;
+ $arg{'tree'}{'hash_base'} = $treebase if defined $treebase;
print "<div class=\"page_nav\">\n" .
(join " | ",
my ($action, $title, $hash, $hash_base) = @_;
my %args = ();
- $args{action} = $action;
- $args{hash} = $hash if $hash;
- $args{hash_base} = $hash_base if $hash_base;
+ $args{'action'} = $action;
+ $args{'hash'} = $hash if $hash;
+ $args{'hash_base'} = $hash_base if $hash_base;
print "<div class=\"header\">\n" .
$cgi->a({-href => href(%args), -class => "title"},
push @projects, $pr;
}
- $order ||= "project";
+ $order ||= $default_projects_order;
$from = 0 unless defined $from;
$to = $#projects if (!defined $to || $#projects < $to);
sub git_project_list {
my $order = $cgi->param('o');
- if (defined $order && $order !~ m/project|descr|owner|age/) {
+ if (defined $order && $order !~ m/none|project|descr|owner|age/) {
die_error(undef, "Unknown order parameter");
}
sub git_forks {
my $order = $cgi->param('o');
- if (defined $order && $order !~ m/project|descr|owner|age/) {
+ if (defined $order && $order !~ m/none|project|descr|owner|age/) {
die_error(undef, "Unknown order parameter");
}
git_project_list_body(\@forklist, undef, 0, 15,
$#forklist <= 15 ? undef :
$cgi->a({-href => href(action=>"forks")}, "..."),
- 'noheader');
+ 'noheader');
}
git_footer_html();
}
$ftype = git_get_type($hash);
if ($ftype !~ "blob") {
- die_error("400 Bad Request", "Object is not a blob");
+ die_error('400 Bad Request', "Object is not a blob");
}
open ($fd, "-|", git_cmd(), "blame", '-p', '--',
$file_name, $hash_base)
my $rev = substr($full_rev, 0, 8);
my $author = $meta->{'author'};
my %date = parse_date($meta->{'author-time'},
- $meta->{'author-tz'});
+ $meta->{'author-tz'});
my $date = $date{'iso-tz'};
if ($group_size) {
$current_color = ++$current_color % $num_colors;
print " rowspan=\"$group_size\"" if ($group_size > 1);
print ">";
print $cgi->a({-href => href(action=>"commit",
- hash=>$full_rev,
- file_name=>$file_name)},
- esc_html($rev));
+ hash=>$full_rev,
+ file_name=>$file_name)},
+ esc_html($rev));
print "</td>\n";
}
open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
- or die_error("could not open git-rev-parse");
+ or die_error(undef, "Open git-rev-parse failed");
my $parent_commit = <$dd>;
close $dd;
chomp($parent_commit);
my $blamed = href(action => 'blame',
- file_name => $meta->{'filename'},
- hash_base => $parent_commit);
+ file_name => $meta->{'filename'},
+ hash_base => $parent_commit);
print "<td class=\"linenr\">";
print $cgi->a({ -href => "$blamed#l$orig_lineno",
- -id => "l$lineno",
- -class => "linenr" },
- esc_html($lineno));
+ -id => "l$lineno",
+ -class => "linenr" },
+ esc_html($lineno));
print "</td>";
print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
print "</tr>\n";
my $name = $project;
$name =~ s/\047/\047\\\047\047/g;
open my $fd, "-|",
- "$git archive --format=tar --prefix=\'$name\'/ $hash | $command"
- or die_error(undef, "Execute git-tar-tree failed.");
+ "$git archive --format=tar --prefix=\'$name\'/ $hash | $command"
+ or die_error(undef, "Execute git-tar-tree failed");
binmode STDOUT, ':raw';
print <$fd>;
binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
$formats_nav .=
'(merge: ' .
join(' ', map {
- $cgi->a({-href => href(action=>"commitdiff",
+ $cgi->a({-href => href(action=>"commit",
hash=>$_)},
esc_html(substr($_, 0, 7)));
} @$parents ) .
# difftree output is not printed for merges
open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
@diff_opts, $parent, $hash, "--"
- or die_error(undef, "Open git-diff-tree failed");
+ or die_error(undef, "Open git-diff-tree failed");
@difftree = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading git-diff-tree failed");
}
# read raw output
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
$hash_parent_base, $hash_base,
- "--", $file_name
+ "--", (defined $file_parent ? $file_parent : ()), $file_name
or die_error(undef, "Open git-diff-tree failed");
@difftree = map { chomp; $_ } <$fd>;
close $fd
# open patch output
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
- '-p', $hash_parent_base, $hash_base,
- "--", $file_name
+ '-p', ($format eq 'html' ? "--full-index" : ()),
+ $hash_parent_base, $hash_base,
+ "--", (defined $file_parent ? $file_parent : ()), $file_name
or die_error(undef, "Open git-diff-tree failed");
}
}
# open patch output
- open $fd, "-|", git_cmd(), "diff", '-p', @diff_opts,
+ open $fd, "-|", git_cmd(), "diff", @diff_opts,
+ '-p', ($format eq 'html' ? "--full-index" : ()),
$hash_parent, $hash, "--"
or die_error(undef, "Open git-diff failed");
} else {
if ($page > 0) {
$paging_nav .=
$cgi->a({-href => href(action=>"search", hash=>$hash,
- searchtext=>$searchtext, searchtype=>$searchtype)},
- "first");
+ searchtext=>$searchtext, searchtype=>$searchtype)},
+ "first");
$paging_nav .= " ⋅ " .
$cgi->a({-href => href(action=>"search", hash=>$hash,
- searchtext=>$searchtext, searchtype=>$searchtype,
- page=>$page-1),
- -accesskey => "p", -title => "Alt-p"}, "prev");
+ searchtext=>$searchtext, searchtype=>$searchtype,
+ page=>$page-1),
+ -accesskey => "p", -title => "Alt-p"}, "prev");
} else {
$paging_nav .= "first";
$paging_nav .= " ⋅ prev";
if ($#commitlist >= 100) {
$paging_nav .= " ⋅ " .
$cgi->a({-href => href(action=>"search", hash=>$hash,
- searchtext=>$searchtext, searchtype=>$searchtype,
- page=>$page+1),
- -accesskey => "n", -title => "Alt-n"}, "next");
+ searchtext=>$searchtext, searchtype=>$searchtype,
+ page=>$page+1),
+ -accesskey => "n", -title => "Alt-n"}, "next");
} else {
$paging_nav .= " ⋅ next";
}
if ($#commitlist >= 100) {
$next_link =
$cgi->a({-href => href(action=>"search", hash=>$hash,
- searchtext=>$searchtext, searchtype=>$searchtype,
- page=>$page+1),
- -accesskey => "n", -title => "Alt-n"}, "next");
+ searchtext=>$searchtext, searchtype=>$searchtype,
+ page=>$page+1),
+ -accesskey => "n", -title => "Alt-n"}, "next");
}
git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
return 80;
}
-static void oom(void)
-{
- fprintf(stderr, "git: out of memory\n");
- exit(1);
-}
-
static inline void mput_char(char c, unsigned int num)
{
while(num--)
struct cmdname *ent;
if (cmdname_alloc <= cmdname_cnt) {
cmdname_alloc = cmdname_alloc + 200;
- cmdname = realloc(cmdname, cmdname_alloc * sizeof(*cmdname));
- if (!cmdname)
- oom();
+ cmdname = xrealloc(cmdname, cmdname_alloc * sizeof(*cmdname));
}
- ent = malloc(sizeof(*ent) + len);
- if (!ent)
- oom();
+ ent = xmalloc(sizeof(*ent) + len);
ent->len = len;
memcpy(ent->name, name, len);
ent->name[len] = 0;
struct alt_base
{
- const char *base;
- int path_len;
+ char *base;
int got_indices;
struct packed_git *packs;
struct alt_base *next;
SHA1_Init(&obj_req->c);
- url = xmalloc(strlen(obj_req->repo->base) + 50);
- obj_req->url = xmalloc(strlen(obj_req->repo->base) + 50);
+ url = xmalloc(strlen(obj_req->repo->base) + 51);
+ obj_req->url = xmalloc(strlen(obj_req->repo->base) + 51);
strcpy(url, obj_req->repo->base);
posn = url + strlen(obj_req->repo->base);
- strcpy(posn, "objects/");
- posn += 8;
+ strcpy(posn, "/objects/");
+ posn += 9;
memcpy(posn, hex, 2);
posn += 2;
*(posn++) = '/';
SHA1_Init(&obj_req->c);
if (prev_posn>0) {
prev_posn = 0;
- lseek(obj_req->local, SEEK_SET, 0);
+ lseek(obj_req->local, 0, SEEK_SET);
ftruncate(obj_req->local, 0);
}
}
int serverlen = 0;
struct alt_base *newalt;
char *target = NULL;
- char *path;
if (data[i] == '/') {
/* This counts
* http://git.host/pub/scm/linux.git/
newalt->base = target;
newalt->got_indices = 0;
newalt->packs = NULL;
- path = strstr(target, "//");
- if (path) {
- path = strchr(path+2, '/');
- if (path)
- newalt->path_len = strlen(path);
- }
while (tail->next != NULL)
tail = tail->next;
int len, baselen, ch;
baselen = strlen(base);
- len = baselen + 6; /* "refs/" + NUL */
+ len = baselen + 7; /* "/refs/" + NUL */
for (cp = ref; (ch = *cp) != 0; cp++, len++)
if (needs_quote(ch))
len += 2; /* extra two hex plus replacement % */
qref = xmalloc(len);
memcpy(qref, base, baselen);
- memcpy(qref + baselen, "refs/", 5);
- for (cp = ref, dp = qref + baselen + 5; (ch = *cp) != 0; cp++) {
+ memcpy(qref + baselen, "/refs/", 6);
+ for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
if (needs_quote(ch)) {
*dp++ = '%';
*dp++ = hex((ch >> 4) & 0xF);
const char **write_ref = NULL;
char **commit_id;
const char *url;
- char *path;
+ char *s;
int arg = 1;
int rc = 0;
no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
alt = xmalloc(sizeof(*alt));
- alt->base = url;
+ alt->base = xmalloc(strlen(url) + 1);
+ strcpy(alt->base, url);
+ for (s = alt->base + strlen(alt->base) - 1; *s == '/'; --s)
+ *s = 0;
alt->got_indices = 0;
alt->packs = NULL;
alt->next = NULL;
- path = strstr(url, "//");
- if (path) {
- path = strchr(path+2, '/');
- if (path)
- alt->path_len = strlen(path);
- }
if (pull(commits, commit_id, write_ref, url))
rc = 1;
SHA1_Init(&request->c);
if (prev_posn>0) {
prev_posn = 0;
- lseek(request->local_fileno, SEEK_SET, 0);
+ lseek(request->local_fileno, 0, SEEK_SET);
ftruncate(request->local_fileno, 0);
}
}
static char git_default_date[50];
-static void copy_gecos(struct passwd *w, char *name, int sz)
+static void copy_gecos(const struct passwd *w, char *name, size_t sz)
{
char *src, *dst;
- int len, nlen;
+ size_t len, nlen;
nlen = strlen(w->pw_name);
}
-static void copy_email(struct passwd *pw)
+static void copy_email(const struct passwd *pw)
{
/*
* Make up a fake email address
* (name + '@' + hostname [+ '.' + domainname])
*/
- int len = strlen(pw->pw_name);
+ size_t len = strlen(pw->pw_name);
if (len > sizeof(git_default_email)/2)
die("Your sysadmin must hate you!");
memcpy(git_default_email, pw->pw_name, len);
datestamp(git_default_date, sizeof(git_default_date));
}
-static int add_raw(char *buf, int size, int offset, const char *str)
+static int add_raw(char *buf, size_t size, int offset, const char *str)
{
- int len = strlen(str);
+ size_t len = strlen(str);
if (offset + len > size)
return size;
memcpy(buf + offset, str, len);
* Copy over a string to the destination, but avoid special
* characters ('\n', '<' and '>') and remove crud at the end
*/
-static int copy(char *buf, int size, int offset, const char *src)
+static int copy(char *buf, size_t size, int offset, const char *src)
{
- int i, len;
+ size_t i, len;
unsigned char c;
/* Remove crud from the beginning.. */
if (!pack_name) {
static char tmpfile[PATH_MAX];
snprintf(tmpfile, sizeof(tmpfile),
- "%s/pack_XXXXXX", get_object_directory());
+ "%s/tmp_pack_XXXXXX", get_object_directory());
output_fd = mkstemp(tmpfile);
pack_name = xstrdup(tmpfile);
} else
if (size != has_size || type != has_type ||
memcmp(data, has_data, size) != 0)
die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
+ free(has_data);
}
}
if (!index_name) {
static char tmpfile[PATH_MAX];
snprintf(tmpfile, sizeof(tmpfile),
- "%s/index_XXXXXX", get_object_directory());
+ "%s/tmp_idx_XXXXXX", get_object_directory());
fd = mkstemp(tmpfile);
index_name = xstrdup(tmpfile);
} else {
#include "cache.h"
static struct lock_file *lock_file_list;
+static const char *alternate_index_output;
static void remove_lock_file(void)
{
return i;
}
+int hold_locked_index(struct lock_file *lk, int die_on_error)
+{
+ return hold_lock_file_for_update(lk, get_index_file(), die_on_error);
+}
+
+void set_alternate_index_output(const char *name)
+{
+ alternate_index_output = name;
+}
+
+int commit_locked_index(struct lock_file *lk)
+{
+ if (alternate_index_output) {
+ int result = rename(lk->filename, alternate_index_output);
+ lk->filename[0] = 0;
+ return result;
+ }
+ else
+ return commit_lock_file(lk);
+}
+
void rollback_lock_file(struct lock_file *lk)
{
if (lk->filename[0])
#include "log-tree.h"
#include "reflog-walk.h"
+struct decoration name_decoration = { "object names" };
+
static void show_parents(struct commit *commit, int abbrev)
{
struct commit_list *p;
}
}
+static void show_decorations(struct commit *commit)
+{
+ const char *prefix;
+ struct name_decoration *decoration;
+
+ decoration = lookup_decoration(&name_decoration, &commit->object);
+ if (!decoration)
+ return;
+ prefix = " (";
+ while (decoration) {
+ printf("%s%s", prefix, decoration->name);
+ prefix = ", ";
+ decoration = decoration->next;
+ }
+ putchar(')');
+}
+
/*
* Search for "^[-A-Za-z]+: [^@]+@" pattern. It usually matches
* Signed-off-by: and Acked-by: lines.
fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
if (opt->parents)
show_parents(commit, abbrev_commit);
+ show_decorations(commit);
putchar(opt->diffopt.line_termination);
return;
}
if (opt->total > 0) {
static char buffer[64];
snprintf(buffer, sizeof(buffer),
- "Subject: [PATCH %0*d/%d] ",
+ "Subject: [%s %0*d/%d] ",
+ opt->subject_prefix,
digits_in_number(opt->total),
opt->nr, opt->total);
subject = buffer;
- } else if (opt->total == 0)
- subject = "Subject: [PATCH] ";
- else
+ } else if (opt->total == 0) {
+ 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(" (from %s)",
diff_unique_abbrev(parent->object.sha1,
abbrev_commit));
+ show_decorations(commit);
printf("%s",
diff_get_color(opt->diffopt.color_diff, DIFF_RESET));
putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
--- /dev/null
+#include "cache.h"
+#include "tree.h"
+#include "tree-walk.h"
+
+static int score_missing(unsigned mode, const char *path)
+{
+ int score;
+
+ if (S_ISDIR(mode))
+ score = -1000;
+ else if (S_ISLNK(mode))
+ score = -500;
+ else
+ score = -50;
+ return score;
+}
+
+static int score_differs(unsigned mode1, unsigned mode2, const char *path)
+{
+ int score;
+
+ if (S_ISDIR(mode1) != S_ISDIR(mode2))
+ score = -100;
+ else if (S_ISLNK(mode1) != S_ISLNK(mode2))
+ score = -50;
+ else
+ score = -5;
+ return score;
+}
+
+static int score_matches(unsigned mode1, unsigned mode2, const char *path)
+{
+ int score;
+
+ /* Heh, we found SHA-1 collisions between different kind of objects */
+ if (S_ISDIR(mode1) != S_ISDIR(mode2))
+ score = -100;
+ else if (S_ISLNK(mode1) != S_ISLNK(mode2))
+ score = -50;
+
+ else if (S_ISDIR(mode1))
+ score = 1000;
+ else if (S_ISLNK(mode1))
+ score = 500;
+ else
+ score = 250;
+ return score;
+}
+
+/*
+ * Inspect two trees, and give a score that tells how similar they are.
+ */
+static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
+{
+ struct tree_desc one;
+ struct tree_desc two;
+ void *one_buf, *two_buf;
+ int score = 0;
+ enum object_type type;
+ unsigned long size;
+
+ one_buf = read_sha1_file(hash1, &type, &size);
+ if (!one_buf)
+ die("unable to read tree (%s)", sha1_to_hex(hash1));
+ if (type != OBJ_TREE)
+ die("%s is not a tree", sha1_to_hex(hash1));
+ init_tree_desc(&one, one_buf, size);
+ two_buf = read_sha1_file(hash2, &type, &size);
+ if (!two_buf)
+ die("unable to read tree (%s)", sha1_to_hex(hash2));
+ if (type != OBJ_TREE)
+ die("%s is not a tree", sha1_to_hex(hash2));
+ init_tree_desc(&two, two_buf, size);
+ while (one.size | two.size) {
+ const unsigned char *elem1 = elem1;
+ const unsigned char *elem2 = elem2;
+ const char *path1 = path1;
+ const char *path2 = path2;
+ unsigned mode1 = mode1;
+ unsigned mode2 = mode2;
+ int cmp;
+
+ if (one.size)
+ elem1 = tree_entry_extract(&one, &path1, &mode1);
+ if (two.size)
+ elem2 = tree_entry_extract(&two, &path2, &mode2);
+
+ if (!one.size) {
+ /* two has more entries */
+ score += score_missing(mode2, path2);
+ update_tree_entry(&two);
+ continue;
+ }
+ if (!two.size) {
+ /* two lacks this entry */
+ score += score_missing(mode1, path1);
+ update_tree_entry(&one);
+ continue;
+ }
+ cmp = base_name_compare(path1, strlen(path1), mode1,
+ path2, strlen(path2), mode2);
+ if (cmp < 0) {
+ /* path1 does not appear in two */
+ score += score_missing(mode1, path1);
+ update_tree_entry(&one);
+ continue;
+ }
+ else if (cmp > 0) {
+ /* path2 does not appear in one */
+ score += score_missing(mode2, path2);
+ update_tree_entry(&two);
+ continue;
+ }
+ else if (hashcmp(elem1, elem2))
+ /* they are different */
+ score += score_differs(mode1, mode2, path1);
+ else
+ /* same subtree or blob */
+ score += score_matches(mode1, mode2, path1);
+ update_tree_entry(&one);
+ update_tree_entry(&two);
+ }
+ free(one_buf);
+ free(two_buf);
+ return score;
+}
+
+/*
+ * Match one itself and its subtrees with two and pick the best match.
+ */
+static void match_trees(const unsigned char *hash1,
+ const unsigned char *hash2,
+ int *best_score,
+ char **best_match,
+ char *base,
+ int recurse_limit)
+{
+ struct tree_desc one;
+ void *one_buf;
+ enum object_type type;
+ unsigned long size;
+
+ one_buf = read_sha1_file(hash1, &type, &size);
+ if (!one_buf)
+ die("unable to read tree (%s)", sha1_to_hex(hash1));
+ if (type != OBJ_TREE)
+ die("%s is not a tree", sha1_to_hex(hash1));
+ init_tree_desc(&one, one_buf, size);
+
+ while (one.size) {
+ const char *path;
+ const unsigned char *elem;
+ unsigned mode;
+ int score;
+
+ elem = tree_entry_extract(&one, &path, &mode);
+ if (!S_ISDIR(mode))
+ goto next;
+ score = score_trees(elem, hash2);
+ if (*best_score < score) {
+ char *newpath;
+ newpath = xmalloc(strlen(base) + strlen(path) + 1);
+ sprintf(newpath, "%s%s", base, path);
+ free(*best_match);
+ *best_match = newpath;
+ *best_score = score;
+ }
+ if (recurse_limit) {
+ char *newbase;
+ newbase = xmalloc(strlen(base) + strlen(path) + 2);
+ sprintf(newbase, "%s%s/", base, path);
+ match_trees(elem, hash2, best_score, best_match,
+ newbase, recurse_limit - 1);
+ free(newbase);
+ }
+
+ next:
+ update_tree_entry(&one);
+ }
+ free(one_buf);
+}
+
+/*
+ * A tree "hash1" has a subdirectory at "prefix". Come up with a
+ * tree object by replacing it with another tree "hash2".
+ */
+static int splice_tree(const unsigned char *hash1,
+ char *prefix,
+ const unsigned char *hash2,
+ unsigned char *result)
+{
+ char *subpath;
+ int toplen;
+ char *buf;
+ unsigned long sz;
+ struct tree_desc desc;
+ unsigned char *rewrite_here;
+ const unsigned char *rewrite_with;
+ unsigned char subtree[20];
+ enum object_type type;
+ int status;
+
+ subpath = strchr(prefix, '/');
+ if (!subpath)
+ toplen = strlen(prefix);
+ else {
+ toplen = subpath - prefix;
+ subpath++;
+ }
+
+ buf = read_sha1_file(hash1, &type, &sz);
+ if (!buf)
+ die("cannot read tree %s", sha1_to_hex(hash1));
+ init_tree_desc(&desc, buf, sz);
+
+ rewrite_here = NULL;
+ while (desc.size) {
+ const char *name;
+ unsigned mode;
+ const unsigned char *sha1;
+
+ sha1 = tree_entry_extract(&desc, &name, &mode);
+ if (strlen(name) == toplen &&
+ !memcmp(name, prefix, toplen)) {
+ if (!S_ISDIR(mode))
+ die("entry %s in tree %s is not a tree",
+ name, sha1_to_hex(hash1));
+ rewrite_here = (unsigned char *) sha1;
+ break;
+ }
+ update_tree_entry(&desc);
+ }
+ if (!rewrite_here)
+ die("entry %.*s not found in tree %s",
+ toplen, prefix, sha1_to_hex(hash1));
+ if (subpath) {
+ status = splice_tree(rewrite_here, subpath, hash2, subtree);
+ if (status)
+ return status;
+ rewrite_with = subtree;
+ }
+ else
+ rewrite_with = hash2;
+ hashcpy(rewrite_here, rewrite_with);
+ status = write_sha1_file(buf, sz, tree_type, result);
+ free(buf);
+ return status;
+}
+
+/*
+ * We are trying to come up with a merge between one and two that
+ * results in a tree shape similar to one. The tree two might
+ * correspond to a subtree of one, in which case it needs to be
+ * shifted down by prefixing otherwise empty directories. On the
+ * other hand, it could cover tree one and we might need to pick a
+ * subtree of it.
+ */
+void shift_tree(const unsigned char *hash1,
+ const unsigned char *hash2,
+ unsigned char *shifted,
+ int depth_limit)
+{
+ char *add_prefix;
+ char *del_prefix;
+ int add_score, del_score;
+
+ add_score = del_score = score_trees(hash1, hash2);
+ add_prefix = xcalloc(1, 1);
+ del_prefix = xcalloc(1, 1);
+
+ /*
+ * See if one's subtree resembles two; if so we need to prefix
+ * two with a few fake trees to match the prefix.
+ */
+ match_trees(hash1, hash2, &add_score, &add_prefix, "", depth_limit);
+
+ /*
+ * See if two's subtree resembles one; if so we need to
+ * pick only subtree of two.
+ */
+ match_trees(hash2, hash1, &del_score, &del_prefix, "", depth_limit);
+
+ /* Assume we do not have to do any shifting */
+ hashcpy(shifted, hash2);
+
+ if (add_score < del_score) {
+ /* We need to pick a subtree of two */
+ unsigned mode;
+
+ if (!*del_prefix)
+ return;
+
+ if (get_tree_entry(hash2, del_prefix, shifted, &mode))
+ die("cannot find path %s in tree %s",
+ del_prefix, sha1_to_hex(hash2));
+ return;
+ }
+
+ if (!*add_prefix)
+ return;
+
+ splice_tree(hash1, add_prefix, hash2, shifted);
+}
+
#include "path-list.h"
#include "xdiff-interface.h"
+static int subtree_merge;
+
+static struct tree *shift_tree_object(struct tree *one, struct tree *two)
+{
+ unsigned char shifted[20];
+
+ /*
+ * NEEDSWORK: this limits the recursion depth to hardcoded
+ * value '2' to avoid excessive overhead.
+ */
+ shift_tree(one->object.sha1, two->object.sha1, shifted, 2);
+ if (!hashcmp(two->object.sha1, shifted))
+ return two;
+ return lookup_tree(shifted);
+}
+
/*
* A virtual commit has
* - (const char *)commit->util set to the name, and
struct cache_entry *ce;
ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
if (!ce)
- return error("cache_addinfo failed: %s", strerror(cache_errno));
+ return error("addinfo_cache failed for path '%s'", path);
return add_cache_entry(ce, options);
}
{
struct tree *result = NULL;
- if (unmerged_index())
+ if (unmerged_index()) {
+ int i;
+ output(0, "There are unmerged index entries:");
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (ce_stage(ce))
+ output(0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name);
+ }
return NULL;
+ }
if (!active_cache_tree)
active_cache_tree = cache_tree();
if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
int fd;
- if (mkdir_p(path, 0777))
- die("failed to create path %s: %s", path, strerror(errno));
- unlink(path);
+ int status;
+ const char *msg = "failed to create path '%s'%s";
+
+ status = mkdir_p(path, 0777);
+ if (status) {
+ if (status == -3) {
+ /* something else exists */
+ error(msg, path, ": perhaps a D/F conflict?");
+ update_wd = 0;
+ goto update_index;
+ }
+ die(msg, path, "");
+ }
+ if (unlink(path)) {
+ if (errno == EISDIR) {
+ /* something else exists */
+ error(msg, path, ": perhaps a D/F conflict?");
+ update_wd = 0;
+ goto update_index;
+ }
+ if (errno != ENOENT)
+ die("failed to unlink %s "
+ "in preparation to update: %s",
+ path, strerror(errno));
+ }
if (mode & 0100)
mode = 0777;
else
die("do not know what to do with %06o %s '%s'",
mode, sha1_to_hex(sha), path);
}
+ update_index:
if (update_cache)
add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
}
ren2_dst, branch1, dst_name2);
remove_file(0, ren2_dst, 0);
}
- update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
- update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
+ if (index_only) {
+ remove_file_from_cache(dst_name1);
+ remove_file_from_cache(dst_name2);
+ /*
+ * Uncomment to leave the conflicting names in the resulting tree
+ *
+ * update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1);
+ * update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2);
+ */
+ } else {
+ update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
+ update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
+ }
while (delp--)
free(del[delp]);
}
if (strcmp(ren1_dst, ren2_dst) != 0) {
clean_merge = 0;
output(1, "CONFLICT (rename/rename): "
- "Rename %s->%s in branch %s "
- "rename %s->%s in %s",
+ "Rename \"%s\"->\"%s\" in branch \"%s\" "
+ "rename \"%s\"->\"%s\" in \"%s\"%s",
src, ren1_dst, branch1,
- src, ren2_dst, branch2);
+ src, ren2_dst, branch2,
+ index_only ? " (left unresolved)": "");
+ if (index_only) {
+ remove_file_from_cache(src);
+ update_file(0, ren1->pair->one->sha1,
+ ren1->pair->one->mode, src);
+ }
conflict_rename_rename(ren1, branch1, ren2, branch2);
} else {
struct merge_file_info mfi;
return clean_merge;
}
-static unsigned char *has_sha(const unsigned char *sha)
+static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
{
- return is_null_sha1(sha) ? NULL: (unsigned char *)sha;
+ return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
}
/* Per entry merge function */
print_index_entry("\tpath: ", entry);
*/
int clean_merge = 1;
- unsigned char *o_sha = has_sha(entry->stages[1].sha);
- unsigned char *a_sha = has_sha(entry->stages[2].sha);
- unsigned char *b_sha = has_sha(entry->stages[3].sha);
unsigned o_mode = entry->stages[1].mode;
unsigned a_mode = entry->stages[2].mode;
unsigned b_mode = entry->stages[3].mode;
+ unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
+ unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
+ unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
if (o_sha && (!a_sha || !b_sha)) {
/* Case A: Deleted in one */
update_file_flags(mfi.sha, mfi.mode, path,
0 /* update_cache */, 1 /* update_working_directory */);
}
+ } else if (!o_sha && !a_sha && !b_sha) {
+ /*
+ * this entry was deleted altogether. a_mode == 0 means
+ * we had that path and want to actively remove it.
+ */
+ remove_file(1, path, !a_mode);
} else
die("Fatal merge failure, shouldn't happen.");
struct tree **result)
{
int code, clean;
+
+ if (subtree_merge) {
+ merge = shift_tree_object(head, merge);
+ common = shift_tree_object(head, common);
+ }
+
if (sha_eq(common->object.sha1, merge->object.sha1)) {
output(0, "Already uptodate!");
*result = head;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
+ if (argv[0]) {
+ int namelen = strlen(argv[0]);
+ if (8 < namelen &&
+ !strcmp(argv[0] + namelen - 8, "-subtree"))
+ subtree_merge = 1;
+ }
+
git_config(merge_config);
if (getenv("GIT_MERGE_VERBOSITY"))
verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
if (show(3))
printf("Merging %s with %s\n", branch1, branch2);
- index_fd = hold_lock_file_for_update(lock, get_index_file(), 1);
+ index_fd = hold_locked_index(lock, 1);
for (i = 0; i < bases_count; i++) {
struct commit *ancestor = get_ref(bases[i]);
if (active_cache_changed &&
(write_cache(index_fd, active_cache, active_nr) ||
- close(index_fd) || commit_lock_file(lock)))
+ close(index_fd) || commit_locked_index(lock)))
die ("unable to write %s", get_index_file());
return clean ? 0: 1;
#include "cache.h"
#include "object.h"
+#include "decorate.h"
int track_object_refs = 0;
-static unsigned int refs_hash_size, nr_object_refs;
-static struct object_refs **refs_hash;
+static struct decoration ref_decorate;
-static unsigned int hash_obj(struct object *obj, unsigned int n)
+struct object_refs *lookup_object_refs(struct object *base)
{
- unsigned int hash = *(unsigned int *)obj->sha1;
- return hash % n;
+ return lookup_decoration(&ref_decorate, base);
}
-static void insert_ref_hash(struct object_refs *ref, struct object_refs **hash, unsigned int size)
+static void add_object_refs(struct object *obj, struct object_refs *refs)
{
- int j = hash_obj(ref->base, size);
-
- while (hash[j]) {
- j++;
- if (j >= size)
- j = 0;
- }
- hash[j] = ref;
-}
-
-static void grow_refs_hash(void)
-{
- int i;
- int new_hash_size = (refs_hash_size + 1000) * 3 / 2;
- struct object_refs **new_hash;
-
- new_hash = xcalloc(new_hash_size, sizeof(struct object_refs *));
- for (i = 0; i < refs_hash_size; i++) {
- struct object_refs *ref = refs_hash[i];
- if (!ref)
- continue;
- insert_ref_hash(ref, new_hash, new_hash_size);
- }
- free(refs_hash);
- refs_hash = new_hash;
- refs_hash_size = new_hash_size;
-}
-
-static void add_object_refs(struct object *obj, struct object_refs *ref)
-{
- int nr = nr_object_refs + 1;
-
- if (nr > refs_hash_size * 2 / 3)
- grow_refs_hash();
- ref->base = obj;
- insert_ref_hash(ref, refs_hash, refs_hash_size);
- nr_object_refs = nr;
-}
-
-struct object_refs *lookup_object_refs(struct object *obj)
-{
- struct object_refs *ref;
- int j;
-
- /* nothing to lookup */
- if (!refs_hash_size)
- return NULL;
- j = hash_obj(obj, refs_hash_size);
- while ((ref = refs_hash[j]) != NULL) {
- if (ref->base == obj)
- break;
- j++;
- if (j >= refs_hash_size)
- j = 0;
- }
- return ref;
+ if (add_decoration(&ref_decorate, obj, refs))
+ die("object %s tried to add refs twice!", sha1_to_hex(obj->sha1));
}
struct object_refs *alloc_object_refs(unsigned count)
struct object_refs {
unsigned count;
- struct object *base;
struct object *ref[FLEX_ARRAY]; /* more */
};
*/
nr_objects = num_packed_objects(p);
for (i = 0, err = 0; i < nr_objects; i++) {
- unsigned char sha1[20];
+ const unsigned char *sha1;
void *data;
enum object_type type;
unsigned long size;
off_t offset;
- if (nth_packed_object_sha1(p, i, sha1))
+ sha1 = nth_packed_object_sha1(p, i);
+ if (!sha1)
die("internal error pack-check nth-packed-object");
offset = find_pack_entry_one(sha1, p);
if (!offset)
memset(chain_histogram, 0, sizeof(chain_histogram));
for (i = 0; i < nr_objects; i++) {
- unsigned char sha1[20], base_sha1[20];
+ const unsigned char *sha1;
+ unsigned char base_sha1[20];
const char *type;
unsigned long size;
unsigned long store_size;
off_t offset;
unsigned int delta_chain_length;
- if (nth_packed_object_sha1(p, i, sha1))
+ sha1 = nth_packed_object_sha1(p, i);
+ if (!sha1)
die("internal error pack-check nth-packed-object");
offset = find_pack_entry_one(sha1, p);
if (!offset)
--- /dev/null
+#include "cache.h"
+#include "diff.h"
+#include "commit.h"
+#include "patch-ids.h"
+
+static int commit_patch_id(struct commit *commit, struct diff_options *options,
+ unsigned char *sha1)
+{
+ if (commit->parents)
+ diff_tree_sha1(commit->parents->item->object.sha1,
+ commit->object.sha1, "", options);
+ else
+ diff_root_tree_sha1(commit->object.sha1, "", options);
+ diffcore_std(options);
+ return diff_flush_patch_id(options, sha1);
+}
+
+static uint32_t take2(const unsigned char *id)
+{
+ return ((id[0] << 8) | id[1]);
+}
+
+/*
+ * Conventional binary search loop looks like this:
+ *
+ * do {
+ * int mi = (lo + hi) / 2;
+ * int cmp = "entry pointed at by mi" minus "target";
+ * if (!cmp)
+ * return (mi is the wanted one)
+ * if (cmp > 0)
+ * hi = mi; "mi is larger than target"
+ * else
+ * lo = mi+1; "mi is smaller than target"
+ * } while (lo < hi);
+ *
+ * The invariants are:
+ *
+ * - When entering the loop, lo points at a slot that is never
+ * above the target (it could be at the target), hi points at a
+ * slot that is guaranteed to be above the target (it can never
+ * be at the target).
+ *
+ * - We find a point 'mi' between lo and hi (mi could be the same
+ * as lo, but never can be the same as hi), and check if it hits
+ * the target. There are three cases:
+ *
+ * - if it is a hit, we are happy.
+ *
+ * - if it is strictly higher than the target, we update hi with
+ * it.
+ *
+ * - if it is strictly lower than the target, we update lo to be
+ * one slot after it, because we allow lo to be at the target.
+ *
+ * When choosing 'mi', we do not have to take the "middle" but
+ * anywhere in between lo and hi, as long as lo <= mi < hi is
+ * satisfied. When we somehow know that the distance between the
+ * target and lo is much shorter than the target and hi, we could
+ * pick mi that is much closer to lo than the midway.
+ */
+static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)
+{
+ int hi = nr;
+ int lo = 0;
+ int mi = 0;
+
+ if (!nr)
+ return -1;
+
+ if (nr != 1) {
+ unsigned lov, hiv, miv, ofs;
+
+ for (ofs = 0; ofs < 18; ofs += 2) {
+ lov = take2(table[0]->patch_id + ofs);
+ hiv = take2(table[nr-1]->patch_id + ofs);
+ miv = take2(id + ofs);
+ if (miv < lov)
+ return -1;
+ if (hiv < miv)
+ return -1 - nr;
+ if (lov != hiv) {
+ /*
+ * At this point miv could be equal
+ * to hiv (but id could still be higher);
+ * the invariant of (mi < hi) should be
+ * kept.
+ */
+ mi = (nr-1) * (miv - lov) / (hiv - lov);
+ if (lo <= mi && mi < hi)
+ break;
+ die("oops");
+ }
+ }
+ if (18 <= ofs)
+ die("cannot happen -- lo and hi are identical");
+ }
+
+ do {
+ int cmp;
+ cmp = hashcmp(table[mi]->patch_id, id);
+ if (!cmp)
+ return mi;
+ if (cmp > 0)
+ hi = mi;
+ else
+ lo = mi + 1;
+ mi = (hi + lo) / 2;
+ } while (lo < hi);
+ return -lo-1;
+}
+
+#define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */
+struct patch_id_bucket {
+ struct patch_id_bucket *next;
+ int nr;
+ struct patch_id bucket[BUCKET_SIZE];
+};
+
+int init_patch_ids(struct patch_ids *ids)
+{
+ memset(ids, 0, sizeof(*ids));
+ diff_setup(&ids->diffopts);
+ ids->diffopts.recursive = 1;
+ if (diff_setup_done(&ids->diffopts) < 0)
+ return error("diff_setup_done failed");
+ return 0;
+}
+
+int free_patch_ids(struct patch_ids *ids)
+{
+ struct patch_id_bucket *next, *patches;
+
+ free(ids->table);
+ for (patches = ids->patches; patches; patches = next) {
+ next = patches->next;
+ free(patches);
+ }
+ return 0;
+}
+
+static struct patch_id *add_commit(struct commit *commit,
+ struct patch_ids *ids,
+ int no_add)
+{
+ struct patch_id_bucket *bucket;
+ struct patch_id *ent;
+ unsigned char sha1[20];
+ int pos;
+
+ if (commit_patch_id(commit, &ids->diffopts, sha1))
+ return NULL;
+ pos = patch_pos(ids->table, ids->nr, sha1);
+ if (0 <= pos)
+ return ids->table[pos];
+ if (no_add)
+ return NULL;
+
+ pos = -1 - pos;
+
+ bucket = ids->patches;
+ if (!bucket || (BUCKET_SIZE <= bucket->nr)) {
+ bucket = xcalloc(1, sizeof(*bucket));
+ bucket->next = ids->patches;
+ ids->patches = bucket;
+ }
+ ent = &bucket->bucket[bucket->nr++];
+ hashcpy(ent->patch_id, sha1);
+
+ if (ids->alloc <= ids->nr) {
+ ids->alloc = alloc_nr(ids->nr);
+ ids->table = xrealloc(ids->table, sizeof(ent) * ids->alloc);
+ }
+ if (pos < ids->nr)
+ memmove(ids->table + pos + 1, ids->table + pos,
+ sizeof(ent) * (ids->nr - pos));
+ ids->nr++;
+ ids->table[pos] = ent;
+ return ids->table[pos];
+}
+
+struct patch_id *has_commit_patch_id(struct commit *commit,
+ struct patch_ids *ids)
+{
+ return add_commit(commit, ids, 1);
+}
+
+struct patch_id *add_commit_patch_id(struct commit *commit,
+ struct patch_ids *ids)
+{
+ return add_commit(commit, ids, 0);
+}
--- /dev/null
+#ifndef PATCH_IDS_H
+#define PATCH_IDS_H
+
+struct patch_id {
+ unsigned char patch_id[20];
+ char seen;
+};
+
+struct patch_ids {
+ struct diff_options diffopts;
+ int nr, alloc;
+ struct patch_id **table;
+ struct patch_id_bucket *patches;
+};
+
+int init_patch_ids(struct patch_ids *);
+int free_patch_ids(struct patch_ids *);
+struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
+struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *);
+
+#endif /* PATCH_IDS_H */
struct cache_tree *active_cache_tree;
-int cache_errno;
-
static void *cache_mmap;
static size_t cache_mmap_size;
return 0;
}
-int add_file_to_index(const char *path, int verbose)
+int add_file_to_cache(const char *path, int verbose)
{
int size, namelen;
struct stat st;
continue;
if (p->name[len] != '/')
continue;
+ if (!ce_stage(p) && !p->ce_mode)
+ continue;
retval = -1;
if (!ok_to_replace)
break;
pos = cache_name_pos(name, ntohs(create_ce_flags(len, stage)));
if (pos >= 0) {
- retval = -1;
- if (!ok_to_replace)
- break;
- remove_cache_entry_at(pos);
- continue;
+ /*
+ * Found one, but not so fast. This could
+ * be a marker that says "I was here, but
+ * I am being removed". Such an entry is
+ * not a part of the resulting tree, and
+ * it is Ok to have a directory at the same
+ * path.
+ */
+ if (stage || active_cache[pos]->ce_mode) {
+ retval = -1;
+ if (!ok_to_replace)
+ break;
+ remove_cache_entry_at(pos);
+ continue;
+ }
}
+ else
+ pos = -pos-1;
/*
* Trivial optimization: if we find an entry that
* already matches the sub-directory, then we know
* we're ok, and we can exit.
*/
- pos = -pos-1;
while (pos < active_nr) {
struct cache_entry *p = active_cache[pos];
if ((ce_namelen(p) <= len) ||
(p->name[len] != '/') ||
memcmp(p->name, name, len))
break; /* not our subdirectory */
- if (ce_stage(p) == stage)
+ if (ce_stage(p) == stage && (stage || p->ce_mode))
/* p is at the same stage as our entry, and
* is a subdirectory of what we are looking
* at, so we cannot have conflicts at our
*/
static int check_file_directory_conflict(const struct cache_entry *ce, int pos, int ok_to_replace)
{
+ int retval;
+
+ /*
+ * When ce is an "I am going away" entry, we allow it to be added
+ */
+ if (!ce_stage(ce) && !ce->ce_mode)
+ return 0;
+
/*
* We check if the path is a sub-path of a subsequent pathname
* first, since removing those will not change the position
- * in the array
+ * in the array.
*/
- int retval = has_file_name(ce, pos, ok_to_replace);
+ retval = has_file_name(ce, pos, ok_to_replace);
+
/*
* Then check if the path might have a clashing sub-directory
* before it.
* For example, you'd want to do this after doing a "git-read-tree",
* to link up the stat cache details with the proper files.
*/
-struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
+static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really, int *err)
{
struct stat st;
struct cache_entry *updated;
int changed, size;
if (lstat(ce->name, &st) < 0) {
- cache_errno = errno;
+ if (err)
+ *err = errno;
return NULL;
}
}
if (ce_modified(ce, &st, really)) {
- cache_errno = EINVAL;
+ if (err)
+ *err = EINVAL;
return NULL;
}
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce, *new;
+ int cache_errno = 0;
+
ce = active_cache[i];
if (ce_stage(ce)) {
while ((i < active_nr) &&
continue;
}
- new = refresh_cache_entry(ce, really);
+ new = refresh_cache_ent(ce, really, &cache_errno);
if (new == ce)
continue;
if (!new) {
return has_errors;
}
+struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
+{
+ return refresh_cache_ent(ce, really, NULL);
+}
+
static int verify_hdr(struct cache_header *hdr, unsigned long size)
{
SHA_CTX c;
return commit_lock_file(&packlock);
}
-int delete_ref(const char *refname, unsigned char *sha1)
+int delete_ref(const char *refname, const unsigned char *sha1)
{
struct ref_lock *lock;
int err, i, ret = 0, flag = 0;
goto rollback;
}
- if (!prefixcmp(oldref, "refs/heads/") &&
- !prefixcmp(newref, "refs/heads/")) {
- char oldsection[1024], newsection[1024];
-
- snprintf(oldsection, 1024, "branch.%s", oldref + 11);
- snprintf(newsection, 1024, "branch.%s", newref + 11);
- if (git_config_rename_section(oldsection, newsection) < 0)
- return 1;
- }
-
return 0;
rollback:
unlock_ref(lock);
return -1;
}
+ if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
+ /*
+ * Special hack: If a branch is updated directly and HEAD
+ * points to it (may happen on the remote side of a push
+ * for example) then logically the HEAD reflog should be
+ * updated too.
+ * A generic solution implies reverse symref information,
+ * but finding all symrefs pointing to the given branch
+ * would be rather costly for this rare event (the direct
+ * update of a branch) to be worth it. So let's cheat and
+ * check with HEAD only which should cover 99% of all usage
+ * scenarios (even 100% of the default ones).
+ */
+ unsigned char head_sha1[20];
+ int head_flag;
+ const char *head_ref;
+ head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
+ if (head_ref && (head_flag & REF_ISSYMREF) &&
+ !strcmp(head_ref, lock->ref_name))
+ log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
+ }
if (commit_lock_file(lock->lk)) {
error("Couldn't set %s", lock->ref_name);
unlock_ref(lock);
#include "revision.h"
#include "grep.h"
#include "reflog-walk.h"
+#include "patch-ids.h"
static char *path_name(struct name_path *path, const char *name)
{
{
struct commit_list *parent = commit->parents;
unsigned left_flag;
+ int add, rest;
if (commit->object.flags & ADDED)
return;
return;
left_flag = (commit->object.flags & SYMMETRIC_LEFT);
- parent = commit->parents;
- while (parent) {
+
+ rest = !revs->first_parent_only;
+ for (parent = commit->parents, add = 1; parent; add = rest) {
struct commit *p = parent->item;
parent = parent->next;
-
parse_commit(p);
p->object.flags |= left_flag;
if (p->object.flags & SEEN)
continue;
p->object.flags |= SEEN;
- insert_by_date(p, list);
+ if (add)
+ insert_by_date(p, list);
}
}
+static void cherry_pick_list(struct commit_list *list)
+{
+ struct commit_list *p;
+ int left_count = 0, right_count = 0;
+ int left_first;
+ struct patch_ids ids;
+
+ /* First count the commits on the left and on the right */
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ unsigned flags = commit->object.flags;
+ if (flags & BOUNDARY)
+ ;
+ else if (flags & SYMMETRIC_LEFT)
+ left_count++;
+ else
+ right_count++;
+ }
+
+ left_first = left_count < right_count;
+ init_patch_ids(&ids);
+
+ /* Compute patch-ids for one side */
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ unsigned flags = commit->object.flags;
+
+ if (flags & BOUNDARY)
+ continue;
+ /*
+ * If we have fewer left, left_first is set and we omit
+ * commits on the right branch in this loop. If we have
+ * fewer right, we skip the left ones.
+ */
+ if (left_first != !!(flags & SYMMETRIC_LEFT))
+ continue;
+ commit->util = add_commit_patch_id(commit, &ids);
+ }
+
+ /* Check the other side */
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ struct patch_id *id;
+ unsigned flags = commit->object.flags;
+
+ if (flags & BOUNDARY)
+ continue;
+ /*
+ * If we have fewer left, left_first is set and we omit
+ * commits on the left branch in this loop.
+ */
+ if (left_first == !!(flags & SYMMETRIC_LEFT))
+ continue;
+
+ /*
+ * Have we seen the same patch id?
+ */
+ id = has_commit_patch_id(commit, &ids);
+ if (!id)
+ continue;
+ id->seen = 1;
+ commit->object.flags |= SHOWN;
+ }
+
+ /* Now check the original side for seen ones */
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ struct patch_id *ent;
+
+ ent = commit->util;
+ if (!ent)
+ continue;
+ if (ent->seen)
+ commit->object.flags |= SHOWN;
+ commit->util = NULL;
+ }
+
+ free_patch_ids(&ids);
+}
+
static void limit_list(struct rev_info *revs)
{
struct commit_list *list = revs->commits;
continue;
p = &commit_list_insert(commit, p)->next;
}
+ if (revs->cherry_pick)
+ cherry_pick_list(newlist);
+
revs->commits = newlist;
}
add_pending_object(cb->all_revs, o, "");
}
else if (!cb->warned_bad_reflog) {
- warn("reflog of '%s' references pruned commits",
+ warning("reflog of '%s' references pruned commits",
cb->name_for_errormsg);
cb->warned_bad_reflog = 1;
}
revs->min_age = -1;
revs->skip_count = -1;
revs->max_count = -1;
+ revs->subject_prefix = "PATCH";
revs->prune_fn = NULL;
revs->prune_data = NULL;
handle_all(revs, flags);
continue;
}
+ if (!strcmp(arg, "--first-parent")) {
+ revs->first_parent_only = 1;
+ continue;
+ }
if (!strcmp(arg, "--reflog")) {
handle_reflog(revs, flags);
continue;
revs->left_right = 1;
continue;
}
+ if (!strcmp(arg, "--cherry-pick")) {
+ revs->cherry_pick = 1;
+ continue;
+ }
if (!strcmp(arg, "--objects")) {
revs->tag_objects = 1;
revs->tree_objects = 1;
boundary:2,
left_right:1,
parents:1,
- reverse:1;
+ reverse:1,
+ cherry_pick:1,
+ first_parent_only:1;
/* Diff flags */
unsigned int diff:1,
const char *add_signoff;
const char *extra_headers;
const char *log_reencode;
+ const char *subject_prefix;
int no_inline;
/* Filter by commit log message */
return (uint32_t)((p->index_size - 20 - 20 - 4*256) / 24);
}
-int nth_packed_object_sha1(const struct packed_git *p, uint32_t n,
- unsigned char* sha1)
+const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
+ uint32_t n)
{
const unsigned char *index = p->index_data;
index += 4 * 256;
if (num_packed_objects(p) <= n)
- return -1;
- hashcpy(sha1, index + 24 * n + 4);
- return 0;
+ return NULL;
+ return index + 24 * n + 4;
}
off_t find_pack_entry_one(const unsigned char *sha1,
return error("sha1 file %s: %s\n", filename, strerror(errno));
}
- snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+ snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
fd = mkstemp(tmpfile);
if (fd < 0) {
if (write_buffer(fd, compressed, size) < 0)
die("unable to write sha1 file");
fchmod(fd, 0444);
- close(fd);
+ if (close(fd))
+ die("unable to write sha1 file");
free(compressed);
return move_temp_to_file(tmpfile, filename);
int ret;
SHA_CTX c;
- snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+ snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
local = mkstemp(tmpfile);
if (local < 0) {
} while (1);
inflateEnd(&stream);
- close(local);
+ fchmod(local, 0444);
+ if (close(local) != 0)
+ die("unable to write sha1 file");
SHA1_Final(real_sha1, &c);
if (ret != Z_STREAM_END) {
unlink(tmpfile);
static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
{
struct packed_git *p;
- unsigned char found_sha1[20];
+ const unsigned char *found_sha1 = NULL;
int found = 0;
prepare_packed_git();
uint32_t first = 0, last = num;
while (first < last) {
uint32_t mid = (first + last) / 2;
- unsigned char now[20];
+ const unsigned char *now;
int cmp;
- nth_packed_object_sha1(p, mid, now);
+ now = nth_packed_object_sha1(p, mid);
cmp = hashcmp(match, now);
if (!cmp) {
first = mid;
last = mid;
}
if (first < num) {
- unsigned char now[20], next[20];
- nth_packed_object_sha1(p, first, now);
+ const unsigned char *now, *next;
+ now = nth_packed_object_sha1(p, first);
if (match_sha(len, match, now)) {
- if (nth_packed_object_sha1(p, first+1, next) ||
- !match_sha(len, match, next)) {
+ next = nth_packed_object_sha1(p, first+1);
+ if (!next|| !match_sha(len, match, next)) {
/* unique within this pack */
if (!found) {
- hashcpy(found_sha1, now);
+ found_sha1 = now;
found++;
}
else if (hashcmp(found_sha1, now)) {
9 exists O!=A missing no merge must match A and be
up-to-date, if exists.
------------------------------------------------------------------
- 10 exists O==A missing remove ditto
+ 10 exists O==A missing no merge must match A
------------------------------------------------------------------
11 exists O!=A O!=B no merge must match A and be
A!=B up-to-date, if exists.
--- /dev/null
+#!/bin/sh
+
+test_description='merge-recursive backend test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup 1' '
+
+ echo hello >a &&
+ o0=$(git hash-object a) &&
+ cp a b &&
+ cp a A &&
+ mkdir d &&
+ cp a d/e &&
+
+ test_tick &&
+ git add a b A d/e &&
+ git commit -m initial &&
+ c0=$(git rev-parse --verify HEAD) &&
+ git branch side &&
+ git branch df-1 &&
+ git branch df-2 &&
+ git branch df-3 &&
+ git branch remove &&
+
+ echo hello >>a &&
+ cp a d/e &&
+ o1=$(git hash-object a) &&
+
+ git add a d/e &&
+
+ test_tick &&
+ git commit -m "master modifies a and d/e" &&
+ c1=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o1 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o1 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+'
+
+test_expect_success 'setup 2' '
+
+ rm -rf [Aabd] &&
+ git checkout side &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual &&
+
+ echo goodbye >>a &&
+ o2=$(git hash-object a) &&
+
+ git add a &&
+
+ test_tick &&
+ git commit -m "side modifies a" &&
+ c2=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o2 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o2 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+'
+
+test_expect_success 'setup 3' '
+
+ rm -rf [Aabd] &&
+ git checkout df-1 &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual &&
+
+ rm -f b && mkdir b && echo df-1 >b/c && git add b/c &&
+ o3=$(git hash-object b/c) &&
+
+ test_tick &&
+ git commit -m "df-1 makes b/c" &&
+ c3=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o3 b/c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 a"
+ echo "100644 $o3 0 b/c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+'
+
+test_expect_success 'setup 4' '
+
+ rm -rf [Aabd] &&
+ git checkout df-2 &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual &&
+
+ rm -f a && mkdir a && echo df-2 >a/c && git add a/c &&
+ o4=$(git hash-object a/c) &&
+
+ test_tick &&
+ git commit -m "df-2 makes a/c" &&
+ c4=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o4 a/c"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o4 0 a/c"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+'
+
+test_expect_success 'setup 5' '
+
+ rm -rf [Aabd] &&
+ git checkout remove &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual &&
+
+ rm -f b &&
+ echo remove-conflict >a &&
+
+ git add a &&
+ git rm b &&
+ o5=$(git hash-object a) &&
+
+ test_tick &&
+ git commit -m "remove removes b and modifies a" &&
+ c5=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o5 a"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o5 0 a"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_expect_success 'setup 6' '
+
+ rm -rf [Aabd] &&
+ git checkout df-3 &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ git diff -u expected actual &&
+
+ rm -fr d && echo df-3 >d && git add d &&
+ o6=$(git hash-object d) &&
+
+ test_tick &&
+ git commit -m "df-3 makes d" &&
+ c6=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 A"
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o6 d"
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o6 0 d"
+ ) >expected &&
+ git diff -u expected actual
+'
+
+test_expect_success 'merge-recursive simple' '
+
+ rm -fr [Aabd] &&
+ git checkout -f "$c2" &&
+
+ git-merge-recursive "$c0" -- "$c2" "$c1"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 1 a"
+ echo "100644 $o2 2 a"
+ echo "100644 $o1 3 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive remove conflict' '
+
+ rm -fr [Aabd] &&
+ git checkout -f "$c1" &&
+
+ git-merge-recursive "$c0" -- "$c1" "$c5"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive remove conflict' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 1 a"
+ echo "100644 $o1 2 a"
+ echo "100644 $o5 3 a"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f simple' '
+ rm -fr [Aabd] &&
+ git reset --hard &&
+ git checkout -f "$c1" &&
+
+ git-merge-recursive "$c0" -- "$c1" "$c3"
+'
+
+test_expect_success 'merge-recursive result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o1 0 a"
+ echo "100644 $o3 0 b/c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+ rm -fr [Aabd] &&
+ git reset --hard &&
+ git checkout -f "$c1" &&
+
+ git-merge-recursive "$c0" -- "$c1" "$c4"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 1 a"
+ echo "100644 $o1 2 a"
+ echo "100644 $o4 0 a/c"
+ echo "100644 $o0 0 b"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict the other way' '
+
+ rm -fr [Aabd] &&
+ git reset --hard &&
+ git checkout -f "$c4" &&
+
+ git-merge-recursive "$c0" -- "$c4" "$c1"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result the other way' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 1 a"
+ echo "100644 $o1 3 a"
+ echo "100644 $o4 0 a/c"
+ echo "100644 $o0 0 b"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+ rm -fr [Aabd] &&
+ git reset --hard &&
+ git checkout -f "$c1" &&
+
+ git-merge-recursive "$c0" -- "$c1" "$c6"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o6 3 d"
+ echo "100644 $o0 1 d/e"
+ echo "100644 $o1 2 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+ rm -fr [Aabd] &&
+ git reset --hard &&
+ git checkout -f "$c6" &&
+
+ git-merge-recursive "$c0" -- "$c6" "$c1"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o6 2 d"
+ echo "100644 $o0 1 d/e"
+ echo "100644 $o1 3 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_expect_success 'reset and 3-way merge' '
+
+ git reset --hard "$c2" &&
+ git read-tree -m "$c0" "$c2" "$c1"
+
+'
+
+test_expect_success 'reset and bind merge' '
+
+ git reset --hard master &&
+ git read-tree --prefix=M/ master &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 M/A"
+ echo "100644 $o1 0 M/a"
+ echo "100644 $o0 0 M/b"
+ echo "100644 $o1 0 M/d/e"
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ git diff -u expected actual &&
+
+ git read-tree --prefix=a1/ master &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 M/A"
+ echo "100644 $o1 0 M/a"
+ echo "100644 $o0 0 M/b"
+ echo "100644 $o1 0 M/d/e"
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 a1/A"
+ echo "100644 $o1 0 a1/a"
+ echo "100644 $o0 0 a1/b"
+ echo "100644 $o1 0 a1/d/e"
+ echo "100644 $o0 0 b"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+ git read-tree --prefix=z/ master &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 0 A"
+ echo "100644 $o0 0 M/A"
+ echo "100644 $o1 0 M/a"
+ echo "100644 $o0 0 M/b"
+ echo "100644 $o1 0 M/d/e"
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 a1/A"
+ echo "100644 $o1 0 a1/a"
+ echo "100644 $o0 0 a1/b"
+ echo "100644 $o1 0 a1/d/e"
+ echo "100644 $o0 0 b"
+ echo "100644 $o1 0 d/e"
+ echo "100644 $o0 0 z/A"
+ echo "100644 $o1 0 z/a"
+ echo "100644 $o0 0 z/b"
+ echo "100644 $o1 0 z/d/e"
+ ) >expected &&
+ git diff -u expected actual
+
+'
+
+test_done
+
. ./test-lib.sh
test_expect_success \
- 'prepare an trivial repository' \
+ 'prepare a trivial repository' \
'echo Hello > A &&
git-update-index --add A &&
git-commit -m "Initial commit." &&
git-branch r &&
git-branch -m q r/q'
+mv .git/config .git/config-saved
+
+test_expect_success 'git branch -m q q2 without config should succeed' '
+ git-branch -m q q2 &&
+ git-branch -m q2 q
+'
+
+mv .git/config-saved .git/config
+
git-config branch.s/s.dummy Hello
test_expect_success \
'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
'git-ls-files --error-unmatch baz'
+test_expect_success 'Remove nonexistent file with --ignore-unmatch' '
+ git rm --ignore-unmatch nonexistent
+'
+
+test_expect_success '"rm" command printed' '
+ echo frotz > test-file &&
+ git add test-file &&
+ git commit -m "add file for rm test" &&
+ git rm test-file > rm-output &&
+ test `egrep "^rm " rm-output | wc -l` = 1 &&
+ rm -f test-file rm-output &&
+ git commit -m "remove file from rm test"
+'
+
+test_expect_success '"rm" command suppressed with --quiet' '
+ echo frotz > test-file &&
+ git add test-file &&
+ git commit -m "add file for rm --quiet test" &&
+ git rm --quiet test-file > rm-output &&
+ test `wc -l < rm-output` = 0 &&
+ rm -f test-file rm-output &&
+ git commit -m "remove file from rm --quiet test"
+'
+
# Now, failure cases.
test_expect_success 'Re-add foo and baz' '
git add foo baz &&
! test -d frotz
'
+test_expect_failure 'Remove nonexistent file returns nonzero exit status' '
+ git rm nonexistent
+'
+
test_done
format-patch --inline --stdout initial..side
format-patch --inline --stdout initial..master^
format-patch --inline --stdout initial..master
+format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
diff --abbrev initial..side
diff -r initial..side
--- /dev/null
+$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
+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: [TESTCASE] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+
+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: [TESTCASE] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+
+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--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [TESTCASE] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
cat file2 >file2.orig
git add file1 file2 &&
sed -e "/^B/d" <file1.orig >file1 &&
- sed -e "/^B/d" <file2.orig >file2 &&
+ sed -e "/^[BQ]/d" <file2.orig >file2 &&
+ echo Q | tr -d "\\012" >>file2 &&
cat file1 >file1.mods &&
cat file2 >file2.mods &&
git diff |
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Shawn O. Pearce
+#
+
+test_description='git-apply -p handling.'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir sub &&
+ echo A >sub/file1 &&
+ cp sub/file1 file1 &&
+ git add sub/file1 &&
+ echo B >sub/file1 &&
+ git diff >patch.file &&
+ rm sub/file1 &&
+ rmdir sub
+'
+
+test_expect_success 'apply git diff with -p2' '
+ git apply -p2 patch.file
+'
+
+test_done
git commit -q -a -m first
git checkout -b second master
-git show first:a1 | sed 's/To die, t/To die! T/' > a1
+git show first:a1 |
+sed -e 's/To die, t/To die! T/' > a1
+echo "* END *" >>a1
git commit -q -a -m second
# activate rerere
test_expect_failure 'conflicting merge' 'git pull . first'
-sha1=4f58849a60b4f969a2848966b6d02893b783e8fb
+sha1=$(sed -e 's/\t.*//' .git/rr-cache/MERGE_RR)
rr=.git/rr-cache/$sha1
test_expect_success 'recorded preimage' "grep ======= $rr/preimage"
test_expect_success 'no postimage or thisimage yet' \
"test ! -f $rr/postimage -a ! -f $rr/thisimage"
+test_expect_success 'preimage has right number of lines' '
+
+ cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
+ test $cnt = 9
+
+'
+
git show first:a1 > a1
cat > expect << EOF
--- a/a1
+++ b/a1
-@@ -6,11 +6,7 @@
+@@ -6,17 +6,9 @@
The heart-ache and the thousand natural shocks
That flesh is heir to, 'tis a consummation
Devoutly to be wish'd.
To sleep: perchance to dream: ay, there's the rub;
For in that sleep of death what dreams may come
When we have shuffled off this mortal coil,
+ Must give us pause: there's the respect
+ That makes calamity of so long life;
+-<<<<<<<
+-=======
+-* END *
+->>>>>>>
EOF
-
git rerere diff > out
test_expect_success 'rerere diff' 'git diff expect out'
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='git-shortlog
+'
+
+. ./test-lib.sh
+
+echo 1 > a1
+git add a1
+tree=$(git write-tree)
+commit=$( (echo "Test"; echo) | git commit-tree $tree )
+git update-ref HEAD $commit
+
+echo 2 > a1
+git commit -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1
+
+# test if the wrapping is still valid when replacing all i's by treble clefs.
+echo 3 > a1
+git commit -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\360\235\204\236')" a1
+
+# now fsck up the utf8
+git repo-config i18n.commitencoding non-utf-8
+echo 4 > a1
+git commit -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\370\235\204\236')" a1
+
+echo 5 > a1
+git commit -m "a 12 34 56 78" a1
+
+git shortlog -w HEAD > out
+
+cat > expect << EOF
+A U Thor (5):
+ Test
+ This is a very, very long first line for the commit message to see if
+ it is wrapped correctly
+ Th𝄞s 𝄞s a very, very long f𝄞rst l𝄞ne for the comm𝄞t message to see 𝄞f
+ 𝄞t 𝄞s wrapped correctly
+ Thø\9d\84\9es ø\9d\84\9es a very, very long fø\9d\84\9erst lø\9d\84\9ene for the commø\9d\84\9et
+ message to see ø\9d\84\9ef ø\9d\84\9et ø\9d\84\9es wrapped correctly
+ a 12 34
+ 56 78
+
+EOF
+
+test_expect_success 'shortlog wrapping' 'diff -u expect out'
+
+test_done
git-update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
git-commit-tree $treeid </dev/null)'
+test_expect_success \
+ 'git-archive' \
+ 'git-archive HEAD >b.tar'
+
test_expect_success \
'git-tar-tree' \
- 'git-tar-tree HEAD >b.tar'
+ 'git-tar-tree HEAD >b2.tar'
+
+test_expect_success \
+ 'git-archive vs. git-tar-tree' \
+ 'diff b.tar b2.tar'
test_expect_success \
'validate file modification time' \
'validate file contents with prefix' \
'diff -r a e/prefix/a'
+test_expect_success \
+ 'git-archive --list outside of a git repo' \
+ 'GIT_DIR=some/non-existing/directory git-archive --list'
+
test_done
push(@old,$fn);
--
-David Kågedal
+David Kågedal
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
done'
cd "$TRASH"
-test_expect_success \
- 'compare delta flavors' \
- 'size_2=`stat -c "%s" test-2-${packname_2}.pack` &&
- size_3=`stat -c "%s" test-3-${packname_3}.pack` &&
- test $size_2 -gt $size_3'
+test_expect_success 'compare delta flavors' '
+ perl -e '\''
+ defined($_ = -s $_) or die for @ARGV;
+ exit 1 if $ARGV[0] <= $ARGV[1];
+ '\'' test-2-$packname_2.pack test-3-$packname_3.pack
+'
rm -fr .git2
mkdir .git2
# the bisection point is the head - this is the bad point.
#
-test_output_expect_success "--bisect l5 ^root" 'git-rev-list $_bisect_option l5 ^root' <<EOF
+test_output_expect_success "$_bisect_option l5 ^root" 'git-rev-list $_bisect_option l5 ^root' <<EOF
c3
EOF
test_expect_success setup '
echo Hello > a &&
git add a &&
-git commit -m "Initial commit" a
+git commit -m "Initial commit" a &&
+initial=$(git rev-parse --verify HEAD)
'
test_expect_success path-optimization '
test $(git-rev-list $commit -- . | wc -l) = 1
'
+test_expect_success 'further setup' '
+ git checkout -b side &&
+ echo Irrelevant >c &&
+ git add c &&
+ git commit -m "Side makes an irrelevant commit" &&
+ echo "More Irrelevancy" >c &&
+ git add c &&
+ git commit -m "Side makes another irrelevant commit" &&
+ echo Bye >a &&
+ git add a &&
+ git commit -m "Side touches a" &&
+ side=$(git rev-parse --verify HEAD) &&
+ echo "Yet more Irrelevancy" >c &&
+ git add c &&
+ git commit -m "Side makes yet another irrelevant commit" &&
+ git checkout master &&
+ echo Another >b &&
+ git add b &&
+ git commit -m "Master touches b" &&
+ git merge side &&
+ echo Touched >b &&
+ git add b &&
+ git commit -m "Master touches b again"
+'
+
+test_expect_success 'path optimization 2' '
+ ( echo "$side"; echo "$initial" ) >expected &&
+ git rev-list HEAD -- a >actual &&
+ diff -u expected actual
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git-rev-list --pretty=format test'
+
+. ./test-lib.sh
+
+test_tick
+test_expect_success 'setup' '
+touch foo && git-add foo && git-commit -m "added foo" &&
+ echo changed >foo && git-commit -a -m "changed foo"
+'
+
+# usage: test_format name format_string <expected_output
+test_format() {
+ cat >expect.$1
+ test_expect_success "format $1" "
+git-rev-list --pretty=format:$2 master >output.$1 &&
+git-diff expect.$1 output.$1
+"
+}
+
+test_format hash %H%n%h <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+131a310eb913d107dd3c09a65d1651175898735d
+131a310
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+86c75cf
+EOF
+
+test_format tree %T%n%t <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+fe722612f26da5064c32ca3843aa154bdb0b08a0
+fe72261
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+4d5fcadc293a348e88f777dc0920f11e7d71441c
+4d5fcad
+EOF
+
+test_format parents %P%n%p <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+86c75cf
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+
+
+EOF
+
+# we don't test relative here
+test_format author %an%n%ae%n%ad%n%aD%n%at <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+A U Thor
+author@example.com
+Thu Apr 7 15:13:13 2005 -0700
+Thu, 7 Apr 2005 15:13:13 -0700
+1112911993
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+A U Thor
+author@example.com
+Thu Apr 7 15:13:13 2005 -0700
+Thu, 7 Apr 2005 15:13:13 -0700
+1112911993
+EOF
+
+test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+C O Mitter
+committer@example.com
+Thu Apr 7 15:13:13 2005 -0700
+Thu, 7 Apr 2005 15:13:13 -0700
+1112911993
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+C O Mitter
+committer@example.com
+Thu Apr 7 15:13:13 2005 -0700
+Thu, 7 Apr 2005 15:13:13 -0700
+1112911993
+EOF
+
+test_format encoding %e <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+<unknown>
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+<unknown>
+EOF
+
+test_format subject %s <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+changed foo
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+added foo
+EOF
+
+test_format body %b <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+<unknown>
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+<unknown>
+EOF
+
+test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+\e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+\e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
+EOF
+
+cat >commit-msg <<'EOF'
+Test printing of complex bodies
+
+This commit message is much longer than the others,
+and it will be encoded in iso8859-1. We should therefore
+include an iso8859 character: ¡bueno!
+EOF
+test_expect_success 'setup complex body' '
+git-config i18n.commitencoding iso8859-1 &&
+ echo change2 >foo && git-commit -a -F commit-msg
+'
+
+test_format complex-encoding %e <<'EOF'
+commit f58db70b055c5718631e5c61528b28b12090cdea
+iso8859-1
+commit 131a310eb913d107dd3c09a65d1651175898735d
+<unknown>
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+<unknown>
+EOF
+
+test_format complex-subject %s <<'EOF'
+commit f58db70b055c5718631e5c61528b28b12090cdea
+Test printing of complex bodies
+commit 131a310eb913d107dd3c09a65d1651175898735d
+changed foo
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+added foo
+EOF
+
+test_format complex-body %b <<'EOF'
+commit f58db70b055c5718631e5c61528b28b12090cdea
+This commit message is much longer than the others,
+and it will be encoded in iso8859-1. We should therefore
+include an iso8859 character: ¡bueno!
+
+commit 131a310eb913d107dd3c09a65d1651175898735d
+<unknown>
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+<unknown>
+EOF
+
+test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Christian Couder
+#
+test_description='Tests git-bisect functionality'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+ _line=$1
+ _file=$2
+
+ if [ -f "$_file" ]; then
+ echo "$_line" >> $_file || return $?
+ MSG="Add <$_line> into <$_file>."
+ else
+ echo "$_line" > $_file || return $?
+ git add $_file || return $?
+ MSG="Create file <$_file> with <$_line> inside."
+ fi
+
+ git-commit -m "$MSG" $_file
+}
+
+HASH1=
+HASH3=
+HASH4=
+
+test_expect_success \
+ 'set up basic repo with 1 file (hello) and 4 commits' \
+ 'add_line_into_file "1: Hello World" hello &&
+ add_line_into_file "2: A new day for git" hello &&
+ add_line_into_file "3: Another new day for git" hello &&
+ add_line_into_file "4: Ciao for now" hello &&
+ HASH1=$(git rev-list HEAD | tail -1) &&
+ HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
+ HASH4=$(git rev-list HEAD | head -1)'
+
+test_expect_success 'bisect starts with only one bad' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect bad $HASH4 &&
+ git bisect next
+'
+
+test_expect_success 'bisect does not start with only one good' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 || return 1
+
+ if git bisect next
+ then
+ echo Oops, should have failed.
+ false
+ else
+ :
+ fi
+'
+
+test_expect_success 'bisect start with one bad and good' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect next
+'
+
+# We want to automatically find the commit that
+# introduced "Another" into hello.
+test_expect_success \
+ '"git bisect run" simple case' \
+ 'echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "grep Another hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./test_script.sh > my_bisect_log.txt &&
+ grep "$HASH3 is first bad commit" my_bisect_log.txt &&
+ git bisect reset'
+
+# We want to automatically find the commit that
+# introduced "Ciao" into hello.
+test_expect_success \
+ '"git bisect run" with more complex "git bisect start"' \
+ 'echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "grep Ciao hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start $HASH4 $HASH1 &&
+ git bisect run ./test_script.sh > my_bisect_log.txt &&
+ grep "$HASH4 is first bad commit" my_bisect_log.txt &&
+ git bisect reset'
+
+#
+#
+test_done
+
# Copyright (c) 2006 Junio C Hamano
#
-test_description='git-checkout tests.'
+test_description='git-checkout tests.
+
+Creates master, forks renamer and side branches from it.
+Test switching across them.
+
+ ! [master] Initial A one, A two
+ * [renamer] Renamer R one->uno, M two
+ ! [side] Side M one, D two, A three
+ ---
+ + [side] Side M one, D two, A three
+ * [renamer] Renamer R one->uno, M two
+ +*+ [master] Initial A one, A two
+
+'
. ./test-lib.sh
! test -s current
'
+test_expect_success 'checkout to detach HEAD' '
+
+ git checkout -f renamer && git clean &&
+ git checkout renamer^ &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ if git symbolic-ref HEAD >/dev/null 2>&1
+ then
+ echo "OOPS, HEAD is still symbolic???"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success 'checkout to detach HEAD with branchname^' '
+
+ git checkout -f master && git clean &&
+ git checkout renamer^ &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ if git symbolic-ref HEAD >/dev/null 2>&1
+ then
+ echo "OOPS, HEAD is still symbolic???"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success 'checkout to detach HEAD with HEAD^0' '
+
+ git checkout -f master && git clean &&
+ git checkout HEAD^0 &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ if git symbolic-ref HEAD >/dev/null 2>&1
+ then
+ echo "OOPS, HEAD is still symbolic???"
+ false
+ else
+ : happy
+ fi
+'
+
test_done
--- /dev/null
+#!/bin/sh
+#
+# An example hook script for the post-receive event
+#
+# This script is run after receive-pack has accepted a pack and the
+# repository has been updated. It is passed arguments in through stdin
+# in the form
+# <oldrev> <newrev> <refname>
+# For example:
+# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
+#
+# see contrib/hooks/ for an sample, or uncomment the next line (on debian)
+#
+
+
+#. /usr/share/doc/git-core/contrib/hooks/post-receive-email
+
#!/bin/sh
#
-# An example hook script to mail out commit update information.
-# It can also blocks tags that aren't annotated.
+# An example hook script to blocks unannotated tags from entering.
# Called by git-receive-pack with arguments: refname sha1-old sha1-new
#
# To enable this hook, make this file executable by "chmod +x update".
#
# Config
# ------
-# hooks.mailinglist
-# This is the list that all pushes will go to; leave it blank to not send
-# emails frequently. The log email will list every log entry in full between
-# the old ref value and the new ref value.
-# hooks.announcelist
-# This is the list that all pushes of annotated tags will go to. Leave it
-# blank to just use the mailinglist field. The announce emails list the
-# short log summary of the changes since the last annotated tag
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
#
-# Notes
-# -----
-# All emails have their subjects prefixed with "[SCM]" to aid filtering.
-# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
-# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and info.
-
-# --- Constants
-EMAILPREFIX="[SCM] "
-LOGBEGIN="- Log -----------------------------------------------------------------"
-LOGEND="-----------------------------------------------------------------------"
-DATEFORMAT="%F %R %z"
# --- Command line
refname="$1"
fi
# --- Config
-projectdesc=$(cat $GIT_DIR/description)
-recipients=$(git-repo-config hooks.mailinglist)
-announcerecipients=$(git-repo-config hooks.announcelist)
allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
+# check for no description
+projectdesc=$(sed -e '1p' "$GIT_DIR/description")
+if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then
+ echo "*** Project description file hasn't been set" >&2
+ exit 1
+fi
+
# --- Check types
-newrev_type=$(git-cat-file -t $newrev)
+# if $newrev is 0000...0000, it's a commit to delete a branch
+if [ -z "${newrev##0*}" ]; then
+ newrev_type=commit
+else
+ newrev_type=$(git-cat-file -t $newrev)
+fi
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
- refname_type="tag"
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
- echo "*** The un-annotated tag, $short_refname is not allowed in this repository" >&2
+ echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
- refname_type="annotated tag"
- short_refname=${refname##refs/tags/}
- # change recipients
- if [ -n "$announcerecipients" ]; then
- recipients="$announcerecipients"
- fi
;;
refs/heads/*,commit)
# branch
- refname_type="branch"
- short_refname=${refname##refs/heads/}
;;
refs/remotes/*,commit)
# tracking branch
- refname_type="tracking branch"
- short_refname=${refname##refs/remotes/}
- # Should this even be allowed?
- echo "*** Push-update of tracking branch, $refname. No email generated." >&2
- exit 0
;;
*)
# Anything else (is there anything else?)
- echo "*** Update hook: unknown type of update, \"$newrev_type\", to ref $refname" >&2
+ echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
-# Check if we've got anyone to send to
-if [ -z "$recipients" ]; then
- # If the email isn't sent, then at least give the user some idea of what command
- # would generate the email at a later date
- echo "*** No recipients found - no email will be sent, but the push will continue" >&2
- echo "*** for $0 $1 $2 $3" >&2
- exit 0
-fi
-
-# --- Email parameters
-committer=$(git show --pretty=full -s $newrev | grep "^Commit: " | sed -e "s/^Commit: //")
-describe=$(git describe $newrev 2>/dev/null)
-if [ -z "$describe" ]; then
- describe=$newrev
-fi
-
-# --- Email (all stdout will be the email)
-(
-# Generate header
-cat <<-EOF
-From: $committer
-To: $recipients
-Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname now at $describe
-X-Git-Refname: $refname
-X-Git-Reftype: $refname_type
-X-Git-Oldrev: $oldrev
-X-Git-Newrev: $newrev
-
-Hello,
-
-This is an automated email from the git hooks/update script, it was
-generated because a ref change was pushed to the repository.
-
-Updating $refname_type, $short_refname,
-EOF
-
-case "$refname_type" in
- "tracking branch"|branch)
- if expr "$oldrev" : '0*$' >/dev/null
- then
- # If the old reference is "0000..0000" then this is a new branch
- # and so oldrev is not valid
- echo " as a new $refname_type"
- echo " to $newrev ($newrev_type)"
- echo ""
- echo $LOGBEGIN
- # This shows all log entries that are not already covered by
- # another ref - i.e. commits that are now accessible from this
- # ref that were previously not accessible
- git log $newrev --not --all
- echo $LOGEND
- else
- # oldrev is valid
- oldrev_type=$(git-cat-file -t "$oldrev")
-
- # Now the problem is for cases like this:
- # * --- * --- * --- * (oldrev)
- # \
- # * --- * --- * (newrev)
- # i.e. there is no guarantee that newrev is a strict subset
- # of oldrev - (would have required a force, but that's allowed).
- # So, we can't simply say rev-list $oldrev..$newrev. Instead
- # we find the common base of the two revs and list from there
- baserev=$(git-merge-base $oldrev $newrev)
-
- # Commit with a parent
- for rev in $(git-rev-list $newrev --not $baserev --all)
- do
- revtype=$(git-cat-file -t "$rev")
- echo " via $rev ($revtype)"
- done
- if [ "$baserev" = "$oldrev" ]; then
- echo " from $oldrev ($oldrev_type)"
- else
- echo " based on $baserev"
- echo " from $oldrev ($oldrev_type)"
- echo ""
- echo "This ref update crossed a branch point; i.e. the old rev is not a strict subset"
- echo "of the new rev. This occurs, when you --force push a change in a situation"
- echo "like this:"
- echo ""
- echo " * -- * -- B -- O -- O -- O ($oldrev)"
- echo " \\"
- echo " N -- N -- N ($newrev)"
- echo ""
- echo "Therefore, we assume that you've already had alert emails for all of the O"
- echo "revisions, and now give you all the revisions in the N branch from the common"
- echo "base, B ($baserev), up to the new revision."
- fi
- echo ""
- echo $LOGBEGIN
- git log $newrev --not $baserev --all
- echo $LOGEND
- echo ""
- echo "Diffstat:"
- git-diff-tree --no-color --stat -M -C --find-copies-harder $baserev..$newrev
- fi
- ;;
- "annotated tag")
- # Should we allow changes to annotated tags?
- if expr "$oldrev" : '0*$' >/dev/null
- then
- # If the old reference is "0000..0000" then this is a new atag
- # and so oldrev is not valid
- echo " to $newrev ($newrev_type)"
- else
- echo " to $newrev ($newrev_type)"
- echo " from $oldrev"
- fi
-
- # If this tag succeeds another, then show which tag it replaces
- prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
- if [ -n "$prevtag" ]; then
- echo " replaces $prevtag"
- fi
-
- # Read the tag details
- eval $(git cat-file tag $newrev | \
- sed -n '4s/tagger \([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
- tagged=$(date --date="1970-01-01 00:00:00 +0000 $ts seconds" +"$DATEFORMAT")
-
- echo " tagged by $tagger"
- echo " on $tagged"
-
- echo ""
- echo $LOGBEGIN
- echo ""
-
- if [ -n "$prevtag" ]; then
- git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
- else
- git rev-list --pretty=short $newrev | git shortlog
- fi
-
- echo $LOGEND
- echo ""
- ;;
- *)
- # By default, unannotated tags aren't allowed in; if
- # they are though, it's debatable whether we would even want an
- # email to be generated; however, I don't want to add another config
- # option just for that.
- #
- # Unannotated tags are more about marking a point than releasing
- # a version; therefore we don't do the shortlog summary that we
- # do for annotated tags above - we simply show that the point has
- # been marked, and print the log message for the marked point for
- # reference purposes
- #
- # Note this section also catches any other reference type (although
- # there aren't any) and deals with them in the same way.
- if expr "$oldrev" : '0*$' >/dev/null
- then
- # If the old reference is "0000..0000" then this is a new tag
- # and so oldrev is not valid
- echo " as a new $refname_type"
- echo " to $newrev ($newrev_type)"
- else
- echo " to $newrev ($newrev_type)"
- echo " from $oldrev"
- fi
- echo ""
- echo $LOGBEGIN
- git-show --no-color --root -s $newrev
- echo $LOGEND
- echo ""
- ;;
-esac
-
-# Footer
-cat <<-EOF
-
-hooks/update
----
-Git Source Code Management System
-$0 $1 \\
- $2 \\
- $3
-EOF
-#) | cat >&2
-) | /usr/sbin/sendmail -t
-
# --- Finished
exit 0
--- /dev/null
+#include "cache.h"
+#include "tree.h"
+
+int main(int ac, char **av)
+{
+ unsigned char hash1[20], hash2[20], shifted[20];
+ struct tree *one, *two;
+
+ if (get_sha1(av[1], hash1))
+ die("cannot parse %s as an object name", av[1]);
+ if (get_sha1(av[2], hash2))
+ die("cannot parse %s as an object name", av[2]);
+ one = parse_tree_indirect(hash1);
+ if (!one)
+ die("not a treeish %s", av[1]);
+ two = parse_tree_indirect(hash2);
+ if (!two)
+ die("not a treeish %s", av[2]);
+
+ shift_tree(one->object.sha1, two->object.sha1, shifted, -1);
+ printf("shifted: %s\n", sha1_to_hex(shifted));
+
+ exit(0);
+}
static int unpack_trees_rec(struct tree_entry_list **posns, int len,
const char *base, struct unpack_trees_options *o,
- int *indpos,
struct tree_entry_list *df_conflict_list)
{
int baselen = strlen(base);
cache_name = NULL;
/* Check the cache */
- if (o->merge && *indpos < active_nr) {
+ if (o->merge && o->pos < active_nr) {
/* This is a bit tricky: */
/* If the index has a subdirectory (with
* contents) as the first name, it'll get a
* file case.
*/
- cache_name = active_cache[*indpos]->name;
+ cache_name = active_cache[o->pos]->name;
if (strlen(cache_name) > baselen &&
!memcmp(cache_name, base, baselen)) {
cache_name += baselen;
if (cache_name && !strcmp(cache_name, first)) {
any_files = 1;
- src[0] = active_cache[*indpos];
- remove_cache_entry_at(*indpos);
+ src[0] = active_cache[o->pos];
+ remove_cache_entry_at(o->pos);
}
for (i = 0; i < len; i++) {
#if DBRT_DEBUG > 1
printf("Added %d entries\n", ret);
#endif
- *indpos += ret;
+ o->pos += ret;
} else {
for (i = 0; i < src_size; i++) {
if (src[i]) {
newbase[baselen + pathlen] = '/';
newbase[baselen + pathlen + 1] = '\0';
if (unpack_trees_rec(subposns, len, newbase, o,
- indpos, df_conflict_list)) {
+ df_conflict_list)) {
retval = -1;
goto leave_directory;
}
int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
{
- int indpos = 0;
unsigned len = object_list_length(trees);
struct tree_entry_list **posns;
int i;
posn = posn->next;
}
if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
- o, &indpos, &df_conflict_list))
+ o, &df_conflict_list))
return -1;
}
cache_tree_invalidate_path(active_cache_tree, ce->name);
}
+static int verify_clean_subdirectory(const char *path, const char *action,
+ struct unpack_trees_options *o)
+{
+ /*
+ * we are about to extract "path"; we would not want to lose
+ * anything in the existing directory there.
+ */
+ int namelen;
+ int pos, i;
+ struct dir_struct d;
+ char *pathbuf;
+ int cnt = 0;
+
+ /*
+ * First let's make sure we do not have a local modification
+ * in that directory.
+ */
+ namelen = strlen(path);
+ pos = cache_name_pos(path, namelen);
+ if (0 <= pos)
+ return cnt; /* we have it as nondirectory */
+ pos = -pos - 1;
+ for (i = pos; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ int len = ce_namelen(ce);
+ if (len < namelen ||
+ strncmp(path, ce->name, namelen) ||
+ ce->name[namelen] != '/')
+ break;
+ /*
+ * ce->name is an entry in the subdirectory.
+ */
+ if (!ce_stage(ce)) {
+ verify_uptodate(ce, o);
+ ce->ce_mode = 0;
+ }
+ cnt++;
+ }
+
+ /*
+ * Then we need to make sure that we do not lose a locally
+ * present file that is not ignored.
+ */
+ pathbuf = xmalloc(namelen + 2);
+ memcpy(pathbuf, path, namelen);
+ strcpy(pathbuf+namelen, "/");
+
+ memset(&d, 0, sizeof(d));
+ if (o->dir)
+ d.exclude_per_dir = o->dir->exclude_per_dir;
+ i = read_directory(&d, path, pathbuf, namelen+1, NULL);
+ if (i)
+ die("Updating '%s' would lose untracked files in it",
+ path);
+ free(pathbuf);
+ return cnt;
+}
+
/*
* We do not want to remove or overwrite a working tree file that
* is not tracked, unless it is ignored.
if (o->index_only || o->reset || !o->update)
return;
- if (!lstat(path, &st) && !(o->dir && excluded(o->dir, path)))
+
+ if (!lstat(path, &st)) {
+ int cnt;
+
+ if (o->dir && excluded(o->dir, path))
+ /*
+ * path is explicitly excluded, so it is Ok to
+ * overwrite it.
+ */
+ return;
+ if (S_ISDIR(st.st_mode)) {
+ /*
+ * We are checking out path "foo" and
+ * found "foo/." in the working tree.
+ * This is tricky -- if we have modified
+ * files that are in "foo/" we would lose
+ * it.
+ */
+ cnt = verify_clean_subdirectory(path, action, o);
+
+ /*
+ * If this removed entries from the index,
+ * what that means is:
+ *
+ * (1) the caller unpack_trees_rec() saw path/foo
+ * in the index, and it has not removed it because
+ * it thinks it is handling 'path' as blob with
+ * D/F conflict;
+ * (2) we will return "ok, we placed a merged entry
+ * in the index" which would cause o->pos to be
+ * incremented by one;
+ * (3) however, original o->pos now has 'path/foo'
+ * marked with "to be removed".
+ *
+ * We need to increment it by the number of
+ * deleted entries here.
+ */
+ o->pos += cnt;
+ return;
+ }
+
+ /*
+ * The previous round may already have decided to
+ * delete this path, which is in a subdirectory that
+ * is being replaced with a blob.
+ */
+ cnt = cache_name_pos(path, strlen(path));
+ if (0 <= cnt) {
+ struct cache_entry *ce = active_cache[cnt];
+ if (!ce_stage(ce) && !ce->ce_mode)
+ return;
+ }
+
die("Untracked working tree file '%s' "
"would be %s by merge.", path, action);
+ }
}
static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
return 1;
}
-static int keep_entry(struct cache_entry *ce)
+static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)
{
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
return 1;
int count;
int head_match = 0;
int remote_match = 0;
- const char *path = NULL;
int df_conflict_head = 0;
int df_conflict_remote = 0;
int i;
for (i = 1; i < o->head_idx; i++) {
- if (!stages[i])
+ if (!stages[i] || stages[i] == o->df_conflict_entry)
any_anc_missing = 1;
- else {
- if (!path)
- path = stages[i]->name;
+ else
no_anc_exists = 0;
- }
}
index = stages[0];
remote = NULL;
}
- if (!path && index)
- path = index->name;
- if (!path && head)
- path = head->name;
- if (!path && remote)
- path = remote->name;
-
/* First, if there's a #16 situation, note that to prevent #13
* and #14.
*/
if (o->aggressive) {
int head_deleted = !head && !df_conflict_head;
int remote_deleted = !remote && !df_conflict_remote;
+ const char *path = NULL;
+
+ if (index)
+ path = index->name;
+ else if (head)
+ path = head->name;
+ else if (remote)
+ path = remote->name;
+ else {
+ for (i = 1; i < o->head_idx; i++) {
+ if (stages[i] && stages[i] != o->df_conflict_entry) {
+ path = stages[i]->name;
+ break;
+ }
+ }
+ }
+
/*
* Deleted in both.
* Deleted in one and unchanged in the other.
o->nontrivial_merge = 1;
- /* #2, #3, #4, #6, #7, #9, #11. */
+ /* #2, #3, #4, #6, #7, #9, #10, #11. */
count = 0;
if (!head_match || !remote_match) {
for (i = 1; i < o->head_idx; i++) {
- if (stages[i]) {
- keep_entry(stages[i]);
+ if (stages[i] && stages[i] != o->df_conflict_entry) {
+ keep_entry(stages[i], o);
count++;
break;
}
show_stage_entry(stderr, "remote ", stages[remote_match]);
}
#endif
- if (head) { count += keep_entry(head); }
- if (remote) { count += keep_entry(remote); }
+ if (head) { count += keep_entry(head, o); }
+ if (remote) { count += keep_entry(remote, o); }
return count;
}
struct unpack_trees_options *o)
{
struct cache_entry *current = src[0];
- struct cache_entry *oldtree = src[1], *newtree = src[2];
+ struct cache_entry *oldtree = src[1];
+ struct cache_entry *newtree = src[2];
if (o->merge_size != 2)
return error("Cannot do a twoway merge of %d trees",
o->merge_size);
+ if (oldtree == o->df_conflict_entry)
+ oldtree = NULL;
+ if (newtree == o->df_conflict_entry)
+ newtree = NULL;
+
if (current) {
if ((!oldtree && !newtree) || /* 4 and 5 */
(!oldtree && newtree &&
(oldtree && newtree &&
same(oldtree, newtree)) || /* 14 and 15 */
(oldtree && newtree &&
- !same(oldtree, newtree) && /* 18 and 19*/
+ !same(oldtree, newtree) && /* 18 and 19 */
same(current, newtree))) {
- return keep_entry(current);
+ return keep_entry(current, o);
}
else if (oldtree && !newtree && same(current, oldtree)) {
/* 10 or 11 */
if (a && old)
die("Entry '%s' overlaps. Cannot bind.", a->name);
if (!a)
- return keep_entry(old);
+ return keep_entry(old, o);
else
return merged_entry(a, NULL, o);
}
ce_match_stat(old, &st, 1))
old->ce_flags |= htons(CE_UPDATE);
}
- return keep_entry(old);
+ return keep_entry(old, o);
}
return merged_entry(a, old, o);
}
int verbose_update;
int aggressive;
const char *prefix;
+ int pos;
struct dir_struct *dir;
merge_fn_t fn;
int i;
struct rev_info revs;
+ close(lp_pipe[0]);
pack_pipe = fdopen(lp_pipe[1], "w");
if (create_full_pack)
return -1;
}
-void warn(const char *warn, ...)
+void warning(const char *warn, ...)
{
va_list params;
if (file_exists(x))
add_excludes_from_file(&dir, x);
- read_directory(&dir, ".", "", 0);
+ read_directory(&dir, ".", "", 0, NULL);
for(i = 0; i < dir.nr; i++) {
/* check for matching entry, which is unmerged; lifted from
* builtin-ls-files:show_other_files */