GIT-CFLAGS
+GIT-GUI-VARS
GIT-VERSION-FILE
git
git-add
git-whatchanged
git-write-tree
git-core-*/?*
+gitk-wish
gitweb/gitweb.cgi
test-chmtime
test-date
--- /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.
to run after each revision is checked out to determine if it
is good or bad, to automate the bisection process.
+ - "git log" family learned a new traversal option --first-parent,
+ which does what the name suggests.
-* Updated behaviour of existing commands.
+
+* 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.
- "git remote show $remote" also talks about branches that
would be pushed if you run "git push remote".
- - Using objects from packs is now seriouly optimized by clever
+ - 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
- 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.
+ 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.1-rc1-63-g12d6697
-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.
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
------------
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
------
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
------
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.6/git.html[documentation for release 1.5.0.6]
+* link:RelNotes-1.5.1.txt[release notes for 1.5.1]
-* link:v1.5.0.6/RelNotes-1.5.0.6.txt[release notes for 1.5.0.6]
+* link:v1.5.0.7/git.html[documentation for release 1.5.0.7]
-* link:v1.5.0.5/RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
+* link:RelNotes-1.5.0.7.txt[release notes for 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.6.txt[release notes for 1.5.0.6]
-* link:v1.5.0.2/RelNotes-1.5.0.2.txt[release notes for 1.5.0.2]
+* link:RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
-* link:v1.5.0.1/RelNotes-1.5.0.1.txt[release notes for 1.5.0.1]
+* link:RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
-* link:v1.5.0/RelNotes-1.5.0.txt[release notes for 1.5.0]
+* 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]
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
-------------------------------------------------
$ 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
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
--------
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.1-rc3.GIT
+DEF_VER=v1.5.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)
+# 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
export NO_PERL_MAKEMAKER
endif
+ifeq ($(TCLTK_PATH),)
+NO_TCLTK=NoThanks
+endif
+
QUIET_SUBDIR0 = $(MAKE) -C # space to separate -C and subdir
QUIET_SUBDIR1 =
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 TCLTK_PATH='$(TCLTK_PATH_SQ)' $(QUIET_SUBDIR1) 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,$^) \
$(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.
$(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' \
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" || \
}
/* 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);
}
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 (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();
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)
{
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)
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])
;; 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))))
--- /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
#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);
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;
case "$next_ok,$1" in
no,) false ;;
no,fail)
- echo >&2 'You need to give me at least one good and one bad revisions.'
+ 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 ;;
*)
true ;;
}
bisect_run () {
- # Check that we have everything to run correctly.
- test -d "$GIT_DIR/refs/bisect" || {
- echo >&2 'You need to start by "git bisect start".'
- echo >&2 'And then by "git bisect bad" and "git bisect good".'
- exit 1
- }
bisect_next_check fail
while true
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);
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
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
}
my $gs = Git::SVN->find_by_url($url);
+ unless ($gs) {
+ die "Unable to determine remote information from URL: $url\n";
+ }
if (command(qw/diff-index HEAD --/)) {
print STDERR "Cannot rebase with uncommited changes:\n";
command_noisy('status');
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";
# 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}
%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)
# 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
+ "--", (defined $file_parent ? $file_parent : ()), $file_name
or die_error(undef, "Open git-diff-tree failed");
}
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);
}
}
{
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();
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;
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;
}
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/' -e 's/life;$/life./' > 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 have right number of lines' '
+
+ cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
+ test "$cnt" = 10
+
+'
+
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.
+-=======
+ That makes calamity of so long life;
+->>>>>>>
EOF
-
git rerere diff > out
test_expect_success 'rerere diff' 'git diff expect out'
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
--- /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
+
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 */