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))
$(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) >$@+
--- /dev/null
+GIT v1.5.0.5 Release Notes
+==========================
+
+Fixes since v1.5.0.3
+--------------------
+
+* Bugfixes
+
+ - git-merge (hence git-pull) did not refuse fast-forwarding
+ when the working tree had local changes that would have
+ conflicted with it.
+
+ - 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-gui updates.
+
+* Documentation updates
+
+* User manual updates
+
+
- "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
+ status when it found differences. In the future we might
+ want to make this the default but that would be a rather big
+ backward incompatible change; it will stay as an option for
+ now.
+
+ - "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.
+
+ - "git format-patch --attach" now emits attachments. Use
+ --inline to get an inlined multipart/mixed.
+
- "git name-rev" learned --refs=<pattern>, to limit the tags
used for naming the given revisions only to the ones
matching the given pattern.
- "git bundle" can help sneaker-netting your changes between
repositories.
+ - "git mergetool" can help 3-way file-level conflict
+ resolution with your favorite graphical merge tools.
+
- A new configuration "core.symlinks" can be used to disable
symlinks on filesystems that do not support them; they are
checked out as regular files instead.
* Updated behaviour of existing commands.
+ - "git fsck" does not barf on corrupt loose objects.
+
+ - "git archimport" allows remapping when coming up with git
+ branch names from arch names.
+
- git-svn got almost a rewrite.
- core.autocrlf configuration, when set to 'true', makes git
- "git fetch" (hence "git clone" and "git pull") are less
noisy when the output does not go to tty.
+ - "git fetch" between repositories with many refs were slow
+ even when there are not many changes that needed
+ transferring. This has been sped up by partially rewriting
+ 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.
+
+ - send-email learned configurable bcc and chain-reply-to.
+
+ - Using objects from packs is now seriouly 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
+ with pathspecs is made faster by terminating the comparison
+ between the trees as early as possible.
+
+
* Hooks
- The sample update hook to show how to send out notification
the repository. Earlier, it showed new commits that appeared
on the branch.
+
+* Others
+
+ - git-revert, git-gc and git-cherry-pick are now built-ins.
+
+
--
exec >/var/tmp/1
-O=v1.5.0.3-268-g3ddad98
+O=v1.5.0.5-446-g5d86501
echo O=`git describe master`
git shortlog --no-merges $O..master ^maint
+
Common unit suffixes of 'k', 'm', or 'g' are supported.
+core.deltaBaseCacheLimit::
+ Maximum number of bytes to reserve for caching base objects
+ that multiple deltafied objects reference. By storing the
+ entire decompressed base objects in a cache Git is able
+ to avoid unpacking and decompressing frequently used base
+ objects multiple times.
++
+Default is 16 MiB on all platforms. This should be reasonable
+for all users/operating systems, except on the largest projects.
+You probably do not need to adjust this value.
++
+Common unit suffixes of 'k', 'm', or 'g' are supported.
+
alias.*::
Command aliases for the gitlink:git[1] command wrapper - e.g.
after defining "alias.last = cat-file commit HEAD", the invocation
`git fetch`) to lookup the default branch for merging. Without
this option, `git pull` defaults to merge the first refspec fetched.
Specify multiple values to get an octopus merge.
+ If you wish to setup `git pull` so that it merges into <name> from
+ another branch in the local repository, you can point
+ branch.<name>.merge to the desired branch, and use the special setting
+ `.` (a period) for branch.<name>.remote.
color.branch::
A boolean to enable/disable color in the output of
merge.tool::
Controls which merge resolution program is used by
gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff",
- "meld", "xxdiff", "emerge"
+ "meld", "xxdiff", "emerge", "vimdiff"
merge.verbosity::
Controls the amount of output shown by the recursive merge
-w::
Shorthand for "--ignore-all-space".
+--exit-code::
+ Make the program exit with codes similar to diff(1).
+ 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 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::
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
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 bad <rev>
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.
-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:
+Cutting down bisection by giving path parameter 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
------------
+Bisect run
+~~~~~~~~~~
+
+If you have a script that can tell if the current source code is good
+or bad, you can automatically bisect using:
+
+------------
+$ 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
------
-t or --tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
- kdiff3, tkdiff, meld, xxdiff, and emerge.
+ 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
is not set, this will be prompted for.
--no-signed-off-by-cc::
- Do not add emails found in Signed-off-by: lines to the cc list.
+ Do not add emails found in Signed-off-by: or Cc: lines to the
+ cc list.
--quiet::
Make git-send-email less verbose. One line per email should be
You are reading the documentation for the latest version of git.
Documentation for older releases are available here:
-* link:v1.5.0.3/git.html[documentation for release 1.5.0.3]
+* link:v1.5.0.5/git.html[documentation for release 1.5.0.5]
+
+* link:v1.5.0.5/RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
* link:v1.5.0.3/RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
-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.
--- /dev/null
+How to use git-daemon
+
+Git can be run in inetd mode and in stand alone mode. But all you want is
+let a coworker pull from you, and therefore need to set up a git server
+real quick, right?
+
+Note that git-daemon is not really chatty at the moment, especially when
+things do not go according to plan (e.g. a socket could not be bound).
+
+Another word of warning: if you run
+
+ $ git ls-remote git://127.0.0.1/rule-the-world.git
+
+and you see a message like
+
+ fatal: The remote end hung up unexpectedly
+
+it only means that _something_ went wrong. To find out _what_ went wrong,
+you have to ask the server. (Git refuses to be more precise for your
+security only. Take off your shoes now. You have any coins in your pockets?
+Sorry, not allowed -- who knows what you planned to do with them?)
+
+With these two caveats, let's see an example:
+
+ $ git daemon --reuseaddr --verbose --base-path=/home/gitte/git \
+ --export-all -- /home/gitte/git/rule-the-world.git
+
+(Of course, unless your user name is `gitte` _and_ your repository is in
+~/rule-the-world.git, you have to adjust the paths. If your repository is
+not bare, be aware that you have to type the path to the .git directory!)
+
+This invocation tries to reuse the address if it is already taken
+(this can save you some debugging, because otherwise killing and restarting
+git-daemon could just silently fail to bind to a socket).
+
+Also, it is (relatively) verbose when somebody actually connects to it.
+It also sets the base path, which means that all the projects which can be
+accessed using this daemon have to reside in or under that path.
+
+The option `--export-all` just means that you _don't_ have to create a
+file named `git-daemon-export-ok` in each exported repository. (Otherwise,
+git-daemon would complain loudly, and refuse to cooperate.)
+
+Last of all, the repository which should be exported is specified. It is
+a good practice to put the paths after a "--" separator.
+
+Now, test your daemon with
+
+ $ git ls-remote git://127.0.0.1/rule-the-world.git
+
+If this does not work, find out why, and submit a patch to this document.
+
+++ /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).
--- /dev/null
+Def.: Shallow commits do have parents, but not in the shallow
+repo, and therefore grafts are introduced pretending that
+these commits have no parents.
+
+The basic idea is to write the SHA1s of shallow commits into
+$GIT_DIR/shallow, and handle its contents like the contents
+of $GIT_DIR/info/grafts (with the difference that shallow
+cannot contain parent information).
+
+This information is stored in a new file instead of grafts, or
+even the config, since the user should not touch that file
+at all (even throughout development of the shallow clone, it
+was never manually edited!).
+
+Each line contains exactly one SHA1. When read, a commit_graft
+will be constructed, which has nr_parent < 0 to make it easier
+to discern from user provided grafts.
+
+Since fsck-objects relies on the library to read the objects,
+it honours shallow commits automatically.
+
+There are some unfinished ends of the whole shallow business:
+
+- maybe we have to force non-thin packs when fetching into a
+ shallow repo (ATM they are forced non-thin).
+
+- A special handling of a shallow upstream is needed. At some
+ stage, upload-pack has to check if it sends a shallow commit,
+ and it should send that information early (or fail, if the
+ client does not support shallow repositories). There is no
+ support at all for this in this patch series.
+
+- Instead of locking $GIT_DIR/shallow at the start, just
+ the timestamp of it is noted, and when it comes to writing it,
+ a check is performed if the mtime is still the same, dying if
+ it is not.
+
+- It is unclear how "push into/from a shallow repo" should behave.
+
+- If you deepen a history, you'd want to get the tags of the
+ newly stored (but older!) commits. This does not work right now.
+
+To make a shallow clone, you can call "git-clone --depth 20 repo".
+The result contains only commit chains with a length of at most 20.
+It also writes an appropriate $GIT_DIR/shallow.
+
+You can deepen a shallow repository with "git-fetch --depth 20
+repo branch", which will fetch branch from repo, but stop at depth
+20, updating $GIT_DIR/shallow.
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, named
+"master", and working directory is initialized to the state of
+the project referred to by "master".
-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
---------------------
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
-------------------------------------------------
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.0.GIT
+DEF_VER=v1.5.1-rc1.GIT
LF='
'
SCRIPT_SH = \
git-bisect.sh git-checkout.sh \
git-clean.sh git-clone.sh git-commit.sh \
- git-fetch.sh git-gc.sh \
+ git-fetch.sh \
git-ls-remote.sh \
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
git-pull.sh git-rebase.sh \
builtin-fmt-merge-msg.o \
builtin-for-each-ref.o \
builtin-fsck.o \
+ builtin-gc.o \
builtin-grep.o \
builtin-init-db.o \
builtin-log.o \
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.
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;
}
int *lineno;
};
-static int cmp_suspect(struct origin *a, struct origin *b)
+static inline int same_suspect(struct origin *a, struct origin *b)
{
- int cmp = hashcmp(a->commit->object.sha1, b->commit->object.sha1);
- if (cmp)
- return cmp;
- return strcmp(a->path, b->path);
+ if (a == b)
+ return 1;
+ if (a->commit != b->commit)
+ return 0;
+ return !strcmp(a->path, b->path);
}
-#define cmp_suspect(a, b) ( ((a)==(b)) ? 0 : cmp_suspect(a,b) )
-
static void sanity_check_refcnt(struct scoreboard *);
/*
struct blame_entry *ent, *next;
for (ent = sb->ent; ent && (next = ent->next); ent = next) {
- if (!cmp_suspect(ent->suspect, next->suspect) &&
+ if (same_suspect(ent->suspect, next->suspect) &&
ent->guilty == next->guilty &&
ent->s_lno + ent->num_lines == next->s_lno) {
ent->num_lines += next->num_lines;
int last_in_target = -1;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || cmp_suspect(e->suspect, target))
+ if (e->guilty || !same_suspect(e->suspect, target))
continue;
if (last_in_target < e->s_lno + e->num_lines)
last_in_target = e->s_lno + e->num_lines;
struct blame_entry *e;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || cmp_suspect(e->suspect, target))
+ if (e->guilty || !same_suspect(e->suspect, target))
continue;
if (same <= e->s_lno)
continue;
while (made_progress) {
made_progress = 0;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || cmp_suspect(e->suspect, target))
+ if (e->guilty || !same_suspect(e->suspect, target))
continue;
find_copy_in_blob(sb, e, parent, split, &file_p);
if (split[1].suspect &&
struct blame_list *blame_list = NULL;
for (e = sb->ent, num_ents = 0; e; e = e->next)
- if (!e->guilty && !cmp_suspect(e->suspect, target))
+ if (!e->guilty && same_suspect(e->suspect, target))
num_ents++;
if (num_ents) {
blame_list = xcalloc(num_ents, sizeof(struct blame_list));
for (e = sb->ent, i = 0; e; e = e->next)
- if (!e->guilty && !cmp_suspect(e->suspect, target))
+ if (!e->guilty && same_suspect(e->suspect, target))
blame_list[i++].ent = e;
}
*num_ents_p = num_ents;
origin->file.ptr = NULL;
}
for (e = sb->ent; e; e = e->next) {
- if (cmp_suspect(e->suspect, origin))
+ if (!same_suspect(e->suspect, origin))
continue;
origin_incref(porigin);
origin_decref(e->suspect);
/* Take responsibility for the remaining entries */
for (ent = sb->ent; ent; ent = ent->next)
- if (!cmp_suspect(ent->suspect, suspect))
+ if (same_suspect(ent->suspect, suspect))
found_guilty_entry(ent);
origin_decref(suspect);
return 0;
}
-static void set_branch_defaults(const char *name, const char *real_ref)
+static void set_branch_merge(const char *name, const char *config_remote,
+ const char *config_repo)
{
char key[1024];
+ if (sizeof(key) <=
+ snprintf(key, sizeof(key), "branch.%s.remote", name))
+ die("what a long branch name you have!");
+ git_config_set(key, config_remote);
+
+ /*
+ * We do not have to check if we have enough space for
+ * the 'merge' key, since it's shorter than the
+ * previous 'remote' key, which we already checked.
+ */
+ snprintf(key, sizeof(key), "branch.%s.merge", name);
+ git_config_set(key, config_repo);
+}
+
+static void set_branch_defaults(const char *name, const char *real_ref)
+{
const char *slash = strrchr(real_ref, '/');
if (!slash)
start_len = strlen(real_ref);
base_len = slash - real_ref;
git_config(get_remote_config);
+ if (!config_repo && !config_remote &&
+ !prefixcmp(real_ref, "refs/heads/")) {
+ set_branch_merge(name, ".", real_ref);
+ printf("Branch %s set up to track local branch %s.\n",
+ name, real_ref);
+ }
if (config_repo && config_remote) {
- if (sizeof(key) <=
- snprintf(key, sizeof(key), "branch.%s.remote", name))
- die("what a long branch name you have!");
- git_config_set(key, config_remote);
-
- /*
- * We do not have to check if we have enough space for
- * the 'merge' key, since it's shorter than the
- * previous 'remote' key, which we already checked.
- */
- snprintf(key, sizeof(key), "branch.%s.merge", name);
- git_config_set(key, config_repo);
-
+ set_branch_merge(name, config_remote, config_repo);
printf("Branch %s set up to track remote branch %s.\n",
name, real_ref);
}
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
-#include "exec_cmd.h"
+#include "run-command.h"
/*
* Basic handler for bundle files to connect repositories via sneakernet.
return fd;
}
-/* if in && *in >= 0, take that as input file descriptor instead */
-static int fork_with_pipe(const char **argv, int *in, int *out)
-{
- int needs_in, needs_out;
- int fdin[2], fdout[2], pid;
-
- needs_in = in && *in < 0;
- if (needs_in) {
- if (pipe(fdin) < 0)
- return error("could not setup pipe");
- *in = fdin[1];
- }
-
- needs_out = out && *out < 0;
- if (needs_out) {
- if (pipe(fdout) < 0)
- return error("could not setup pipe");
- *out = fdout[0];
- }
-
- if ((pid = fork()) < 0) {
- if (needs_in) {
- close(fdin[0]);
- close(fdin[1]);
- }
- if (needs_out) {
- close(fdout[0]);
- close(fdout[1]);
- }
- return error("could not fork");
- }
- if (!pid) {
- if (needs_in) {
- dup2(fdin[0], 0);
- close(fdin[0]);
- close(fdin[1]);
- } else if (in) {
- dup2(*in, 0);
- close(*in);
- }
- if (needs_out) {
- dup2(fdout[1], 1);
- close(fdout[0]);
- close(fdout[1]);
- } else if (out) {
- dup2(*out, 1);
- close(*out);
- }
- exit(execv_git_cmd(argv));
- }
- if (needs_in)
- close(fdin[0]);
- else if (in)
- close(*in);
- if (needs_out)
- close(fdout[1]);
- else if (out)
- close(*out);
- return pid;
-}
-
static int list_refs(struct ref_list *r, int argc, const char **argv)
{
int i;
int bundle_fd = -1;
const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
const char **argv_pack = xmalloc(5 * sizeof(const char *));
- int pid, in, out, i, status, ref_count = 0;
+ int i, ref_count = 0;
char buffer[1024];
struct rev_info revs;
+ struct child_process rls;
bundle_fd = (!strcmp(path, "-") ? 1 :
open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
argv_boundary[1] = "--boundary";
argv_boundary[2] = "--pretty=oneline";
argv_boundary[argc + 2] = NULL;
- out = -1;
- pid = fork_with_pipe(argv_boundary, NULL, &out);
- if (pid < 0)
+ memset(&rls, 0, sizeof(rls));
+ rls.argv = argv_boundary;
+ rls.out = -1;
+ rls.git_cmd = 1;
+ if (start_command(&rls))
return -1;
- while ((i = read_string(out, buffer, sizeof(buffer))) > 0) {
+ while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) {
unsigned char sha1[20];
if (buffer[0] == '-') {
write_or_die(bundle_fd, buffer, i);
object->flags |= SHOWN;
}
}
- while ((i = waitpid(pid, &status, 0)) < 0)
- if (errno != EINTR)
- return error("rev-list died");
- if (!WIFEXITED(status) || WEXITSTATUS(status))
- return error("rev-list died %d", WEXITSTATUS(status));
+ if (finish_command(&rls))
+ return error("rev-list died");
/* write references */
argc = setup_revisions(argc, argv, &revs, NULL);
argv_pack[2] = "--stdout";
argv_pack[3] = "--thin";
argv_pack[4] = NULL;
- in = -1;
- out = bundle_fd;
- pid = fork_with_pipe(argv_pack, &in, &out);
- if (pid < 0)
+ memset(&rls, 0, sizeof(rls));
+ rls.argv = argv_pack;
+ rls.in = -1;
+ rls.out = bundle_fd;
+ rls.git_cmd = 1;
+ if (start_command(&rls))
return error("Could not spawn pack-objects");
for (i = 0; i < revs.pending.nr; i++) {
struct object *object = revs.pending.objects[i].item;
if (object->flags & UNINTERESTING)
- write(in, "^", 1);
- write(in, sha1_to_hex(object->sha1), 40);
- write(in, "\n", 1);
+ write(rls.in, "^", 1);
+ write(rls.in, sha1_to_hex(object->sha1), 40);
+ write(rls.in, "\n", 1);
}
- close(in);
- while (waitpid(pid, &status, 0) < 0)
- if (errno != EINTR)
- return -1;
- if (!WIFEXITED(status) || WEXITSTATUS(status))
+ if (finish_command(&rls))
return error ("pack-objects died");
-
- return status;
+ return 0;
}
static int unbundle(struct bundle_header *header, int bundle_fd,
{
const char *argv_index_pack[] = {"index-pack",
"--fix-thin", "--stdin", NULL};
- int pid, status, dev_null;
+ struct child_process ip;
if (verify_bundle(header, 0))
return -1;
- dev_null = open("/dev/null", O_WRONLY);
- if (dev_null < 0)
- return error("Could not open /dev/null");
- pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
- if (pid < 0)
- return error("Could not spawn index-pack");
- while (waitpid(pid, &status, 0) < 0)
- if (errno != EINTR)
- return error("index-pack died");
- if (!WIFEXITED(status) || WEXITSTATUS(status))
- return error("index-pack exited with status %d",
- WEXITSTATUS(status));
+ memset(&ip, 0, sizeof(ip));
+ ip.argv = argv_index_pack;
+ ip.in = bundle_fd;
+ ip.no_stdout = 1;
+ ip.git_cmd = 1;
+ if (run_command(&ip))
+ return error("index-pack died");
return list_heads(header, argc, argv);
}
{
struct rev_info rev;
int nongit = 0;
+ int result;
prefix = setup_git_directory_gently(&nongit);
init_revisions(&rev, prefix);
argc = setup_revisions(argc, argv, &rev, NULL);
if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_RAW;
- return run_diff_files_cmd(&rev, argc, argv);
+ result = run_diff_files_cmd(&rev, argc, argv);
+ return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
}
struct rev_info rev;
int cached = 0;
int i;
+ int result;
init_revisions(&rev, prefix);
git_config(git_default_config); /* no "diff" UI options */
perror("read_cache");
return -1;
}
- return run_diff_index(&rev, cached);
+ result = run_diff_index(&rev, cached);
+ return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
}
}
if (!read_stdin)
- return 0;
+ return opt->diffopt.exit_with_status ?
+ opt->diffopt.has_changes: 0;
if (opt->diffopt.detect_rename)
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
else
diff_tree_stdin(line);
}
- return 0;
+ return opt->diffopt.exit_with_status ? opt->diffopt.has_changes: 0;
}
const char *path = NULL;
struct blobinfo blob[2];
int nongit = 0;
+ int result = 0;
/*
* We could get N tree-ish in the rev.pending_objects list.
if (!ents) {
switch (blobs) {
case 0:
- return run_diff_files_cmd(&rev, argc, argv);
+ result = run_diff_files_cmd(&rev, argc, argv);
break;
case 1:
if (paths != 1)
usage(builtin_diff_usage);
- return builtin_diff_b_f(&rev, argc, argv, blob, path);
+ result = builtin_diff_b_f(&rev, argc, argv, blob, path);
break;
case 2:
if (paths)
usage(builtin_diff_usage);
- return builtin_diff_blobs(&rev, argc, argv, blob);
+ result = builtin_diff_blobs(&rev, argc, argv, blob);
break;
default:
usage(builtin_diff_usage);
else if (blobs)
usage(builtin_diff_usage);
else if (ents == 1)
- return builtin_diff_index(&rev, argc, argv);
+ result = builtin_diff_index(&rev, argc, argv);
else if (ents == 2)
- return builtin_diff_tree(&rev, argc, argv, ent);
+ result = builtin_diff_tree(&rev, argc, argv, ent);
else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
/* diff A...B where there is one sane merge base between
* A and B. We have ent[0] == merge-base, ent[1] == A,
* and ent[2] == B. Show diff between the base and B.
*/
ent[1] = ent[2];
- return builtin_diff_tree(&rev, argc, argv, ent);
+ result = builtin_diff_tree(&rev, argc, argv, ent);
}
else
- return builtin_diff_combined(&rev, argc, argv,
+ result = builtin_diff_combined(&rev, argc, argv,
ent, ents);
- usage(builtin_diff_usage);
+ if (rev.diffopt.exit_with_status)
+ result = rev.diffopt.has_changes;
+ return result;
}
const char *o_name;
const unsigned char *o_sha1;
- desc.buf = item->buffer;
- desc.size = item->size;
+ init_tree_desc(&desc, item->buffer, item->size);
o_mode = 0;
o_name = NULL;
if (strchr(name, '/'))
has_full_path = 1;
- has_zero_pad |= *(char *)desc.buf == '0';
+ has_zero_pad |= *(char *)desc.buffer == '0';
update_tree_entry(&desc);
switch (mode) {
--- /dev/null
+/*
+ * git gc builtin command
+ *
+ * Cleanup unreachable files and optimize the repository.
+ *
+ * Copyright (c) 2007 James Bowes
+ *
+ * Based on git-gc.sh, which is
+ *
+ * Copyright (c) 2006 Shawn O. Pearce
+ */
+
+#include "cache.h"
+#include "run-command.h"
+
+#define FAILED_RUN "failed to run %s"
+
+static const char builtin_gc_usage[] = "git-gc [--prune]";
+
+static int pack_refs = -1;
+
+static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL};
+static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
+static const char *argv_repack[] = {"repack", "-a", "-d", "-l", NULL};
+static const char *argv_prune[] = {"prune", NULL};
+static const char *argv_rerere[] = {"rerere", "gc", NULL};
+
+static int gc_config(const char *var, const char *value)
+{
+ if (!strcmp(var, "gc.packrefs")) {
+ if (!strcmp(value, "notbare"))
+ pack_refs = -1;
+ else
+ pack_refs = git_config_bool(var, value);
+ return 0;
+ }
+ return git_default_config(var, value);
+}
+
+int cmd_gc(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ int prune = 0;
+
+ git_config(gc_config);
+
+ if (pack_refs < 0)
+ pack_refs = !is_bare_repository();
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--prune")) {
+ prune = 1;
+ continue;
+ }
+ /* perhaps other parameters later... */
+ break;
+ }
+ if (i != argc)
+ usage(builtin_gc_usage);
+
+ if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
+ return error(FAILED_RUN, argv_pack_refs[0]);
+
+ if (run_command_v_opt(argv_reflog, RUN_GIT_CMD))
+ return error(FAILED_RUN, argv_reflog[0]);
+
+ if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
+ return error(FAILED_RUN, argv_repack[0]);
+
+ if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
+ return error(FAILED_RUN, argv_prune[0]);
+
+ if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
+ return error(FAILED_RUN, argv_rerere[0]);
+
+ return 0;
+}
* decide if we want to descend into "abc"
* directory.
*/
- strcpy(path_buf + len + entry.pathlen, "/");
+ strcpy(path_buf + len + tree_entry_len(entry.path, entry.sha1), "/");
if (!pathspec_matches(paths, down))
;
enum object_type type;
struct tree_desc sub;
void *data;
- data = read_sha1_file(entry.sha1, &type, &sub.size);
+ unsigned long size;
+
+ data = read_sha1_file(entry.sha1, &type, &size);
if (!data)
die("unable to read tree (%s)",
sha1_to_hex(entry.sha1));
- sub.buf = data;
+ init_tree_desc(&sub, data, size);
hit |= grep_tree(opt, paths, &sub, tree_name, down);
free(data);
}
if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
struct tree_desc tree;
void *data;
+ unsigned long size;
int hit;
data = read_object_with_reference(obj->sha1, tree_type,
- &tree.size, NULL);
+ &size, NULL);
if (!data)
die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
- tree.buf = data;
+ init_tree_desc(&tree, data, size);
hit = grep_tree(opt, paths, &tree, name, "");
free(data);
return hit;
if (!prefixcmp(arg, "--encoding=")) {
arg += 11;
if (strcmp(arg, "none"))
- git_log_output_encoding = strdup(arg);
+ git_log_output_encoding = xstrdup(arg);
else
git_log_output_encoding = "";
}
struct packed_git *p = rix->p;
int num_ent = num_packed_objects(p);
int i;
- void *index = p->index_base + 256;
+ const char *index = p->index_data;
+ index += 4 * 256;
rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
for (i = 0; i < num_ent; i++) {
- unsigned int hl = *((unsigned int *)((char *) index + 24*i));
+ uint32_t hl = *((uint32_t *)(index + 24 * i));
rix->revindex[i].offset = ntohl(hl);
rix->revindex[i].nr = i;
}
return entry[1].offset - ofs;
}
-static unsigned char *find_packed_object_name(struct packed_git *p,
- off_t ofs)
+static const unsigned char *find_packed_object_name(struct packed_git *p,
+ off_t ofs)
{
struct revindex_entry *entry = find_packed_object(p, ofs);
- return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4;
+ return ((unsigned char *)p->index_data) + 4 * 256 + 24 * entry->nr + 4;
}
static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
unsigned long size;
enum object_type type;
- if (entry.pathlen != cmplen ||
+ if (tree_entry_len(entry.path, entry.sha1) != cmplen ||
memcmp(entry.path, name, cmplen) ||
!has_sha1_file(entry.sha1) ||
(type = sha1_object_info(entry.sha1, &size)) < 0)
tree = pbase_tree_get(entry.sha1);
if (!tree)
return;
- sub.buf = tree->tree_data;
- sub.size = tree->tree_size;
+ init_tree_desc(&sub, tree->tree_data, tree->tree_size);
add_pbase_object(&sub, down, downlen, fullname);
pbase_tree_put(tree);
}
else {
struct tree_desc tree;
- tree.buf = it->pcache.tree_data;
- tree.size = it->pcache.tree_size;
+ init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
add_pbase_object(&tree, name, cmplen, name);
}
}
* delta.
*/
if (!no_reuse_delta) {
- unsigned char c, *base_name;
+ unsigned char c;
+ const unsigned char *base_name;
off_t ofs;
unsigned long used_0;
/* there is at least 20 bytes left in the pack */
enum object_type type = sha1_object_info(sha1, NULL);
printf("%s %s\n", sha1_to_hex(sha1),
(type > 0) ? typename(type) : "unknown");
- return 0;
- }
- unlink(mkpath("%s/%s", path, filename));
- rmdir(path);
+ } else
+ unlink(mkpath("%s/%s", path, filename));
return 0;
}
}
fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
}
+ if (!show_only)
+ rmdir(path);
closedir(dir);
return 0;
}
int dest_refspec_nr = refspec_nr;
const char **dest_refspec = refspec;
const char *dest = uri[i];
- const char *sender = "git-send-pack";
+ const char *sender = "send-pack";
if (!prefixcmp(dest, "http://") ||
!prefixcmp(dest, "https://"))
- sender = "git-http-push";
+ sender = "http-push";
else if (thin)
argv[dest_argc++] = "--thin";
argv[0] = sender;
argv[dest_argc] = NULL;
if (verbose)
fprintf(stderr, "Pushing to %s\n", dest);
- err = run_command_v_opt(argv, 0);
+ err = run_command_v_opt(argv, RUN_GIT_CMD);
if (!err)
continue;
switch (err) {
int cnt;
hashcpy(it->sha1, tree->object.sha1);
- desc.buf = tree->buffer;
- desc.size = tree->size;
+ init_tree_desc(&desc, tree->buffer, tree->size);
cnt = 0;
while (tree_entry(&desc, &entry)) {
if (!S_ISDIR(entry.mode))
if (tree->object.flags & INCOMPLETE)
return 0;
- desc.buf = tree->buffer;
- desc.size = tree->size;
- if (!desc.buf) {
+ if (!tree->buffer) {
enum object_type type;
- void *data = read_sha1_file(sha1, &type, &desc.size);
+ unsigned long size;
+ void *data = read_sha1_file(sha1, &type, &size);
if (!data) {
tree->object.flags |= INCOMPLETE;
return 0;
}
- desc.buf = data;
tree->buffer = data;
+ tree->size = size;
}
+ init_tree_desc(&desc, tree->buffer, tree->size);
complete = 1;
while (tree_entry(&desc, &entry)) {
if (!has_sha1_file(entry.sha1) ||
nr++;
p = p->next;
}
- closest = 0;
+ closest = -1;
best = list;
for (p = list; p; p = p->next) {
unsigned char head[20];
struct commit *base, *next;
int i;
- char *oneline, *encoding, *reencoded_message = NULL;
- const char *message;
+ char *oneline, *reencoded_message = NULL;
+ const char *message, *encoding;
git_config(git_default_config);
me = action == REVERT ? "revert" : "cherry-pick";
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 {
extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
extern int cmd_fsck(int argc, const char **argv, const char *prefix);
+extern int cmd_gc(int argc, const char **argv, const char *prefix);
extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
extern int cmd_grep(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix);
extern int zlib_compression_level;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
+extern size_t delta_base_cache_limit;
extern int auto_crlf;
#define GIT_REPO_VERSION 0
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);
extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
-extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
+extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
extern struct packed_git {
struct packed_git *next;
struct pack_window *windows;
- uint32_t *index_base;
- time_t mtime;
+ const void *index_data;
off_t index_size;
off_t pack_size;
+ time_t mtime;
+ int index_version;
int pack_fd;
int pack_local;
unsigned char sha1[20];
extern struct packed_git *parse_pack_index(unsigned char *sha1);
extern struct packed_git *parse_pack_index_file(const unsigned char *sha1,
- char *idx_path);
+ const char *idx_path);
extern void prepare_packed_git(void);
extern void reprepare_packed_git(void);
extern void pack_report(void);
extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
extern void unuse_pack(struct pack_window **);
-extern struct packed_git *add_packed_git(char *, int, int);
+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 off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
extern char git_default_email[MAX_GITNAME];
extern char git_default_name[MAX_GITNAME];
-extern char *git_commit_encoding;
+extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;
extern int copy_fd(int ifd, int ofd);
encoding = get_header(commit, "encoding");
use_encoding = encoding ? encoding : utf8;
if (!strcmp(use_encoding, output_encoding))
- out = strdup(commit->buffer);
+ out = xstrdup(commit->buffer);
else
out = reencode_string(commit->buffer,
output_encoding, use_encoding);
return 0;
}
+ if (!strcmp(var, "core.deltabasecachelimit")) {
+ delta_base_cache_limit = git_config_int(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "core.autocrlf")) {
if (value && !strcasecmp(value, "input")) {
auto_crlf = -1;
}
if (!strcmp(var, "i18n.commitencoding")) {
- git_commit_encoding = strdup(value);
+ git_commit_encoding = xstrdup(value);
return 0;
}
if (!strcmp(var, "i18n.logoutputencoding")) {
- git_log_output_encoding = strdup(value);
+ git_log_output_encoding = xstrdup(value);
return 0;
}
#include "pkt-line.h"
#include "quote.h"
#include "refs.h"
+#include "run-command.h"
static char *server_capabilities;
{
const char *port = STR(DEFAULT_GIT_PORT);
char *colon, *end;
- int pipefd[2][2];
- pid_t pid;
+ const char *argv[4];
+ struct child_process proxy;
if (host[0] == '[') {
end = strchr(host + 1, ']');
port = colon + 1;
}
- if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
- die("unable to create pipe pair for communication");
- pid = fork();
- if (!pid) {
- dup2(pipefd[1][0], 0);
- dup2(pipefd[0][1], 1);
- close(pipefd[0][0]);
- close(pipefd[0][1]);
- close(pipefd[1][0]);
- close(pipefd[1][1]);
- execlp(git_proxy_command, git_proxy_command, host, port, NULL);
- die("exec failed");
- }
- if (pid < 0)
- die("fork failed");
- fd[0] = pipefd[0][0];
- fd[1] = pipefd[1][1];
- close(pipefd[0][1]);
- close(pipefd[1][0]);
+ argv[0] = git_proxy_command;
+ argv[1] = host;
+ argv[2] = port;
+ argv[3] = NULL;
+ memset(&proxy, 0, sizeof(proxy));
+ proxy.argv = argv;
+ proxy.in = -1;
+ proxy.out = -1;
+ if (start_command(&proxy))
+ die("cannot start proxy %s", argv[0]);
+ fd[0] = proxy.out; /* read from proxy stdout */
+ fd[1] = proxy.in; /* write to proxy stdin */
}
#define MAX_CMD_LEN 1024
--- /dev/null
+#!/usr/bin/perl
+#
+# A daemon that waits for update events sent by its companion
+# post-receive-cinotify hook, checks out a new copy of source,
+# compiles it, and emails the guilty parties if the compile
+# (and optionally test suite) fails.
+#
+# To use this daemon, configure it and run it. It will disconnect
+# from your terminal and fork into the background. The daemon must
+# have local filesystem access to the source repositories, as it
+# uses objects/info/alternates to avoid copying objects.
+#
+# Add its companion post-receive-cinotify hook as the post-receive
+# hook to each repository that the daemon should monitor. Yes, a
+# single daemon can monitor more than one repository.
+#
+# To use multiple daemons on the same system, give them each a
+# unique queue file and tmpdir.
+#
+# Global Config
+# -------------
+# Reads from a Git style configuration file. This will be
+# ~/.gitconfig by default but can be overridden by setting
+# the GIT_CONFIG_FILE environment variable before starting.
+#
+# cidaemon.smtpHost
+# Hostname of the SMTP server the daemon will send email
+# through. Defaults to 'localhost'.
+#
+# cidaemon.smtpUser
+# Username to authenticate to the SMTP server as. This
+# variable is optional; if it is not supplied then no
+# authentication will be performed.
+#
+# cidaemon.smtpPassword
+# Password to authenticate to the SMTP server as. This
+# variable is optional. If not supplied but smtpUser was,
+# the daemon prompts for the password before forking into
+# the background.
+#
+# cidaemon.smtpAuth
+# Type of authentication to perform with the SMTP server.
+# If set to 'login' and smtpUser was defined, this will
+# use the AUTH LOGIN command, which is suitable for use
+# with at least one version of Microsoft Exchange Server.
+# If not set the daemon will use whatever auth methods
+# are supported by your version of Net::SMTP.
+#
+# cidaemon.email
+# Email address that daemon generated emails will be sent
+# from. This should be a useful email address within your
+# organization. Required.
+#
+# cidaemon.name
+# Human friendly name that the daemon will send emails as.
+# Defaults to 'cidaemon'.
+#
+# cidaemon.scanDelay
+# Number of seconds to sleep between polls of the queue file.
+# Defaults to 60.
+#
+# cidaemon.recentCache
+# Number of recent commit SHA-1s per repository to cache and
+# skip building if they appear again. This is useful to avoid
+# rebuilding the same commit multiple times just because it was
+# pushed into more than one branch. Defaults to 100.
+#
+# cidaemon.tmpdir
+# Scratch directory to create the builds within. The daemon
+# makes a new subdirectory for each build, then deletes it when
+# the build has finished. The pid file is also placed here.
+# Defaults to '/tmp'.
+#
+# cidaemon.queue
+# Path to the queue file that the post-receive-cinotify hook
+# appends events to. This file is polled by the daemon. It
+# must not be on an NFS mount (uses flock). Required.
+#
+# cidaemon.nocc
+# Perl regex patterns to match against author and committer
+# lines. If a pattern matches, that author or committer will
+# not be notified of a build failure.
+#
+# Per Repository Config
+# ----------------------
+# Read from the source repository's config file.
+#
+# builder.command
+# Shell command to execute the build. This command must
+# return 0 on "success" and non-zero on failure. If you
+# also want to run a test suite, make sure your command
+# does that too. Required.
+#
+# builder.queue
+# Queue file to notify the cidaemon through. Should match
+# cidaemon.queue. If not set the hook will not notify the
+# cidaemon.
+#
+# builder.skip
+# Perl regex patterns of refs that should not be sent to
+# cidaemon. Updates of these refs will be ignored.
+#
+# builder.newBranchBase
+# Glob patterns of refs that should be used to form the
+# 'old' revions of a newly created ref. This should set
+# to be globs that match your 'mainline' branches. This
+# way a build failure of a brand new topic branch does not
+# attempt to email everyone since the beginning of time;
+# instead it only emails those authors of commits not in
+# these 'mainline' branches.
+
+local $ENV{PATH} = join ':', qw(
+ /opt/git/bin
+ /usr/bin
+ /bin
+ );
+
+use strict;
+use warnings;
+use FindBin qw($RealBin);
+use File::Spec;
+use lib File::Spec->catfile($RealBin, '..', 'perl5');
+use Storable qw(retrieve nstore);
+use Fcntl ':flock';
+use POSIX qw(strftime);
+use Getopt::Long qw(:config no_auto_abbrev auto_help);
+
+sub git_config ($;$)
+{
+ my $var = shift;
+ my $required = shift || 0;
+ local *GIT;
+ open GIT, '-|','git','config','--get',$var;
+ my $r = <GIT>;
+ chop $r if $r;
+ close GIT;
+ die "error: $var not set.\n" if ($required && !$r);
+ return $r;
+}
+
+package EXCHANGE_NET_SMTP;
+
+# Microsoft Exchange Server requires an 'AUTH LOGIN'
+# style of authentication. This is different from
+# the default supported by Net::SMTP so we subclass
+# and override the auth method to support that.
+
+use Net::SMTP;
+use Net::Cmd;
+use MIME::Base64 qw(encode_base64);
+our @ISA = qw(Net::SMTP);
+our $auth_type = ::git_config 'cidaemon.smtpAuth';
+
+sub new
+{
+ my $self = shift;
+ my $type = ref($self) || $self;
+ $type->SUPER::new(@_);
+}
+
+sub auth
+{
+ my $self = shift;
+ return $self->SUPER::auth(@_) unless $auth_type eq 'login';
+
+ my $user = encode_base64 shift, '';
+ my $pass = encode_base64 shift, '';
+ return 0 unless CMD_MORE == $self->command("AUTH LOGIN")->response;
+ return 0 unless CMD_MORE == $self->command($user)->response;
+ CMD_OK == $self->command($pass)->response;
+}
+
+package main;
+
+my ($debug_flag, %recent);
+
+my $ex_host = git_config('cidaemon.smtpHost') || 'localhost';
+my $ex_user = git_config('cidaemon.smtpUser');
+my $ex_pass = git_config('cidaemon.smtpPassword');
+
+my $ex_from_addr = git_config('cidaemon.email', 1);
+my $ex_from_name = git_config('cidaemon.name') || 'cidaemon';
+
+my $scan_delay = git_config('cidaemon.scanDelay') || 60;
+my $recent_size = git_config('cidaemon.recentCache') || 100;
+my $tmpdir = git_config('cidaemon.tmpdir') || '/tmp';
+my $queue_name = git_config('cidaemon.queue', 1);
+my $queue_lock = "$queue_name.lock";
+
+my @nocc_list;
+open GIT,'git config --get-all cidaemon.nocc|';
+while (<GIT>) {
+ chop;
+ push @nocc_list, $_;
+}
+close GIT;
+
+sub nocc_author ($)
+{
+ local $_ = shift;
+ foreach my $pat (@nocc_list) {
+ return 1 if /$pat/;
+ }
+ 0;
+}
+
+sub input_echo ($)
+{
+ my $prompt = shift;
+
+ local $| = 1;
+ print $prompt;
+ my $input = <STDIN>;
+ chop $input;
+ return $input;
+}
+
+sub input_noecho ($)
+{
+ my $prompt = shift;
+
+ my $end = sub {system('stty','echo');print "\n";exit};
+ local $SIG{TERM} = $end;
+ local $SIG{INT} = $end;
+ system('stty','-echo');
+
+ local $| = 1;
+ print $prompt;
+ my $input = <STDIN>;
+ system('stty','echo');
+ print "\n";
+ chop $input;
+ return $input;
+}
+
+sub rfc2822_date ()
+{
+ strftime("%a, %d %b %Y %H:%M:%S %Z", localtime);
+}
+
+sub send_email ($$$)
+{
+ my ($subj, $body, $to) = @_;
+ my $now = rfc2822_date;
+ my $to_str = '';
+ my @rcpt_to;
+ foreach (@$to) {
+ my $s = $_;
+ $s =~ s/^/"/;
+ $s =~ s/(\s+<)/"$1/;
+ $to_str .= ', ' if $to_str;
+ $to_str .= $s;
+ push @rcpt_to, $1 if $s =~ /<(.*)>/;
+ }
+ die "Nobody to send to.\n" unless @rcpt_to;
+ my $msg = <<EOF;
+From: "$ex_from_name" <$ex_from_addr>
+To: $to_str
+Date: $now
+Subject: $subj
+
+$body
+EOF
+
+ my $smtp = EXCHANGE_NET_SMTP->new(Host => $ex_host)
+ or die "Cannot connect to $ex_host: $!\n";
+ if ($ex_user && $ex_pass) {
+ $smtp->auth($ex_user,$ex_pass)
+ or die "$ex_host rejected $ex_user\n";
+ }
+ $smtp->mail($ex_from_addr)
+ or die "$ex_host rejected $ex_from_addr\n";
+ scalar($smtp->recipient(@rcpt_to, { SkipBad => 1 }))
+ or die "$ex_host did not accept any addresses.\n";
+ $smtp->data($msg)
+ or die "$ex_host rejected message data\n";
+ $smtp->quit;
+}
+
+sub pop_queue ()
+{
+ open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
+ flock LOCK, LOCK_EX;
+
+ my $queue = -f $queue_name ? retrieve $queue_name : [];
+ my $ent = shift @$queue;
+ nstore $queue, $queue_name;
+
+ flock LOCK, LOCK_UN;
+ close LOCK;
+ $ent;
+}
+
+sub git_exec (@)
+{
+ system('git',@_) == 0 or die "Cannot git " . join(' ', @_) . "\n";
+}
+
+sub git_val (@)
+{
+ open(C, '-|','git',@_);
+ my $r = <C>;
+ chop $r if $r;
+ close C;
+ $r;
+}
+
+sub do_build ($$)
+{
+ my ($git_dir, $new) = @_;
+
+ my $tmp = File::Spec->catfile($tmpdir, "builder$$");
+ system('rm','-rf',$tmp) == 0 or die "Cannot clear $tmp\n";
+ die "Cannot clear $tmp.\n" if -e $tmp;
+
+ my $result = 1;
+ eval {
+ my $command;
+ {
+ local $ENV{GIT_DIR} = $git_dir;
+ $command = git_val 'config','builder.command';
+ }
+ die "No builder.command for $git_dir.\n" unless $command;
+
+ git_exec 'clone','-n','-l','-s',$git_dir,$tmp;
+ chmod 0700, $tmp or die "Cannot lock $tmp\n";
+ chdir $tmp or die "Cannot enter $tmp\n";
+
+ git_exec 'update-ref','HEAD',$new;
+ git_exec 'read-tree','-m','-u','HEAD','HEAD';
+ system $command;
+ if ($? == -1) {
+ print STDERR "failed to execute '$command': $!\n";
+ $result = 1;
+ } elsif ($? & 127) {
+ my $sig = $? & 127;
+ print STDERR "'$command' died from signal $sig\n";
+ $result = 1;
+ } else {
+ my $r = $? >> 8;
+ print STDERR "'$command' exited with $r\n" if $r;
+ $result = $r;
+ }
+ };
+ if ($@) {
+ $result = 2;
+ print STDERR "$@\n";
+ }
+
+ chdir '/';
+ system('rm','-rf',$tmp);
+ rmdir $tmp;
+ $result;
+}
+
+sub build_failed ($$$$$)
+{
+ my ($git_dir, $ref, $old, $new, $msg) = @_;
+
+ $git_dir =~ m,/([^/]+)$,;
+ my $repo_name = $1;
+ $ref =~ s,^refs/(heads|tags)/,,;
+
+ my %authors;
+ my $shortlog;
+ my $revstr;
+ {
+ local $ENV{GIT_DIR} = $git_dir;
+ my @revs = ($new);
+ push @revs, '--not', @$old if @$old;
+ open LOG,'-|','git','rev-list','--pretty=raw',@revs;
+ while (<LOG>) {
+ if (s/^(author|committer) //) {
+ chomp;
+ s/>.*$/>/;
+ $authors{$_} = 1 unless nocc_author $_;
+ }
+ }
+ close LOG;
+ open LOG,'-|','git','shortlog',@revs;
+ $shortlog .= $_ while <LOG>;
+ close LOG;
+ $revstr = join(' ', @revs);
+ }
+
+ my @to = sort keys %authors;
+ unless (@to) {
+ print STDERR "error: No authors in $revstr\n";
+ return;
+ }
+
+ my $subject = "[$repo_name] $ref : Build Failed";
+ my $body = <<EOF;
+Project: $git_dir
+Branch: $ref
+Commits: $revstr
+
+$shortlog
+Build Output:
+--------------------------------------------------------------
+$msg
+EOF
+ send_email($subject, $body, \@to);
+}
+
+sub run_build ($$$$)
+{
+ my ($git_dir, $ref, $old, $new) = @_;
+
+ if ($debug_flag) {
+ my @revs = ($new);
+ push @revs, '--not', @$old if @$old;
+ print "BUILDING $git_dir\n";
+ print " BRANCH: $ref\n";
+ print " COMMITS: ", join(' ', @revs), "\n";
+ }
+
+ local(*R, *W);
+ pipe R, W or die "cannot pipe builder: $!";
+
+ my $builder = fork();
+ if (!defined $builder) {
+ die "cannot fork builder: $!";
+ } elsif (0 == $builder) {
+ close R;
+ close STDIN;open(STDIN, '/dev/null');
+ open(STDOUT, '>&W');
+ open(STDERR, '>&W');
+ exit do_build $git_dir, $new;
+ } else {
+ close W;
+ my $out = '';
+ $out .= $_ while <R>;
+ close R;
+ waitpid $builder, 0;
+ build_failed $git_dir, $ref, $old, $new, $out if $?;
+ }
+
+ print "DONE\n\n" if $debug_flag;
+}
+
+sub daemon_loop ()
+{
+ my $run = 1;
+ my $stop_sub = sub {$run = 0};
+ $SIG{HUP} = $stop_sub;
+ $SIG{INT} = $stop_sub;
+ $SIG{TERM} = $stop_sub;
+
+ mkdir $tmpdir, 0755;
+ my $pidfile = File::Spec->catfile($tmpdir, "cidaemon.pid");
+ open(O, ">$pidfile"); print O "$$\n"; close O;
+
+ while ($run) {
+ my $ent = pop_queue;
+ if ($ent) {
+ my ($git_dir, $ref, $old, $new) = @$ent;
+
+ $ent = $recent{$git_dir};
+ $recent{$git_dir} = $ent = [[], {}] unless $ent;
+ my ($rec_arr, $rec_hash) = @$ent;
+ next if $rec_hash->{$new}++;
+ while (@$rec_arr >= $recent_size) {
+ my $to_kill = shift @$rec_arr;
+ delete $rec_hash->{$to_kill};
+ }
+ push @$rec_arr, $new;
+
+ run_build $git_dir, $ref, $old, $new;
+ } else {
+ sleep $scan_delay;
+ }
+ }
+
+ unlink $pidfile;
+}
+
+$debug_flag = 0;
+GetOptions(
+ 'debug|d' => \$debug_flag,
+ 'smtp-user=s' => \$ex_user,
+) or die "usage: $0 [--debug] [--smtp-user=user]\n";
+
+$ex_pass = input_noecho("$ex_user SMTP password: ")
+ if ($ex_user && !$ex_pass);
+
+if ($debug_flag) {
+ daemon_loop;
+ exit 0;
+}
+
+my $daemon = fork();
+if (!defined $daemon) {
+ die "cannot fork daemon: $!";
+} elsif (0 == $daemon) {
+ close STDIN;open(STDIN, '/dev/null');
+ close STDOUT;open(STDOUT, '>/dev/null');
+ close STDERR;open(STDERR, '>/dev/null');
+ daemon_loop;
+ exit 0;
+} else {
+ print "Daemon $daemon running in the background.\n";
+}
--- /dev/null
+#!/usr/bin/perl
+#
+# A hook that notifies its companion cidaemon through a simple
+# queue file that a ref has been updated via a push (actually
+# by a receive-pack running on the server).
+#
+# See cidaemon for per-repository configuration details.
+#
+# To use this hook, add it as the post-receive hook, make it
+# executable, and set its configuration options.
+#
+
+local $ENV{PATH} = '/opt/git/bin';
+
+use strict;
+use warnings;
+use File::Spec;
+use Storable qw(retrieve nstore);
+use Fcntl ':flock';
+
+my $git_dir = File::Spec->rel2abs($ENV{GIT_DIR});
+my $queue_name = `git config --get builder.queue`;chop $queue_name;
+$queue_name =~ m,^([^\s]+)$,; $queue_name = $1; # untaint
+unless ($queue_name) {
+ 1 while <STDIN>;
+ print STDERR "\nerror: builder.queue not set. Not enqueing.\n\n";
+ exit;
+}
+my $queue_lock = "$queue_name.lock";
+
+my @skip;
+open S, "git config --get-all builder.skip|";
+while (<S>) {
+ chop;
+ push @skip, $_;
+}
+close S;
+
+my @new_branch_base;
+open S, "git config --get-all builder.newBranchBase|";
+while (<S>) {
+ chop;
+ push @new_branch_base, $_;
+}
+close S;
+
+sub skip ($)
+{
+ local $_ = shift;
+ foreach my $p (@skip) {
+ return 1 if /^$p/;
+ }
+ 0;
+}
+
+open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
+flock LOCK, LOCK_EX;
+
+my $queue = -f $queue_name ? retrieve $queue_name : [];
+my %existing;
+foreach my $r (@$queue) {
+ my ($gd, $ref) = @$r;
+ $existing{$gd}{$ref} = $r;
+}
+
+my @new_branch_commits;
+my $loaded_new_branch_commits = 0;
+
+while (<STDIN>) {
+ chop;
+ my ($old, $new, $ref) = split / /, $_, 3;
+
+ next if $old eq $new;
+ next if $new =~ /^0{40}$/;
+ next if skip $ref;
+
+ my $r = $existing{$git_dir}{$ref};
+ if ($r) {
+ $r->[3] = $new;
+ } else {
+ if ($old =~ /^0{40}$/) {
+ if (!$loaded_new_branch_commits && @new_branch_base) {
+ open M,'-|','git','show-ref',@new_branch_base;
+ while (<M>) {
+ ($_) = split / /, $_;
+ push @new_branch_commits, $_;
+ }
+ close M;
+ $loaded_new_branch_commits = 1;
+ }
+ $old = [@new_branch_commits];
+ } else {
+ $old = [$old];
+ }
+
+ $r = [$git_dir, $ref, $old, $new];
+ $existing{$git_dir}{$ref} = $r;
+ push @$queue, $r;
+ }
+}
+nstore $queue, $queue_name;
+
+flock LOCK, LOCK_UN;
+close LOCK;
;;; git.el --- A user interface for git
-;; Copyright (C) 2005, 2006 Alexandre Julliard <julliard@winehq.org>
+;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard <julliard@winehq.org>
;; Version: 1.0
(error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))
(message "Running git %s...done" (car args)))
+(defun git-run-hook (hook env &rest args)
+ "Run a git hook and display its output if any."
+ (let ((dir default-directory)
+ (hook-name (expand-file-name (concat ".git/hooks/" hook))))
+ (or (not (file-executable-p hook-name))
+ (let (status (buffer (get-buffer-create "*Git Hook Output*")))
+ (with-current-buffer buffer
+ (erase-buffer)
+ (cd dir)
+ (setq status
+ (if env
+ (apply #'call-process "env" nil (list buffer t) nil
+ (append (git-get-env-strings env) (list hook-name) args))
+ (apply #'call-process hook-name nil (list buffer t) nil args))))
+ (display-message-or-buffer buffer)
+ (eq 0 status)))))
+
(defun git-get-string-sha1 (string)
"Read a SHA1 from the specified string."
(and string
(when modified
(apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified)))))
+(defun git-run-pre-commit-hook ()
+ "Run the pre-commit hook if any."
+ (unless git-status (error "Not in git-status buffer."))
+ (let ((files (git-marked-files-state 'added 'deleted 'modified)))
+ (or (not files)
+ (not (file-executable-p ".git/hooks/pre-commit"))
+ (let ((index-file (make-temp-file "gitidx")))
+ (unwind-protect
+ (let ((head-tree (unless (git-empty-db-p) (git-rev-parse "HEAD^{tree}"))))
+ (git-read-tree head-tree index-file)
+ (git-update-index index-file files)
+ (git-run-hook "pre-commit" `(("GIT_INDEX_FILE" . ,index-file))))
+ (delete-file index-file))))))
+
(defun git-do-commit ()
"Perform the actual commit using the current buffer as log message."
(interactive)
(git-run-command nil nil "rerere"))
(git-refresh-files)
(git-refresh-ewoc-hf git-status)
- (message "Committed %s." commit))
+ (message "Committed %s." commit)
+ (git-run-hook "post-commit" nil))
(message "Commit aborted."))))
(message "No files to commit.")))
(delete-file index-file))))))
"Commit the marked file(s), asking for a commit message."
(interactive)
(unless git-status (error "Not in git-status buffer."))
- (let ((buffer (get-buffer-create "*git-commit*"))
- (coding-system (git-get-commits-coding-system))
- author-name author-email subject date)
- (when (eq 0 (buffer-size buffer))
- (when (file-readable-p ".dotest/info")
- (with-temp-buffer
- (insert-file-contents ".dotest/info")
- (goto-char (point-min))
- (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
- (setq author-name (match-string 1))
- (setq author-email (match-string 2)))
- (goto-char (point-min))
- (when (re-search-forward "^Subject: \\(.*\\)$" nil t)
- (setq subject (match-string 1)))
- (goto-char (point-min))
- (when (re-search-forward "^Date: \\(.*\\)$" nil t)
- (setq date (match-string 1)))))
- (git-setup-log-buffer buffer author-name author-email subject date))
- (log-edit #'git-do-commit nil #'git-log-edit-files buffer)
- (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
- (setq buffer-file-coding-system coding-system)
- (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))
+ (when (git-run-pre-commit-hook)
+ (let ((buffer (get-buffer-create "*git-commit*"))
+ (coding-system (git-get-commits-coding-system))
+ author-name author-email subject date)
+ (when (eq 0 (buffer-size buffer))
+ (when (file-readable-p ".dotest/info")
+ (with-temp-buffer
+ (insert-file-contents ".dotest/info")
+ (goto-char (point-min))
+ (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
+ (setq author-name (match-string 1))
+ (setq author-email (match-string 2)))
+ (goto-char (point-min))
+ (when (re-search-forward "^Subject: \\(.*\\)$" nil t)
+ (setq subject (match-string 1)))
+ (goto-char (point-min))
+ (when (re-search-forward "^Date: \\(.*\\)$" nil t)
+ (setq date (match-string 1)))))
+ (git-setup-log-buffer buffer author-name author-email subject date))
+ (log-edit #'git-do-commit nil #'git-log-edit-files buffer)
+ (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+ (setq buffer-file-coding-system coding-system)
+ (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
(defun git-find-file ()
"Visit the current file in its own buffer."
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2006, Shawn O. Pearce
+#
+# Cleanup unreachable files and optimize the repository.
+
+USAGE='[--prune]'
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+
+no_prune=:
+while case $# in 0) break ;; esac
+do
+ case "$1" in
+ --prune)
+ no_prune=
+ ;;
+ --)
+ usage
+ ;;
+ esac
+ shift
+done
+
+case "$(git config --get gc.packrefs)" in
+notbare|"")
+ test $(is_bare_repository) = true || pack_refs=true;;
+*)
+ pack_refs=$(git config --bool --get gc.packrefs)
+esac
+
+test "true" != "$pack_refs" ||
+git-pack-refs --prune &&
+git-reflog expire --all &&
+git-repack -a -d -l &&
+$no_prune git-prune &&
+git-rerere gc || exit
else if (!strcmp(argv[1], "--theirs"))
revs->max_count = 3;
else if (!strcmp(argv[1], "-n") ||
- !strcmp(argv[1], "--no-index"))
+ !strcmp(argv[1], "--no-index")) {
revs->max_count = -2;
+ revs->diffopt.exit_with_status = 1;
+ }
else if (!strcmp(argv[1], "-q"))
*silent = 1;
else
break;
} else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) {
i = argc - 3;
+ revs->diffopt.exit_with_status = 1;
break;
}
if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
struct cache_entry *ce = active_cache[i];
int changed;
+ if (revs->diffopt.quiet && revs->diffopt.has_changes)
+ break;
+
if (!ce_path_match(ce, revs->prune_data))
continue;
struct cache_entry *ce = *ac;
int same = (entries > 1) && ce_same_name(ce, ac[1]);
+ if (revs->diffopt.quiet && revs->diffopt.has_changes)
+ break;
+
if (!ce_path_match(ce, pathspec))
goto skip_entry;
if (options->abbrev <= 0 || 40 < options->abbrev)
options->abbrev = 40; /* full */
+ /*
+ * It does not make sense to show the first hit we happened
+ * to have found. It does not make sense not to return with
+ * exit code in such a case either.
+ */
+ if (options->quiet) {
+ options->output_format = DIFF_FORMAT_NO_OUTPUT;
+ options->exit_with_status = 1;
+ }
+
+ /*
+ * If we postprocess in diffcore, we cannot simply return
+ * upon the first hit. We need to run diff as usual.
+ */
+ if (options->pickaxe || options->filter)
+ options->quiet = 0;
+
return 0;
}
options->color_diff = options->color_diff_words = 1;
else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0;
+ else if (!strcmp(arg, "--exit-code"))
+ options->exit_with_status = 1;
+ else if (!strcmp(arg, "--quiet"))
+ options->quiet = 1;
else
return 0;
return 1;
void diffcore_std(struct diff_options *options)
{
+ if (options->quiet)
+ return;
if (options->break_opt != -1)
diffcore_break(options->break_opt);
if (options->detect_rename)
diffcore_order(options->orderfile);
diff_resolve_rename_copy();
diffcore_apply_filter(options->filter);
-}
-
-void diffcore_std_no_resolve(struct diff_options *options)
-{
- if (options->pickaxe)
- diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
- if (options->orderfile)
- diffcore_order(options->orderfile);
- diffcore_apply_filter(options->filter);
+ options->has_changes = !!diff_queued_diff.nr;
}
+
void diff_addremove(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
fill_filespec(two, sha1, mode);
diff_queue(&diff_queued_diff, one, two);
+ options->has_changes = 1;
}
void diff_change(struct diff_options *options,
fill_filespec(two, new_sha1, new_mode);
diff_queue(&diff_queued_diff, one, two);
+ options->has_changes = 1;
}
void diff_unmerge(struct diff_options *options,
silent_on_remove:1,
find_copies_harder:1,
color_diff:1,
- color_diff_words:1;
+ color_diff_words:1,
+ has_changes:1,
+ quiet:1,
+ exit_with_status:1;
int context;
int break_opt;
int detect_rename;
extern void diffcore_std(struct diff_options *);
-extern void diffcore_std_no_resolve(struct diff_options *);
-
#define COMMON_DIFF_OPTIONS_HELP \
"\ncommon diff options:\n" \
" -z output diff-raw with lines terminated with NUL.\n" \
int log_all_ref_updates = -1; /* unspecified */
int warn_ambiguous_refs = 1;
int repository_format_version;
-char *git_commit_encoding;
+const char *git_commit_encoding;
const char *git_log_output_encoding;
int shared_repository = PERM_UMASK;
const char *apply_default_whitespace;
int zlib_compression_level = Z_DEFAULT_COMPRESSION;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
+size_t delta_base_cache_limit = 16 * 1024 * 1024;
int pager_in_use;
int pager_use_color = 1;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
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 (parse_tree(tree))
return -1;
- desc.buf = tree->buffer;
- desc.size = tree->size;
+ init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
struct object *obj = NULL;
# 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
*)
git-mailinfo $keep_subject $utf8 \
.dotest/msg .dotest/patch <$i >.dotest/info || exit 1
- test -s $dotest/patch || {
+ test -s .dotest/patch || {
echo "Patch is empty. Was is split wrong?"
- stop_here $this
+ exit 1
}
git-stripspace < .dotest/msg > .dotest/msg-clean
;;
#!/bin/sh
-USAGE='[start|bad|good|next|reset|visualize|replay|log]'
+USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
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.'
+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
0)
rev=$(git-rev-parse --verify HEAD) ;;
1)
- rev=$(git-rev-parse --verify "$1") ;;
+ rev=$(git-rev-parse --verify "$1^{commit}") ;;
*)
usage ;;
esac || exit
esac
for rev in $revs
do
- rev=$(git-rev-parse --verify "$rev") || exit
+ rev=$(git-rev-parse --verify "$rev^{commit}") || exit
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
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
+ 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
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/BISECT_RUN"
fi
}
bisect_auto_next
}
+bisect_run () {
+ 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
esac
done
-case "$new_branch,$track" in
+case "$newbranch,$track" in
,--*)
die "git checkout: --track and --no-track require -b"
esac
detached=
detach_warn=
+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" != "$old"
then
detached="$new"
(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"
then
- echo >&2 "Previous HEAD position was $old"
+ describe_detached_head 'Previous HEAD position was' "$old"
fi
if [ "X$old" = X ]
if test -n "$branch"
then
GIT_DIR="$GIT_DIR" git-symbolic-ref -m "checkout: moving to $branch" HEAD "refs/heads/$branch"
- if test -z "$quiet"
+ if test -n "$quiet"
+ then
+ true # nothing
+ elif test "refs/heads/$branch" = "$oldbranch"
then
+ echo >&2 "Already on branch \"$branch\""
+ else
echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
fi
elif test -n "$detached"
then
echo >&2 "$detach_warn"
fi
+ describe_detached_head 'HEAD is now at' HEAD
fi
rm -f "$GIT_DIR/MERGE_HEAD"
else
http_fetch "$1/info/refs" "$clone_tmp/refs" ||
die "Cannot get remote repository information.
Perhaps git-update-server-info needs to be run there?"
+ test "z$quiet" = z && v=-v || v=
while read sha1 refname
do
name=`expr "z$refname" : 'zrefs/\(.*\)'` &&
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" ||
# we need to merge with the local changes ( M=successful merge, C=conflict merge )
$log->info("Merging $file_local, $file_old, $file_new");
+ print "M Merging differences between 1.$oldmeta->{revision} and 1.$meta->{revision} into $filename\n";
$log->debug("Temporary directory for merge is $dir");
elsif ( $return == 1 )
{
$log->info("Merged with conflicts");
+ print "E cvs update: conflicts found in $filename\n";
print "M C $filename\n";
# Don't want to actually _DO_ the update if -n specified
$log->info("Created index '$file_index' with for head $state->{module} - exit status $?");
my @committedfiles = ();
+ my %oldmeta;
# foreach file specified on the command line ...
foreach my $filename ( @{$state->{args}} )
next unless ( exists $state->{entries}{$filename}{modified_filename} or not $state->{entries}{$filename}{unchanged} );
my $meta = $updater->getmeta($filename);
+ $oldmeta{$filename} = $meta;
my $wrev = revparse($filename);
$log->debug("Checked-in $dirpart : $filename");
+ print "M $state->{CVSROOT}/$state->{module}/$filename,v <-- $dirpart$filepart\n";
if ( defined $meta->{filehash} && $meta->{filehash} eq "deleted" )
{
+ print "M new revision: delete; previous revision: 1.$oldmeta{$filename}{revision}\n";
print "Remove-entry $dirpart\n";
print "$filename\n";
} else {
+ if ($meta->{revision} == 1) {
+ print "M initial revision: 1.1\n";
+ } else {
+ print "M new revision: 1.$meta->{revision}; previous revision: 1.$oldmeta{$filename}{revision}\n";
+ }
print "Checked-in $dirpart\n";
print "$filename\n";
my $kopts = kopts_from_path($filepart);
}
if ( defined($meta->{revision}) )
{
- print "M Repository revision:\t1." . $meta->{revision} . "\t$state->{repository}/$filename,v\n";
+ print "M Repository revision:\t1." . $meta->{revision} . "\t$state->{CVSROOT}/$state->{module}/$filename,v\n";
print "M Sticky Tag:\t\t(none)\n";
print "M Sticky Date:\t\t(none)\n";
print "M Sticky Options:\t\t(none)\n";
append_fetch_head () {
flags=
- test -n "$verbose" && flags="$flags -v"
- test -n "$force" && flags="$flags -f"
+ test -n "$verbose" && flags="$flags$LF-v"
+ test -n "$force$single_force" && flags="$flags$LF-f"
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
git-fetch--tool $flags append-fetch-head "$@"
}
fi
fi
-fetch_native () {
+fetch_all_at_once () {
eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
eval "$eval"
( : subshell because we muck with IFS
IFS=" $LF"
(
- if test -f "$remote" ; then
+ if test "$remote" = . ; then
+ git-show-ref $rref || echo failed "$remote"
+ elif test -f "$remote" ; then
test -n "$shallow_depth" &&
die "shallow clone with bundle is not supported"
git-bundle unbundle "$remote" $rref ||
}
-fetch_dumb () {
+fetch_per_ref () {
reflist="$1"
refs=
rref=
fetch_main () {
case "$remote" in
http://* | https://* | ftp://* | rsync://* )
- fetch_dumb "$@"
+ fetch_per_ref "$@"
;;
*)
- fetch_native "$@"
+ fetch_all_at_once "$@"
;;
esac
}
+++ /dev/null
-#!/bin/sh
-#
-# Copyright (c) 2006, Shawn O. Pearce
-#
-# Cleanup unreachable files and optimize the repository.
-
-USAGE='[--prune]'
-SUBDIRECTORY_OK=Yes
-. git-sh-setup
-
-no_prune=:
-while case $# in 0) break ;; esac
-do
- case "$1" in
- --prune)
- no_prune=
- ;;
- --)
- usage
- ;;
- esac
- shift
-done
-
-case "$(git config --get gc.packrefs)" in
-notbare|"")
- test $(is_bare_repository) = true || pack_refs=true;;
-*)
- pack_refs=$(git config --bool --get gc.packrefs)
-esac
-
-test "true" != "$pack_refs" ||
-git-pack-refs --prune &&
-git-reflog expire --all &&
-git-repack -a -d -l &&
-$no_prune git-prune &&
-git-rerere gc || exit
# 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
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
# Again the most common case of merging one remote.
echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
git-update-index --refresh 2>/dev/null
- new_head=$(git-rev-parse --verify "$1^0") &&
- git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
msg="Fast forward"
if test -n "$have_message"
then
msg="$msg (no commit created; -m option ignored)"
fi
+ new_head=$(git-rev-parse --verify "$1^0") &&
+ git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
finish "$new_head" "$msg" || exit
dropsave
exit 0
mv -- "$BACKUP" "$path.orig"
fi
;;
- meld)
+ meld|vimdiff)
touch "$BACKUP"
- meld -- "$LOCAL" "$path" "$REMOTE"
+ $merge_tool -- "$LOCAL" "$path" "$REMOTE"
if test "$path" -nt "$BACKUP" ; then
status=0;
else
if test -z "$merge_tool"; then
merge_tool=`git-config merge.tool`
- if test $merge_tool = kdiff3 -o $merge_tool = tkdiff -o \
- $merge_tool = xxdiff -o $merge_tool = meld ; then
- unset merge_tool
- fi
+ case "$merge_tool" in
+ kdiff3 | tkdiff | xxdiff | meld | emerge | vimdiff)
+ ;; # happy
+ *)
+ echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
+ echo >&2 "Resetting to default..."
+ unset merge_tool
+ ;;
+ esac
fi
if test -z "$merge_tool" ; then
merge_tool=meld
elif type emacs >/dev/null 2>&1; then
merge_tool=emerge
+ elif type vimdiff >/dev/null 2>&1; then
+ merge_tool=vimdiff
else
echo "No available merge resolution programs available."
exit 1
fi
case "$merge_tool" in
- kdiff3|tkdiff|meld|xxdiff)
+ kdiff3|tkdiff|meld|xxdiff|vimdiff)
if ! type "$merge_tool" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available"
exit 1
*/*)
echo ''
;;
+ .)
+ echo self
+ ;;
*)
if test "$(git-config --get "remote.$1.url")"
then
'')
echo "$1"
;;
+ self)
+ echo "$1"
+ ;;
config)
git-config --get "remote.$1.url"
;;
get_remote_default_refs_for_push () {
data_source=$(get_data_source "$1")
case "$data_source" in
- '' | branches)
+ '' | branches | self)
;; # no default push mapping, just send matching refs.
config)
git-config --get-all "remote.$1.push" ;;
case "$data_source" in
'')
echo "HEAD:" ;;
+ self)
+ canon_refs_list_for_fetch -d "$1" \
+ $(git-for-each-ref --format='%(refname):')
+ ;;
config)
canon_refs_list_for_fetch -d "$1" \
$(git-config --get-all "remote.$1.fetch") ;;
}' "$GIT_DIR/remotes/$1")
;;
*)
- die "internal error: get-remote-default-ref-for-push $1" ;;
+ die "internal error: get-remote-default-ref-for-fetch $1" ;;
esac
}
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
$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 {
Defaults to on.
--no-signed-off-cc Suppress the automatic addition of email addresses
- that appear in a Signed-off-by: line, to the cc: list.
- Note: Using this option is not recommended.
+ that appear in Signed-off-by: or Cc: lines to the cc:
+ list. Note: Using this option is not recommended.
--smtp-server If set, specifies the outgoing SMTP server to use.
Defaults to localhost.
}
} else {
$message .= $_;
- if (/^Signed-off-by: (.*)$/i && !$no_signed_off_cc) {
- my $c = $1;
+ if (/^(Signed-off-by|Cc): (.*)$/i && !$no_signed_off_cc) {
+ my $c = $2;
chomp $c;
push @cc, $c;
printf("(sob) Adding cc: %s from line '%s'\n",
{ "format-patch", cmd_format_patch, RUN_SETUP },
{ "fsck", cmd_fsck, RUN_SETUP },
{ "fsck-objects", cmd_fsck, RUN_SETUP },
+ { "gc", cmd_gc, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id },
{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
{ "help", cmd_help },
bindkey <Key-Return> {findnext 0}
bindkey ? findprev
bindkey f nextfile
+ bindkey <F5> updatecommits
bind . <Control-q> doquit
bind . <Control-f> dofind
bind . <Control-g> {findnext 0}
<Ctrl-plus> Increase font size
<Ctrl-KP-> Decrease font size
<Ctrl-minus> Decrease font size
+<F5> Update
} \
-justify left -bg white -border 2 -relief sunken
pack $w.m -side top -fill both
} 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
}
--- /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.
+
+
+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).
+
+
+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;
}
$formats_nav .=
'(merge: ' .
join(' ', map {
- $cgi->a({-href => href(action=>"commitdiff",
+ $cgi->a({-href => href(action=>"commit",
hash=>$_)},
esc_html(substr($_, 0, 7)));
} @$parents ) .
me.elem = name;
me.elem_len = strlen(name);
- desc.buf = tree->buffer;
- desc.size = tree->size;
+ init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
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
static void sha1_object(const void *data, unsigned long size,
enum object_type type, unsigned char *sha1)
{
- SHA_CTX ctx;
- char header[50];
- int header_size;
- const char *type_str;
-
- switch (type) {
- case OBJ_COMMIT: type_str = commit_type; break;
- case OBJ_TREE: type_str = tree_type; break;
- case OBJ_BLOB: type_str = blob_type; break;
- case OBJ_TAG: type_str = tag_type; break;
- default:
- die("bad type %d", type);
+ hash_sha1_file(data, size, typename(type), sha1);
+ if (has_sha1_file(sha1)) {
+ void *has_data;
+ enum object_type has_type;
+ unsigned long has_size;
+ has_data = read_sha1_file(sha1, &has_type, &has_size);
+ if (!has_data)
+ die("cannot read existing object %s", sha1_to_hex(sha1));
+ if (size != has_size || type != has_type ||
+ memcmp(data, has_data, size) != 0)
+ die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
}
-
- header_size = sprintf(header, "%s %lu", type_str, size) + 1;
-
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, header, header_size);
- SHA1_Update(&ctx, data, size);
- SHA1_Final(sha1, &ctx);
}
static void resolve_delta(struct object_entry *delta_obj, void *base_data,
return size;
}
-static void append_obj_to_pack(void *buf,
+static void append_obj_to_pack(const unsigned char *sha1, void *buf,
unsigned long size, enum object_type type)
{
struct object_entry *obj = &objects[nr_objects++];
write_or_die(output_fd, header, n);
obj[1].offset = obj[0].offset + n;
obj[1].offset += write_compressed(output_fd, buf, size);
- sha1_object(buf, size, type, obj->sha1);
+ hashcpy(obj->sha1, sha1);
}
static int delta_pos_compare(const void *_a, const void *_b)
resolve_delta(child, data, size, type);
}
- append_obj_to_pack(data, size, type);
+ if (check_sha1_signature(d->base.sha1, data, size, typename(type)))
+ die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
+ append_obj_to_pack(d->base.sha1, data, size, type);
free(data);
if (verbose)
percent = display_progress(nr_resolved_deltas,
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 {
me.elem = name;
me.elem_len = strlen(name);
- desc.buf = tree->buffer;
- desc.size = tree->size;
+ init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
}
/* If we got ENOENT there is no point continuing. */
if (errno == ENOENT) {
- if (warn_if_not_exists)
- fprintf(stderr, "does not exist %s\n", source);
- return -1;
+ if (!warn_if_not_exists)
+ return -1;
+ return error("does not exist %s", source);
}
}
if (use_symlink) {
if (stat(source, &st)) {
if (!warn_if_not_exists && errno == ENOENT)
return -1;
- fprintf(stderr, "cannot stat %s: %s\n", source,
- strerror(errno));
- return -1;
+ return error("cannot stat %s: %s", source,
+ strerror(errno));
}
if (!symlink(source, dest)) {
pull_say("symlink %s\n", hex);
if (ifd < 0) {
if (!warn_if_not_exists && errno == ENOENT)
return -1;
- fprintf(stderr, "cannot open %s\n", source);
- return -1;
+ return error("cannot open %s", source);
}
ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (ofd < 0) {
- fprintf(stderr, "cannot open %s\n", dest);
close(ifd);
- return -1;
+ return error("cannot open %s", dest);
}
status = copy_fd(ifd, ofd);
close(ofd);
if (status)
- fprintf(stderr, "cannot write %s\n", dest);
- else
- pull_say("copy %s\n", hex);
- return status;
+ return error("cannot write %s", dest);
+ pull_say("copy %s\n", hex);
+ return 0;
}
- fprintf(stderr, "failed to copy %s with given copy methods.\n", hex);
- return -1;
+ return error("failed to copy %s with given copy methods.", hex);
}
static int fetch_pack(const unsigned char *sha1)
ifd = open(filename, O_RDONLY);
if (ifd < 0) {
close(ifd);
- fprintf(stderr, "cannot open %s\n", filename);
- return -1;
+ return error("cannot open %s", filename);
}
if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
close(ifd);
- fprintf(stderr, "cannot read from %s\n", filename);
- return -1;
+ return error("cannot read from %s", filename);
}
close(ifd);
pull_say("ref %s\n", sha1_to_hex(sha1));
#include "cache.h"
+#include "run-command.h"
static const char *pgm;
-static const char *arguments[8];
+static const char *arguments[9];
static int one_shot, quiet;
static int err;
static void run_program(void)
{
- pid_t pid = fork();
- int status;
-
- if (pid < 0)
- die("unable to fork");
- if (!pid) {
- execlp(pgm, arguments[0],
- arguments[1],
- arguments[2],
- arguments[3],
- arguments[4],
- arguments[5],
- arguments[6],
- arguments[7],
- NULL);
- die("unable to execute '%s'", pgm);
- }
- if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status)) {
+ struct child_process child;
+ memset(&child, 0, sizeof(child));
+ child.argv = arguments;
+ if (run_command(&child)) {
if (one_shot) {
err++;
} else {
arguments[5] = "";
arguments[6] = "";
arguments[7] = "";
+ arguments[8] = NULL;
found = 0;
do {
static char hexbuf[4][60];
static int unresolved_directory(const char *base, struct name_entry n[3])
{
- int baselen;
+ int baselen, pathlen;
char *newbase;
struct name_entry *p;
struct tree_desc t[3];
if (!S_ISDIR(p->mode))
return 0;
baselen = strlen(base);
- newbase = xmalloc(baselen + p->pathlen + 2);
+ pathlen = tree_entry_len(p->path, p->sha1);
+ newbase = xmalloc(baselen + pathlen + 2);
memcpy(newbase, base, baselen);
- memcpy(newbase + baselen, p->path, p->pathlen);
- memcpy(newbase + baselen + p->pathlen, "/", 2);
+ memcpy(newbase + baselen, p->path, pathlen);
+ memcpy(newbase + baselen + pathlen, "/", 2);
buf0 = fill_tree_descriptor(t+0, n[0].sha1);
buf1 = fill_tree_descriptor(t+1, n[1].sha1);
if (buffer) {
struct object *obj;
- if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0)
- printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
+ if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
+ error("sha1 mismatch %s\n", sha1_to_hex(sha1));
+ return NULL;
+ }
obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
if (!eaten)
struct pack_window **w_curs)
{
off_t index_size = p->index_size;
- void *index_base = p->index_base;
+ const unsigned char *index_base = p->index_data;
SHA_CTX ctx;
unsigned char sha1[20];
off_t offset = 0, pack_sig = p->pack_size - 20;
if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL)))
return error("Packfile %s SHA1 mismatch with itself",
p->pack_name);
- if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40))
+ if (hashcmp(sha1, index_base + index_size - 40))
return error("Packfile %s SHA1 mismatch with idx",
p->pack_name);
unuse_pack(w_curs);
int verify_pack(struct packed_git *p, int verbose)
{
off_t index_size = p->index_size;
- void *index_base = p->index_base;
+ const unsigned char *index_base = p->index_data;
SHA_CTX ctx;
unsigned char sha1[20];
int ret;
SHA1_Init(&ctx);
SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20));
SHA1_Final(sha1, &ctx);
- if (hashcmp(sha1, (unsigned char *)index_base + index_size - 20))
+ if (hashcmp(sha1, index_base + index_size - 20))
ret = error("Packfile index for %s SHA1 mismatch",
p->pack_name);
struct llist_item {
struct llist_item *next;
- unsigned char *sha1;
+ const unsigned char *sha1;
};
static struct llist {
struct llist_item *front;
return ret;
}
-static inline struct llist_item * llist_insert(struct llist *list,
- struct llist_item *after,
- unsigned char *sha1)
+static inline struct llist_item *llist_insert(struct llist *list,
+ struct llist_item *after,
+ const unsigned char *sha1)
{
struct llist_item *new = llist_item_get();
new->sha1 = sha1;
return new;
}
-static inline struct llist_item *llist_insert_back(struct llist *list, unsigned char *sha1)
+static inline struct llist_item *llist_insert_back(struct llist *list,
+ const unsigned char *sha1)
{
return llist_insert(list, list->back, sha1);
}
-static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint)
+static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
+ const unsigned char *sha1, struct llist_item *hint)
{
struct llist_item *prev = NULL, *l;
static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
{
int p1_off, p2_off;
- unsigned char *p1_base, *p2_base;
+ const unsigned char *p1_base, *p2_base;
struct llist_item *p1_hint = NULL, *p2_hint = NULL;
p1_off = p2_off = 256 * 4 + 4;
- p1_base = (unsigned char *) p1->pack->index_base;
- p2_base = (unsigned char *) p2->pack->index_base;
+ p1_base = p1->pack->index_data;
+ p2_base = p2->pack->index_data;
while (p1_off <= p1->pack->index_size - 3 * 20 &&
p2_off <= p2->pack->index_size - 3 * 20)
{
size_t ret = 0;
int p1_off, p2_off;
- unsigned char *p1_base, *p2_base;
+ const unsigned char *p1_base, *p2_base;
p1_off = p2_off = 256 * 4 + 4;
- p1_base = (unsigned char *)p1->index_base;
- p2_base = (unsigned char *)p2->index_base;
+ p1_base = p1->index_data;
+ p2_base = p2->index_data;
while (p1_off <= p1->index_size - 3 * 20 &&
p2_off <= p2->index_size - 3 * 20)
{
struct pack_list l;
size_t off;
- unsigned char *base;
+ const unsigned char *base;
if (!p->pack_local && !(alt_odb || verbose))
return NULL;
llist_init(&l.all_objects);
off = 256 * 4 + 4;
- base = (unsigned char *)p->index_base;
+ base = p->index_data;
while (off <= p->index_size - 3 * 20) {
llist_insert_back(l.all_objects, base + off);
off += 24;
};
/*
- * Packed object index header
- *
- * struct pack_idx_header {
- * uint32_t idx_signature;
- * uint32_t idx_version;
- * };
- *
- * Note: this header isn't active yet. In future versions of git
- * we may change the index file format. At that time we would start
- * the first four bytes of the new index format with this signature,
- * as all older git binaries would find this value illegal and abort
- * reading the file.
+ * The first four bytes of index formats later than version 1 should
+ * start with this signature, as all older git binaries would find this
+ * value illegal and abort reading the file.
*
* This is the case because the number of objects in a packfile
* cannot exceed 1,431,660,000 as every object would need at least
- * 3 bytes of data and the overall packfile cannot exceed 4 GiB due
- * to the 32 bit offsets used by the index. Clearly the signature
- * exceeds this maximum.
+ * 3 bytes of data and the overall packfile cannot exceed 4 GiB with
+ * version 1 of the index file due to the offsets limited to 32 bits.
+ * Clearly the signature exceeds this maximum.
*
* Very old git binaries will also compare the first 4 bytes to the
* next 4 bytes in the index and abort with a "non-monotonic index"
*/
#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */
+/*
+ * Packed object index header
+ */
+struct pack_idx_header {
+ uint32_t idx_signature;
+ uint32_t idx_version;
+};
+
+
extern int verify_pack(struct packed_git *, int);
#define PH_ERROR_EOF (-1)
me.elem = name;
me.elem_len = strlen(name);
- desc.buf = tree->buffer;
- desc.size = tree->size;
+ init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
}
} else {
const char *keeper[6];
- int fd[2], s, len, status;
- pid_t pid;
+ int s, len, status;
char keep_arg[256];
char packname[46];
+ struct child_process ip;
s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid());
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
keeper[3] = hdr_arg;
keeper[4] = keep_arg;
keeper[5] = NULL;
-
- if (pipe(fd) < 0)
- return "index-pack pipe failed";
- pid = fork();
- if (pid < 0)
+ memset(&ip, 0, sizeof(ip));
+ ip.argv = keeper;
+ ip.out = -1;
+ ip.git_cmd = 1;
+ if (start_command(&ip))
return "index-pack fork failed";
- if (!pid) {
- dup2(fd[1], 1);
- close(fd[1]);
- close(fd[0]);
- execv_git_cmd(keeper);
- die("execv of index-pack failed");
- }
- close(fd[1]);
/*
* The first thing we expects from index-pack's output
* later on. If we don't get that then tough luck with it.
*/
for (len = 0;
- len < 46 && (s = xread(fd[0], packname+len, 46-len)) > 0;
+ len < 46 && (s = xread(ip.out, packname+len, 46-len)) > 0;
len += s);
- close(fd[0]);
if (len == 46 && packname[45] == '\n' &&
memcmp(packname, "keep\t", 5) == 0) {
char path[PATH_MAX];
pack_lockfile = xstrdup(path);
}
- /* Then wrap our index-pack process. */
- while (waitpid(pid, &status, 0) < 0)
- if (errno != EINTR)
- return "waitpid failed";
- if (WIFEXITED(status)) {
- int code = WEXITSTATUS(status);
- if (code)
- return "index-pack exited with error code";
+ status = finish_command(&ip);
+ if (!status) {
reprepare_packed_git();
return NULL;
}
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);
if (parse_tree(tree) < 0)
die("bad tree %s", sha1_to_hex(obj->sha1));
- desc.buf = tree->buffer;
- desc.size = tree->size;
+ init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
mark_tree_uninteresting(lookup_tree(entry.sha1));
return 1;
}
+/*
+ * The goal is to get REV_TREE_NEW as the result only if the
+ * diff consists of all '+' (and no other changes), and
+ * REV_TREE_DIFFERENT otherwise (of course if the trees are
+ * the same we want REV_TREE_SAME). That means that once we
+ * get to REV_TREE_DIFFERENT, we do not have to look any further.
+ */
static int tree_difference = REV_TREE_SAME;
static void file_add_remove(struct diff_options *options,
diff = REV_TREE_NEW;
}
tree_difference = diff;
+ if (tree_difference == REV_TREE_DIFFERENT)
+ options->has_changes = 1;
}
static void file_change(struct diff_options *options,
const char *base, const char *path)
{
tree_difference = REV_TREE_DIFFERENT;
+ options->has_changes = 1;
}
int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
if (!t2)
return REV_TREE_DIFFERENT;
tree_difference = REV_TREE_SAME;
+ revs->pruning.has_changes = 0;
if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
&revs->pruning) < 0)
return REV_TREE_DIFFERENT;
{
int retval;
void *tree;
+ unsigned long size;
struct tree_desc empty, real;
if (!t1)
return 0;
- tree = read_object_with_reference(t1->object.sha1, tree_type, &real.size, NULL);
+ tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
if (!tree)
return 0;
- real.buf = tree;
+ init_tree_desc(&real, tree, size);
+ init_tree_desc(&empty, "", 0);
- empty.buf = "";
- empty.size = 0;
-
- tree_difference = 0;
+ tree_difference = REV_TREE_SAME;
+ revs->pruning.has_changes = 0;
retval = diff_tree(&empty, &real, "", &revs->pruning);
free(tree);
- return retval >= 0 && !tree_difference;
+ return retval >= 0 && (tree_difference == REV_TREE_SAME);
}
static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
revs->ignore_merges = 1;
revs->simplify_history = 1;
revs->pruning.recursive = 1;
+ revs->pruning.quiet = 1;
revs->pruning.add_remove = file_add_remove;
revs->pruning.change = file_change;
revs->lifo = 1;
if (!prefixcmp(arg, "--encoding=")) {
arg += 11;
if (strcmp(arg, "none"))
- git_log_output_encoding = strdup(arg);
+ git_log_output_encoding = xstrdup(arg);
else
git_log_output_encoding = "";
continue;
close(fd[1]);
}
+static inline void dup_devnull(int to)
+{
+ int fd = open("/dev/null", O_RDWR);
+ dup2(fd, to);
+ close(fd);
+}
+
int start_command(struct child_process *cmd)
{
- int need_in = !cmd->no_stdin && cmd->in < 0;
- int fdin[2];
+ int need_in, need_out;
+ int fdin[2], fdout[2];
+ need_in = !cmd->no_stdin && cmd->in < 0;
if (need_in) {
if (pipe(fdin) < 0)
return -ERR_RUN_COMMAND_PIPE;
cmd->close_in = 1;
}
+ need_out = !cmd->no_stdout
+ && !cmd->stdout_to_stderr
+ && cmd->out < 0;
+ if (need_out) {
+ if (pipe(fdout) < 0) {
+ if (need_in)
+ close_pair(fdin);
+ return -ERR_RUN_COMMAND_PIPE;
+ }
+ cmd->out = fdout[0];
+ cmd->close_out = 1;
+ }
+
cmd->pid = fork();
if (cmd->pid < 0) {
if (need_in)
close_pair(fdin);
+ if (need_out)
+ close_pair(fdout);
return -ERR_RUN_COMMAND_FORK;
}
if (!cmd->pid) {
- if (cmd->no_stdin) {
- int fd = open("/dev/null", O_RDWR);
- dup2(fd, 0);
- close(fd);
- } else if (need_in) {
+ if (cmd->no_stdin)
+ dup_devnull(0);
+ else if (need_in) {
dup2(fdin[0], 0);
close_pair(fdin);
} else if (cmd->in) {
close(cmd->in);
}
- if (cmd->stdout_to_stderr)
+ if (cmd->no_stdout)
+ dup_devnull(1);
+ else if (cmd->stdout_to_stderr)
dup2(2, 1);
+ else if (need_out) {
+ dup2(fdout[1], 1);
+ close_pair(fdout);
+ } else if (cmd->out > 1) {
+ dup2(cmd->out, 1);
+ close(cmd->out);
+ }
+
if (cmd->git_cmd) {
execv_git_cmd(cmd->argv);
} else {
else if (cmd->in)
close(cmd->in);
+ if (need_out)
+ close(fdout[1]);
+ else if (cmd->out > 1)
+ close(cmd->out);
+
return 0;
}
{
if (cmd->close_in)
close(cmd->in);
+ if (cmd->close_out)
+ close(cmd->out);
for (;;) {
int status, code;
const char **argv;
pid_t pid;
int in;
+ int out;
unsigned close_in:1;
+ unsigned close_out:1;
unsigned no_stdin:1;
+ unsigned no_stdout:1;
unsigned git_cmd:1; /* if this is to be git sub-command */
unsigned stdout_to_stderr:1;
};
#include "tag.h"
#include "refs.h"
#include "pkt-line.h"
-#include "exec_cmd.h"
+#include "run-command.h"
static const char send_pack_usage[] =
"git-send-pack [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
*/
static int pack_objects(int fd, struct ref *refs)
{
- int pipe_fd[2];
- pid_t pid;
-
- if (pipe(pipe_fd) < 0)
- return error("send-pack: pipe failed");
- pid = fork();
- if (pid < 0)
- return error("send-pack: unable to fork git-pack-objects");
- if (!pid) {
- /*
- * The child becomes pack-objects --revs; we feed
- * the revision parameters to it via its stdin and
- * let its stdout go back to the other end.
- */
- static const char *args[] = {
- "pack-objects",
- "--all-progress",
- "--revs",
- "--stdout",
- NULL,
- NULL,
- };
- if (use_thin_pack)
- args[4] = "--thin";
- dup2(pipe_fd[0], 0);
- dup2(fd, 1);
- close(pipe_fd[0]);
- close(pipe_fd[1]);
- close(fd);
- execv_git_cmd(args);
- die("git-pack-objects exec failed (%s)", strerror(errno));
- }
+ /*
+ * The child becomes pack-objects --revs; we feed
+ * the revision parameters to it via its stdin and
+ * let its stdout go back to the other end.
+ */
+ const char *args[] = {
+ "pack-objects",
+ "--all-progress",
+ "--revs",
+ "--stdout",
+ NULL,
+ NULL,
+ };
+ struct child_process po;
+
+ if (use_thin_pack)
+ args[4] = "--thin";
+ memset(&po, 0, sizeof(po));
+ po.argv = args;
+ po.in = -1;
+ po.out = fd;
+ po.git_cmd = 1;
+ if (start_command(&po))
+ die("git-pack-objects failed (%s)", strerror(errno));
/*
* We feed the pack-objects we just spawned with revision
* parameters by writing to the pipe.
*/
- close(pipe_fd[0]);
- close(fd);
-
while (refs) {
char buf[42];
memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
buf[0] = '^';
buf[41] = '\n';
- if (!write_or_whine(pipe_fd[1], buf, 42,
+ if (!write_or_whine(po.in, buf, 42,
"send-pack: send refs"))
break;
}
if (!is_null_sha1(refs->new_sha1)) {
memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
buf[40] = '\n';
- if (!write_or_whine(pipe_fd[1], buf, 41,
+ if (!write_or_whine(po.in, buf, 41,
"send-pack: send refs"))
break;
}
refs = refs->next;
}
- close(pipe_fd[1]);
-
- for (;;) {
- int status, code;
- pid_t waiting = waitpid(pid, &status, 0);
- if (waiting < 0) {
- if (errno == EINTR)
- continue;
- return error("waitpid failed (%s)", strerror(errno));
- }
- if ((waiting != pid) || WIFSIGNALED(status) ||
- !WIFEXITED(status))
- return error("pack-objects died with strange error");
- code = WEXITSTATUS(status);
- if (code)
- return -code;
- return 0;
- }
+ if (finish_command(&po))
+ return error("pack-objects died with strange error");
+ return 0;
}
static void unmark_and_free(struct commit_list *list, unsigned int mark)
pack_mapped, peak_pack_mapped);
}
-static int check_packed_git_idx(const char *path,
- unsigned long *idx_size_,
- void **idx_map_)
+static int check_packed_git_idx(const char *path, struct packed_git *p)
{
void *idx_map;
- uint32_t *index;
+ struct pack_idx_header *hdr;
size_t idx_size;
- uint32_t nr, i;
+ uint32_t nr, i, *index;
int fd = open(path, O_RDONLY);
struct stat st;
+
if (fd < 0)
return -1;
if (fstat(fd, &st)) {
idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- index = idx_map;
- *idx_map_ = idx_map;
- *idx_size_ = idx_size;
-
/* a future index format would start with this, as older git
* binaries would fail the non-monotonic index check below.
* give a nicer warning to the user if we can.
*/
- if (index[0] == htonl(PACK_IDX_SIGNATURE)) {
+ hdr = idx_map;
+ if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
munmap(idx_map, idx_size);
return error("index file %s is a newer version"
" and is not supported by this binary"
}
nr = 0;
+ index = idx_map;
for (i = 0; i < 256; i++) {
uint32_t n = ntohl(index[i]);
if (n < nr) {
return error("wrong index file size in %s", path);
}
+ p->index_version = 1;
+ p->index_data = idx_map;
+ p->index_size = idx_size;
return 0;
}
return error("end of packfile %s is unavailable", p->pack_name);
if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
return error("packfile %s signature is unavailable", p->pack_name);
- idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40;
+ idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
if (hashcmp(sha1, idx_sha1))
return error("packfile %s does not match index", p->pack_name);
return 0;
return win->base + offset;
}
-struct packed_git *add_packed_git(char *path, int path_len, int local)
+struct packed_git *add_packed_git(const char *path, int path_len, int local)
{
struct stat st;
- struct packed_git *p;
- unsigned long idx_size;
- void *idx_map;
- unsigned char sha1[20];
+ struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2);
- if (check_packed_git_idx(path, &idx_size, &idx_map))
+ /*
+ * Make sure a corresponding .pack file exists and that
+ * the index looks sane.
+ */
+ path_len -= strlen(".idx");
+ if (path_len < 1)
return NULL;
-
- /* do we have a corresponding .pack file? */
- strcpy(path + path_len - 4, ".pack");
- if (stat(path, &st) || !S_ISREG(st.st_mode)) {
- munmap(idx_map, idx_size);
+ memcpy(p->pack_name, path, path_len);
+ strcpy(p->pack_name + path_len, ".pack");
+ if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) ||
+ check_packed_git_idx(path, p)) {
+ free(p);
return NULL;
}
+
/* ok, it looks sane as far as we can check without
* actually mapping the pack file.
*/
- p = xmalloc(sizeof(*p) + path_len + 2);
- strcpy(p->pack_name, path);
- p->index_size = idx_size;
p->pack_size = st.st_size;
- p->index_base = idx_map;
p->next = NULL;
p->windows = NULL;
p->pack_fd = -1;
p->pack_local = local;
p->mtime = st.st_mtime;
- if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
- hashcpy(p->sha1, sha1);
+ if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
+ hashclr(p->sha1);
return p;
}
return parse_pack_index_file(sha1, path);
}
-struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_path)
+struct packed_git *parse_pack_index_file(const unsigned char *sha1,
+ const char *idx_path)
{
- struct packed_git *p;
- unsigned long idx_size;
- void *idx_map;
- char *path;
+ const char *path = sha1_pack_name(sha1);
+ struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2);
- if (check_packed_git_idx(idx_path, &idx_size, &idx_map))
+ if (check_packed_git_idx(idx_path, p)) {
+ free(p);
return NULL;
+ }
- path = sha1_pack_name(sha1);
-
- p = xmalloc(sizeof(*p) + strlen(path) + 2);
strcpy(p->pack_name, path);
- p->index_size = idx_size;
p->pack_size = 0;
- p->index_base = idx_map;
p->next = NULL;
p->windows = NULL;
p->pack_fd = -1;
n = size;
memcpy(buf, (char *) buffer + bytes, n);
bytes = n;
- if (bytes < size) {
+ if (bytes <= size) {
+ /*
+ * The above condition must be (bytes <= size), not
+ * (bytes < size). In other words, even though we
+ * expect no more output and set avail_out to zer0,
+ * the input zlib stream may have bytes that express
+ * "this concludes the stream", and we *do* want to
+ * eat that input.
+ *
+ * Otherwise we would not be able to test that we
+ * consumed all the input to reach the expected size;
+ * we also want to check that zlib tells us that all
+ * went well with status == Z_STREAM_END at the end.
+ */
stream->next_out = buf + bytes;
stream->avail_out = size - bytes;
while (status == Z_OK)
status = inflate(stream, Z_FINISH);
}
buf[size] = 0;
- if ((status == Z_OK || status == Z_STREAM_END) && !stream->avail_in) {
+ if (status == Z_STREAM_END && !stream->avail_in) {
inflateEnd(stream);
return buf;
}
return buffer;
}
+#define MAX_DELTA_CACHE (256)
+
+static size_t delta_base_cached;
+
+static struct delta_base_cache_lru_list {
+ struct delta_base_cache_lru_list *prev;
+ struct delta_base_cache_lru_list *next;
+} delta_base_cache_lru = { &delta_base_cache_lru, &delta_base_cache_lru };
+
+static struct delta_base_cache_entry {
+ struct delta_base_cache_lru_list lru;
+ void *data;
+ struct packed_git *p;
+ off_t base_offset;
+ unsigned long size;
+ enum object_type type;
+} delta_base_cache[MAX_DELTA_CACHE];
+
+static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
+{
+ unsigned long hash;
+
+ hash = (unsigned long)p + (unsigned long)base_offset;
+ hash += (hash >> 8) + (hash >> 16);
+ return hash % MAX_DELTA_CACHE;
+}
+
+static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
+ unsigned long *base_size, enum object_type *type, int keep_cache)
+{
+ void *ret;
+ unsigned long hash = pack_entry_hash(p, base_offset);
+ struct delta_base_cache_entry *ent = delta_base_cache + hash;
+
+ ret = ent->data;
+ if (ret && ent->p == p && ent->base_offset == base_offset)
+ goto found_cache_entry;
+ return unpack_entry(p, base_offset, type, base_size);
+
+found_cache_entry:
+ if (!keep_cache) {
+ ent->data = NULL;
+ ent->lru.next->prev = ent->lru.prev;
+ ent->lru.prev->next = ent->lru.next;
+ delta_base_cached -= ent->size;
+ }
+ else {
+ ret = xmalloc(ent->size + 1);
+ memcpy(ret, ent->data, ent->size);
+ ((char *)ret)[ent->size] = 0;
+ }
+ *type = ent->type;
+ *base_size = ent->size;
+ return ret;
+}
+
+static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
+{
+ if (ent->data) {
+ free(ent->data);
+ ent->data = NULL;
+ ent->lru.next->prev = ent->lru.prev;
+ ent->lru.prev->next = ent->lru.next;
+ delta_base_cached -= ent->size;
+ }
+}
+
+static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
+ void *base, unsigned long base_size, enum object_type type)
+{
+ unsigned long hash = pack_entry_hash(p, base_offset);
+ struct delta_base_cache_entry *ent = delta_base_cache + hash;
+ struct delta_base_cache_lru_list *lru;
+
+ release_delta_base_cache(ent);
+ delta_base_cached += base_size;
+
+ for (lru = delta_base_cache_lru.next;
+ delta_base_cached > delta_base_cache_limit
+ && lru != &delta_base_cache_lru;
+ lru = lru->next) {
+ struct delta_base_cache_entry *f = (void *)lru;
+ if (f->type == OBJ_BLOB)
+ release_delta_base_cache(f);
+ }
+ for (lru = delta_base_cache_lru.next;
+ delta_base_cached > delta_base_cache_limit
+ && lru != &delta_base_cache_lru;
+ lru = lru->next) {
+ struct delta_base_cache_entry *f = (void *)lru;
+ release_delta_base_cache(f);
+ }
+
+ ent->p = p;
+ ent->base_offset = base_offset;
+ ent->type = type;
+ ent->data = base;
+ ent->size = base_size;
+ ent->lru.next = &delta_base_cache_lru;
+ ent->lru.prev = delta_base_cache_lru.prev;
+ delta_base_cache_lru.prev->next = &ent->lru;
+ delta_base_cache_lru.prev = &ent->lru;
+}
+
static void *unpack_delta_entry(struct packed_git *p,
struct pack_window **w_curs,
off_t curpos,
off_t base_offset;
base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
- base = unpack_entry(p, base_offset, type, &base_size);
+ base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0);
if (!base)
die("failed to read delta base object"
" at %"PRIuMAX" from %s",
if (!result)
die("failed to apply delta");
free(delta_data);
- free(base);
+ add_delta_base_cache(p, base_offset, base, base_size, *type);
return result;
}
int nth_packed_object_sha1(const struct packed_git *p, uint32_t n,
unsigned char* sha1)
{
- void *index = p->index_base + 256;
+ const unsigned char *index = p->index_data;
+ index += 4 * 256;
if (num_packed_objects(p) <= n)
return -1;
- hashcpy(sha1, (unsigned char *) index + (24 * n) + 4);
+ hashcpy(sha1, index + 24 * n + 4);
return 0;
}
off_t find_pack_entry_one(const unsigned char *sha1,
struct packed_git *p)
{
- uint32_t *level1_ofs = p->index_base;
+ const uint32_t *level1_ofs = p->index_data;
int hi = ntohl(level1_ofs[*sha1]);
int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
- void *index = p->index_base + 256;
+ const unsigned char *index = p->index_data;
+
+ index += 4 * 256;
do {
int mi = (lo + hi) / 2;
- int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
+ int cmp = hashcmp(index + 24 * mi + 4, sha1);
if (!cmp)
return ntohl(*((uint32_t *)((char *)index + (24 * mi))));
if (cmp > 0)
if (!find_pack_entry(sha1, &e, NULL))
return NULL;
else
- return unpack_entry(e.p, e.offset, type, size);
+ return cache_or_unpack_entry(e.p, e.offset, size, type, 1);
}
/*
}
}
-static void write_sha1_file_prepare(void *buf, unsigned long len,
+static void write_sha1_file_prepare(const void *buf, unsigned long len,
const char *type, unsigned char *sha1,
char *hdr, int *hdrlen)
{
stream->avail_out -= hdrlen;
}
-int hash_sha1_file(void *buf, unsigned long len, const char *type,
+int hash_sha1_file(const void *buf, unsigned long len, const char *type,
unsigned char *sha1)
{
char hdr[32];
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
- int size;
+ int size, ret;
unsigned char *compressed;
z_stream stream;
unsigned char sha1[20];
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) {
/* Then the data itself.. */
stream.next_in = buf;
stream.avail_in = len;
- while (deflate(&stream, Z_FINISH) == Z_OK)
- /* nothing */;
- deflateEnd(&stream);
+ ret = deflate(&stream, Z_FINISH);
+ if (ret != Z_STREAM_END)
+ die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
+
+ ret = deflateEnd(&stream);
+ if (ret != Z_OK)
+ die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
+
size = stream.total_out;
if (write_buffer(fd, compressed, size) < 0)
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);
+ fchmod(local, 0444);
close(local);
SHA1_Final(real_sha1, &c);
if (ret != Z_STREAM_END) {
git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
(git-show-ref -q refs/remotes/local/master || git-fetch local) &&
git-branch --no-track my2 local/master &&
+ git-config branch.autosetupmerge false &&
! test $(git-config branch.my2.remote) = local &&
! test $(git-config branch.my2.merge) = refs/heads/master'
+test_expect_success 'test local tracking setup' \
+ 'git branch --track my6 s &&
+ test $(git-config branch.my6.remote) = . &&
+ test $(git-config branch.my6.merge) = refs/heads/s'
+
# Keep this test last, as it changes the current branch
cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
--- /dev/null
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo 1 >a &&
+ git add . &&
+ git commit -m first &&
+ echo 2 >b &&
+ git add . &&
+ git commit -a -m second
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+ git diff-tree --exit-code HEAD^ HEAD
+ test $? = 1
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+ git diff-tree --exit-code HEAD^ HEAD -- a
+ test $? = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+ git diff-tree --exit-code HEAD^ HEAD -- b
+ test $? = 1
+'
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+ echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
+ test $? = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+ git diff-tree --exit-code HEAD HEAD
+ test $? = 0
+'
+test_expect_success 'git diff-files' '
+ git diff-files --exit-code
+ test $? = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+ git diff-index --exit-code --cached HEAD
+ test $? = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+ git diff-index --exit-code --cached HEAD^
+ test $? = 1
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+ echo text >>b &&
+ echo 3 >c &&
+ git add . && {
+ git diff-index --exit-code --cached HEAD^
+ test $? = 1
+ }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+ git commit -m "text in b" && {
+ git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
+ test $? = 1
+ }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+ git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b
+ test $? = 0
+'
+test_expect_success 'git diff-files' '
+ echo 3 >>c && {
+ git diff-files --exit-code
+ test $? = 1
+ }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+ git update-index c && {
+ git diff-index --exit-code --cached HEAD
+ test $? = 1
+ }
+'
+
+test_done
--- /dev/null
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo 1 >a &&
+ git add . &&
+ git commit -m first &&
+ echo 2 >b &&
+ git add . &&
+ git commit -a -m second
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+ git diff-tree --quiet HEAD^ HEAD >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+ git diff-tree --quiet HEAD^ HEAD -- a >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+ git diff-tree --quiet HEAD^ HEAD -- b >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+'
+# this diff outputs one line: sha1 of the given head
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+ echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
+ test $? = 1 && test $(wc -l <cnt) = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+ git diff-tree --quiet HEAD HEAD >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+ git diff-files --quiet >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+ git diff-index --quiet --cached HEAD >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+ git diff-index --quiet --cached HEAD^ >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+ echo text >>b &&
+ echo 3 >c &&
+ git add . && {
+ git diff-index --quiet --cached HEAD^ >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+ }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+ git commit -m "text in b" && {
+ git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+ }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+ git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+ echo 3 >>c && {
+ git diff-files --quiet >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+ }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+ git update-index c && {
+ git diff-index --quiet --cached HEAD >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+ }
+'
+
+test_done
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 |
cd "$TRASH"
test_expect_success \
- 'pack with delta' \
+ 'pack with REF_DELTA' \
'pwd &&
packname_2=$(git-pack-objects test-2 <obj-list)'
mkdir .git2
test_expect_success \
- 'unpack with delta' \
+ 'unpack with REF_DELTA' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
git-init &&
unset GIT_OBJECT_DIRECTORY
cd "$TRASH/.git2"
test_expect_success \
- 'check unpack with delta' \
+ 'check unpack with REF_DELTA' \
'(cd ../.git && find objects -type f -print) |
while read path
do
done'
cd "$TRASH"
+test_expect_success \
+ 'pack with OFS_DELTA' \
+ 'pwd &&
+ packname_3=$(git-pack-objects --delta-base-offset test-3 <obj-list)'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+ 'unpack with OFS_DELTA' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ git-init &&
+ git-unpack-objects -n <test-3-${packname_3}.pack &&
+ git-unpack-objects <test-3-${packname_3}.pack'
+
+unset GIT_OBJECT_DIRECTORY
+cd "$TRASH/.git2"
+test_expect_success \
+ 'check unpack with OFS_DELTA' \
+ '(cd ../.git && find objects -type f -print) |
+ while read path
+ do
+ cmp $path ../.git/$path || {
+ echo $path differs.
+ return 1
+ }
+ 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'
+
rm -fr .git2
mkdir .git2
} >current &&
diff expect current'
-
test_expect_success \
- 'use packed deltified objects' \
+ 'use packed deltified (REF_DELTA) objects' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- rm -f .git2/objects/pack/test-?.idx &&
+ rm .git2/objects/pack/test-* &&
cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
git-diff-tree --root -p $commit &&
while read object
} >current &&
diff expect current'
+test_expect_success \
+ 'use packed deltified (OFS_DELTA) objects' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ rm .git2/objects/pack/test-* &&
+ cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && {
+ git-diff-tree --root -p $commit &&
+ while read object
+ do
+ t=`git-cat-file -t $object` &&
+ git-cat-file $t $object || return 1
+ done <obj-list
+ } >current &&
+ diff expect current'
+
unset GIT_OBJECT_DIRECTORY
test_expect_success \
'verify pack' \
- 'git-verify-pack test-1-${packname_1}.idx test-2-${packname_2}.idx'
+ 'git-verify-pack test-1-${packname_1}.idx \
+ test-2-${packname_2}.idx \
+ test-3-${packname_3}.idx'
test_expect_success \
'corrupt a pack and see if verify catches' \
git-index-pack test-3.pack &&
cmp test-3.idx test-2-${packname_2}.idx &&
+ cp test-3-${packname_3}.pack test-3.pack &&
+ git-index-pack -o tmp.idx test-3-${packname_3}.pack &&
+ cmp tmp.idx test-3-${packname_3}.idx &&
+
+ git-index-pack test-3.pack &&
+ cmp test-3.idx test-3-${packname_3}.idx &&
+
:'
+test_expect_success \
+ 'fake a SHA1 hash collision' \
+ 'test -f .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67 &&
+ cp -f .git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
+ .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
+
+test_expect_failure \
+ 'make sure index-pack detects the SHA1 collision' \
+ 'git-index-pack -o bad.idx test-3.pack'
+
test_done
diff file cloned/file
'
+test_expect_success 'test . as a remote' '
+
+ git branch copy master &&
+ git config branch.copy.remote . &&
+ git config branch.copy.merge refs/heads/master &&
+ echo updated >file &&
+ git commit -a -m updated &&
+ git checkout copy &&
+ test `cat file` = file &&
+ git pull &&
+ test `cat file` = updated
+'
+
+test_expect_success 'the default remote . should not break explicit pull' '
+ git checkout -b second master^ &&
+ echo modified >file &&
+ git commit -a -m modified &&
+ git checkout copy &&
+ git reset --hard HEAD^ &&
+ test `cat file` = file &&
+ git pull . second &&
+ test `cat file` = modified
+'
+
test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Christian Couder
+#
+test_description='Tests git-bisect run functionality'
+
+. ./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)'
+
+# 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'
+
+#
+#
+test_done
+
#!/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
+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)
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
#include "diff.h"
#include "tree.h"
-static char *malloc_base(const char *base, const char *path, int pathlen)
+static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
{
- int baselen = strlen(base);
char *newbase = xmalloc(baselen + pathlen + 2);
memcpy(newbase, base, baselen);
memcpy(newbase + baselen, path, pathlen);
}
static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
- const char *base);
+ const char *base, int baselen);
-static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt)
{
unsigned mode1, mode2;
const char *path1, *path2;
sha1 = tree_entry_extract(t1, &path1, &mode1);
sha2 = tree_entry_extract(t2, &path2, &mode2);
- pathlen1 = strlen(path1);
- pathlen2 = strlen(path2);
+ pathlen1 = tree_entry_len(path1, sha1);
+ pathlen2 = tree_entry_len(path2, sha2);
cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
if (cmp < 0) {
- show_entry(opt, "-", t1, base);
+ show_entry(opt, "-", t1, base, baselen);
return -1;
}
if (cmp > 0) {
- show_entry(opt, "+", t2, base);
+ show_entry(opt, "+", t2, base, baselen);
return 1;
}
if (!opt->find_copies_harder && !hashcmp(sha1, sha2) && mode1 == mode2)
* file, we need to consider it a remove and an add.
*/
if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
- show_entry(opt, "-", t1, base);
- show_entry(opt, "+", t2, base);
+ show_entry(opt, "-", t1, base, baselen);
+ show_entry(opt, "+", t2, base, baselen);
return 0;
}
if (opt->recursive && S_ISDIR(mode1)) {
int retval;
- char *newbase = malloc_base(base, path1, pathlen1);
+ char *newbase = malloc_base(base, baselen, path1, pathlen1);
if (opt->tree_in_recursive)
opt->change(opt, mode1, mode2,
sha1, sha2, base, path1);
return 0;
}
-static int interesting(struct tree_desc *desc, const char *base, struct diff_options *opt)
+/*
+ * Is a tree entry interesting given the pathspec we have?
+ *
+ * Return:
+ * - 2 for "yes, and all subsequent entries will be"
+ * - 1 for yes
+ * - zero for no
+ * - negative for "no, and no subsequent entries will be either"
+ */
+static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt)
{
const char *path;
+ const unsigned char *sha1;
unsigned mode;
int i;
- int baselen, pathlen;
+ int pathlen;
+ int never_interesting = -1;
if (!opt->nr_paths)
return 1;
- (void)tree_entry_extract(desc, &path, &mode);
+ sha1 = tree_entry_extract(desc, &path, &mode);
- pathlen = strlen(path);
- baselen = strlen(base);
+ pathlen = tree_entry_len(path, sha1);
- for (i=0; i < opt->nr_paths; i++) {
+ for (i = 0; i < opt->nr_paths; i++) {
const char *match = opt->paths[i];
int matchlen = opt->pathlens[i];
+ int m = -1; /* signals that we haven't called strncmp() */
if (baselen >= matchlen) {
/* If it doesn't match, move along... */
if (strncmp(base, match, matchlen))
continue;
- /* The base is a subdirectory of a path which was specified. */
- return 1;
+ /*
+ * The base is a subdirectory of a path which
+ * was specified, so all of them are interesting.
+ */
+ return 2;
}
/* Does the base match? */
match += baselen;
matchlen -= baselen;
+ if (never_interesting) {
+ /*
+ * We have not seen any match that sorts later
+ * than the current path.
+ */
+
+ /*
+ * Does match sort strictly earlier than path
+ * with their common parts?
+ */
+ m = strncmp(match, path,
+ (matchlen < pathlen) ? matchlen : pathlen);
+ if (m < 0)
+ continue;
+
+ /*
+ * If we come here even once, that means there is at
+ * least one pathspec that would sort equal to or
+ * later than the path we are currently looking at.
+ * In other words, if we have never reached this point
+ * after iterating all pathspecs, it means all
+ * pathspecs are either outside of base, or inside the
+ * base but sorts strictly earlier than the current
+ * one. In either case, they will never match the
+ * subsequent entries. In such a case, we initialized
+ * the variable to -1 and that is what will be
+ * returned, allowing the caller to terminate early.
+ */
+ never_interesting = 0;
+ }
+
if (pathlen > matchlen)
continue;
continue;
}
- if (strncmp(path, match, pathlen))
- continue;
-
- return 1;
+ if (m == -1)
+ /*
+ * we cheated and did not do strncmp(), so we do
+ * that here.
+ */
+ m = strncmp(match, path, pathlen);
+
+ /*
+ * If common part matched earlier then it is a hit,
+ * because we rejected the case where path is not a
+ * leading directory and is shorter than match.
+ */
+ if (!m)
+ return 1;
}
- return 0; /* No matches */
+ return never_interesting; /* No matches */
}
/* A whole sub-tree went away or appeared */
-static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)
+static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
{
+ int all_interesting = 0;
while (desc->size) {
- if (interesting(desc, base, opt))
- show_entry(opt, prefix, desc, base);
+ int show;
+
+ if (all_interesting)
+ show = 1;
+ else {
+ show = tree_entry_interesting(desc, base, baselen,
+ opt);
+ if (show == 2)
+ all_interesting = 1;
+ }
+ if (show < 0)
+ break;
+ if (show)
+ show_entry(opt, prefix, desc, base, baselen);
update_tree_entry(desc);
}
}
/* A file entry went away or appeared */
static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
- const char *base)
+ const char *base, int baselen)
{
unsigned mode;
const char *path;
if (opt->recursive && S_ISDIR(mode)) {
enum object_type type;
- char *newbase = malloc_base(base, path, strlen(path));
+ int pathlen = tree_entry_len(path, sha1);
+ char *newbase = malloc_base(base, baselen, path, pathlen);
struct tree_desc inner;
void *tree;
+ unsigned long size;
- tree = read_sha1_file(sha1, &type, &inner.size);
+ tree = read_sha1_file(sha1, &type, &size);
if (!tree || type != OBJ_TREE)
die("corrupt tree sha %s", sha1_to_hex(sha1));
- inner.buf = tree;
- show_tree(opt, prefix, &inner, newbase);
+ init_tree_desc(&inner, tree, size);
+ show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
free(tree);
free(newbase);
}
}
-int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
{
- while (t1->size | t2->size) {
- if (opt->nr_paths && t1->size && !interesting(t1, base, opt)) {
- update_tree_entry(t1);
- continue;
+ int all_interesting = 0;
+ while (t->size) {
+ int show;
+
+ if (all_interesting)
+ show = 1;
+ else {
+ show = tree_entry_interesting(t, base, baselen, opt);
+ if (show == 2)
+ all_interesting = 1;
}
- if (opt->nr_paths && t2->size && !interesting(t2, base, opt)) {
- update_tree_entry(t2);
+ if (!show) {
+ update_tree_entry(t);
continue;
}
+ /* Skip it all? */
+ if (show < 0)
+ t->size = 0;
+ return;
+ }
+}
+
+int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+{
+ int baselen = strlen(base);
+
+ for (;;) {
+ if (opt->quiet && opt->has_changes)
+ break;
+ if (opt->nr_paths) {
+ skip_uninteresting(t1, base, baselen, opt);
+ skip_uninteresting(t2, base, baselen, opt);
+ }
if (!t1->size) {
- show_entry(opt, "+", t2, base);
+ if (!t2->size)
+ break;
+ show_entry(opt, "+", t2, base, baselen);
update_tree_entry(t2);
continue;
}
if (!t2->size) {
- show_entry(opt, "-", t1, base);
+ show_entry(opt, "-", t1, base, baselen);
update_tree_entry(t1);
continue;
}
- switch (compare_tree_entry(t1, t2, base, opt)) {
+ switch (compare_tree_entry(t1, t2, base, baselen, opt)) {
case -1:
update_tree_entry(t1);
continue;
{
void *tree1, *tree2;
struct tree_desc t1, t2;
+ unsigned long size1, size2;
int retval;
- tree1 = read_object_with_reference(old, tree_type, &t1.size, NULL);
+ tree1 = read_object_with_reference(old, tree_type, &size1, NULL);
if (!tree1)
die("unable to read source tree (%s)", sha1_to_hex(old));
- tree2 = read_object_with_reference(new, tree_type, &t2.size, NULL);
+ tree2 = read_object_with_reference(new, tree_type, &size2, NULL);
if (!tree2)
die("unable to read destination tree (%s)", sha1_to_hex(new));
- t1.buf = tree1;
- t2.buf = tree2;
+ init_tree_desc(&t1, tree1, size1);
+ init_tree_desc(&t2, tree2, size2);
retval = diff_tree(&t1, &t2, base, opt);
free(tree1);
free(tree2);
{
int retval;
void *tree;
+ unsigned long size;
struct tree_desc empty, real;
- tree = read_object_with_reference(new, tree_type, &real.size, NULL);
+ tree = read_object_with_reference(new, tree_type, &size, NULL);
if (!tree)
die("unable to read root tree (%s)", sha1_to_hex(new));
- real.buf = tree;
+ init_tree_desc(&real, tree, size);
- empty.size = 0;
- empty.buf = "";
+ init_tree_desc(&empty, "", 0);
retval = diff_tree(&empty, &real, base, opt);
free(tree);
return retval;
#include "tree-walk.h"
#include "tree.h"
+static const char *get_mode(const char *str, unsigned int *modep)
+{
+ unsigned char c;
+ unsigned int mode = 0;
+
+ while ((c = *str++) != ' ') {
+ if (c < '0' || c > '7')
+ return NULL;
+ mode = (mode << 3) + (c - '0');
+ }
+ *modep = mode;
+ return str;
+}
+
+static void decode_tree_entry(struct tree_desc *desc, const void *buf, unsigned long size)
+{
+ const char *path;
+ unsigned int mode, len;
+
+ path = get_mode(buf, &mode);
+ if (!path)
+ die("corrupt tree file");
+ len = strlen(path) + 1;
+
+ /* Initialize the descriptor entry */
+ desc->entry.path = path;
+ desc->entry.mode = mode;
+ desc->entry.sha1 = (const unsigned char *)(path + len);
+}
+
+void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+{
+ desc->buffer = buffer;
+ desc->size = size;
+ if (size)
+ decode_tree_entry(desc, buffer, size);
+}
+
void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
{
unsigned long size = 0;
if (!buf)
die("unable to read tree %s", sha1_to_hex(sha1));
}
- desc->size = size;
- desc->buf = buf;
+ init_tree_desc(desc, buf, size);
return buf;
}
static int entry_compare(struct name_entry *a, struct name_entry *b)
{
return base_name_compare(
- a->path, a->pathlen, a->mode,
- b->path, b->pathlen, b->mode);
+ a->path, tree_entry_len(a->path, a->sha1), a->mode,
+ b->path, tree_entry_len(b->path, b->sha1), b->mode);
}
static void entry_clear(struct name_entry *a)
static void entry_extract(struct tree_desc *t, struct name_entry *a)
{
- a->sha1 = tree_entry_extract(t, &a->path, &a->mode);
- a->pathlen = strlen(a->path);
+ *a = t->entry;
}
void update_tree_entry(struct tree_desc *desc)
{
- const void *buf = desc->buf;
+ const void *buf = desc->buffer;
+ const unsigned char *end = desc->entry.sha1 + 20;
unsigned long size = desc->size;
- int len = strlen(buf) + 1 + 20;
+ unsigned long len = end - (const unsigned char *)buf;
if (size < len)
die("corrupt tree file");
- desc->buf = (char *) buf + len;
- desc->size = size - len;
-}
-
-static const char *get_mode(const char *str, unsigned int *modep)
-{
- unsigned char c;
- unsigned int mode = 0;
-
- while ((c = *str++) != ' ') {
- if (c < '0' || c > '7')
- return NULL;
- mode = (mode << 3) + (c - '0');
- }
- *modep = mode;
- return str;
-}
-
-const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
-{
- const void *tree = desc->buf;
- unsigned long size = desc->size;
- int len = strlen(tree)+1;
- const unsigned char *sha1 = (unsigned char *) tree + len;
- const char *path;
- unsigned int mode;
-
- path = get_mode(tree, &mode);
- if (!path || size < len + 20)
- die("corrupt tree file");
- *pathp = path;
- *modep = canon_mode(mode);
- return sha1;
+ buf = end;
+ size -= len;
+ desc->buffer = buf;
+ desc->size = size;
+ if (size)
+ decode_tree_entry(desc, buf, size);
}
int tree_entry(struct tree_desc *desc, struct name_entry *entry)
{
- const void *tree = desc->buf;
- const char *path;
- unsigned long len, size = desc->size;
-
- if (!size)
+ if (!desc->size)
return 0;
- path = get_mode(tree, &entry->mode);
- if (!path)
- die("corrupt tree file");
-
- entry->path = path;
- len = strlen(path);
- entry->pathlen = len;
-
- path += len + 1;
- entry->sha1 = (const unsigned char *) path;
-
- path += 20;
- len = path - (char *) tree;
- if (len > size)
- die("corrupt tree file");
-
- desc->buf = path;
- desc->size = size - len;
+ *entry = desc->entry;
+ update_tree_entry(desc);
return 1;
}
sha1 = tree_entry_extract(t, &entry, mode);
update_tree_entry(t);
- entrylen = strlen(entry);
+ entrylen = tree_entry_len(entry, sha1);
if (entrylen > namelen)
continue;
cmp = memcmp(name, entry, entrylen);
{
int retval;
void *tree;
+ unsigned long size;
struct tree_desc t;
unsigned char root[20];
- tree = read_object_with_reference(tree_sha1, tree_type, &t.size, root);
+ tree = read_object_with_reference(tree_sha1, tree_type, &size, root);
if (!tree)
return -1;
return 0;
}
- t.buf = tree;
+ init_tree_desc(&t, tree, size);
retval = find_tree_entry(&t, name, sha1, mode);
free(tree);
return retval;
#ifndef TREE_WALK_H
#define TREE_WALK_H
-struct tree_desc {
- const void *buf;
- unsigned long size;
-};
-
struct name_entry {
const unsigned char *sha1;
const char *path;
unsigned int mode;
- int pathlen;
};
+struct tree_desc {
+ const void *buffer;
+ struct name_entry entry;
+ unsigned int size;
+};
+
+static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
+{
+ *pathp = desc->entry.path;
+ *modep = canon_mode(desc->entry.mode);
+ return desc->entry.sha1;
+}
+
+static inline int tree_entry_len(const char *name, const unsigned char *sha1)
+{
+ return (char *)sha1 - (char *)name - 1;
+}
+
void update_tree_entry(struct tree_desc *);
+void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
const unsigned char *tree_entry_extract(struct tree_desc *, const char **, unsigned int *);
/* Helper function that does both of the above and returns true for success */
if (parse_tree(tree))
return -1;
- desc.buf = tree->buffer;
- desc.size = tree->size;
+ init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (!match_tree_entry(base, baselen, entry.path, entry.mode, match))
if (S_ISDIR(entry.mode)) {
int retval;
char *newbase;
+ unsigned int pathlen = tree_entry_len(entry.path, entry.sha1);
- newbase = xmalloc(baselen + 1 + entry.pathlen);
+ newbase = xmalloc(baselen + 1 + pathlen);
memcpy(newbase, base, baselen);
- memcpy(newbase + baselen, entry.path, entry.pathlen);
- newbase[baselen + entry.pathlen] = '/';
+ memcpy(newbase + baselen, entry.path, pathlen);
+ newbase[baselen + pathlen] = '/';
retval = read_tree_recursive(lookup_tree(entry.sha1),
newbase,
- baselen + entry.pathlen + 1,
+ baselen + pathlen + 1,
stage, match, fn);
free(newbase);
if (retval)
struct name_entry entry;
/* Count how many entries there are.. */
- desc.buf = item->buffer;
- desc.size = item->size;
- while (desc.size) {
+ init_tree_desc(&desc, item->buffer, item->size);
+ while (tree_entry(&desc, &entry))
n_refs++;
- update_tree_entry(&desc);
- }
/* Allocate object refs and walk it again.. */
i = 0;
refs = alloc_object_refs(n_refs);
- desc.buf = item->buffer;
- desc.size = item->size;
+ init_tree_desc(&desc, item->buffer, item->size);
while (tree_entry(&desc, &entry)) {
struct object *obj;
if (!tree->object.parsed)
parse_tree(tree);
- desc.buf = tree->buffer;
- desc.size = tree->size;
+ init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &one)) {
struct tree_entry_list *entry;
return 0;
}
-unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
+static unsigned long xdl_hash_record_with_whitespace(char const **data,
+ char const *top, long flags) {
unsigned long ha = 5381;
char const *ptr = *data;
for (; ptr < top && *ptr != '\n'; ptr++) {
- if (isspace(*ptr) && (flags & XDF_WHITESPACE_FLAGS)) {
+ if (isspace(*ptr)) {
const char *ptr2 = ptr;
while (ptr + 1 < top && isspace(ptr[1])
&& ptr[1] != '\n')
}
+unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
+ unsigned long ha = 5381;
+ char const *ptr = *data;
+
+ if (flags & XDF_WHITESPACE_FLAGS)
+ return xdl_hash_record_with_whitespace(data, top, flags);
+
+ for (; ptr < top && *ptr != '\n'; ptr++) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr;
+ }
+ *data = ptr < top ? ptr + 1: ptr;
+
+ return ha;
+}
+
+
unsigned int xdl_hashbits(unsigned int size) {
unsigned int val = 1, bits = 0;