mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit 'mingw/master' into msysgit/merge-mingw-v2
Conflicts:
Makefile
RelNotes
builtin-ls-files.c
builtin-tag.c
cache.h
compat/mingw.c
config.c
connect.c
cpio.sh
diff.c
exec_cmd.c
git-gui/Makefile
git-gui/lib/commit.tcl
git-gui/lib/console.tcl
git-mergetool.sh
lockfile.c
path.c
rsh.c
run-command.c
setup.c
show-index.c
spawn-pipe.c
t/Makefile
t/t0000-basic.sh
t/t1300-repo-config.sh
t/t7501-commit.sh
t/test-lib.sh
Resolve as follows
--- Makefile
- mingw/devel removes
SHELL_PATH = /bin/sh
PERL_PATH = /bin/perl
This looks ok. Both are set early in the Makefile to sensible values.
mingw accepts to execute /usr/bin/perl.
- NO_SYMLINKS is no longer needed. Should be auto-detected.
- According to our 0e2bdc35af
we want
NO_R_TO_GCC_LINKER = YesPlease
take our before their change.
- Conflict prefix, SCRIPT_SH:
our 7999f434d7 set prefix =
their 4a7c98dbaf removes cpio emulator
resolve to achieve both.
- Conflict NO_MEMMEM, THREADED_DELTA_SEARCH: take theirs
--- RelNotes
take our: removed file
--- builtin-ls-files.c
Conflict write_name_quoted: take their change.
--- builtin-tag.c
Conflict strip CR
our 7734ad404c adds strip CR
their fd17f5b5f7 modifies code to use strbuf
resolve by removing our code. TODO: we probably need a replacement?
--- cache.h
Conflict is_absolute_path()
our ef5af72062 ifdef
their 637fc51696 ifndef
both achieve the same.
our is a bit more strict but we take their code because we want
to reduce differences to mingw.
--- compat/mingw.c
Conflict at end of file:
our 194c1dbb5a adds git_exit()
resolve by taking their first, followed by our.
--- config.c
Conflict 'fd ='
our 0a453a237e merge junio/master
introduced strange 'fd ='. Resolve by removing 'fd ='.
--- connect.c
- Conflict 'host must have at least 2 chars ...' take their code.
- git_connect(): take their implementation.
--- cpio.sh
Accepted their delete file.
--- diff.c
Resolve using their implementation.
--- exec_cmd.c
Resolve using their implementation.
--- git-gui/**
Resolve using our implementation.
--- git-mergetool.sh
Resolve using their implementation
--- lockfile.c
trivial resolution (empty line removed)
--- path.c
Conflict 'tmp': accepting their implementation, trying TMP, TEMP on all platforms.
--- rsh.c
Accept their delete file.
--- run-command.c
Resolve using their implementation
--- setup.c
Resolve using their implementation
--- show-index.c
Conflict PRIuMAX
our 89697a4c15 fix warning
their 5be507fc95 PRIuMAX
resolve fixing warning in their code.
--- spawn-pipe.c
Conflict environ vs lookup_prog: resolve taking neither
--- t/Makefile
our d1f83218dc --no-hardlinks
their c603988c10 automtically detect symlink support
Resolve using our --no-hardlinks but removing --no-symlinks.
--- t/0000-basic.sh
Resolve using their implementation.
--- t/t1300-repo-config.sh
Resolve using their implementation.
--- t/t7501-commit.sh
Resolve using their implementation.
--- t/test-lib.sh
our d1f83218dc --no-hardlink
their c603988c10 automatically detect symlink support
Resolve using our --no-hardlinks but removing --no-symlinks.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -25,7 +25,6 @@ git-clone
|
||||
git-commit
|
||||
git-commit-tree
|
||||
git-config
|
||||
git-convert-objects
|
||||
git-count-objects
|
||||
git-cvsexportcommit
|
||||
git-cvsimport
|
||||
@@ -129,7 +128,6 @@ git-status
|
||||
git-stripspace
|
||||
git-submodule
|
||||
git-svn
|
||||
git-svnimport
|
||||
git-symbolic-ref
|
||||
git-tag
|
||||
git-tar-tree
|
||||
@@ -172,4 +170,6 @@ config.status
|
||||
config.mak.autogen
|
||||
config.mak.append
|
||||
configure
|
||||
cpio
|
||||
tags
|
||||
TAGS
|
||||
cscope*
|
||||
|
||||
1
.mailmap
1
.mailmap
@@ -37,6 +37,7 @@ Sam Vilain <sam@vilain.net>
|
||||
Santi Béjar <sbejar@gmail.com>
|
||||
Sean Estabrooks <seanlkml@sympatico.ca>
|
||||
Shawn O. Pearce <spearce@spearce.org>
|
||||
Steven Grimm <koreth@midwinter.com>
|
||||
Theodore Ts'o <tytso@mit.edu>
|
||||
Tony Luck <tony.luck@intel.com>
|
||||
Uwe Kleine-König <Uwe_Zeisberger@digi.com>
|
||||
|
||||
@@ -123,7 +123,7 @@ cmd-list.made: cmd-list.perl $(MAN1_TXT)
|
||||
perl ./cmd-list.perl
|
||||
date >$@
|
||||
|
||||
git.7 git.html: git.txt core-intro.txt
|
||||
git.7 git.html: git.txt
|
||||
|
||||
clean:
|
||||
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 *.texi *.texi+ howto-index.txt howto/*.html doc.dep
|
||||
|
||||
10
Documentation/RelNotes-1.5.3.1.txt
Normal file
10
Documentation/RelNotes-1.5.3.1.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
GIT v1.5.3.1 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.3
|
||||
------------------
|
||||
|
||||
This is solely to fix the generated RPM's dependencies. We used
|
||||
to have git-p4 package but we do not anymore. As suggested on
|
||||
the mailing list, this release makes git-core "Obsolete" git-p4,
|
||||
so that yum update would not complain.
|
||||
58
Documentation/RelNotes-1.5.3.2.txt
Normal file
58
Documentation/RelNotes-1.5.3.2.txt
Normal file
@@ -0,0 +1,58 @@
|
||||
GIT v1.5.3.2 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.3.1
|
||||
--------------------
|
||||
|
||||
* git-push sent thin packs by default, which was not good for
|
||||
the public distribution server (no point in saving transfer
|
||||
while pushing; no point in making the resulting pack less
|
||||
optimum).
|
||||
|
||||
* git-svn sometimes terminated with "Malformed network data" when
|
||||
talking over svn:// protocol.
|
||||
|
||||
* git-send-email re-issued the same message-id about 10% of the
|
||||
time if you fired off 30 messages within a single second.
|
||||
|
||||
* git-stash was not terminating the log message of commits it
|
||||
internally creates with LF.
|
||||
|
||||
* git-apply failed to check the size of the patch hunk when its
|
||||
beginning part matched the remainder of the preimage exactly,
|
||||
even though the preimage recorded in the hunk was much larger
|
||||
(therefore the patch should not have applied), leading to a
|
||||
segfault.
|
||||
|
||||
* "git rm foo && git commit foo" complained that 'foo' needs to
|
||||
be added first, instead of committing the removal, which was a
|
||||
nonsense.
|
||||
|
||||
* git grep -c said "/dev/null: 0".
|
||||
|
||||
* git-add -u failed to recognize a blob whose type changed
|
||||
between the index and the work tree.
|
||||
|
||||
* The limit to rename detection has been tightened a lot to
|
||||
reduce performance problems with a huge change.
|
||||
|
||||
* cvsimport and svnimport barfed when the input tried to move
|
||||
a tag.
|
||||
|
||||
* "git apply -pN" did not chop the right number of directories.
|
||||
|
||||
* "git svnimport" did not like SVN tags with funny characters in them.
|
||||
|
||||
* git-gui 0.8.3, with assorted fixes, including:
|
||||
|
||||
- font-chooser on X11 was unusable with large number of fonts;
|
||||
- a diff that contained a deleted symlink made it barf;
|
||||
- an untracked symbolic link to a directory made it fart;
|
||||
- a file with % in its name made it vomit;
|
||||
|
||||
|
||||
Documentation updates
|
||||
---------------------
|
||||
|
||||
User manual has been somewhat restructured. I think the new
|
||||
organization is much easier to read.
|
||||
31
Documentation/RelNotes-1.5.3.3.txt
Normal file
31
Documentation/RelNotes-1.5.3.3.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
GIT v1.5.3.3 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.3.2
|
||||
--------------------
|
||||
|
||||
* git-quiltimport did not like it when a patch described in the
|
||||
series file does not exist.
|
||||
|
||||
* p4 importer missed executable bit in some cases.
|
||||
|
||||
* The default shell on some FreeBSD did not execute the
|
||||
argument parsing code correctly and made git unusable.
|
||||
|
||||
* git-svn incorrectly spawned pager even when the user user
|
||||
explicitly asked not to.
|
||||
|
||||
* sample post-receive hook overquoted the envelope sender
|
||||
value.
|
||||
|
||||
* git-am got confused when the patch contained a change that is
|
||||
only about type and not contents.
|
||||
|
||||
* git-mergetool did not show our and their version of the
|
||||
conflicted file when started from a subdirectory of the
|
||||
project.
|
||||
|
||||
* git-mergetool did not pass correct options when invoking diff3.
|
||||
|
||||
* git-log sometimes invoked underlying "diff" machinery
|
||||
unnecessarily.
|
||||
35
Documentation/RelNotes-1.5.3.4.txt
Normal file
35
Documentation/RelNotes-1.5.3.4.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
GIT v1.5.3.4 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.3.3
|
||||
--------------------
|
||||
|
||||
* Change to "git-ls-files" in v1.5.3.3 that was introduced to support
|
||||
partial commit of removal better had a segfaulting bug, which was
|
||||
diagnosed and fixed by Keith and Carl.
|
||||
|
||||
* Performance improvements for rename detection has been backported
|
||||
from the 'master' branch.
|
||||
|
||||
* "git-for-each-ref --format='%(numparent)'" was not working
|
||||
correctly at all, and --format='%(parent)' was not working for
|
||||
merge commits.
|
||||
|
||||
* Sample "post-receive-hook" incorrectly sent out push
|
||||
notification e-mails marked as "From: " the committer of the
|
||||
commit that happened to be at the tip of the branch that was
|
||||
pushed, not from the person who pushed.
|
||||
|
||||
* "git-remote" did not exit non-zero status upon error.
|
||||
|
||||
* "git-add -i" did not respond very well to EOF from tty nor
|
||||
bogus input.
|
||||
|
||||
* "git-rebase -i" squash subcommand incorrectly made the
|
||||
author of later commit the author of resulting commit,
|
||||
instead of taking from the first one in the squashed series.
|
||||
|
||||
* "git-stash apply --index" was not documented.
|
||||
|
||||
* autoconfiguration learned that "ar" command is found as "gas" on
|
||||
some systems.
|
||||
94
Documentation/RelNotes-1.5.3.5.txt
Normal file
94
Documentation/RelNotes-1.5.3.5.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
GIT v1.5.3.5 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.3.4
|
||||
--------------------
|
||||
|
||||
* Comes with git-gui 0.8.4.
|
||||
|
||||
* "git-config" silently ignored options after --list; now it will
|
||||
error out with a usage message.
|
||||
|
||||
* "git-config --file" failed if the argument used a relative path
|
||||
as it changed directories before opening the file.
|
||||
|
||||
* "git-config --file" now displays a proper error message if it
|
||||
cannot read the file specified on the command line.
|
||||
|
||||
* "git-config", "git-diff", "git-apply" failed if run from a
|
||||
subdirectory with relative GIT_DIR and GIT_WORK_TREE set.
|
||||
|
||||
* "git-blame" crashed if run during a merge conflict.
|
||||
|
||||
* "git-add -i" did not handle single line hunks correctly.
|
||||
|
||||
* "git-rebase -i" and "git-stash apply" failed if external diff
|
||||
drivers were used for one or more files in a commit. They now
|
||||
avoid calling the external diff drivers.
|
||||
|
||||
* "git-log --follow" did not work unless diff generation (e.g. -p)
|
||||
was also requested.
|
||||
|
||||
* "git-log --follow -B" did not work at all. Fixed.
|
||||
|
||||
* "git-log -M -B" did not correctly handle cases of very large files
|
||||
being renamed and replaced by very small files in the same commit.
|
||||
|
||||
* "git-log" printed extra newlines between commits when a diff
|
||||
was generated internally (e.g. -S or --follow) but not displayed.
|
||||
|
||||
* "git-push" error message is more helpful when pushing to a
|
||||
repository with no matching refs and none specified.
|
||||
|
||||
* "git-push" now respects + (force push) on wildcard refspecs,
|
||||
matching the behavior of git-fetch.
|
||||
|
||||
* "git-filter-branch" now updates the working directory when it
|
||||
has finished filtering the current branch.
|
||||
|
||||
* "git-instaweb" no longer fails on Mac OS X.
|
||||
|
||||
* "git-cvsexportcommit" didn't always create new parent directories
|
||||
before trying to create new child directories. Fixed.
|
||||
|
||||
* "git-fetch" printed a scary (but bogus) error message while
|
||||
fetching a tag that pointed to a tree or blob. The error did
|
||||
not impact correctness, only user perception. The bogus error
|
||||
is no longer printed.
|
||||
|
||||
* "git-ls-files --ignored" did not properly descend into non-ignored
|
||||
directories that themselves contained ignored files if d_type
|
||||
was not supported by the filesystem. This bug impacted systems
|
||||
such as AFS. Fixed.
|
||||
|
||||
* Git segfaulted when reading an invalid .gitattributes file. Fixed.
|
||||
|
||||
* post-receive-email example hook fixed was fixed for
|
||||
non-fast-forward updates.
|
||||
|
||||
* Documentation updates for supported (but previously undocumented)
|
||||
options of "git-archive" and "git-reflog".
|
||||
|
||||
* "make clean" no longer deletes the configure script that ships
|
||||
with the git tarball, making multiple architecture builds easier.
|
||||
|
||||
* "git-remote show origin" spewed a warning message from Perl
|
||||
when no remote is defined for the current branch via
|
||||
branch.<name>.remote configuration settings.
|
||||
|
||||
* Building with NO_PERL_MAKEMAKER excessively rebuilt contents
|
||||
of perl/ subdirectory by rewriting perl.mak.
|
||||
|
||||
* http.sslVerify configuration settings were not used in scripted
|
||||
Porcelains.
|
||||
|
||||
* "git-add" leaked a bit of memory while scanning for files to add.
|
||||
|
||||
* A few workarounds to squelch false warnings from recent gcc have
|
||||
been added.
|
||||
|
||||
* "git-send-pack $remote frotz" segfaulted when there is nothing
|
||||
named 'frotz' on the local end.
|
||||
|
||||
* "git-rebase -interactive" did not handle its "--strategy" option
|
||||
properly.
|
||||
@@ -152,7 +152,7 @@ Updates since v1.5.2
|
||||
cloning locally.
|
||||
|
||||
- URL used for "git clone" and friends can specify nonstandard SSH port
|
||||
by using sh://host:port/path/to/repo syntax.
|
||||
by using ssh://host:port/path/to/repo syntax.
|
||||
|
||||
- "git bundle create" can now create a bundle without negative refs,
|
||||
i.e. "everything since the beginning up to certain points".
|
||||
|
||||
59
Documentation/RelNotes-1.5.4.txt
Normal file
59
Documentation/RelNotes-1.5.4.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
GIT v1.5.4 Release Notes
|
||||
========================
|
||||
|
||||
Updates since v1.5.3
|
||||
--------------------
|
||||
|
||||
* Comes with much improved gitk.
|
||||
|
||||
* git-reset is now built-in.
|
||||
|
||||
* git-send-email can optionally talk over ssmtp and use SMTP-AUTH.
|
||||
|
||||
* git-rebase learned --whitespace option.
|
||||
|
||||
* git-remote knows --mirror mode.
|
||||
|
||||
* git-merge can call the "post-merge" hook.
|
||||
|
||||
* git-pack-objects can optionally run deltification with multiple threads.
|
||||
|
||||
* git-archive can optionally substitute keywords in files marked with
|
||||
export-subst attribute.
|
||||
|
||||
* git-for-each-ref learned %(xxxdate:<dateformat>) syntax to
|
||||
show the various date fields in different formats.
|
||||
|
||||
* git-gc --auto is a low-impact way to automatically run a
|
||||
variant of git-repack that does not lose unreferenced objects
|
||||
(read: safer than the usual one) after the user accumulates
|
||||
too many loose objects.
|
||||
|
||||
* git-push has been rewritten in C.
|
||||
|
||||
* git-push learned --dry-run option to show what would happen
|
||||
if a push is run.
|
||||
|
||||
* git-remote learned "rm" subcommand.
|
||||
|
||||
* git-rebase --interactive mode can now work on detached HEAD.
|
||||
|
||||
* git-cvsserver can be run via git-shell.
|
||||
|
||||
* git-am and git-rebase are far less verbose.
|
||||
|
||||
* git-pull learned to pass --[no-]ff option to underlying git-merge.
|
||||
|
||||
* Various Perforce importer updates.
|
||||
|
||||
Fixes since v1.5.3
|
||||
------------------
|
||||
|
||||
All of the fixes in v1.5.3 maintenance series are included in
|
||||
this release, unless otherwise noted.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.3.4-450-g952a9e5
|
||||
echo O=`git describe refs/heads/master`
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
@@ -94,7 +94,6 @@ git-clone mainporcelain
|
||||
git-commit mainporcelain
|
||||
git-commit-tree plumbingmanipulators
|
||||
git-config ancillarymanipulators
|
||||
git-convert-objects ancillarymanipulators
|
||||
git-count-objects ancillaryinterrogators
|
||||
git-cvsexportcommit foreignscminterface
|
||||
git-cvsimport foreignscminterface
|
||||
@@ -186,7 +185,6 @@ git-status mainporcelain
|
||||
git-stripspace purehelpers
|
||||
git-submodule mainporcelain
|
||||
git-svn foreignscminterface
|
||||
git-svnimport foreignscminterface
|
||||
git-symbolic-ref plumbingmanipulators
|
||||
git-tag mainporcelain
|
||||
git-tar-tree plumbinginterrogators
|
||||
|
||||
@@ -188,7 +188,7 @@ core.worktree::
|
||||
Set the path to the working tree. The value will not be
|
||||
used in combination with repositories found automatically in
|
||||
a .git directory (i.e. $GIT_DIR is not set).
|
||||
This can be overriden by the GIT_WORK_TREE environment
|
||||
This can be overridden by the GIT_WORK_TREE environment
|
||||
variable and the '--work-tree' command line option.
|
||||
|
||||
core.logAllRefUpdates::
|
||||
@@ -324,10 +324,11 @@ branch.<name>.remote::
|
||||
If this option is not given, `git fetch` defaults to remote "origin".
|
||||
|
||||
branch.<name>.merge::
|
||||
When in branch <name>, it tells `git fetch` the default refspec to
|
||||
be marked for merging in FETCH_HEAD. The value has exactly to match
|
||||
a remote part of one of the refspecs which are fetched from the remote
|
||||
given by "branch.<name>.remote".
|
||||
When in branch <name>, it tells `git fetch` the default
|
||||
refspec to be marked for merging in FETCH_HEAD. The value is
|
||||
handled like the remote part of a refspec, and must match a
|
||||
ref which is fetched from the remote given by
|
||||
"branch.<name>.remote".
|
||||
The merge information is used by `git pull` (which at first calls
|
||||
`git fetch`) to lookup the default branch for merging. Without
|
||||
this option, `git pull` defaults to merge the first refspec fetched.
|
||||
@@ -337,6 +338,12 @@ branch.<name>.merge::
|
||||
branch.<name>.merge to the desired branch, and use the special setting
|
||||
`.` (a period) for branch.<name>.remote.
|
||||
|
||||
branch.<name>.mergeoptions::
|
||||
Sets default options for merging into branch <name>. The syntax and
|
||||
supported options are equal to that of gitlink:git-merge[1], but
|
||||
option values containing whitespace characters are currently not
|
||||
supported.
|
||||
|
||||
clean.requireForce::
|
||||
A boolean to make git-clean do nothing unless given -f or -n. Defaults
|
||||
to false.
|
||||
@@ -439,6 +446,19 @@ gc.aggressiveWindow::
|
||||
algorithm used by 'git gc --aggressive'. This defaults
|
||||
to 10.
|
||||
|
||||
gc.auto::
|
||||
When there are approximately more than this many loose
|
||||
objects in the repository, `git gc --auto` will pack them.
|
||||
Some Porcelain commands use this command to perform a
|
||||
light-weight garbage collection from time to time. Setting
|
||||
this to 0 disables it.
|
||||
|
||||
gc.autopacklimit::
|
||||
When there are more than this many packs that are not
|
||||
marked with `*.keep` file in the repository, `git gc
|
||||
--auto` consolidates them into one larger pack. Setting
|
||||
this to 0 disables this.
|
||||
|
||||
gc.packrefs::
|
||||
`git gc` does not run `git pack-refs` in a bare repository by
|
||||
default so that older dumb-transport clients can still fetch
|
||||
@@ -579,7 +599,7 @@ merge.summary::
|
||||
|
||||
merge.tool::
|
||||
Controls which merge resolution program is used by
|
||||
gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff",
|
||||
gitlink:git-mergetool[1]. Valid values are: "kdiff3", "tkdiff",
|
||||
"meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
|
||||
|
||||
merge.verbosity::
|
||||
@@ -588,7 +608,7 @@ merge.verbosity::
|
||||
message if conflicts were detected. Level 1 outputs only
|
||||
conflicts, 2 outputs conflicts and file changes. Level 5 and
|
||||
above outputs debugging information. The default is level 2.
|
||||
Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable.
|
||||
Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
|
||||
|
||||
merge.<driver>.name::
|
||||
Defines a human readable name for a custom low-level
|
||||
@@ -630,9 +650,17 @@ pack.deltaCacheSize::
|
||||
A value of 0 means no limit. Defaults to 0.
|
||||
|
||||
pack.deltaCacheLimit::
|
||||
The maxium size of a delta, that is cached in
|
||||
The maximum size of a delta, that is cached in
|
||||
gitlink:git-pack-objects[1]. Defaults to 1000.
|
||||
|
||||
pack.threads::
|
||||
Specifies the number of threads to spawn when searching for best
|
||||
delta matches. This requires that gitlink:git-pack-objects[1]
|
||||
be compiled with pthreads otherwise this option is ignored with a
|
||||
warning. This is meant to reduce packing time on multiprocessor
|
||||
machines. The required amount of memory for the delta search window
|
||||
is however multiplied by the number of threads.
|
||||
|
||||
pull.octopus::
|
||||
The default merge strategy to use when pulling multiple branches
|
||||
at once.
|
||||
|
||||
@@ -1,592 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
GIT - the stupid content tracker
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
"git" can mean anything, depending on your mood.
|
||||
|
||||
- random three-letter combination that is pronounceable, and not
|
||||
actually used by any common UNIX command. The fact that it is a
|
||||
mispronunciation of "get" may or may not be relevant.
|
||||
- stupid. contemptible and despicable. simple. Take your pick from the
|
||||
dictionary of slang.
|
||||
- "global information tracker": you're in a good mood, and it actually
|
||||
works for you. Angels sing, and a light suddenly fills the room.
|
||||
- "goddamn idiotic truckload of sh*t": when it breaks
|
||||
|
||||
This is a (not so) stupid but extremely fast directory content manager.
|
||||
It doesn't do a whole lot at its core, but what it 'does' do is track
|
||||
directory contents efficiently.
|
||||
|
||||
There are two object abstractions: the "object database", and the
|
||||
"current directory cache" aka "index".
|
||||
|
||||
The Object Database
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
The object database is literally just a content-addressable collection
|
||||
of objects. All objects are named by their content, which is
|
||||
approximated by the SHA1 hash of the object itself. Objects may refer
|
||||
to other objects (by referencing their SHA1 hash), and so you can
|
||||
build up a hierarchy of objects.
|
||||
|
||||
All objects have a statically determined "type" aka "tag", which is
|
||||
determined at object creation time, and which identifies the format of
|
||||
the object (i.e. how it is used, and how it can refer to other
|
||||
objects). There are currently four different object types: "blob",
|
||||
"tree", "commit" and "tag".
|
||||
|
||||
A "blob" object cannot refer to any other object, and is, like the type
|
||||
implies, a pure storage object containing some user data. It is used to
|
||||
actually store the file data, i.e. a blob object is associated with some
|
||||
particular version of some file.
|
||||
|
||||
A "tree" object is an object that ties one or more "blob" objects into a
|
||||
directory structure. In addition, a tree object can refer to other tree
|
||||
objects, thus creating a directory hierarchy.
|
||||
|
||||
A "commit" object ties such directory hierarchies together into
|
||||
a DAG of revisions - each "commit" is associated with exactly one tree
|
||||
(the directory hierarchy at the time of the commit). In addition, a
|
||||
"commit" refers to one or more "parent" commit objects that describe the
|
||||
history of how we arrived at that directory hierarchy.
|
||||
|
||||
As a special case, a commit object with no parents is called the "root"
|
||||
object, and is the point of an initial project commit. Each project
|
||||
must have at least one root, and while you can tie several different
|
||||
root objects together into one project by creating a commit object which
|
||||
has two or more separate roots as its ultimate parents, that's probably
|
||||
just going to confuse people. So aim for the notion of "one root object
|
||||
per project", even if git itself does not enforce that.
|
||||
|
||||
A "tag" object symbolically identifies and can be used to sign other
|
||||
objects. It contains the identifier and type of another object, a
|
||||
symbolic name (of course!) and, optionally, a signature.
|
||||
|
||||
Regardless of object type, all objects share the following
|
||||
characteristics: they are all deflated with zlib, and have a header
|
||||
that not only specifies their type, but also provides size information
|
||||
about the data in the object. It's worth noting that the SHA1 hash
|
||||
that is used to name the object is the hash of the original data
|
||||
plus this header, so `sha1sum` 'file' does not match the object name
|
||||
for 'file'.
|
||||
(Historical note: in the dawn of the age of git the hash
|
||||
was the sha1 of the 'compressed' object.)
|
||||
|
||||
As a result, the general consistency of an object can always be tested
|
||||
independently of the contents or the type of the object: all objects can
|
||||
be validated by verifying that (a) their hashes match the content of the
|
||||
file and (b) the object successfully inflates to a stream of bytes that
|
||||
forms a sequence of <ascii type without space> + <space> + <ascii decimal
|
||||
size> + <byte\0> + <binary object data>.
|
||||
|
||||
The structured objects can further have their structure and
|
||||
connectivity to other objects verified. This is generally done with
|
||||
the `git-fsck` program, which generates a full dependency graph
|
||||
of all objects, and verifies their internal consistency (in addition
|
||||
to just verifying their superficial consistency through the hash).
|
||||
|
||||
The object types in some more detail:
|
||||
|
||||
Blob Object
|
||||
~~~~~~~~~~~
|
||||
A "blob" object is nothing but a binary blob of data, and doesn't
|
||||
refer to anything else. There is no signature or any other
|
||||
verification of the data, so while the object is consistent (it 'is'
|
||||
indexed by its sha1 hash, so the data itself is certainly correct), it
|
||||
has absolutely no other attributes. No name associations, no
|
||||
permissions. It is purely a blob of data (i.e. normally "file
|
||||
contents").
|
||||
|
||||
In particular, since the blob is entirely defined by its data, if two
|
||||
files in a directory tree (or in multiple different versions of the
|
||||
repository) have the same contents, they will share the same blob
|
||||
object. The object is totally independent of its location in the
|
||||
directory tree, and renaming a file does not change the object that
|
||||
file is associated with in any way.
|
||||
|
||||
A blob is typically created when gitlink:git-update-index[1]
|
||||
(or gitlink:git-add[1]) is run, and its data can be accessed by
|
||||
gitlink:git-cat-file[1].
|
||||
|
||||
Tree Object
|
||||
~~~~~~~~~~~
|
||||
The next hierarchical object type is the "tree" object. A tree object
|
||||
is a list of mode/name/blob data, sorted by name. Alternatively, the
|
||||
mode data may specify a directory mode, in which case instead of
|
||||
naming a blob, that name is associated with another TREE object.
|
||||
|
||||
Like the "blob" object, a tree object is uniquely determined by the
|
||||
set contents, and so two separate but identical trees will always
|
||||
share the exact same object. This is true at all levels, i.e. it's
|
||||
true for a "leaf" tree (which does not refer to any other trees, only
|
||||
blobs) as well as for a whole subdirectory.
|
||||
|
||||
For that reason a "tree" object is just a pure data abstraction: it
|
||||
has no history, no signatures, no verification of validity, except
|
||||
that since the contents are again protected by the hash itself, we can
|
||||
trust that the tree is immutable and its contents never change.
|
||||
|
||||
So you can trust the contents of a tree to be valid, the same way you
|
||||
can trust the contents of a blob, but you don't know where those
|
||||
contents 'came' from.
|
||||
|
||||
Side note on trees: since a "tree" object is a sorted list of
|
||||
"filename+content", you can create a diff between two trees without
|
||||
actually having to unpack two trees. Just ignore all common parts,
|
||||
and your diff will look right. In other words, you can effectively
|
||||
(and efficiently) tell the difference between any two random trees by
|
||||
O(n) where "n" is the size of the difference, rather than the size of
|
||||
the tree.
|
||||
|
||||
Side note 2 on trees: since the name of a "blob" depends entirely and
|
||||
exclusively on its contents (i.e. there are no names or permissions
|
||||
involved), you can see trivial renames or permission changes by
|
||||
noticing that the blob stayed the same. However, renames with data
|
||||
changes need a smarter "diff" implementation.
|
||||
|
||||
A tree is created with gitlink:git-write-tree[1] and
|
||||
its data can be accessed by gitlink:git-ls-tree[1].
|
||||
Two trees can be compared with gitlink:git-diff-tree[1].
|
||||
|
||||
Commit Object
|
||||
~~~~~~~~~~~~~
|
||||
The "commit" object is an object that introduces the notion of
|
||||
history into the picture. In contrast to the other objects, it
|
||||
doesn't just describe the physical state of a tree, it describes how
|
||||
we got there, and why.
|
||||
|
||||
A "commit" is defined by the tree-object that it results in, the
|
||||
parent commits (zero, one or more) that led up to that point, and a
|
||||
comment on what happened. Again, a commit is not trusted per se:
|
||||
the contents are well-defined and "safe" due to the cryptographically
|
||||
strong signatures at all levels, but there is no reason to believe
|
||||
that the tree is "good" or that the merge information makes sense.
|
||||
The parents do not have to actually have any relationship with the
|
||||
result, for example.
|
||||
|
||||
Note on commits: unlike real SCM's, commits do not contain
|
||||
rename information or file mode change information. All of that is
|
||||
implicit in the trees involved (the result tree, and the result trees
|
||||
of the parents), and describing that makes no sense in this idiotic
|
||||
file manager.
|
||||
|
||||
A commit is created with gitlink:git-commit-tree[1] and
|
||||
its data can be accessed by gitlink:git-cat-file[1].
|
||||
|
||||
Trust
|
||||
~~~~~
|
||||
An aside on the notion of "trust". Trust is really outside the scope
|
||||
of "git", but it's worth noting a few things. First off, since
|
||||
everything is hashed with SHA1, you 'can' trust that an object is
|
||||
intact and has not been messed with by external sources. So the name
|
||||
of an object uniquely identifies a known state - just not a state that
|
||||
you may want to trust.
|
||||
|
||||
Furthermore, since the SHA1 signature of a commit refers to the
|
||||
SHA1 signatures of the tree it is associated with and the signatures
|
||||
of the parent, a single named commit specifies uniquely a whole set
|
||||
of history, with full contents. You can't later fake any step of the
|
||||
way once you have the name of a commit.
|
||||
|
||||
So to introduce some real trust in the system, the only thing you need
|
||||
to do is to digitally sign just 'one' special note, which includes the
|
||||
name of a top-level commit. Your digital signature shows others
|
||||
that you trust that commit, and the immutability of the history of
|
||||
commits tells others that they can trust the whole history.
|
||||
|
||||
In other words, you can easily validate a whole archive by just
|
||||
sending out a single email that tells the people the name (SHA1 hash)
|
||||
of the top commit, and digitally sign that email using something
|
||||
like GPG/PGP.
|
||||
|
||||
To assist in this, git also provides the tag object...
|
||||
|
||||
Tag Object
|
||||
~~~~~~~~~~
|
||||
Git provides the "tag" object to simplify creating, managing and
|
||||
exchanging symbolic and signed tokens. The "tag" object at its
|
||||
simplest simply symbolically identifies another object by containing
|
||||
the sha1, type and symbolic name.
|
||||
|
||||
However it can optionally contain additional signature information
|
||||
(which git doesn't care about as long as there's less than 8k of
|
||||
it). This can then be verified externally to git.
|
||||
|
||||
Note that despite the tag features, "git" itself only handles content
|
||||
integrity; the trust framework (and signature provision and
|
||||
verification) has to come from outside.
|
||||
|
||||
A tag is created with gitlink:git-mktag[1],
|
||||
its data can be accessed by gitlink:git-cat-file[1],
|
||||
and the signature can be verified by
|
||||
gitlink:git-verify-tag[1].
|
||||
|
||||
|
||||
The "index" aka "Current Directory Cache"
|
||||
-----------------------------------------
|
||||
The index is a simple binary file, which contains an efficient
|
||||
representation of a virtual directory content at some random time. It
|
||||
does so by a simple array that associates a set of names, dates,
|
||||
permissions and content (aka "blob") objects together. The cache is
|
||||
always kept ordered by name, and names are unique (with a few very
|
||||
specific rules) at any point in time, but the cache has no long-term
|
||||
meaning, and can be partially updated at any time.
|
||||
|
||||
In particular, the index certainly does not need to be consistent with
|
||||
the current directory contents (in fact, most operations will depend on
|
||||
different ways to make the index 'not' be consistent with the directory
|
||||
hierarchy), but it has three very important attributes:
|
||||
|
||||
'(a) it can re-generate the full state it caches (not just the
|
||||
directory structure: it contains pointers to the "blob" objects so
|
||||
that it can regenerate the data too)'
|
||||
|
||||
As a special case, there is a clear and unambiguous one-way mapping
|
||||
from a current directory cache to a "tree object", which can be
|
||||
efficiently created from just the current directory cache without
|
||||
actually looking at any other data. So a directory cache at any one
|
||||
time uniquely specifies one and only one "tree" object (but has
|
||||
additional data to make it easy to match up that tree object with what
|
||||
has happened in the directory)
|
||||
|
||||
'(b) it has efficient methods for finding inconsistencies between that
|
||||
cached state ("tree object waiting to be instantiated") and the
|
||||
current state.'
|
||||
|
||||
'(c) it can additionally efficiently represent information about merge
|
||||
conflicts between different tree objects, allowing each pathname to be
|
||||
associated with sufficient information about the trees involved that
|
||||
you can create a three-way merge between them.'
|
||||
|
||||
Those are the three ONLY things that the directory cache does. It's a
|
||||
cache, and the normal operation is to re-generate it completely from a
|
||||
known tree object, or update/compare it with a live tree that is being
|
||||
developed. If you blow the directory cache away entirely, you generally
|
||||
haven't lost any information as long as you have the name of the tree
|
||||
that it described.
|
||||
|
||||
At the same time, the index is at the same time also the
|
||||
staging area for creating new trees, and creating a new tree always
|
||||
involves a controlled modification of the index file. In particular,
|
||||
the index file can have the representation of an intermediate tree that
|
||||
has not yet been instantiated. So the index can be thought of as a
|
||||
write-back cache, which can contain dirty information that has not yet
|
||||
been written back to the backing store.
|
||||
|
||||
|
||||
|
||||
The Workflow
|
||||
------------
|
||||
Generally, all "git" operations work on the index file. Some operations
|
||||
work *purely* on the index file (showing the current state of the
|
||||
index), but most operations move data to and from the index file. Either
|
||||
from the database or from the working directory. Thus there are four
|
||||
main combinations:
|
||||
|
||||
1) working directory -> index
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You update the index with information from the working directory with
|
||||
the gitlink:git-update-index[1] command. You
|
||||
generally update the index information by just specifying the filename
|
||||
you want to update, like so:
|
||||
|
||||
git-update-index filename
|
||||
|
||||
but to avoid common mistakes with filename globbing etc, the command
|
||||
will not normally add totally new entries or remove old entries,
|
||||
i.e. it will normally just update existing cache entries.
|
||||
|
||||
To tell git that yes, you really do realize that certain files no
|
||||
longer exist, or that new files should be added, you
|
||||
should use the `--remove` and `--add` flags respectively.
|
||||
|
||||
NOTE! A `--remove` flag does 'not' mean that subsequent filenames will
|
||||
necessarily be removed: if the files still exist in your directory
|
||||
structure, the index will be updated with their new status, not
|
||||
removed. The only thing `--remove` means is that update-cache will be
|
||||
considering a removed file to be a valid thing, and if the file really
|
||||
does not exist any more, it will update the index accordingly.
|
||||
|
||||
As a special case, you can also do `git-update-index --refresh`, which
|
||||
will refresh the "stat" information of each index to match the current
|
||||
stat information. It will 'not' update the object status itself, and
|
||||
it will only update the fields that are used to quickly test whether
|
||||
an object still matches its old backing store object.
|
||||
|
||||
2) index -> object database
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You write your current index file to a "tree" object with the program
|
||||
|
||||
git-write-tree
|
||||
|
||||
that doesn't come with any options - it will just write out the
|
||||
current index into the set of tree objects that describe that state,
|
||||
and it will return the name of the resulting top-level tree. You can
|
||||
use that tree to re-generate the index at any time by going in the
|
||||
other direction:
|
||||
|
||||
3) object database -> index
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You read a "tree" file from the object database, and use that to
|
||||
populate (and overwrite - don't do this if your index contains any
|
||||
unsaved state that you might want to restore later!) your current
|
||||
index. Normal operation is just
|
||||
|
||||
git-read-tree <sha1 of tree>
|
||||
|
||||
and your index file will now be equivalent to the tree that you saved
|
||||
earlier. However, that is only your 'index' file: your working
|
||||
directory contents have not been modified.
|
||||
|
||||
4) index -> working directory
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You update your working directory from the index by "checking out"
|
||||
files. This is not a very common operation, since normally you'd just
|
||||
keep your files updated, and rather than write to your working
|
||||
directory, you'd tell the index files about the changes in your
|
||||
working directory (i.e. `git-update-index`).
|
||||
|
||||
However, if you decide to jump to a new version, or check out somebody
|
||||
else's version, or just restore a previous tree, you'd populate your
|
||||
index file with read-tree, and then you need to check out the result
|
||||
with
|
||||
|
||||
git-checkout-index filename
|
||||
|
||||
or, if you want to check out all of the index, use `-a`.
|
||||
|
||||
NOTE! git-checkout-index normally refuses to overwrite old files, so
|
||||
if you have an old version of the tree already checked out, you will
|
||||
need to use the "-f" flag ('before' the "-a" flag or the filename) to
|
||||
'force' the checkout.
|
||||
|
||||
|
||||
Finally, there are a few odds and ends which are not purely moving
|
||||
from one representation to the other:
|
||||
|
||||
5) Tying it all together
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
To commit a tree you have instantiated with "git-write-tree", you'd
|
||||
create a "commit" object that refers to that tree and the history
|
||||
behind it - most notably the "parent" commits that preceded it in
|
||||
history.
|
||||
|
||||
Normally a "commit" has one parent: the previous state of the tree
|
||||
before a certain change was made. However, sometimes it can have two
|
||||
or more parent commits, in which case we call it a "merge", due to the
|
||||
fact that such a commit brings together ("merges") two or more
|
||||
previous states represented by other commits.
|
||||
|
||||
In other words, while a "tree" represents a particular directory state
|
||||
of a working directory, a "commit" represents that state in "time",
|
||||
and explains how we got there.
|
||||
|
||||
You create a commit object by giving it the tree that describes the
|
||||
state at the time of the commit, and a list of parents:
|
||||
|
||||
git-commit-tree <tree> -p <parent> [-p <parent2> ..]
|
||||
|
||||
and then giving the reason for the commit on stdin (either through
|
||||
redirection from a pipe or file, or by just typing it at the tty).
|
||||
|
||||
git-commit-tree will return the name of the object that represents
|
||||
that commit, and you should save it away for later use. Normally,
|
||||
you'd commit a new `HEAD` state, and while git doesn't care where you
|
||||
save the note about that state, in practice we tend to just write the
|
||||
result to the file pointed at by `.git/HEAD`, so that we can always see
|
||||
what the last committed state was.
|
||||
|
||||
Here is an ASCII art by Jon Loeliger that illustrates how
|
||||
various pieces fit together.
|
||||
|
||||
------------
|
||||
|
||||
commit-tree
|
||||
commit obj
|
||||
+----+
|
||||
| |
|
||||
| |
|
||||
V V
|
||||
+-----------+
|
||||
| Object DB |
|
||||
| Backing |
|
||||
| Store |
|
||||
+-----------+
|
||||
^
|
||||
write-tree | |
|
||||
tree obj | |
|
||||
| | read-tree
|
||||
| | tree obj
|
||||
V
|
||||
+-----------+
|
||||
| Index |
|
||||
| "cache" |
|
||||
+-----------+
|
||||
update-index ^
|
||||
blob obj | |
|
||||
| |
|
||||
checkout-index -u | | checkout-index
|
||||
stat | | blob obj
|
||||
V
|
||||
+-----------+
|
||||
| Working |
|
||||
| Directory |
|
||||
+-----------+
|
||||
|
||||
------------
|
||||
|
||||
|
||||
6) Examining the data
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can examine the data represented in the object database and the
|
||||
index with various helper tools. For every object, you can use
|
||||
gitlink:git-cat-file[1] to examine details about the
|
||||
object:
|
||||
|
||||
git-cat-file -t <objectname>
|
||||
|
||||
shows the type of the object, and once you have the type (which is
|
||||
usually implicit in where you find the object), you can use
|
||||
|
||||
git-cat-file blob|tree|commit|tag <objectname>
|
||||
|
||||
to show its contents. NOTE! Trees have binary content, and as a result
|
||||
there is a special helper for showing that content, called
|
||||
`git-ls-tree`, which turns the binary content into a more easily
|
||||
readable form.
|
||||
|
||||
It's especially instructive to look at "commit" objects, since those
|
||||
tend to be small and fairly self-explanatory. In particular, if you
|
||||
follow the convention of having the top commit name in `.git/HEAD`,
|
||||
you can do
|
||||
|
||||
git-cat-file commit HEAD
|
||||
|
||||
to see what the top commit was.
|
||||
|
||||
7) Merging multiple trees
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Git helps you do a three-way merge, which you can expand to n-way by
|
||||
repeating the merge procedure arbitrary times until you finally
|
||||
"commit" the state. The normal situation is that you'd only do one
|
||||
three-way merge (two parents), and commit it, but if you like to, you
|
||||
can do multiple parents in one go.
|
||||
|
||||
To do a three-way merge, you need the two sets of "commit" objects
|
||||
that you want to merge, use those to find the closest common parent (a
|
||||
third "commit" object), and then use those commit objects to find the
|
||||
state of the directory ("tree" object) at these points.
|
||||
|
||||
To get the "base" for the merge, you first look up the common parent
|
||||
of two commits with
|
||||
|
||||
git-merge-base <commit1> <commit2>
|
||||
|
||||
which will return you the commit they are both based on. You should
|
||||
now look up the "tree" objects of those commits, which you can easily
|
||||
do with (for example)
|
||||
|
||||
git-cat-file commit <commitname> | head -1
|
||||
|
||||
since the tree object information is always the first line in a commit
|
||||
object.
|
||||
|
||||
Once you know the three trees you are going to merge (the one
|
||||
"original" tree, aka the common case, and the two "result" trees, aka
|
||||
the branches you want to merge), you do a "merge" read into the
|
||||
index. This will complain if it has to throw away your old index contents, so you should
|
||||
make sure that you've committed those - in fact you would normally
|
||||
always do a merge against your last commit (which should thus match
|
||||
what you have in your current index anyway).
|
||||
|
||||
To do the merge, do
|
||||
|
||||
git-read-tree -m -u <origtree> <yourtree> <targettree>
|
||||
|
||||
which will do all trivial merge operations for you directly in the
|
||||
index file, and you can just write the result out with
|
||||
`git-write-tree`.
|
||||
|
||||
Historical note. We did not have `-u` facility when this
|
||||
section was first written, so we used to warn that
|
||||
the merge is done in the index file, not in your
|
||||
working tree, and your working tree will not match your
|
||||
index after this step.
|
||||
This is no longer true. The above command, thanks to `-u`
|
||||
option, updates your working tree with the merge results for
|
||||
paths that have been trivially merged.
|
||||
|
||||
|
||||
8) Merging multiple trees, continued
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sadly, many merges aren't trivial. If there are files that have
|
||||
been added, moved or removed, or if both branches have modified the
|
||||
same file, you will be left with an index tree that contains "merge
|
||||
entries" in it. Such an index tree can 'NOT' be written out to a tree
|
||||
object, and you will have to resolve any such merge clashes using
|
||||
other tools before you can write out the result.
|
||||
|
||||
You can examine such index state with `git-ls-files --unmerged`
|
||||
command. An example:
|
||||
|
||||
------------------------------------------------
|
||||
$ git-read-tree -m $orig HEAD $target
|
||||
$ git-ls-files --unmerged
|
||||
100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello.c
|
||||
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello.c
|
||||
100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello.c
|
||||
------------------------------------------------
|
||||
|
||||
Each line of the `git-ls-files --unmerged` output begins with
|
||||
the blob mode bits, blob SHA1, 'stage number', and the
|
||||
filename. The 'stage number' is git's way to say which tree it
|
||||
came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD`
|
||||
tree, and stage3 `$target` tree.
|
||||
|
||||
Earlier we said that trivial merges are done inside
|
||||
`git-read-tree -m`. For example, if the file did not change
|
||||
from `$orig` to `HEAD` nor `$target`, or if the file changed
|
||||
from `$orig` to `HEAD` and `$orig` to `$target` the same way,
|
||||
obviously the final outcome is what is in `HEAD`. What the
|
||||
above example shows is that file `hello.c` was changed from
|
||||
`$orig` to `HEAD` and `$orig` to `$target` in a different way.
|
||||
You could resolve this by running your favorite 3-way merge
|
||||
program, e.g. `diff3` or `merge`, on the blob objects from
|
||||
these three stages yourself, like this:
|
||||
|
||||
------------------------------------------------
|
||||
$ git-cat-file blob 263414f... >hello.c~1
|
||||
$ git-cat-file blob 06fa6a2... >hello.c~2
|
||||
$ git-cat-file blob cc44c73... >hello.c~3
|
||||
$ merge hello.c~2 hello.c~1 hello.c~3
|
||||
------------------------------------------------
|
||||
|
||||
This would leave the merge result in `hello.c~2` file, along
|
||||
with conflict markers if there are conflicts. After verifying
|
||||
the merge result makes sense, you can tell git what the final
|
||||
merge result for this file is by:
|
||||
|
||||
mv -f hello.c~2 hello.c
|
||||
git-update-index hello.c
|
||||
|
||||
When a path is in unmerged state, running `git-update-index` for
|
||||
that path tells git to mark the path resolved.
|
||||
|
||||
The above is the description of a git merge at the lowest level,
|
||||
to help you understand what conceptually happens under the hood.
|
||||
In practice, nobody, not even git itself, uses three `git-cat-file`
|
||||
for this. There is `git-merge-index` program that extracts the
|
||||
stages to temporary files and calls a "merge" script on it:
|
||||
|
||||
git-merge-index git-merge-one-file hello.c
|
||||
|
||||
and that is what higher level `git merge -s resolve` is implemented
|
||||
with.
|
||||
@@ -4,34 +4,24 @@ A git core tutorial for developers
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This is trying to be a short tutorial on setting up and using a git
|
||||
repository, mainly because being hands-on and using explicit examples is
|
||||
often the best way of explaining what is going on.
|
||||
This tutorial explains how to use the "core" git programs to set up and
|
||||
work with a git repository.
|
||||
|
||||
In normal life, most people wouldn't use the "core" git programs
|
||||
directly, but rather script around them to make them more palatable.
|
||||
Understanding the core git stuff may help some people get those scripts
|
||||
done, though, and it may also be instructive in helping people
|
||||
understand what it is that the higher-level helper scripts are actually
|
||||
doing.
|
||||
If you just need to use git as a revision control system you may prefer
|
||||
to start with link:tutorial.html[a tutorial introduction to git] or
|
||||
link:user-manual.html[the git user manual].
|
||||
|
||||
However, an understanding of these low-level tools can be helpful if
|
||||
you want to understand git's internals.
|
||||
|
||||
The core git is often called "plumbing", with the prettier user
|
||||
interfaces on top of it called "porcelain". You may not want to use the
|
||||
plumbing directly very often, but it can be good to know what the
|
||||
plumbing does for when the porcelain isn't flushing.
|
||||
|
||||
The material presented here often goes deep describing how things
|
||||
work internally. If you are mostly interested in using git as a
|
||||
SCM, you can skip them during your first pass.
|
||||
|
||||
[NOTE]
|
||||
And those "too deep" descriptions are often marked as Note.
|
||||
|
||||
[NOTE]
|
||||
If you are already familiar with another version control system,
|
||||
like CVS, you may want to take a look at
|
||||
link:everyday.html[Everyday GIT in 20 commands or so] first
|
||||
before reading this.
|
||||
Deeper technical details are often marked as Notes, which you can
|
||||
skip on your first reading.
|
||||
|
||||
|
||||
Creating a git repository
|
||||
@@ -563,13 +553,8 @@ can explore on your own.
|
||||
|
||||
[NOTE]
|
||||
Most likely, you are not directly using the core
|
||||
git Plumbing commands, but using Porcelain like Cogito on top
|
||||
of it. Cogito works a bit differently and you usually do not
|
||||
have to run `git-update-index` yourself for changed files (you
|
||||
do tell underlying git about additions and removals via
|
||||
`cg-add` and `cg-rm` commands). Just before you make a commit
|
||||
with `cg-commit`, Cogito figures out which files you modified,
|
||||
and runs `git-update-index` on them for you.
|
||||
git Plumbing commands, but using Porcelain such as `git-add`, `git-rm'
|
||||
and `git-commit'.
|
||||
|
||||
|
||||
Tagging a version
|
||||
@@ -696,8 +681,8 @@ $ git reset
|
||||
|
||||
and in fact a lot of the common git command combinations can be scripted
|
||||
with the `git xyz` interfaces. You can learn things by just looking
|
||||
at what the various git scripts do. For example, `git reset` is the
|
||||
above two lines implemented in `git-reset`, but some things like
|
||||
at what the various git scripts do. For example, `git reset` used to be
|
||||
the above two lines implemented in `git-reset`, but some things like
|
||||
`git status` and `git commit` are slightly more complex scripts around
|
||||
the basic git commands.
|
||||
|
||||
@@ -815,8 +800,8 @@ you have, you can say
|
||||
$ git branch
|
||||
------------
|
||||
|
||||
which is nothing more than a simple script around `ls .git/refs/heads`.
|
||||
There will be asterisk in front of the branch you are currently on.
|
||||
which used to be nothing more than a simple script around `ls .git/refs/heads`.
|
||||
There will be an asterisk in front of the branch you are currently on.
|
||||
|
||||
Sometimes you may wish to create a new branch _without_ actually
|
||||
checking it out and switching to it. If so, just use the command
|
||||
@@ -893,7 +878,7 @@ script called `git merge`, which wants to know which branches you want
|
||||
to resolve and what the merge is all about:
|
||||
|
||||
------------
|
||||
$ git merge "Merge work in mybranch" HEAD mybranch
|
||||
$ git merge -m "Merge work in mybranch" mybranch
|
||||
------------
|
||||
|
||||
where the first argument is going to be used as the commit message if
|
||||
@@ -962,7 +947,7 @@ the later output lines is used to show commits contained in the
|
||||
`master` branch, and the second column for the `mybranch`
|
||||
branch. Three commits are shown along with their log messages.
|
||||
All of them have non blank characters in the first column (`*`
|
||||
shows an ordinary commit on the current branch, `.` is a merge commit), which
|
||||
shows an ordinary commit on the current branch, `-` is a merge commit), which
|
||||
means they are now part of the `master` branch. Only the "Some
|
||||
work" commit has the plus `+` character in the second column,
|
||||
because `mybranch` has not been merged to incorporate these
|
||||
@@ -980,7 +965,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
|
||||
|
||||
------------
|
||||
$ git checkout mybranch
|
||||
$ git merge "Merge upstream changes." HEAD master
|
||||
$ git merge -m "Merge upstream changes." master
|
||||
------------
|
||||
|
||||
This outputs something like this (the actual commit object names
|
||||
@@ -1096,7 +1081,7 @@ to help dumb transport downloaders.
|
||||
There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
|
||||
programs, which are 'commit walkers'; they outlived their
|
||||
usefulness when git Native and SSH transports were introduced,
|
||||
and not used by `git pull` or `git push` scripts.
|
||||
and are not used by `git pull` or `git push` scripts.
|
||||
|
||||
Once you fetch from the remote repository, you `merge` that
|
||||
with your current branch.
|
||||
@@ -1203,7 +1188,7 @@ $ mb=$(git-merge-base HEAD mybranch)
|
||||
|
||||
The command writes the commit object name of the common ancestor
|
||||
to the standard output, so we captured its output to a variable,
|
||||
because we will be using it in the next step. BTW, the common
|
||||
because we will be using it in the next step. By the way, the common
|
||||
ancestor commit is the "New day." commit in this case. You can
|
||||
tell it by:
|
||||
|
||||
@@ -1469,7 +1454,7 @@ Although git is a truly distributed system, it is often
|
||||
convenient to organize your project with an informal hierarchy
|
||||
of developers. Linux kernel development is run this way. There
|
||||
is a nice illustration (page 17, "Merges to Mainline") in
|
||||
link:http://tinyurl.com/a2jdg[Randy Dunlap's presentation].
|
||||
link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf[Randy Dunlap's presentation].
|
||||
|
||||
It should be stressed that this hierarchy is purely *informal*.
|
||||
There is nothing fundamental in git that enforces the "chain of
|
||||
@@ -1622,8 +1607,8 @@ in both of them. You could merge in 'diff-fix' first and then
|
||||
'commit-fix' next, like this:
|
||||
|
||||
------------
|
||||
$ git merge 'Merge fix in diff-fix' master diff-fix
|
||||
$ git merge 'Merge fix in commit-fix' master commit-fix
|
||||
$ git merge -m 'Merge fix in diff-fix' diff-fix
|
||||
$ git merge -m 'Merge fix in commit-fix' commit-fix
|
||||
------------
|
||||
|
||||
Which would result in:
|
||||
@@ -1686,5 +1671,3 @@ merge two at a time, documenting how you resolved the conflicts,
|
||||
and the reason why you preferred changes made in one side over
|
||||
the other. Otherwise it would make the project history harder
|
||||
to follow, not easier.
|
||||
|
||||
[ to be continued.. cvsimports ]
|
||||
|
||||
@@ -179,8 +179,8 @@
|
||||
|
||||
--ext-diff::
|
||||
Allow an external diff helper to be executed. If you set an
|
||||
external diff driver with gitlink:gitattributes(5), you need
|
||||
to use this option with gitlink:git-log(1) and friends.
|
||||
external diff driver with gitlink:gitattributes[5], you need
|
||||
to use this option with gitlink:git-log[1] and friends.
|
||||
|
||||
--no-ext-diff::
|
||||
Disallow external diff drivers.
|
||||
|
||||
@@ -10,7 +10,7 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index]
|
||||
[--apply] [--no-add] [--index-info] [-R | --reverse]
|
||||
[--apply] [--no-add] [--build-fake-ancestor <file>] [-R | --reverse]
|
||||
[--allow-binary-replacement | --binary] [--reject] [-z]
|
||||
[-pNUM] [-CNUM] [--inaccurate-eof] [--cached]
|
||||
[--whitespace=<nowarn|warn|error|error-all|strip>]
|
||||
@@ -63,12 +63,15 @@ OPTIONS
|
||||
cached data, apply the patch, and store the result in the index,
|
||||
without using the working tree. This implies '--index'.
|
||||
|
||||
--index-info::
|
||||
--build-fake-ancestor <file>::
|
||||
Newer git-diff output has embedded 'index information'
|
||||
for each blob to help identify the original version that
|
||||
the patch applies to. When this flag is given, and if
|
||||
the original version of the blob is available locally,
|
||||
outputs information about them to the standard output.
|
||||
the original versions of the blobs is available locally,
|
||||
builds a temporary index containing those blobs.
|
||||
+
|
||||
When a pure mode change is encountered (which has no index information),
|
||||
the information is read from the current index instead.
|
||||
|
||||
-R, --reverse::
|
||||
Apply the patch in reverse.
|
||||
|
||||
@@ -10,12 +10,14 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
|
||||
[--remote=<repo>] <tree-ish> [path...]
|
||||
[--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
|
||||
[path...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Creates an archive of the specified format containing the tree
|
||||
structure for the named tree. If <prefix> is specified it is
|
||||
structure for the named tree, and writes it out to the standard
|
||||
output. If <prefix> is specified it is
|
||||
prepended to the filenames in the archive.
|
||||
|
||||
'git-archive' behaves differently when given a tree ID versus when
|
||||
@@ -31,7 +33,7 @@ OPTIONS
|
||||
-------
|
||||
|
||||
--format=<fmt>::
|
||||
Format of the resulting archive: 'tar', 'zip'... The default
|
||||
Format of the resulting archive: 'tar' or 'zip'. The default
|
||||
is 'tar'.
|
||||
|
||||
--list, -l::
|
||||
@@ -51,6 +53,10 @@ OPTIONS
|
||||
Instead of making a tar archive from local repository,
|
||||
retrieve a tar archive from a remote repository.
|
||||
|
||||
--exec=<git-upload-archive>::
|
||||
Used with --remote to specify the path to the
|
||||
git-upload-archive executable on the remote side.
|
||||
|
||||
<tree-ish>::
|
||||
The tree or commit to produce an archive for.
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@ The command takes various subcommands, and different options depending
|
||||
on the subcommand:
|
||||
|
||||
git bisect start [<bad> [<good>...]] [--] [<paths>...]
|
||||
git bisect bad <rev>
|
||||
git bisect good <rev>
|
||||
git bisect bad [<rev>]
|
||||
git bisect good [<rev>...]
|
||||
git bisect skip [<rev>...]
|
||||
git bisect reset [<branch>]
|
||||
git bisect visualize
|
||||
git bisect replay <logfile>
|
||||
@@ -134,6 +135,20 @@ $ git reset --hard HEAD~3 # try 3 revs before what
|
||||
Then compile and test the one you chose to try. After that, tell
|
||||
bisect what the result was as usual.
|
||||
|
||||
Bisect skip
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Instead of choosing by yourself a nearby commit, you may just want git
|
||||
to do it for you using:
|
||||
|
||||
------------
|
||||
$ git bisect skip # Current version cannot be tested
|
||||
------------
|
||||
|
||||
But computing the commit to test may be slower afterwards and git may
|
||||
eventually not be able to tell the first bad among a bad and one or
|
||||
more "skip"ped commits.
|
||||
|
||||
Cutting down bisection by giving more parameters to bisect start
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -167,14 +182,18 @@ $ 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.
|
||||
exit with code 0 in case the current source code is good. Exit with a
|
||||
code between 1 and 127 (inclusive), except 125, if 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".)
|
||||
|
||||
The special exit code 125 should be used when the current source code
|
||||
cannot be tested. If the "run" script exits with this code, the current
|
||||
revision will be skipped, see `git bisect skip` above.
|
||||
|
||||
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
|
||||
|
||||
@@ -26,6 +26,10 @@ It will start out with a head equal to the one given as <start-point>.
|
||||
If no <start-point> is given, the branch will be created with a head
|
||||
equal to that of the currently checked out branch.
|
||||
|
||||
Note that this will create the new branch, but it will not switch the
|
||||
working tree to it; use "git checkout <newbranch>" to switch to the
|
||||
new branch.
|
||||
|
||||
When a local branch is started off a remote branch, git can setup the
|
||||
branch so that gitlink:git-pull[1] will appropriately merge from that
|
||||
remote branch. If this behavior is desired, it is possible to make it
|
||||
@@ -91,6 +95,21 @@ OPTIONS
|
||||
--no-abbrev::
|
||||
Display the full sha1s in output listing rather than abbreviating them.
|
||||
|
||||
--track::
|
||||
Set up configuration so that git-pull will automatically
|
||||
retrieve data from the remote branch. Use this if you always
|
||||
pull from the same remote branch into the new branch, or if you
|
||||
don't want to use "git pull <repository> <refspec>" explicitly. Set the
|
||||
branch.autosetupmerge configuration variable to true if you
|
||||
want git-checkout and git-branch to always behave as if
|
||||
'--track' were given.
|
||||
|
||||
--no-track::
|
||||
When -b is given and a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will not retrieve data
|
||||
from the remote branch, ignoring the branch.autosetupmerge
|
||||
configuration variable.
|
||||
|
||||
<branchname>::
|
||||
The name of the branch to create or delete.
|
||||
The new branch name must pass all checks defined by
|
||||
|
||||
@@ -103,14 +103,20 @@ We set a tag in R1 (lastR2bundle) after the previous such transport,
|
||||
and move it afterwards to help build the bundle.
|
||||
|
||||
in R1 on A:
|
||||
|
||||
------------
|
||||
$ git-bundle create mybundle master ^lastR2bundle
|
||||
$ git tag -f lastR2bundle master
|
||||
------------
|
||||
|
||||
(move mybundle from A to B by some mechanism)
|
||||
|
||||
in R2 on B:
|
||||
|
||||
------------
|
||||
$ git-bundle verify mybundle
|
||||
$ git-fetch mybundle refspec
|
||||
------------
|
||||
|
||||
where refspec is refInBundle:localRef
|
||||
|
||||
@@ -124,9 +130,11 @@ Also, with something like this in your config:
|
||||
You can first sneakernet the bundle file to ~/tmp/file.bdl and
|
||||
then these commands:
|
||||
|
||||
------------
|
||||
$ git ls-remote bundle
|
||||
$ git fetch bundle
|
||||
$ git pull bundle
|
||||
------------
|
||||
|
||||
would treat it as if it is talking with a remote side over the
|
||||
network.
|
||||
|
||||
@@ -50,7 +50,9 @@ OPTIONS
|
||||
--track::
|
||||
When -b is given and a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will automatically
|
||||
retrieve data from the remote branch. Set the
|
||||
retrieve data from the remote branch. Use this if you always
|
||||
pull from the same remote branch into the new branch, or if you
|
||||
don't want to use "git pull <repository> <refspec>" explicitly. Set the
|
||||
branch.autosetupmerge configuration variable to true if you
|
||||
want git-checkout and git-branch to always behave as if
|
||||
'--track' were given.
|
||||
|
||||
@@ -27,11 +27,12 @@ OPTIONS
|
||||
message prior committing.
|
||||
|
||||
-x::
|
||||
Cause the command to append which commit was
|
||||
cherry-picked after the original commit message when
|
||||
making a commit. Do not use this option if you are
|
||||
cherry-picking from your private branch because the
|
||||
information is useless to the recipient. If on the
|
||||
When recording the commit, append to the original commit
|
||||
message a note that indicates which commit this change
|
||||
was cherry-picked from. Append the note only for cherry
|
||||
picks without conflicts. Do not use this option if
|
||||
you are cherry-picking from your private branch because
|
||||
the information is useless to the recipient. If on the
|
||||
other hand you are cherry-picking between two publicly
|
||||
visible branches (e.g. backporting a fix to a
|
||||
maintenance branch for an older release from a
|
||||
|
||||
@@ -68,7 +68,7 @@ OPTIONS
|
||||
automatically setup .git/objects/info/alternates to
|
||||
obtain objects from the reference repository. Using
|
||||
an already existing repository as an alternate will
|
||||
require less objects to be copied from the repository
|
||||
require fewer objects to be copied from the repository
|
||||
being cloned, reducing network and local storage costs.
|
||||
|
||||
--quiet::
|
||||
@@ -111,11 +111,11 @@ OPTIONS
|
||||
|
||||
--depth <depth>::
|
||||
Create a 'shallow' clone with a history truncated to the
|
||||
specified number of revs. A shallow repository has
|
||||
specified number of revisions. A shallow repository has a
|
||||
number of limitations (you cannot clone or fetch from
|
||||
it, nor push from nor into it), but is adequate if you
|
||||
want to only look at near the tip of a large project
|
||||
with a long history, and would want to send in a fixes
|
||||
are only interested in the recent history of a large project
|
||||
with a long history, and would want to send in fixes
|
||||
as patches.
|
||||
|
||||
<repository>::
|
||||
|
||||
@@ -142,7 +142,7 @@ FILES
|
||||
If not set explicitly with '--file', there are three files where
|
||||
git-config will search for configuration options:
|
||||
|
||||
.git/config::
|
||||
$GIT_DIR/config::
|
||||
Repository specific configuration file. (The filename is
|
||||
of course relative to the repository root, not the working
|
||||
directory.)
|
||||
|
||||
@@ -73,7 +73,7 @@ Merge one patch into CVS::
|
||||
$ export GIT_DIR=~/project/.git
|
||||
$ cd ~/project_cvs_checkout
|
||||
$ git-cvsexportcommit -v <commit-sha1>
|
||||
$ cvs commit -F .mgs <files>
|
||||
$ cvs commit -F .msg <files>
|
||||
------------
|
||||
|
||||
Merge pending patches into CVS automatically -- only if you really know what you are doing::
|
||||
|
||||
@@ -125,7 +125,7 @@ $ git diff topic...master <3>
|
||||
+
|
||||
<1> Changes between the tips of the topic and the master branches.
|
||||
<2> Same as above.
|
||||
<3> Changes that occured on the master branch since when the topic
|
||||
<3> Changes that occurred on the master branch since when the topic
|
||||
branch was started off it.
|
||||
|
||||
Limiting the diff output::
|
||||
|
||||
@@ -180,8 +180,7 @@ A significantly faster version:
|
||||
git filter-branch --index-filter 'git update-index --remove filename' HEAD
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Now, you will get the rewritten history saved in the branch 'newbranch'
|
||||
(your current branch is left untouched).
|
||||
Now, you will get the rewritten history saved in HEAD.
|
||||
|
||||
To set a commit (which typically is at the tip of another
|
||||
history) to be the parent of the current initial commit, in
|
||||
@@ -220,12 +219,7 @@ git filter-branch --commit-filter '
|
||||
fi' HEAD
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Note that the changes introduced by the commits, and not reverted by
|
||||
subsequent commits, will still be in the rewritten branch. If you want
|
||||
to throw out _changes_ together with the commits, you should use the
|
||||
interactive mode of gitlink:git-rebase[1].
|
||||
|
||||
The function 'skip_commits' is defined as follows:
|
||||
The function 'skip_commit' is defined as follows:
|
||||
|
||||
--------------------------
|
||||
skip_commit()
|
||||
|
||||
@@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to
|
||||
the object referred by the ref does not cause an error. It
|
||||
returns an empty string instead.
|
||||
|
||||
As a special case for the date-type fields, you may specify a format for
|
||||
the date by adding one of `:default`, `:relative`, `:short`, `:local`,
|
||||
`:iso8601` or `:rfc2822` to the end of the fieldname; e.g.
|
||||
`%(taggerdate:relative)`.
|
||||
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
@@ -168,7 +168,7 @@ git-format-patch origin::
|
||||
is created in the current directory.
|
||||
|
||||
git-format-patch \--root origin::
|
||||
Extract all commits which that leads to 'origin' since the
|
||||
Extract all commits that lead to 'origin' since the
|
||||
inception of the project.
|
||||
|
||||
git-format-patch -M -B origin::
|
||||
|
||||
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-gc' [--prune] [--aggressive]
|
||||
'git-gc' [--prune] [--aggressive] [--auto]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -19,7 +19,8 @@ created from prior invocations of gitlink:git-add[1].
|
||||
|
||||
Users are encouraged to run this task on a regular basis within
|
||||
each repository to maintain good disk space utilization and good
|
||||
operating performance.
|
||||
operating performance. Some git commands may automatically run
|
||||
`git-gc`; see the `--auto` flag below for details.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@@ -43,6 +44,25 @@ OPTIONS
|
||||
persistent, so this option only needs to be used occasionally; every
|
||||
few hundred changesets or so.
|
||||
|
||||
--auto::
|
||||
With this option, `git gc` checks whether any housekeeping is
|
||||
required; if not, it exits without performing any work.
|
||||
Some git commands run `git gc --auto` after performing
|
||||
operations that could create many loose objects.
|
||||
+
|
||||
Housekeeping is required if there are too many loose objects or
|
||||
too many packs in the repository. If the number of loose objects
|
||||
exceeds the value of the `gc.auto` configuration variable, then
|
||||
all loose objects are combined into a single pack using
|
||||
`git-repack -d -l`. Setting the value of `gc.auto` to 0
|
||||
disables automatic packing of loose objects.
|
||||
+
|
||||
If the number of packs exceeds the value of `gc.autopacklimit`,
|
||||
then existing packs (except those marked with a `.keep` file)
|
||||
are consolidated into a single pack by using the `-A` option of
|
||||
`git-repack`. Setting `gc.autopacklimit` to 0 disables
|
||||
automatic consolidation of packs.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ git-http-push - Push objects over HTTP/DAV to another repository
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-http-push' [--all] [--force] [--verbose] <url> <ref> [<ref>...]
|
||||
'git-http-push' [--all] [--dry-run] [--force] [--verbose] <url> <ref> [<ref>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -30,6 +30,9 @@ OPTIONS
|
||||
the remote repository can lose commits; use it with
|
||||
care.
|
||||
|
||||
--dry-run::
|
||||
Do everything except actually send the updates.
|
||||
|
||||
--verbose::
|
||||
Report the list of objects being walked locally and the
|
||||
list of objects successfully sent to the remote repository.
|
||||
|
||||
@@ -43,7 +43,7 @@ OPTIONS
|
||||
a default name determined from the pack content. If
|
||||
<pack-file> is not specified consider using --keep to
|
||||
prevent a race condition between this process and
|
||||
gitlink::git-repack[1] .
|
||||
gitlink::git-repack[1].
|
||||
|
||||
--fix-thin::
|
||||
It is possible for gitlink:git-pack-objects[1] to build
|
||||
|
||||
@@ -27,7 +27,7 @@ OPTIONS
|
||||
The HTTP daemon command-line that will be executed.
|
||||
Command-line options may be specified here, and the
|
||||
configuration file will be added at the end of the command-line.
|
||||
Currently, lighttpd and apache2 are the only supported servers.
|
||||
Currently lighttpd, apache2 and webrick are supported.
|
||||
(Default: lighttpd)
|
||||
|
||||
-m|--module-path::
|
||||
|
||||
@@ -65,7 +65,7 @@ $ git rev-parse not-lost-anymore
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano 濱野 純 <junkio@cox.net>
|
||||
Written by Junio C Hamano <gitster@pobox.com>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
|
||||
@@ -15,7 +15,7 @@ SYNOPSIS
|
||||
[-x <pattern>|--exclude=<pattern>]
|
||||
[-X <file>|--exclude-from=<file>]
|
||||
[--exclude-per-directory=<file>]
|
||||
[--error-unmatch]
|
||||
[--error-unmatch] [--with-tree=<tree-ish>]
|
||||
[--full-name] [--abbrev] [--] [<file>]\*
|
||||
|
||||
DESCRIPTION
|
||||
@@ -81,6 +81,13 @@ OPTIONS
|
||||
If any <file> does not appear in the index, treat this as an
|
||||
error (return 1).
|
||||
|
||||
--with-tree=<tree-ish>::
|
||||
When using --error-unmatch to expand the user supplied
|
||||
<file> (i.e. path pattern) arguments to paths, pretend
|
||||
that paths which were removed in the index since the
|
||||
named <tree-ish> are still present. Using this option
|
||||
with `-s` or `-u` options does not make any sense.
|
||||
|
||||
-t::
|
||||
Identify the file status with the following tags (followed by
|
||||
a space) at the start of each line:
|
||||
|
||||
@@ -40,7 +40,7 @@ If "git-merge-index" is called with multiple <file>s (or -a) then it
|
||||
processes them in turn only stopping if merge returns a non-zero exit
|
||||
code.
|
||||
|
||||
Typically this is run with the a script calling git's imitation of
|
||||
Typically this is run with a script calling git's imitation of
|
||||
the merge command from the RCS package.
|
||||
|
||||
A sample script called "git-merge-one-file" is included in the
|
||||
|
||||
@@ -11,26 +11,27 @@ SYNOPSIS
|
||||
[verse]
|
||||
'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]...
|
||||
[-m <msg>] <remote> <remote>...
|
||||
'git-merge' <msg> HEAD <remote>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This is the top-level interface to the merge machinery
|
||||
which drives multiple merge strategy scripts.
|
||||
|
||||
The second syntax (<msg> `HEAD` <remote>) is supported for
|
||||
historical reasons. Do not use it from the command line or in
|
||||
new scripts. It is the same as `git merge -m <msg> <remote>`.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
include::merge-options.txt[]
|
||||
|
||||
<msg>::
|
||||
-m <msg>::
|
||||
The commit message to be used for the merge commit (in case
|
||||
it is created). The `git-fmt-merge-msg` script can be used
|
||||
to give a good default for automated `git-merge` invocations.
|
||||
|
||||
<head>::
|
||||
Our branch head commit. This has to be `HEAD`, so new
|
||||
syntax does not require it
|
||||
|
||||
<remote>::
|
||||
Other branch head merged into our branch. You need at
|
||||
least one <remote>. Specifying more than one <remote>
|
||||
@@ -56,8 +57,12 @@ merge.verbosity::
|
||||
message if conflicts were detected. Level 1 outputs only
|
||||
conflicts, 2 outputs conflicts and file changes. Level 5 and
|
||||
above outputs debugging information. The default is level 2.
|
||||
Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable.
|
||||
Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
|
||||
|
||||
branch.<name>.mergeoptions::
|
||||
Sets default options for merging into branch <name>. The syntax and
|
||||
supported options are equal to that of git-merge, but option values
|
||||
containing whitespace characters are currently not supported.
|
||||
|
||||
HOW MERGE WORKS
|
||||
---------------
|
||||
|
||||
@@ -25,12 +25,18 @@ OPTIONS
|
||||
-t or --tool=<tool>::
|
||||
Use the merge resolution program specified by <tool>.
|
||||
Valid merge tools are:
|
||||
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, and opendiff
|
||||
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
|
||||
+
|
||||
If a merge resolution program is not specified, 'git mergetool'
|
||||
will use the configuration variable merge.tool. If the
|
||||
configuration variable merge.tool is not set, 'git mergetool'
|
||||
will pick a suitable default.
|
||||
+
|
||||
You can explicitly provide a full path to the tool by setting the
|
||||
configuration variable mergetool.<tool>.path. For example, you
|
||||
can configure the absolute path to kdiff3 by setting
|
||||
mergetool.kdiff3.path. Otherwise, 'git mergetool' assumes the tool
|
||||
is available in PATH.
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
@@ -25,16 +25,16 @@ is efficient to access. The packed archive format (.pack) is
|
||||
designed to be unpackable without having anything else, but for
|
||||
random access, accompanied with the pack index file (.idx).
|
||||
|
||||
Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
|
||||
any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
|
||||
enables git to read from such an archive.
|
||||
|
||||
'git-unpack-objects' command can read the packed archive and
|
||||
expand the objects contained in the pack into "one-file
|
||||
one-object" format; this is typically done by the smart-pull
|
||||
commands when a pack is created on-the-fly for efficient network
|
||||
transport by their peers.
|
||||
|
||||
Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
|
||||
any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
|
||||
enables git to read from such an archive.
|
||||
|
||||
In a packed archive, an object is either stored as a compressed
|
||||
whole, or as a difference from some other object. The latter is
|
||||
often called a delta.
|
||||
@@ -155,12 +155,8 @@ base-name::
|
||||
generated pack. If not specified, pack compression level is
|
||||
determined first by pack.compression, then by core.compression,
|
||||
and defaults to -1, the zlib default, if neither is set.
|
||||
Data copied from loose objects will be recompressed
|
||||
if core.legacyheaders was true when they were created or if
|
||||
the loose compression level (see core.loosecompression and
|
||||
core.compression) is now a different value than the pack
|
||||
compression level. Add --no-reuse-object if you want to force
|
||||
a uniform compression level on all data no matter the source.
|
||||
Add \--no-reuse-object if you want to force a uniform compression
|
||||
level on all data no matter the source.
|
||||
|
||||
--delta-base-offset::
|
||||
A packed archive can express base object of a delta as
|
||||
@@ -173,6 +169,14 @@ base-name::
|
||||
length, this option typically shrinks the resulting
|
||||
packfile by 3-5 per-cent.
|
||||
|
||||
--threads=<n>::
|
||||
Specifies the number of threads to spawn when searching for best
|
||||
delta matches. This requires that pack-objects be compiled with
|
||||
pthreads otherwise this option is ignored with a warning.
|
||||
This is meant to reduce packing time on multiprocessor machines.
|
||||
The required amount of memory for the delta search window is
|
||||
however multiplied by the number of threads.
|
||||
|
||||
--index-version=<version>[,<offset>]::
|
||||
This is intended to be used by the test suite only. It allows
|
||||
to force the version for the generated pack index, and to force
|
||||
|
||||
@@ -13,7 +13,7 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This program search the `$GIT_OBJECT_DIR` for all objects that currently
|
||||
This program searches the `$GIT_OBJECT_DIR` for all objects that currently
|
||||
exist in a pack file as well as the independent object directories.
|
||||
|
||||
All such extra objects are removed.
|
||||
|
||||
@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-push' [--all] [--tags] [--receive-pack=<git-receive-pack>]
|
||||
'git-push' [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>]
|
||||
[--repo=all] [-f | --force] [-v] [<repository> <refspec>...]
|
||||
|
||||
DESCRIPTION
|
||||
@@ -48,7 +48,7 @@ even if it does not result in a fast forward update.
|
||||
Note: If no explicit refspec is found, (that is neither
|
||||
on the command line nor in any Push line of the
|
||||
corresponding remotes file---see below), then all the
|
||||
refs that exist both on the local side and on the remote
|
||||
heads that exist both on the local side and on the remote
|
||||
side are updated.
|
||||
+
|
||||
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
|
||||
@@ -61,7 +61,10 @@ the remote repository.
|
||||
|
||||
\--all::
|
||||
Instead of naming each ref to push, specifies that all
|
||||
refs be pushed.
|
||||
refs under `$GIT_DIR/refs/heads/` be pushed.
|
||||
|
||||
\--dry-run::
|
||||
Do everything except actually send the updates.
|
||||
|
||||
\--tags::
|
||||
All refs under `$GIT_DIR/refs/tags` are pushed, in
|
||||
@@ -117,6 +120,12 @@ git push origin master:satellite/master::
|
||||
the ref that matches `satellite/master` (most likely, it would
|
||||
be `refs/remotes/satellite/master`) in `origin` repository with it.
|
||||
|
||||
git push origin master:refs/heads/experimental::
|
||||
Create the branch `experimental` in the `origin` repository
|
||||
by copying the current `master` branch. This form is usually
|
||||
needed to create a new branch in the remote repository as
|
||||
there is no `experimental` branch to match.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano <junkio@cox.net>, later rewritten in C
|
||||
|
||||
@@ -8,8 +8,9 @@ git-rebase - Forward-port local commits to the updated upstream head
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-C<n>]
|
||||
[-p | --preserve-merges] [--onto <newbase>] <upstream> [<branch>]
|
||||
'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
|
||||
[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
|
||||
[--onto <newbase>] <upstream> [<branch>]
|
||||
'git-rebase' --continue | --skip | --abort
|
||||
|
||||
DESCRIPTION
|
||||
@@ -27,7 +28,10 @@ The current branch is reset to <upstream>, or <newbase> if the
|
||||
`git reset --hard <upstream>` (or <newbase>).
|
||||
|
||||
The commits that were previously saved into the temporary area are
|
||||
then reapplied to the current branch, one by one, in order.
|
||||
then reapplied to the current branch, one by one, in order. Note that
|
||||
any commits in HEAD which introduce the same textual changes as a commit
|
||||
in HEAD..<upstream> are omitted (i.e., a patch already accepted upstream
|
||||
with a different commit message or timestamp will be skipped).
|
||||
|
||||
It is possible that a merge failure will prevent this process from being
|
||||
completely automatic. You will have to resolve any such merge failure
|
||||
@@ -61,6 +65,26 @@ would be:
|
||||
The latter form is just a short-hand of `git checkout topic`
|
||||
followed by `git rebase master`.
|
||||
|
||||
If the upstream branch already contains a change you have made (e.g.,
|
||||
because you mailed a patch which was applied upstream), then that commit
|
||||
will be skipped. For example, running `git-rebase master` on the
|
||||
following history (in which A' and A introduce the same set of changes,
|
||||
but have different committer information):
|
||||
|
||||
------------
|
||||
A---B---C topic
|
||||
/
|
||||
D---E---A'---F master
|
||||
------------
|
||||
|
||||
will result in:
|
||||
|
||||
------------
|
||||
B'---C' topic
|
||||
/
|
||||
D---E---A'---F master
|
||||
------------
|
||||
|
||||
Here is how you would transplant a topic branch based on one
|
||||
branch to another, to pretend that you forked the topic branch
|
||||
from the latter branch, using `rebase --onto`.
|
||||
@@ -209,6 +233,10 @@ OPTIONS
|
||||
context exist they all must match. By default no context is
|
||||
ever ignored.
|
||||
|
||||
--whitespace=<nowarn|warn|error|error-all|strip>::
|
||||
This flag is passed to the `git-apply` program
|
||||
(see gitlink:git-apply[1]) that applies the patch.
|
||||
|
||||
-i, \--interactive::
|
||||
Make a list of the commits which are about to be rebased. Let the
|
||||
user edit that list before rebasing. This mode can also be used to
|
||||
@@ -293,7 +321,7 @@ rebasing.
|
||||
If you want to fold two or more commits into one, replace the command
|
||||
"pick" with "squash" for the second and subsequent commit. If the
|
||||
commits had different authors, it will attribute the squashed commit to
|
||||
the author of the last commit.
|
||||
the author of the first commit.
|
||||
|
||||
In both cases, or when a "pick" does not succeed (because of merge
|
||||
errors), the loop will stop to let you fix things, and you can continue
|
||||
|
||||
@@ -16,7 +16,7 @@ The command takes various subcommands, and different options
|
||||
depending on the subcommand:
|
||||
|
||||
[verse]
|
||||
git reflog expire [--dry-run] [--stale-fix]
|
||||
git reflog expire [--dry-run] [--stale-fix] [--verbose]
|
||||
[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
|
||||
|
||||
git reflog [show] [log-options]
|
||||
@@ -68,6 +68,9 @@ them.
|
||||
--all::
|
||||
Instead of listing <refs> explicitly, prune all refs.
|
||||
|
||||
--verbose::
|
||||
Print extra information on screen.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano <junkio@cox.net>
|
||||
|
||||
@@ -10,7 +10,8 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-remote'
|
||||
'git-remote' add [-t <branch>] [-m <branch>] [-f] <name> <url>
|
||||
'git-remote' add [-t <branch>] [-m <branch>] [-f] [--mirror] <name> <url>
|
||||
'git-remote' rm <name>
|
||||
'git-remote' show <name>
|
||||
'git-remote' prune <name>
|
||||
'git-remote' update [group]
|
||||
@@ -45,6 +46,15 @@ multiple branches without grabbing all branches.
|
||||
With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
|
||||
up to point at remote's `<master>` branch instead of whatever
|
||||
branch the `HEAD` at the remote repository actually points at.
|
||||
+
|
||||
In mirror mode, enabled with `--mirror`, the refs will not be stored
|
||||
in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option
|
||||
only makes sense in bare repositories.
|
||||
|
||||
'rm'::
|
||||
|
||||
Remove the remote named <name>. All remote tracking branches and
|
||||
configuration settings for the remote are removed.
|
||||
|
||||
'show'::
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ SYNOPSIS
|
||||
[ \--pretty | \--header ]
|
||||
[ \--bisect ]
|
||||
[ \--bisect-vars ]
|
||||
[ \--bisect-all ]
|
||||
[ \--merge ]
|
||||
[ \--reverse ]
|
||||
[ \--walk-reflogs ]
|
||||
@@ -354,6 +355,21 @@ the expected number of commits to be tested if `bisect_rev`
|
||||
turns out to be bad to `bisect_bad`, and the number of commits
|
||||
we are bisecting right now to `bisect_all`.
|
||||
|
||||
--bisect-all::
|
||||
|
||||
This outputs all the commit objects between the included and excluded
|
||||
commits, ordered by their distance to the included and excluded
|
||||
commits. The farthest from them is displayed first. (This is the only
|
||||
one displayed by `--bisect`.)
|
||||
|
||||
This is useful because it makes it easy to choose a good commit to
|
||||
test when you want to avoid to test some of them for some reason (they
|
||||
may not compile for example).
|
||||
|
||||
This option can be used along with `--bisect-vars`, in this case,
|
||||
after all the sorted commit objects, there will be the same text as if
|
||||
`--bisect-vars` had been used alone.
|
||||
|
||||
--
|
||||
|
||||
Commit Ordering
|
||||
|
||||
@@ -75,6 +75,12 @@ The --cc option must be repeated for each user you want on the cc list.
|
||||
Make git-send-email less verbose. One line per email should be
|
||||
all that is output.
|
||||
|
||||
--identity::
|
||||
A configuration identity. When given, causes values in the
|
||||
'sendemail.<identity>' subsection to take precedence over
|
||||
values in the 'sendemail' section. The default identity is
|
||||
the value of 'sendemail.identity'.
|
||||
|
||||
--smtp-server::
|
||||
If set, specifies the outgoing SMTP server to use (e.g.
|
||||
`smtp.example.com` or a raw IP address). Alternatively it can
|
||||
@@ -85,6 +91,22 @@ The --cc option must be repeated for each user you want on the cc list.
|
||||
`/usr/lib/sendmail` if such program is available, or
|
||||
`localhost` otherwise.
|
||||
|
||||
--smtp-server-port::
|
||||
Specifies a port different from the default port (SMTP
|
||||
servers typically listen to smtp port 25 and ssmtp port
|
||||
465).
|
||||
|
||||
--smtp-user, --smtp-pass::
|
||||
Username and password for SMTP-AUTH. Defaults are the values of
|
||||
the configuration values 'sendemail.smtpuser' and
|
||||
'sendemail.smtppass', but see also 'sendemail.identity'.
|
||||
If not set, authentication is not attempted.
|
||||
|
||||
--smtp-ssl::
|
||||
If set, connects to the SMTP server using SSL.
|
||||
Default is the value of the 'sendemail.smtpssl' configuration value;
|
||||
if that is unspecified, does not use SSL.
|
||||
|
||||
--subject::
|
||||
Specify the initial subject of the email thread.
|
||||
Only necessary if --compose is also set. If --compose
|
||||
@@ -122,6 +144,13 @@ The --to option must be repeated for each user you want on the to list.
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
sendemail.identity::
|
||||
The default configuration identity. When specified,
|
||||
'sendemail.<identity>.<item>' will have higher precedence than
|
||||
'sendemail.<item>'. This is useful to declare multiple SMTP
|
||||
identities and to hoist sensitive authentication information
|
||||
out of the repository and into the global configuation file.
|
||||
|
||||
sendemail.aliasesfile::
|
||||
To avoid typing long email addresses, point this to one or more
|
||||
email aliases files. You must also supply 'sendemail.aliasfiletype'.
|
||||
@@ -130,6 +159,9 @@ sendemail.aliasfiletype::
|
||||
Format of the file(s) specified in sendemail.aliasesfile. Must be
|
||||
one of 'mutt', 'mailrc', 'pine', or 'gnus'.
|
||||
|
||||
sendemail.to::
|
||||
Email address (or alias) to always send to.
|
||||
|
||||
sendemail.cccmd::
|
||||
Command to execute to generate per patch file specific "Cc:"s.
|
||||
|
||||
@@ -141,7 +173,16 @@ sendemail.chainreplyto::
|
||||
parameter.
|
||||
|
||||
sendemail.smtpserver::
|
||||
Default smtp server to use.
|
||||
Default SMTP server to use.
|
||||
|
||||
sendemail.smtpuser::
|
||||
Default SMTP-AUTH username.
|
||||
|
||||
sendemail.smtppass::
|
||||
Default SMTP-AUTH password.
|
||||
|
||||
sendemail.smtpssl::
|
||||
Boolean value specifying the default to the '--smtp-ssl' parameter.
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
@@ -8,7 +8,7 @@ git-send-pack - Push objects over git protocol to another repository
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-send-pack' [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
|
||||
'git-send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -32,7 +32,10 @@ OPTIONS
|
||||
|
||||
\--all::
|
||||
Instead of explicitly specifying which refs to update,
|
||||
update all refs that locally exist.
|
||||
update all heads that locally exist.
|
||||
|
||||
\--dry-run::
|
||||
Do everything except actually send the updates.
|
||||
|
||||
\--force::
|
||||
Usually, the command refuses to update a remote ref that
|
||||
@@ -70,7 +73,7 @@ With '--all' flag, all refs that exist locally are transferred to
|
||||
the remote side. You cannot specify any '<ref>' if you use
|
||||
this flag.
|
||||
|
||||
Without '--all' and without any '<ref>', the refs that exist
|
||||
Without '--all' and without any '<ref>', the heads that exist
|
||||
both on the local side and on the remote side are updated.
|
||||
|
||||
When one or more '<ref>' are specified explicitly, it can be either a
|
||||
|
||||
@@ -57,13 +57,13 @@ stash@{1}: On master: 9cc0589... Add git-stash
|
||||
|
||||
show [<stash>]::
|
||||
|
||||
Show the changes recorded in the stash as a diff between the the
|
||||
Show the changes recorded in the stash as a diff between the
|
||||
stashed state and its original parent. When no `<stash>` is given,
|
||||
shows the latest one. By default, the command shows the diffstat, but
|
||||
it will accept any format known to `git-diff` (e.g., `git-stash show
|
||||
-p stash@\{1}` to view the second most recent stash in patch form).
|
||||
|
||||
apply [<stash>]::
|
||||
apply [--index] [<stash>]::
|
||||
|
||||
Restore the changes recorded in the stash on top of the current
|
||||
working tree state. When no `<stash>` is given, applies the latest
|
||||
@@ -71,6 +71,11 @@ apply [<stash>]::
|
||||
+
|
||||
This operation can fail with conflicts; you need to resolve them
|
||||
by hand in the working tree.
|
||||
+
|
||||
If the `--index` option is used, then tries to reinstate not only the working
|
||||
tree's changes, but also the index's ones. However, this can fail, when you
|
||||
have conflicts (which are stored in the index, where you therefore can no
|
||||
longer apply the changes as they were originally).
|
||||
|
||||
clear::
|
||||
Remove all the stashed states. Note that those states will then
|
||||
|
||||
@@ -21,6 +21,9 @@ add::
|
||||
repository is cloned at the specified path, added to the
|
||||
changeset and registered in .gitmodules. If no path is
|
||||
specified, the path is deduced from the repository specification.
|
||||
If the repository url begins with ./ or ../, it is stored as
|
||||
given but resolved as a relative path from the main project's
|
||||
url when cloning.
|
||||
|
||||
status::
|
||||
Show the status of the submodules. This will print the SHA-1 of the
|
||||
|
||||
@@ -404,7 +404,7 @@ section because they affect the 'git-svn-id:' metadata line.
|
||||
BASIC EXAMPLES
|
||||
--------------
|
||||
|
||||
Tracking and contributing to a the trunk of a Subversion-managed project:
|
||||
Tracking and contributing to the trunk of a Subversion-managed project:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
# Clone a repo (like git clone):
|
||||
@@ -478,11 +478,12 @@ previous commits in SVN.
|
||||
DESIGN PHILOSOPHY
|
||||
-----------------
|
||||
Merge tracking in Subversion is lacking and doing branched development
|
||||
with Subversion is cumbersome as a result. git-svn does not do
|
||||
automated merge/branch tracking by default and leaves it entirely up to
|
||||
the user on the git side. git-svn does however follow copy
|
||||
history of the directory that it is tracking, however (much like
|
||||
how 'svn log' works).
|
||||
with Subversion can be cumbersome as a result. While git-svn can track
|
||||
copy history (including branches and tags) for repositories adopting a
|
||||
standard layout, it cannot yet represent merge history that happened
|
||||
inside git back upstream to SVN users. Therefore it is advised that
|
||||
users keep history as linear as possible inside git to ease
|
||||
compatibility with SVN (see the CAVEATS section below).
|
||||
|
||||
CAVEATS
|
||||
-------
|
||||
|
||||
@@ -112,7 +112,7 @@ You really want to call the new version "X" too, 'even though'
|
||||
others have already seen the old one. So just use "git tag -f"
|
||||
again, as if you hadn't already published the old one.
|
||||
|
||||
However, Git does *not* (and it should not)change tags behind
|
||||
However, Git does *not* (and it should not) change tags behind
|
||||
users back. So if somebody already got the old tag, doing a "git
|
||||
pull" on your tree shouldn't just make them overwrite the old
|
||||
one.
|
||||
@@ -214,6 +214,27 @@ having tracking branches. Again, the heuristic to automatically
|
||||
follow such tags is a good thing.
|
||||
|
||||
|
||||
On Backdating Tags
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you have imported some changes from another VCS and would like
|
||||
to add tags for major releases of your work, it is useful to be able
|
||||
to specify the date to embed inside of the tag object. The data in
|
||||
the tag object affects, for example, the ordering of tags in the
|
||||
gitweb interface.
|
||||
|
||||
To set the date used in future tag objects, set the environment
|
||||
variable GIT_AUTHOR_DATE to one or more of the date and time. The
|
||||
date and time can be specified in a number of ways; the most common
|
||||
is "YYYY-MM-DD HH:MM".
|
||||
|
||||
An example follows.
|
||||
|
||||
------------
|
||||
$ GIT_AUTHOR_DATE="2006-10-02 10:31" git tag -s v1.0.1
|
||||
------------
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>,
|
||||
|
||||
@@ -22,6 +22,9 @@ Alternative/Augmentative Porcelains
|
||||
providing generally smoother user experience than the "raw" Core GIT
|
||||
itself and indeed many other version control systems.
|
||||
|
||||
Cogito is no longer maintained as most of its functionality
|
||||
is now in core GIT.
|
||||
|
||||
|
||||
- *pg* (http://www.spearce.org/category/projects/scm/pg/)
|
||||
|
||||
@@ -33,7 +36,7 @@ Alternative/Augmentative Porcelains
|
||||
- *StGit* (http://www.procode.org/stgit/)
|
||||
|
||||
Stacked GIT provides a quilt-like patch management functionality in the
|
||||
GIT environment. You can easily manage your patches in the scope of GIT
|
||||
GIT environment. You can easily manage your patches in the scope of GIT
|
||||
until they get merged upstream.
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,15 @@ unreleased) version of git, that is available from 'master'
|
||||
branch of the `git.git` repository.
|
||||
Documentation for older releases are available here:
|
||||
|
||||
* link:v1.5.2.5/git.html[documentation for release 1.5.2.5]
|
||||
* link:v1.5.3/git.html[documentation for release 1.5.3]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.5.3.5.txt[1.5.3.5],
|
||||
link:RelNotes-1.5.3.4.txt[1.5.3.4],
|
||||
link:RelNotes-1.5.3.3.txt[1.5.3.3],
|
||||
link:RelNotes-1.5.3.2.txt[1.5.3.2],
|
||||
link:RelNotes-1.5.3.1.txt[1.5.3.1],
|
||||
link:RelNotes-1.5.3.txt[1.5.3].
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.5.2.5.txt[1.5.2.5],
|
||||
@@ -131,9 +139,9 @@ FURTHER DOCUMENTATION
|
||||
See the references above to get started using git. The following is
|
||||
probably more detail than necessary for a first-time user.
|
||||
|
||||
The <<Discussion,Discussion>> section below and the
|
||||
link:core-tutorial.html[Core tutorial] both provide introductions to the
|
||||
underlying git architecture.
|
||||
The link:user-manual.html#git-concepts[git concepts chapter of the
|
||||
user-manual] and the link:core-tutorial.html[Core tutorial] both provide
|
||||
introductions to the underlying git architecture.
|
||||
|
||||
See also the link:howto-index.html[howto] documents for some useful
|
||||
examples.
|
||||
@@ -320,7 +328,7 @@ For a more complete list of ways to spell object names, see
|
||||
File/Directory Structure
|
||||
------------------------
|
||||
|
||||
Please see link:repository-layout.html[repository layout] document.
|
||||
Please see the link:repository-layout.html[repository layout] document.
|
||||
|
||||
Read link:hooks.html[hooks] for more details about each hook.
|
||||
|
||||
@@ -330,7 +338,7 @@ Higher level SCMs may provide and manage additional information in the
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
Please see link:glossary.html[glossary] document.
|
||||
Please see the link:glossary.html[glossary] document.
|
||||
|
||||
|
||||
Environment Variables
|
||||
@@ -471,7 +479,56 @@ for further details.
|
||||
|
||||
Discussion[[Discussion]]
|
||||
------------------------
|
||||
include::core-intro.txt[]
|
||||
|
||||
More detail on the following is available from the
|
||||
link:user-manual.html#git-concepts[git concepts chapter of the
|
||||
user-manual] and the link:core-tutorial.html[Core tutorial].
|
||||
|
||||
A git project normally consists of a working directory with a ".git"
|
||||
subdirectory at the top level. The .git directory contains, among other
|
||||
things, a compressed object database representing the complete history
|
||||
of the project, an "index" file which links that history to the current
|
||||
contents of the working tree, and named pointers into that history such
|
||||
as tags and branch heads.
|
||||
|
||||
The object database contains objects of three main types: blobs, which
|
||||
hold file data; trees, which point to blobs and other trees to build up
|
||||
directory heirarchies; and commits, which each reference a single tree
|
||||
and some number of parent commits.
|
||||
|
||||
The commit, equivalent to what other systems call a "changeset" or
|
||||
"version", represents a step in the project's history, and each parent
|
||||
represents an immediately preceding step. Commits with more than one
|
||||
parent represent merges of independent lines of development.
|
||||
|
||||
All objects are named by the SHA1 hash of their contents, normally
|
||||
written as a string of 40 hex digits. Such names are globally unique.
|
||||
The entire history leading up to a commit can be vouched for by signing
|
||||
just that commit. A fourth object type, the tag, is provided for this
|
||||
purpose.
|
||||
|
||||
When first created, objects are stored in individual files, but for
|
||||
efficiency may later be compressed together into "pack files".
|
||||
|
||||
Named pointers called refs mark interesting points in history. A ref
|
||||
may contain the SHA1 name of an object or the name of another ref. Refs
|
||||
with names beginning `ref/head/` contain the SHA1 name of the most
|
||||
recent commit (or "head") of a branch under developement. SHA1 names of
|
||||
tags of interest are stored under `ref/tags/`. A special ref named
|
||||
`HEAD` contains the name of the currently checked-out branch.
|
||||
|
||||
The index file is initialized with a list of all paths and, for each
|
||||
path, a blob object and a set of attributes. The blob object represents
|
||||
the contents of the file as of the head of the current branch. The
|
||||
attributes (last modified time, size, etc.) are taken from the
|
||||
corresponding file in the working tree. Subsequent changes to the
|
||||
working tree can be found by comparing these attributes. The index may
|
||||
be updated with new content, and new commits may be created from the
|
||||
content stored in the index.
|
||||
|
||||
The index is also capable of storing multiple entries (called "stages")
|
||||
for a given pathname. These stages are used to hold the various
|
||||
unmerged version of a file when a merge is in progress.
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
@@ -145,17 +145,6 @@ sign `$` upon checkout. Any byte sequence that begins with
|
||||
with `$Id$` upon check-in.
|
||||
|
||||
|
||||
Interaction between checkin/checkout attributes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In the check-in codepath, the worktree file is first converted
|
||||
with `ident` (if specified), and then with `crlf` (again, if
|
||||
specified and applicable).
|
||||
|
||||
In the check-out codepath, the blob content is first converted
|
||||
with `crlf`, and then `ident`.
|
||||
|
||||
|
||||
`filter`
|
||||
^^^^^^^^
|
||||
|
||||
@@ -175,11 +164,10 @@ but makes the filter a no-op passthru.
|
||||
The content filtering is done to massage the content into a
|
||||
shape that is more convenient for the platform, filesystem, and
|
||||
the user to use. The keyword here is "more convenient" and not
|
||||
"turning something unusable into usable". In other words, it is
|
||||
"hanging yourself because we gave you a long rope" if your
|
||||
project uses filtering mechanism in such a way that it makes
|
||||
your project unusable unless the checkout is done with a
|
||||
specific filter in effect.
|
||||
"turning something unusable into usable". In other words, the
|
||||
intent is that if someone unsets the filter driver definition,
|
||||
or does not have the appropriate filter program, the project
|
||||
should still be usable.
|
||||
|
||||
|
||||
Interaction between checkin/checkout attributes
|
||||
@@ -421,6 +409,23 @@ frotz unspecified
|
||||
----------------------------------------------------------------
|
||||
|
||||
|
||||
Creating an archive
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
`export-subst`
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
If the attribute `export-subst` is set for a file then git will expand
|
||||
several placeholders when adding this file to an archive. The
|
||||
expansion depends on the availability of a commit ID, i.e. if
|
||||
gitlink:git-archive[1] has been given a tree instead of a commit or a
|
||||
tag then no replacement will be done. The placeholders are the same
|
||||
as those for the option `--pretty=format:` of gitlink:git-log[1],
|
||||
except that they need to be wrapped like this: `$Format:PLACEHOLDERS$`
|
||||
in the file. E.g. the string `$Format:%H$` will be replaced by the
|
||||
commit hash.
|
||||
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
|
||||
@@ -26,7 +26,7 @@ precedence, the last matching pattern decides the outcome):
|
||||
|
||||
* Patterns read from a `.gitignore` file in the same directory
|
||||
as the path, or in any parent directory, with patterns in the
|
||||
higher level files (up to the root) being overriden by those in
|
||||
higher level files (up to the root) being overridden by those in
|
||||
lower level files down to the directory containing the file.
|
||||
These patterns match relative to the location of the
|
||||
`.gitignore` file. A project normally includes such
|
||||
|
||||
@@ -69,7 +69,7 @@ gitk --since="2 weeks ago" \-- gitk::
|
||||
The "--" is necessary to avoid confusion with the *branch* named
|
||||
'gitk'
|
||||
|
||||
gitk --max-count=100 --all -- Makefile::
|
||||
gitk --max-count=100 --all \-- Makefile::
|
||||
|
||||
Show at most 100 changes made to the file 'Makefile'. Instead of only
|
||||
looking for changes in the current branch look in all branches.
|
||||
|
||||
@@ -52,8 +52,8 @@ GIT Glossary
|
||||
[[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
|
||||
as a new series of changes on top of a different codebase. In GIT, this is
|
||||
performed by the "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 commit.
|
||||
|
||||
@@ -281,7 +281,7 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
[[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
|
||||
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].
|
||||
|
||||
@@ -301,8 +301,8 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
[[def_push]]push::
|
||||
Pushing a <<def_branch,branch>> means to get the branch's
|
||||
<<def_head_ref,head ref>> from a remote <<def_repository,repository>>,
|
||||
find out if it is an ancestor to the branch's local
|
||||
head ref is a direct, and in that case, putting all
|
||||
find out if it is a direct ancestor to the branch's local
|
||||
head ref, and in that case, putting all
|
||||
objects, which are <<def_reachable,reachable>> from the local
|
||||
head ref, and which are missing from the remote
|
||||
repository, into the remote
|
||||
@@ -347,7 +347,7 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
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]
|
||||
gitlink:git-push[1].
|
||||
|
||||
[[def_repository]]repository::
|
||||
A collection of <<def_ref,refs>> together with an
|
||||
|
||||
@@ -87,6 +87,33 @@ parameter, and is invoked after a commit is made.
|
||||
This hook is meant primarily for notification, and cannot affect
|
||||
the outcome of `git-commit`.
|
||||
|
||||
post-checkout
|
||||
-----------
|
||||
|
||||
This hook is invoked when a `git-checkout` is run after having updated the
|
||||
worktree. The hook is given three parameters: the ref of the previous HEAD,
|
||||
the ref of the new HEAD (which may or may not have changed), and a flag
|
||||
indicating whether the checkout was a branch checkout (changing branches,
|
||||
flag=1) or a file checkout (retrieving a file from the index, flag=0).
|
||||
This hook cannot affect the outcome of `git-checkout`.
|
||||
|
||||
This hook can be used to perform repository validity checks, auto-display
|
||||
differences from the previous HEAD if different, or set working dir metadata
|
||||
properties.
|
||||
|
||||
post-merge
|
||||
-----------
|
||||
|
||||
This hook is invoked by `git-merge`, which happens when a `git pull`
|
||||
is done on a local repository. The hook takes a single parameter, a status
|
||||
flag specifying whether or not the merge being done was a squash merge.
|
||||
This hook cannot affect the outcome of `git-merge`.
|
||||
|
||||
This hook can be used in conjunction with a corresponding pre-commit hook to
|
||||
save and restore any form of metadata associated with the working tree
|
||||
(eg: permissions/ownership, ACLS, etc). See contrib/hooks/setgitperms.perl
|
||||
for an example of how to do this.
|
||||
|
||||
[[pre-receive]]
|
||||
pre-receive
|
||||
-----------
|
||||
|
||||
@@ -158,11 +158,11 @@ This uses two files, $GIT_DIR/info/allowed-users and
|
||||
allowed-groups, to describe which heads can be pushed into by
|
||||
whom. The format of each file would look like this:
|
||||
|
||||
refs/heads/master junio
|
||||
refs/heads/master junio
|
||||
refs/heads/cogito$ pasky
|
||||
refs/heads/bw/ linus
|
||||
refs/heads/tmp/ *
|
||||
refs/tags/v[0-9]* junio
|
||||
refs/heads/bw/.* linus
|
||||
refs/heads/tmp/.* .*
|
||||
refs/tags/v[0-9].* junio
|
||||
|
||||
With this, Linus can push or create "bw/penguin" or "bw/zebra"
|
||||
or "bw/panda" branches, Pasky can do only "cogito", and JC can
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
not autocommit, to give the user a chance to inspect and
|
||||
further tweak the merge result before committing.
|
||||
|
||||
--commit::
|
||||
Perform the merge and commit the result. This option can
|
||||
be used to override --no-commit.
|
||||
|
||||
--squash::
|
||||
Produce the working tree and index state as if a real
|
||||
merge happened, but do not actually make a commit or
|
||||
@@ -19,6 +23,19 @@
|
||||
top of the current branch whose effect is the same as
|
||||
merging another branch (or more in case of an octopus).
|
||||
|
||||
--no-squash::
|
||||
Perform the merge and commit the result. This option can
|
||||
be used to override --squash.
|
||||
|
||||
--no-ff::
|
||||
Generate a merge commit even if the merge resolved as a
|
||||
fast-forward.
|
||||
|
||||
--ff::
|
||||
Do not generate a merge commit if the merge resolved as
|
||||
a fast-forward, only update the branch pointer. This is
|
||||
the default behavior of git-merge.
|
||||
|
||||
-s <strategy>, \--strategy=<strategy>::
|
||||
Use the given merge strategy; can be supplied more than
|
||||
once to specify them in the order they should be tried.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
3
INSTALL
3
INSTALL
@@ -79,6 +79,9 @@ Issues of note:
|
||||
- "perl" and POSIX-compliant shells are needed to use most of
|
||||
the barebone Porcelainish scripts.
|
||||
|
||||
- "cpio" is used by git-merge for saving and restoring the index,
|
||||
and by git-clone when doing a local (possibly hardlinked) clone.
|
||||
|
||||
- Some platform specific issues are dealt with Makefile rules,
|
||||
but depending on your specific installation, you may not
|
||||
have all the libraries/tools needed, or you may have
|
||||
|
||||
118
Makefile
118
Makefile
@@ -28,6 +28,8 @@ all::
|
||||
#
|
||||
# Define NO_STRCASESTR if you don't have strcasestr.
|
||||
#
|
||||
# Define NO_MEMMEM if you don't have memmem.
|
||||
#
|
||||
# Define NO_STRLCPY if you don't have strlcpy.
|
||||
#
|
||||
# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
|
||||
@@ -36,6 +38,8 @@ all::
|
||||
#
|
||||
# Define NO_SETENV if you don't have setenv in the C library.
|
||||
#
|
||||
# Define NO_MKDTEMP if you don't have mkdtemp in the C library.
|
||||
#
|
||||
# Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
|
||||
# Enable it on Windows. By default, symrefs are still used.
|
||||
#
|
||||
@@ -125,6 +129,9 @@ all::
|
||||
# If not set it defaults to the bare 'wish'. If it is set to the empty
|
||||
# string then NO_TCLTK will be forced (this is used by configure script).
|
||||
#
|
||||
# Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
|
||||
# parallel delta searching when packing objects.
|
||||
#
|
||||
|
||||
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
||||
@"$(SHELL_PATH)" ./GIT-VERSION-GEN
|
||||
@@ -163,6 +170,7 @@ GITWEB_CONFIG = gitweb_config.perl
|
||||
GITWEB_HOME_LINK_STR = projects
|
||||
GITWEB_SITENAME =
|
||||
GITWEB_PROJECTROOT = /pub/git
|
||||
GITWEB_PROJECT_MAXDEPTH = 2007
|
||||
GITWEB_EXPORT_OK =
|
||||
GITWEB_STRICT_EXPORT =
|
||||
GITWEB_BASE_URL =
|
||||
@@ -205,11 +213,10 @@ BASIC_LDFLAGS =
|
||||
SCRIPT_SH = \
|
||||
git-bisect.sh git-checkout.sh \
|
||||
git-clean.sh git-clone.sh git-commit.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 git-rebase--interactive.sh \
|
||||
git-repack.sh git-request-pull.sh git-reset.sh \
|
||||
git-repack.sh git-request-pull.sh \
|
||||
git-sh-setup.sh \
|
||||
git-am.sh \
|
||||
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
|
||||
@@ -221,8 +228,7 @@ SCRIPT_SH = \
|
||||
SCRIPT_PERL = \
|
||||
git-add--interactive.perl \
|
||||
git-archimport.perl git-cvsimport.perl git-relink.perl \
|
||||
git-cvsserver.perl git-remote.perl \
|
||||
git-svnimport.perl git-cvsexportcommit.perl \
|
||||
git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
|
||||
git-send-email.perl git-svn.perl
|
||||
|
||||
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||
@@ -232,13 +238,13 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||
# ... and all the rest that could be moved out of bindir to gitexecdir
|
||||
PROGRAMS = \
|
||||
git-fetch-pack$X \
|
||||
git-hash-object$X git-index-pack$X git-local-fetch$X \
|
||||
git-hash-object$X git-index-pack$X \
|
||||
git-fast-import$X \
|
||||
git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
|
||||
git-peek-remote$X git-receive-pack$X \
|
||||
git-send-pack$X git-shell$X \
|
||||
git-show-index$X git-ssh-fetch$X \
|
||||
git-ssh-upload$X git-unpack-file$X \
|
||||
git-show-index$X \
|
||||
git-unpack-file$X \
|
||||
git-update-server-info$X \
|
||||
git-upload-pack$X \
|
||||
git-pack-redundant$X git-var$X \
|
||||
@@ -266,9 +272,6 @@ ifndef NO_TCLTK
|
||||
OTHER_PROGRAMS += gitk-wish
|
||||
endif
|
||||
|
||||
# Backward compatibility -- to be removed after 1.0
|
||||
PROGRAMS += git-ssh-pull$X git-ssh-push$X
|
||||
|
||||
# Set paths to tools early so that they can be used for version tests.
|
||||
ifndef SHELL_PATH
|
||||
SHELL_PATH = /bin/sh
|
||||
@@ -283,13 +286,13 @@ LIB_FILE=libgit.a
|
||||
XDIFF_LIB=xdiff/lib.a
|
||||
|
||||
LIB_H = \
|
||||
archive.h blob.h cache.h commit.h csum-file.h delta.h grep.h \
|
||||
archive.h blob.h cache.h cache-tree.h commit.h csum-file.h delta.h grep.h \
|
||||
diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
|
||||
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
|
||||
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
|
||||
spawn-pipe.h \
|
||||
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
|
||||
remote.h
|
||||
mailmap.h remote.h transport.h diffcore.h hash.h
|
||||
|
||||
DIFF_OBJS = \
|
||||
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
|
||||
@@ -299,7 +302,7 @@ DIFF_OBJS = \
|
||||
LIB_OBJS = \
|
||||
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
|
||||
date.o diff-delta.o entry.o exec_cmd.o ident.o \
|
||||
interpolate.o \
|
||||
interpolate.o hash.o \
|
||||
lockfile.o \
|
||||
spawn-pipe.o \
|
||||
patch-ids.o \
|
||||
@@ -312,7 +315,8 @@ LIB_OBJS = \
|
||||
write_or_die.o trace.o list-objects.o grep.o match-trees.o \
|
||||
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
|
||||
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
|
||||
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o
|
||||
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
|
||||
transport.o bundle.o walker.o
|
||||
|
||||
BUILTIN_OBJS = \
|
||||
builtin-add.o \
|
||||
@@ -333,6 +337,8 @@ BUILTIN_OBJS = \
|
||||
builtin-diff-files.o \
|
||||
builtin-diff-index.o \
|
||||
builtin-diff-tree.o \
|
||||
builtin-fetch.o \
|
||||
builtin-fetch-pack.o \
|
||||
builtin-fetch--tool.o \
|
||||
builtin-fmt-merge-msg.o \
|
||||
builtin-for-each-ref.o \
|
||||
@@ -357,6 +363,7 @@ BUILTIN_OBJS = \
|
||||
builtin-reflog.o \
|
||||
builtin-config.o \
|
||||
builtin-rerere.o \
|
||||
builtin-reset.o \
|
||||
builtin-rev-list.o \
|
||||
builtin-rev-parse.o \
|
||||
builtin-revert.o \
|
||||
@@ -400,23 +407,27 @@ ifeq ($(uname_S),Darwin)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
OLD_ICONV = UnfortunatelyYes
|
||||
NO_STRLCPY = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
endif
|
||||
ifeq ($(uname_S),SunOS)
|
||||
NEEDS_SOCKET = YesPlease
|
||||
NEEDS_NSL = YesPlease
|
||||
SHELL_PATH = /bin/bash
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_HSTRERROR = YesPlease
|
||||
ifeq ($(uname_R),5.8)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
NO_UNSETENV = YesPlease
|
||||
NO_SETENV = YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
NO_C99_FORMAT = YesPlease
|
||||
NO_STRTOUMAX = YesPlease
|
||||
endif
|
||||
ifeq ($(uname_R),5.9)
|
||||
NO_UNSETENV = YesPlease
|
||||
NO_SETENV = YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
NO_C99_FORMAT = YesPlease
|
||||
NO_STRTOUMAX = YesPlease
|
||||
endif
|
||||
@@ -428,6 +439,7 @@ ifeq ($(uname_O),Cygwin)
|
||||
NO_D_TYPE_IN_DIRENT = YesPlease
|
||||
NO_D_INO_IN_DIRENT = YesPlease
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_SYMLINK_HEAD = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
|
||||
@@ -441,11 +453,13 @@ ifeq ($(uname_O),Cygwin)
|
||||
endif
|
||||
ifeq ($(uname_S),FreeBSD)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
BASIC_CFLAGS += -I/usr/local/include
|
||||
BASIC_LDFLAGS += -L/usr/local/lib
|
||||
endif
|
||||
ifeq ($(uname_S),OpenBSD)
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
BASIC_CFLAGS += -I/usr/local/include
|
||||
BASIC_LDFLAGS += -L/usr/local/lib
|
||||
@@ -460,6 +474,7 @@ ifeq ($(uname_S),NetBSD)
|
||||
endif
|
||||
ifeq ($(uname_S),AIX)
|
||||
NO_STRCASESTR=YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_STRLCPY = YesPlease
|
||||
NEEDS_LIBICONV=YesPlease
|
||||
endif
|
||||
@@ -471,6 +486,7 @@ ifeq ($(uname_S),IRIX64)
|
||||
NO_IPV6=YesPlease
|
||||
NO_SETENV=YesPlease
|
||||
NO_STRCASESTR=YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_STRLCPY = YesPlease
|
||||
NO_SOCKADDR_STORAGE=YesPlease
|
||||
SHELL_PATH=/usr/gnu/bin/bash
|
||||
@@ -479,8 +495,6 @@ ifeq ($(uname_S),IRIX64)
|
||||
BASIC_LDFLAGS += -L/usr/lib32
|
||||
endif
|
||||
ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
SHELL_PATH = /bin/sh
|
||||
PERL_PATH = /bin/perl
|
||||
NO_MMAP=YesPlease
|
||||
NO_PREAD=YesPlease
|
||||
NO_OPENSSL=YesPlease
|
||||
@@ -492,10 +506,12 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
NO_UNSETENV=YesPlease
|
||||
NO_STRCASESTR=YesPlease
|
||||
NO_STRLCPY=YesPlease
|
||||
NO_ICONV=YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
OLD_ICONV = YesPlease
|
||||
NO_C99_FORMAT = YesPlease
|
||||
NO_STRTOUMAX = YesPlease
|
||||
NO_SYMLINKS=YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
NO_SVN_TESTS=YesPlease
|
||||
NO_PERL_MAKEMAKER=YesPlease
|
||||
NO_R_TO_GCC_LINKER = YesPlease
|
||||
@@ -505,7 +521,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
X = .exe
|
||||
NOEXECTEMPL = .noexec
|
||||
prefix =
|
||||
SCRIPT_SH += cpio.sh
|
||||
endif
|
||||
ifneq (,$(findstring arm,$(uname_M)))
|
||||
ARM_SHA1 = YesPlease
|
||||
@@ -537,7 +552,9 @@ else
|
||||
CC_LD_DYNPATH = -R
|
||||
endif
|
||||
|
||||
ifndef NO_CURL
|
||||
ifdef NO_CURL
|
||||
BASIC_CFLAGS += -DNO_CURL
|
||||
else
|
||||
ifdef CURLDIR
|
||||
# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
|
||||
BASIC_CFLAGS += -I$(CURLDIR)/include
|
||||
@@ -545,7 +562,9 @@ ifndef NO_CURL
|
||||
else
|
||||
CURL_LIBCURL = -lcurl
|
||||
endif
|
||||
PROGRAMS += git-http-fetch$X
|
||||
BUILTIN_OBJS += builtin-http-fetch.o
|
||||
EXTLIBS += $(CURL_LIBCURL)
|
||||
LIB_OBJS += http.o http-walker.o
|
||||
curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
|
||||
ifeq "$(curl_check)" "070908"
|
||||
ifndef NO_EXPAT
|
||||
@@ -627,6 +646,10 @@ ifdef NO_SETENV
|
||||
COMPAT_CFLAGS += -DNO_SETENV
|
||||
COMPAT_OBJS += compat/setenv.o
|
||||
endif
|
||||
ifdef NO_MKDTEMP
|
||||
COMPAT_CFLAGS += -DNO_MKDTEMP
|
||||
COMPAT_OBJS += compat/mkdtemp.o
|
||||
endif
|
||||
ifdef NO_UNSETENV
|
||||
COMPAT_CFLAGS += -DNO_UNSETENV
|
||||
COMPAT_OBJS += compat/unsetenv.o
|
||||
@@ -694,6 +717,15 @@ ifdef NO_HSTRERROR
|
||||
COMPAT_CFLAGS += -DNO_HSTRERROR
|
||||
COMPAT_OBJS += compat/hstrerror.o
|
||||
endif
|
||||
ifdef NO_MEMMEM
|
||||
COMPAT_CFLAGS += -DNO_MEMMEM
|
||||
COMPAT_OBJS += compat/memmem.o
|
||||
endif
|
||||
|
||||
ifdef THREADED_DELTA_SEARCH
|
||||
BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
|
||||
EXTLIBS += -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(TCLTK_PATH),)
|
||||
NO_TCLTK=NoThanks
|
||||
@@ -817,12 +849,12 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
|
||||
|
||||
perl/perl.mak: GIT-CFLAGS
|
||||
perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
|
||||
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
|
||||
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
|
||||
$(QUIET_GEN)$(RM) $@ $@+ && \
|
||||
INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
|
||||
INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
|
||||
sed -e '1{' \
|
||||
-e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \
|
||||
-e ' h' \
|
||||
@@ -848,6 +880,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
|
||||
-e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
|
||||
-e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
|
||||
-e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
|
||||
-e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
|
||||
-e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
|
||||
-e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
|
||||
-e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
|
||||
@@ -907,35 +940,23 @@ http.o: http.c GIT-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
|
||||
|
||||
ifdef NO_EXPAT
|
||||
http-fetch.o: http-fetch.c http.h GIT-CFLAGS
|
||||
http-walker.o: http-walker.c http.h GIT-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
|
||||
endif
|
||||
|
||||
git-%$X: %.o $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
ssh-pull.o: ssh-fetch.c
|
||||
ssh-push.o: ssh-upload.c
|
||||
git-local-fetch$X: fetch.o
|
||||
git-ssh-fetch$X: rsh.o fetch.o
|
||||
git-ssh-upload$X: rsh.o
|
||||
git-ssh-pull$X: rsh.o fetch.o
|
||||
git-ssh-push$X: rsh.o
|
||||
|
||||
git-imap-send$X: imap-send.o $(LIB_FILE)
|
||||
|
||||
http.o http-fetch.o http-push.o: http.h
|
||||
git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
|
||||
http.o http-walker.o http-push.o: http.h
|
||||
|
||||
git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
|
||||
|
||||
$(LIB_OBJS) $(BUILTIN_OBJS) fetch.o: $(LIB_H)
|
||||
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
|
||||
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
|
||||
$(DIFF_OBJS): diffcore.h
|
||||
|
||||
$(LIB_FILE): $(LIB_OBJS)
|
||||
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
|
||||
@@ -949,10 +970,6 @@ $(XDIFF_LIB): $(XDIFF_OBJS)
|
||||
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
|
||||
|
||||
|
||||
perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
|
||||
(cd perl && $(PERL_PATH) Makefile.PL \
|
||||
PREFIX='$(prefix_SQ)')
|
||||
|
||||
doc:
|
||||
$(MAKE) -C Documentation all
|
||||
|
||||
@@ -967,6 +984,10 @@ tags:
|
||||
$(RM) tags
|
||||
$(FIND) . -name '*.[hcS]' -print | xargs ctags -a
|
||||
|
||||
cscope:
|
||||
$(RM) cscope*
|
||||
$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
|
||||
|
||||
### Detect prefix changes
|
||||
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
|
||||
$(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
|
||||
@@ -1002,7 +1023,6 @@ all:: $(TEST_PROGRAMS)
|
||||
# However, the environment gets quite big, and some programs have problems
|
||||
# with that.
|
||||
|
||||
export NO_SYMLINKS
|
||||
export NO_SVN_TESTS
|
||||
|
||||
test: all
|
||||
@@ -1112,14 +1132,17 @@ dist-doc:
|
||||
|
||||
### Cleaning rules
|
||||
|
||||
distclean: clean
|
||||
$(RM) configure
|
||||
|
||||
clean:
|
||||
$(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
|
||||
$(LIB_FILE) $(XDIFF_LIB)
|
||||
$(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
|
||||
$(RM) $(TEST_PROGRAMS)
|
||||
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
|
||||
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
|
||||
$(RM) -r autom4te.cache
|
||||
$(RM) configure config.log config.mak.autogen config.mak.append config.status config.cache
|
||||
$(RM) config.log config.mak.autogen config.mak.append config.status config.cache
|
||||
$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
|
||||
$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
|
||||
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
|
||||
@@ -1135,7 +1158,7 @@ endif
|
||||
$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
|
||||
|
||||
.PHONY: all install clean strip
|
||||
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags .FORCE-GIT-CFLAGS
|
||||
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
|
||||
|
||||
### Check documentation
|
||||
#
|
||||
@@ -1146,8 +1169,7 @@ check-docs::
|
||||
git-merge-octopus | git-merge-ours | git-merge-recursive | \
|
||||
git-merge-resolve | git-merge-stupid | \
|
||||
git-add--interactive | git-fsck-objects | git-init-db | \
|
||||
git-repo-config | git-fetch--tool | \
|
||||
git-ssh-pull | git-ssh-push ) continue ;; \
|
||||
git-repo-config | git-fetch--tool ) continue ;; \
|
||||
esac ; \
|
||||
test -f "Documentation/$$v.txt" || \
|
||||
echo "no doc: $$v"; \
|
||||
|
||||
@@ -28,6 +28,15 @@ In order to compile this code you need:
|
||||
zlib-1.2.3-mingwPORT-1.tar
|
||||
w32api-3.6.tar.gz
|
||||
tcltk-8.4.1-1.exe (for gitk, git-gui)
|
||||
libiconv-1.9.2-1-{lib,bin,lib}.zip (for git-am,
|
||||
from http://gnuwin32.sourceforge.net/packages/libiconv.htm)
|
||||
|
||||
I am using these settings in config.mak to have pointers to the right tools:
|
||||
|
||||
TCL_PATH=tclsh84
|
||||
TCLTK_PATH=wish84
|
||||
SHELL_PATH=D:/MSYS/1.0/bin/sh
|
||||
PERL_PATH=D:/MSYS/1.0/bin/perl
|
||||
|
||||
|
||||
STATUS
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "strbuf.h"
|
||||
#include "tar.h"
|
||||
#include "builtin.h"
|
||||
#include "archive.h"
|
||||
@@ -17,6 +16,7 @@ static unsigned long offset;
|
||||
static time_t archive_time;
|
||||
static int tar_umask = 002;
|
||||
static int verbose;
|
||||
static const struct commit *commit;
|
||||
|
||||
/* writes out the whole block, but only if it is full */
|
||||
static void write_if_needed(void)
|
||||
@@ -78,19 +78,6 @@ static void write_trailer(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void strbuf_append_string(struct strbuf *sb, const char *s)
|
||||
{
|
||||
int slen = strlen(s);
|
||||
int total = sb->len + slen;
|
||||
if (total + 1 > sb->alloc) {
|
||||
sb->buf = xrealloc(sb->buf, total + 1);
|
||||
sb->alloc = total + 1;
|
||||
}
|
||||
memcpy(sb->buf + sb->len, s, slen);
|
||||
sb->len = total;
|
||||
sb->buf[total] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* pax extended header records have the format "%u %s=%s\n". %u contains
|
||||
* the size of the whole string (including the %u), the first %s is the
|
||||
@@ -100,26 +87,17 @@ static void strbuf_append_string(struct strbuf *sb, const char *s)
|
||||
static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
|
||||
const char *value, unsigned int valuelen)
|
||||
{
|
||||
char *p;
|
||||
int len, total, tmp;
|
||||
int len, tmp;
|
||||
|
||||
/* "%u %s=%s\n" */
|
||||
len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
|
||||
for (tmp = len; tmp > 9; tmp /= 10)
|
||||
len++;
|
||||
|
||||
total = sb->len + len;
|
||||
if (total > sb->alloc) {
|
||||
sb->buf = xrealloc(sb->buf, total);
|
||||
sb->alloc = total;
|
||||
}
|
||||
|
||||
p = sb->buf;
|
||||
p += sprintf(p, "%u %s=", len, keyword);
|
||||
memcpy(p, value, valuelen);
|
||||
p += valuelen;
|
||||
*p = '\n';
|
||||
sb->len = total;
|
||||
strbuf_grow(sb, len);
|
||||
strbuf_addf(sb, "%u %s=", len, keyword);
|
||||
strbuf_add(sb, value, valuelen);
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
|
||||
static unsigned int ustar_header_chksum(const struct ustar_header *header)
|
||||
@@ -153,8 +131,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
||||
struct strbuf ext_header;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
ext_header.buf = NULL;
|
||||
ext_header.len = ext_header.alloc = 0;
|
||||
strbuf_init(&ext_header, 0);
|
||||
|
||||
if (!sha1) {
|
||||
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
|
||||
@@ -166,7 +143,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
||||
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
|
||||
} else {
|
||||
if (verbose)
|
||||
fprintf(stderr, "%.*s\n", path->len, path->buf);
|
||||
fprintf(stderr, "%.*s\n", (int)path->len, path->buf);
|
||||
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
|
||||
*header.typeflag = TYPEFLAG_DIR;
|
||||
mode = (mode | 0777) & ~tar_umask;
|
||||
@@ -225,8 +202,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
||||
|
||||
if (ext_header.len > 0) {
|
||||
write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
|
||||
free(ext_header.buf);
|
||||
}
|
||||
strbuf_release(&ext_header);
|
||||
write_blocked(&header, sizeof(header));
|
||||
if (S_ISREG(mode) && buffer && size > 0)
|
||||
write_blocked(buffer, size);
|
||||
@@ -235,11 +212,11 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
|
||||
static void write_global_extended_header(const unsigned char *sha1)
|
||||
{
|
||||
struct strbuf ext_header;
|
||||
ext_header.buf = NULL;
|
||||
ext_header.len = ext_header.alloc = 0;
|
||||
|
||||
strbuf_init(&ext_header, 0);
|
||||
strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
|
||||
write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
|
||||
free(ext_header.buf);
|
||||
strbuf_release(&ext_header);
|
||||
}
|
||||
|
||||
static int git_tar_config(const char *var, const char *value)
|
||||
@@ -260,32 +237,22 @@ static int write_tar_entry(const unsigned char *sha1,
|
||||
const char *base, int baselen,
|
||||
const char *filename, unsigned mode, int stage)
|
||||
{
|
||||
static struct strbuf path;
|
||||
int filenamelen = strlen(filename);
|
||||
static struct strbuf path = STRBUF_INIT;
|
||||
void *buffer;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
|
||||
if (!path.alloc) {
|
||||
path.buf = xmalloc(PATH_MAX);
|
||||
path.alloc = PATH_MAX;
|
||||
path.len = path.eof = 0;
|
||||
}
|
||||
if (path.alloc < baselen + filenamelen + 1) {
|
||||
free(path.buf);
|
||||
path.buf = xmalloc(baselen + filenamelen + 1);
|
||||
path.alloc = baselen + filenamelen + 1;
|
||||
}
|
||||
memcpy(path.buf, base, baselen);
|
||||
memcpy(path.buf + baselen, filename, filenamelen);
|
||||
path.len = baselen + filenamelen;
|
||||
path.buf[path.len] = '\0';
|
||||
strbuf_reset(&path);
|
||||
strbuf_grow(&path, PATH_MAX);
|
||||
strbuf_add(&path, base, baselen);
|
||||
strbuf_addstr(&path, filename);
|
||||
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
|
||||
strbuf_append_string(&path, "/");
|
||||
strbuf_addch(&path, '/');
|
||||
buffer = NULL;
|
||||
size = 0;
|
||||
} else {
|
||||
buffer = convert_sha1_file(path.buf, sha1, mode, &type, &size);
|
||||
buffer = sha1_file_to_archive(path.buf, sha1, mode, &type,
|
||||
&size, commit);
|
||||
if (!buffer)
|
||||
die("cannot read %s", sha1_to_hex(sha1));
|
||||
}
|
||||
@@ -304,6 +271,7 @@ int write_tar_archive(struct archiver_args *args)
|
||||
|
||||
archive_time = args->time;
|
||||
verbose = args->verbose;
|
||||
commit = args->commit;
|
||||
|
||||
if (args->commit_sha1)
|
||||
write_global_extended_header(args->commit_sha1);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
static int verbose;
|
||||
static int zip_date;
|
||||
static int zip_time;
|
||||
static const struct commit *commit;
|
||||
|
||||
static unsigned char *zip_dir;
|
||||
static unsigned int zip_dir_size;
|
||||
@@ -191,11 +192,13 @@ static int write_zip_entry(const unsigned char *sha1,
|
||||
compressed_size = 0;
|
||||
} else if (S_ISREG(mode) || S_ISLNK(mode)) {
|
||||
method = 0;
|
||||
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0;
|
||||
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
|
||||
(mode & 0111) ? ((mode) << 16) : 0;
|
||||
if (S_ISREG(mode) && zlib_compression_level != 0)
|
||||
method = 8;
|
||||
result = 0;
|
||||
buffer = convert_sha1_file(path, sha1, mode, &type, &size);
|
||||
buffer = sha1_file_to_archive(path, sha1, mode, &type, &size,
|
||||
commit);
|
||||
if (!buffer)
|
||||
die("cannot read %s", sha1_to_hex(sha1));
|
||||
crc = crc32(crc, buffer, size);
|
||||
@@ -229,7 +232,8 @@ static int write_zip_entry(const unsigned char *sha1,
|
||||
}
|
||||
|
||||
copy_le32(dirent.magic, 0x02014b50);
|
||||
copy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0);
|
||||
copy_le16(dirent.creator_version,
|
||||
S_ISLNK(mode) || (S_ISREG(mode) && (mode & 0111)) ? 0x0317 : 0);
|
||||
copy_le16(dirent.version, 10);
|
||||
copy_le16(dirent.flags, 0);
|
||||
copy_le16(dirent.compression_method, method);
|
||||
@@ -316,6 +320,7 @@ int write_zip_archive(struct archiver_args *args)
|
||||
zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
|
||||
zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
|
||||
verbose = args->verbose;
|
||||
commit = args->commit;
|
||||
|
||||
if (args->base && plen > 0 && args->base[plen - 1] == '/') {
|
||||
char *base = xstrdup(args->base);
|
||||
|
||||
@@ -8,6 +8,7 @@ struct archiver_args {
|
||||
const char *base;
|
||||
struct tree *tree;
|
||||
const unsigned char *commit_sha1;
|
||||
const struct commit *commit;
|
||||
time_t time;
|
||||
const char **pathspec;
|
||||
unsigned int verbose : 1;
|
||||
@@ -42,4 +43,6 @@ extern int write_tar_archive(struct archiver_args *);
|
||||
extern int write_zip_archive(struct archiver_args *);
|
||||
extern void *parse_extra_zip_args(int argc, const char **argv);
|
||||
|
||||
extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit);
|
||||
|
||||
#endif /* ARCHIVE_H */
|
||||
|
||||
7
attr.c
7
attr.c
@@ -160,12 +160,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
|
||||
else if (!equals)
|
||||
e->setto = ATTR__TRUE;
|
||||
else {
|
||||
char *value;
|
||||
int vallen = ep - equals;
|
||||
value = xmalloc(vallen);
|
||||
memcpy(value, equals+1, vallen-1);
|
||||
value[vallen-1] = 0;
|
||||
e->setto = value;
|
||||
e->setto = xmemdupz(equals + 1, ep - equals - 1);
|
||||
}
|
||||
e->attr = git_attr(cp, len);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "diffcore.h"
|
||||
#include "commit.h"
|
||||
#include "revision.h"
|
||||
#include "run-command.h"
|
||||
|
||||
static const char builtin_add_usage[] =
|
||||
"git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] <filepattern>...";
|
||||
@@ -44,6 +45,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
|
||||
die("pathspec '%s' did not match any files",
|
||||
pathspec[i]);
|
||||
}
|
||||
free(seen);
|
||||
}
|
||||
|
||||
static void fill_directory(struct dir_struct *dir, const char **pathspec,
|
||||
@@ -71,12 +73,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
|
||||
baselen = common_prefix(pathspec);
|
||||
path = ".";
|
||||
base = "";
|
||||
if (baselen) {
|
||||
char *common = xmalloc(baselen + 1);
|
||||
memcpy(common, *pathspec, baselen);
|
||||
common[baselen] = 0;
|
||||
path = base = common;
|
||||
}
|
||||
if (baselen)
|
||||
path = base = xmemdupz(*pathspec, baselen);
|
||||
|
||||
/* Read the directory and prune it */
|
||||
read_directory(dir, path, base, baselen, pathspec);
|
||||
@@ -95,14 +93,14 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
const char *path = p->one->path;
|
||||
switch (p->status) {
|
||||
default:
|
||||
die("unexpacted diff status %c", p->status);
|
||||
die("unexpected diff status %c", p->status);
|
||||
case DIFF_STATUS_UNMERGED:
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
case DIFF_STATUS_TYPE_CHANGED:
|
||||
add_file_to_cache(path, verbose);
|
||||
break;
|
||||
case DIFF_STATUS_DELETED:
|
||||
remove_file_from_cache(path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
if (verbose)
|
||||
printf("remove '%s'\n", path);
|
||||
break;
|
||||
@@ -110,7 +108,7 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
}
|
||||
}
|
||||
|
||||
static void update(int verbose, const char *prefix, const char **files)
|
||||
void add_files_to_cache(int verbose, const char *prefix, const char **files)
|
||||
{
|
||||
struct rev_info rev;
|
||||
init_revisions(&rev, prefix);
|
||||
@@ -119,8 +117,6 @@ static void update(int verbose, const char *prefix, const char **files)
|
||||
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
rev.diffopt.format_callback_data = &verbose;
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
run_diff_files(&rev, 0);
|
||||
}
|
||||
|
||||
@@ -139,6 +135,7 @@ static void refresh(int verbose, const char **pathspec)
|
||||
if (!seen[i])
|
||||
die("pathspec '%s' did not match any files", pathspec[i]);
|
||||
}
|
||||
free(seen);
|
||||
}
|
||||
|
||||
static int git_add_config(const char *var, const char *value)
|
||||
@@ -153,6 +150,13 @@ static int git_add_config(const char *var, const char *value)
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
int interactive_add(void)
|
||||
{
|
||||
const char *argv[2] = { "add--interactive", NULL };
|
||||
|
||||
return run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static struct lock_file lock_file;
|
||||
|
||||
static const char ignore_error[] =
|
||||
@@ -172,12 +176,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
add_interactive++;
|
||||
}
|
||||
if (add_interactive) {
|
||||
const char *args[] = { "add--interactive", NULL };
|
||||
|
||||
if (add_interactive != 1 || argc != 2)
|
||||
if (argc != 2)
|
||||
die("add --interactive does not take any parameters");
|
||||
execv_git_cmd(args);
|
||||
exit(1);
|
||||
exit(interactive_add());
|
||||
}
|
||||
|
||||
git_config(git_add_config);
|
||||
@@ -217,7 +218,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (take_worktree_changes) {
|
||||
update(verbose, prefix, argv + i);
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
add_files_to_cache(verbose, prefix, argv + i);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
||||
566
builtin-apply.c
566
builtin-apply.c
@@ -41,7 +41,7 @@ static int apply_in_reverse;
|
||||
static int apply_with_reject;
|
||||
static int apply_verbosely;
|
||||
static int no_add;
|
||||
static int show_index_info;
|
||||
static const char *fake_ancestor;
|
||||
static int line_termination = '\n';
|
||||
static unsigned long p_context = ULONG_MAX;
|
||||
static const char apply_usage[] =
|
||||
@@ -152,7 +152,7 @@ struct patch {
|
||||
unsigned int is_rename:1;
|
||||
struct fragment *fragments;
|
||||
char *result;
|
||||
unsigned long resultsize;
|
||||
size_t resultsize;
|
||||
char old_sha1_prefix[41];
|
||||
char new_sha1_prefix[41];
|
||||
struct patch *next;
|
||||
@@ -163,15 +163,14 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c
|
||||
fputs(pre, output);
|
||||
if (patch->old_name && patch->new_name &&
|
||||
strcmp(patch->old_name, patch->new_name)) {
|
||||
write_name_quoted(NULL, 0, patch->old_name, 1, output);
|
||||
quote_c_style(patch->old_name, NULL, output, 0);
|
||||
fputs(" => ", output);
|
||||
write_name_quoted(NULL, 0, patch->new_name, 1, output);
|
||||
}
|
||||
else {
|
||||
quote_c_style(patch->new_name, NULL, output, 0);
|
||||
} else {
|
||||
const char *n = patch->new_name;
|
||||
if (!n)
|
||||
n = patch->old_name;
|
||||
write_name_quoted(NULL, 0, n, 1, output);
|
||||
quote_c_style(n, NULL, output, 0);
|
||||
}
|
||||
fputs(post, output);
|
||||
}
|
||||
@@ -179,36 +178,18 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c
|
||||
#define CHUNKSIZE (8192)
|
||||
#define SLOP (16)
|
||||
|
||||
static void *read_patch_file(int fd, unsigned long *sizep)
|
||||
static void read_patch_file(struct strbuf *sb, int fd)
|
||||
{
|
||||
unsigned long size = 0, alloc = CHUNKSIZE;
|
||||
void *buffer = xmalloc(alloc);
|
||||
|
||||
for (;;) {
|
||||
ssize_t nr = alloc - size;
|
||||
if (nr < 1024) {
|
||||
alloc += CHUNKSIZE;
|
||||
buffer = xrealloc(buffer, alloc);
|
||||
nr = alloc - size;
|
||||
}
|
||||
nr = xread(fd, (char *) buffer + size, nr);
|
||||
if (!nr)
|
||||
break;
|
||||
if (nr < 0)
|
||||
die("git-apply: read returned %s", strerror(errno));
|
||||
size += nr;
|
||||
}
|
||||
*sizep = size;
|
||||
if (strbuf_read(sb, fd, 0) < 0)
|
||||
die("git-apply: read returned %s", strerror(errno));
|
||||
|
||||
/*
|
||||
* Make sure that we have some slop in the buffer
|
||||
* so that we can do speculative "memcmp" etc, and
|
||||
* see to it that it is NUL-filled.
|
||||
*/
|
||||
if (alloc < size + SLOP)
|
||||
buffer = xrealloc(buffer, size + SLOP);
|
||||
memset((char *) buffer + size, 0, SLOP);
|
||||
return buffer;
|
||||
strbuf_grow(sb, SLOP);
|
||||
memset(sb->buf + sb->len, 0, SLOP);
|
||||
}
|
||||
|
||||
static unsigned long linelen(const char *buffer, unsigned long size)
|
||||
@@ -244,35 +225,33 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
|
||||
{
|
||||
int len;
|
||||
const char *start = line;
|
||||
char *name;
|
||||
|
||||
if (*line == '"') {
|
||||
struct strbuf name;
|
||||
|
||||
/* Proposed "new-style" GNU patch/diff format; see
|
||||
* http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
|
||||
*/
|
||||
name = unquote_c_style(line, NULL);
|
||||
if (name) {
|
||||
char *cp = name;
|
||||
while (p_value) {
|
||||
cp = strchr(name, '/');
|
||||
strbuf_init(&name, 0);
|
||||
if (!unquote_c_style(&name, line, NULL)) {
|
||||
char *cp;
|
||||
|
||||
for (cp = name.buf; p_value; p_value--) {
|
||||
cp = strchr(cp, '/');
|
||||
if (!cp)
|
||||
break;
|
||||
cp++;
|
||||
p_value--;
|
||||
}
|
||||
if (cp) {
|
||||
/* name can later be freed, so we need
|
||||
* to memmove, not just return cp
|
||||
*/
|
||||
memmove(name, cp, strlen(cp) + 1);
|
||||
strbuf_remove(&name, 0, cp - name.buf);
|
||||
free(def);
|
||||
return name;
|
||||
}
|
||||
else {
|
||||
free(name);
|
||||
name = NULL;
|
||||
return strbuf_detach(&name, NULL);
|
||||
}
|
||||
}
|
||||
strbuf_release(&name);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
@@ -304,13 +283,10 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
|
||||
int deflen = strlen(def);
|
||||
if (deflen < len && !strncmp(start, def, deflen))
|
||||
return def;
|
||||
free(def);
|
||||
}
|
||||
|
||||
name = xmalloc(len + 1);
|
||||
memcpy(name, start, len);
|
||||
name[len] = 0;
|
||||
free(def);
|
||||
return name;
|
||||
return xmemdupz(start, len);
|
||||
}
|
||||
|
||||
static int count_slashes(const char *cp)
|
||||
@@ -583,29 +559,30 @@ static const char *stop_at_slash(const char *line, int llen)
|
||||
*/
|
||||
static char *git_header_name(char *line, int llen)
|
||||
{
|
||||
int len;
|
||||
const char *name;
|
||||
const char *second = NULL;
|
||||
size_t len;
|
||||
|
||||
line += strlen("diff --git ");
|
||||
llen -= strlen("diff --git ");
|
||||
|
||||
if (*line == '"') {
|
||||
const char *cp;
|
||||
char *first = unquote_c_style(line, &second);
|
||||
if (!first)
|
||||
return NULL;
|
||||
struct strbuf first;
|
||||
struct strbuf sp;
|
||||
|
||||
strbuf_init(&first, 0);
|
||||
strbuf_init(&sp, 0);
|
||||
|
||||
if (unquote_c_style(&first, line, &second))
|
||||
goto free_and_fail1;
|
||||
|
||||
/* advance to the first slash */
|
||||
cp = stop_at_slash(first, strlen(first));
|
||||
if (!cp || cp == first) {
|
||||
/* we do not accept absolute paths */
|
||||
free_first_and_fail:
|
||||
free(first);
|
||||
return NULL;
|
||||
}
|
||||
len = strlen(cp+1);
|
||||
memmove(first, cp+1, len+1); /* including NUL */
|
||||
cp = stop_at_slash(first.buf, first.len);
|
||||
/* we do not accept absolute paths */
|
||||
if (!cp || cp == first.buf)
|
||||
goto free_and_fail1;
|
||||
strbuf_remove(&first, 0, cp + 1 - first.buf);
|
||||
|
||||
/* second points at one past closing dq of name.
|
||||
* find the second name.
|
||||
@@ -614,40 +591,40 @@ static char *git_header_name(char *line, int llen)
|
||||
second++;
|
||||
|
||||
if (line + llen <= second)
|
||||
goto free_first_and_fail;
|
||||
goto free_and_fail1;
|
||||
if (*second == '"') {
|
||||
char *sp = unquote_c_style(second, NULL);
|
||||
if (!sp)
|
||||
goto free_first_and_fail;
|
||||
cp = stop_at_slash(sp, strlen(sp));
|
||||
if (!cp || cp == sp) {
|
||||
free_both_and_fail:
|
||||
free(sp);
|
||||
goto free_first_and_fail;
|
||||
}
|
||||
if (unquote_c_style(&sp, second, NULL))
|
||||
goto free_and_fail1;
|
||||
cp = stop_at_slash(sp.buf, sp.len);
|
||||
if (!cp || cp == sp.buf)
|
||||
goto free_and_fail1;
|
||||
/* They must match, otherwise ignore */
|
||||
if (strcmp(cp+1, first))
|
||||
goto free_both_and_fail;
|
||||
free(sp);
|
||||
return first;
|
||||
if (strcmp(cp + 1, first.buf))
|
||||
goto free_and_fail1;
|
||||
strbuf_release(&sp);
|
||||
return strbuf_detach(&first, NULL);
|
||||
}
|
||||
|
||||
/* unquoted second */
|
||||
cp = stop_at_slash(second, line + llen - second);
|
||||
if (!cp || cp == second)
|
||||
goto free_first_and_fail;
|
||||
goto free_and_fail1;
|
||||
cp++;
|
||||
if (line + llen - cp != len + 1 ||
|
||||
memcmp(first, cp, len))
|
||||
goto free_first_and_fail;
|
||||
return first;
|
||||
if (line + llen - cp != first.len + 1 ||
|
||||
memcmp(first.buf, cp, first.len))
|
||||
goto free_and_fail1;
|
||||
return strbuf_detach(&first, NULL);
|
||||
|
||||
free_and_fail1:
|
||||
strbuf_release(&first);
|
||||
strbuf_release(&sp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* unquoted first name */
|
||||
name = stop_at_slash(line, llen);
|
||||
if (!name || name == line)
|
||||
return NULL;
|
||||
|
||||
name++;
|
||||
|
||||
/* since the first name is unquoted, a dq if exists must be
|
||||
@@ -655,28 +632,30 @@ static char *git_header_name(char *line, int llen)
|
||||
*/
|
||||
for (second = name; second < line + llen; second++) {
|
||||
if (*second == '"') {
|
||||
const char *cp = second;
|
||||
struct strbuf sp;
|
||||
const char *np;
|
||||
char *sp = unquote_c_style(second, NULL);
|
||||
|
||||
if (!sp)
|
||||
return NULL;
|
||||
np = stop_at_slash(sp, strlen(sp));
|
||||
if (!np || np == sp) {
|
||||
free_second_and_fail:
|
||||
free(sp);
|
||||
return NULL;
|
||||
}
|
||||
strbuf_init(&sp, 0);
|
||||
if (unquote_c_style(&sp, second, NULL))
|
||||
goto free_and_fail2;
|
||||
|
||||
np = stop_at_slash(sp.buf, sp.len);
|
||||
if (!np || np == sp.buf)
|
||||
goto free_and_fail2;
|
||||
np++;
|
||||
len = strlen(np);
|
||||
if (len < cp - name &&
|
||||
|
||||
len = sp.buf + sp.len - np;
|
||||
if (len < second - name &&
|
||||
!strncmp(np, name, len) &&
|
||||
isspace(name[len])) {
|
||||
/* Good */
|
||||
memmove(sp, np, len + 1);
|
||||
return sp;
|
||||
strbuf_remove(&sp, 0, np - sp.buf);
|
||||
return strbuf_detach(&sp, NULL);
|
||||
}
|
||||
goto free_second_and_fail;
|
||||
|
||||
free_and_fail2:
|
||||
strbuf_release(&sp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,10 +679,7 @@ static char *git_header_name(char *line, int llen)
|
||||
break;
|
||||
}
|
||||
if (second[len] == '\n' && !memcmp(name, second, len)) {
|
||||
char *ret = xmalloc(len + 1);
|
||||
memcpy(ret, name, len);
|
||||
ret[len] = 0;
|
||||
return ret;
|
||||
return xmemdupz(name, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1397,96 +1373,66 @@ static const char minuses[]= "--------------------------------------------------
|
||||
|
||||
static void show_stats(struct patch *patch)
|
||||
{
|
||||
const char *prefix = "";
|
||||
char *name = patch->new_name;
|
||||
char *qname = NULL;
|
||||
int len, max, add, del, total;
|
||||
struct strbuf qname;
|
||||
char *cp = patch->new_name ? patch->new_name : patch->old_name;
|
||||
int max, add, del;
|
||||
|
||||
if (!name)
|
||||
name = patch->old_name;
|
||||
|
||||
if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
|
||||
qname = xmalloc(len + 1);
|
||||
quote_c_style(name, qname, NULL, 0);
|
||||
name = qname;
|
||||
}
|
||||
strbuf_init(&qname, 0);
|
||||
quote_c_style(cp, &qname, NULL, 0);
|
||||
|
||||
/*
|
||||
* "scale" the filename
|
||||
*/
|
||||
len = strlen(name);
|
||||
max = max_len;
|
||||
if (max > 50)
|
||||
max = 50;
|
||||
if (len > max) {
|
||||
char *slash;
|
||||
prefix = "...";
|
||||
max -= 3;
|
||||
name += len - max;
|
||||
slash = strchr(name, '/');
|
||||
if (slash)
|
||||
name = slash;
|
||||
|
||||
if (qname.len > max) {
|
||||
cp = strchr(qname.buf + qname.len + 3 - max, '/');
|
||||
if (!cp)
|
||||
cp = qname.buf + qname.len + 3 - max;
|
||||
strbuf_splice(&qname, 0, cp - qname.buf, "...", 3);
|
||||
}
|
||||
len = max;
|
||||
|
||||
if (patch->is_binary) {
|
||||
printf(" %-*s | Bin\n", max, qname.buf);
|
||||
strbuf_release(&qname);
|
||||
return;
|
||||
}
|
||||
|
||||
printf(" %-*s |", max, qname.buf);
|
||||
strbuf_release(&qname);
|
||||
|
||||
/*
|
||||
* scale the add/delete
|
||||
*/
|
||||
max = max_change;
|
||||
if (max + len > 70)
|
||||
max = 70 - len;
|
||||
|
||||
max = max + max_change > 70 ? 70 - max : max_change;
|
||||
add = patch->lines_added;
|
||||
del = patch->lines_deleted;
|
||||
total = add + del;
|
||||
|
||||
if (max_change > 0) {
|
||||
total = (total * max + max_change / 2) / max_change;
|
||||
int total = ((add + del) * max + max_change / 2) / max_change;
|
||||
add = (add * max + max_change / 2) / max_change;
|
||||
del = total - add;
|
||||
}
|
||||
if (patch->is_binary)
|
||||
printf(" %s%-*s | Bin\n", prefix, len, name);
|
||||
else
|
||||
printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
|
||||
len, name, patch->lines_added + patch->lines_deleted,
|
||||
add, pluses, del, minuses);
|
||||
free(qname);
|
||||
printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
|
||||
add, pluses, del, minuses);
|
||||
}
|
||||
|
||||
static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p)
|
||||
static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
|
||||
{
|
||||
int fd;
|
||||
unsigned long got;
|
||||
unsigned long nsize;
|
||||
char *nbuf;
|
||||
unsigned long size = *size_p;
|
||||
char *buf = *buf_p;
|
||||
|
||||
switch (st->st_mode & S_IFMT) {
|
||||
case S_IFLNK:
|
||||
return readlink(path, buf, size) != size;
|
||||
strbuf_grow(buf, st->st_size);
|
||||
if (readlink(path, buf->buf, st->st_size) != st->st_size)
|
||||
return -1;
|
||||
strbuf_setlen(buf, st->st_size);
|
||||
return 0;
|
||||
case S_IFREG:
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return error("unable to open %s", path);
|
||||
got = 0;
|
||||
for (;;) {
|
||||
ssize_t ret = xread(fd, buf + got, size - got);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
got += ret;
|
||||
}
|
||||
close(fd);
|
||||
nsize = got;
|
||||
nbuf = convert_to_git(path, buf, &nsize);
|
||||
if (nbuf) {
|
||||
free(buf);
|
||||
*buf_p = nbuf;
|
||||
*alloc_p = nsize;
|
||||
*size_p = nsize;
|
||||
}
|
||||
return got != size;
|
||||
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
|
||||
return error("unable to open or read %s", path);
|
||||
convert_to_git(path, buf->buf, buf->len, buf);
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@@ -1514,7 +1460,8 @@ static int find_offset(const char *buf, unsigned long size, const char *fragment
|
||||
}
|
||||
|
||||
/* Exact line number? */
|
||||
if (!memcmp(buf + start, fragment, fragsize))
|
||||
if ((start + fragsize <= size) &&
|
||||
!memcmp(buf + start, fragment, fragsize))
|
||||
return start;
|
||||
|
||||
/*
|
||||
@@ -1590,12 +1537,6 @@ static void remove_last_line(const char **rbuf, int *rsize)
|
||||
*rsize = offset + 1;
|
||||
}
|
||||
|
||||
struct buffer_desc {
|
||||
char *buffer;
|
||||
unsigned long size;
|
||||
unsigned long alloc;
|
||||
};
|
||||
|
||||
static int apply_line(char *output, const char *patch, int plen)
|
||||
{
|
||||
/* plen is number of bytes to be copied from patch,
|
||||
@@ -1641,15 +1582,22 @@ static int apply_line(char *output, const char *patch, int plen)
|
||||
|
||||
buf = output;
|
||||
if (need_fix_leading_space) {
|
||||
int consecutive_spaces = 0;
|
||||
/* between patch[1..last_tab_in_indent] strip the
|
||||
* funny spaces, updating them to tab as needed.
|
||||
*/
|
||||
for (i = 1; i < last_tab_in_indent; i++, plen--) {
|
||||
char ch = patch[i];
|
||||
if (ch != ' ')
|
||||
if (ch != ' ') {
|
||||
consecutive_spaces = 0;
|
||||
*output++ = ch;
|
||||
else if ((i % 8) == 0)
|
||||
*output++ = '\t';
|
||||
} else {
|
||||
consecutive_spaces++;
|
||||
if (consecutive_spaces == 8) {
|
||||
*output++ = '\t';
|
||||
consecutive_spaces = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
fixed = 1;
|
||||
i = last_tab_in_indent;
|
||||
@@ -1665,10 +1613,9 @@ static int apply_line(char *output, const char *patch, int plen)
|
||||
return output + plen - buf;
|
||||
}
|
||||
|
||||
static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)
|
||||
static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof)
|
||||
{
|
||||
int match_beginning, match_end;
|
||||
char *buf = desc->buffer;
|
||||
const char *patch = frag->patch;
|
||||
int offset, size = frag->size;
|
||||
char *old = xmalloc(size);
|
||||
@@ -1779,24 +1726,17 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
|
||||
lines = 0;
|
||||
pos = frag->newpos;
|
||||
for (;;) {
|
||||
offset = find_offset(buf, desc->size,
|
||||
offset = find_offset(buf->buf, buf->len,
|
||||
oldlines, oldsize, pos, &lines);
|
||||
if (match_end && offset + oldsize != desc->size)
|
||||
if (match_end && offset + oldsize != buf->len)
|
||||
offset = -1;
|
||||
if (match_beginning && offset)
|
||||
offset = -1;
|
||||
if (offset >= 0) {
|
||||
int diff;
|
||||
unsigned long size, alloc;
|
||||
|
||||
if (new_whitespace == strip_whitespace &&
|
||||
(desc->size - oldsize - offset == 0)) /* end of file? */
|
||||
(buf->len - oldsize - offset == 0)) /* end of file? */
|
||||
newsize -= new_blank_lines_at_end;
|
||||
|
||||
diff = newsize - oldsize;
|
||||
size = desc->size + diff;
|
||||
alloc = desc->alloc;
|
||||
|
||||
/* Warn if it was necessary to reduce the number
|
||||
* of context lines.
|
||||
*/
|
||||
@@ -1806,19 +1746,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
|
||||
" to apply fragment at %d\n",
|
||||
leading, trailing, pos + lines);
|
||||
|
||||
if (size > alloc) {
|
||||
alloc = size + 8192;
|
||||
desc->alloc = alloc;
|
||||
buf = xrealloc(buf, alloc);
|
||||
desc->buffer = buf;
|
||||
}
|
||||
desc->size = size;
|
||||
memmove(buf + offset + newsize,
|
||||
buf + offset + oldsize,
|
||||
size - offset - newsize);
|
||||
memcpy(buf + offset, newlines, newsize);
|
||||
strbuf_splice(buf, offset, oldsize, newlines, newsize);
|
||||
offset = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1854,12 +1783,11 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
|
||||
static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
|
||||
{
|
||||
unsigned long dst_size;
|
||||
struct fragment *fragment = patch->fragments;
|
||||
void *data;
|
||||
void *result;
|
||||
unsigned long len;
|
||||
void *dst;
|
||||
|
||||
/* Binary patch is irreversible without the optional second hunk */
|
||||
if (apply_in_reverse) {
|
||||
@@ -1870,29 +1798,24 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
|
||||
? patch->new_name : patch->old_name);
|
||||
fragment = fragment->next;
|
||||
}
|
||||
data = (void*) fragment->patch;
|
||||
switch (fragment->binary_patch_method) {
|
||||
case BINARY_DELTA_DEFLATED:
|
||||
result = patch_delta(desc->buffer, desc->size,
|
||||
data,
|
||||
fragment->size,
|
||||
&dst_size);
|
||||
free(desc->buffer);
|
||||
desc->buffer = result;
|
||||
break;
|
||||
dst = patch_delta(buf->buf, buf->len, fragment->patch,
|
||||
fragment->size, &len);
|
||||
if (!dst)
|
||||
return -1;
|
||||
/* XXX patch_delta NUL-terminates */
|
||||
strbuf_attach(buf, dst, len, len + 1);
|
||||
return 0;
|
||||
case BINARY_LITERAL_DEFLATED:
|
||||
free(desc->buffer);
|
||||
desc->buffer = data;
|
||||
dst_size = fragment->size;
|
||||
break;
|
||||
strbuf_reset(buf);
|
||||
strbuf_add(buf, fragment->patch, fragment->size);
|
||||
return 0;
|
||||
}
|
||||
if (!desc->buffer)
|
||||
return -1;
|
||||
desc->size = desc->alloc = dst_size;
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
{
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
unsigned char sha1[20];
|
||||
@@ -1911,7 +1834,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
/* See if the old one matches what the patch
|
||||
* applies to.
|
||||
*/
|
||||
hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
|
||||
hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
|
||||
return error("the patch applies to '%s' (%s), "
|
||||
"which does not match the "
|
||||
@@ -1920,16 +1843,14 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
}
|
||||
else {
|
||||
/* Otherwise, the old one must be empty. */
|
||||
if (desc->size)
|
||||
if (buf->len)
|
||||
return error("the patch applies to an empty "
|
||||
"'%s' but it is not empty", name);
|
||||
}
|
||||
|
||||
get_sha1_hex(patch->new_sha1_prefix, sha1);
|
||||
if (is_null_sha1(sha1)) {
|
||||
free(desc->buffer);
|
||||
desc->alloc = desc->size = 0;
|
||||
desc->buffer = NULL;
|
||||
strbuf_release(buf);
|
||||
return 0; /* deletion patch */
|
||||
}
|
||||
|
||||
@@ -1937,43 +1858,44 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
/* We already have the postimage */
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *result;
|
||||
|
||||
free(desc->buffer);
|
||||
desc->buffer = read_sha1_file(sha1, &type, &size);
|
||||
if (!desc->buffer)
|
||||
result = read_sha1_file(sha1, &type, &size);
|
||||
if (!result)
|
||||
return error("the necessary postimage %s for "
|
||||
"'%s' cannot be read",
|
||||
patch->new_sha1_prefix, name);
|
||||
desc->alloc = desc->size = size;
|
||||
}
|
||||
else {
|
||||
/* We have verified desc matches the preimage;
|
||||
/* XXX read_sha1_file NUL-terminates */
|
||||
strbuf_attach(buf, result, size, size + 1);
|
||||
} else {
|
||||
/* We have verified buf matches the preimage;
|
||||
* apply the patch data to it, which is stored
|
||||
* in the patch->fragments->{patch,size}.
|
||||
*/
|
||||
if (apply_binary_fragment(desc, patch))
|
||||
if (apply_binary_fragment(buf, patch))
|
||||
return error("binary patch does not apply to '%s'",
|
||||
name);
|
||||
|
||||
/* verify that the result matches */
|
||||
hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
|
||||
hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
|
||||
return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
|
||||
return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
|
||||
name, patch->new_sha1_prefix, sha1_to_hex(sha1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
|
||||
static int apply_fragments(struct strbuf *buf, struct patch *patch)
|
||||
{
|
||||
struct fragment *frag = patch->fragments;
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
|
||||
if (patch->is_binary)
|
||||
return apply_binary(desc, patch);
|
||||
return apply_binary(buf, patch);
|
||||
|
||||
while (frag) {
|
||||
if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
|
||||
if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {
|
||||
error("patch failed: %s:%ld", name, frag->oldpos);
|
||||
if (!apply_with_reject)
|
||||
return -1;
|
||||
@@ -1984,76 +1906,56 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
|
||||
unsigned long *size_p)
|
||||
static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
|
||||
{
|
||||
if (!ce)
|
||||
return 0;
|
||||
|
||||
if (S_ISGITLINK(ntohl(ce->ce_mode))) {
|
||||
*buf_p = xmalloc(100);
|
||||
*size_p = snprintf(*buf_p, 100,
|
||||
"Subproject commit %s\n", sha1_to_hex(ce->sha1));
|
||||
strbuf_grow(buf, 100);
|
||||
strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
|
||||
} else {
|
||||
enum object_type type;
|
||||
*buf_p = read_sha1_file(ce->sha1, &type, size_p);
|
||||
if (!*buf_p)
|
||||
unsigned long sz;
|
||||
char *result;
|
||||
|
||||
result = read_sha1_file(ce->sha1, &type, &sz);
|
||||
if (!result)
|
||||
return -1;
|
||||
/* XXX read_sha1_file NUL-terminates */
|
||||
strbuf_attach(buf, result, sz, sz + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
|
||||
{
|
||||
char *buf;
|
||||
unsigned long size, alloc;
|
||||
struct buffer_desc desc;
|
||||
struct strbuf buf;
|
||||
|
||||
size = 0;
|
||||
alloc = 0;
|
||||
buf = NULL;
|
||||
strbuf_init(&buf, 0);
|
||||
if (cached) {
|
||||
if (read_file_or_gitlink(ce, &buf, &size))
|
||||
if (read_file_or_gitlink(ce, &buf))
|
||||
return error("read of %s failed", patch->old_name);
|
||||
alloc = size;
|
||||
} else if (patch->old_name) {
|
||||
if (S_ISGITLINK(patch->old_mode)) {
|
||||
if (ce)
|
||||
read_file_or_gitlink(ce, &buf, &size);
|
||||
else {
|
||||
if (ce) {
|
||||
read_file_or_gitlink(ce, &buf);
|
||||
} else {
|
||||
/*
|
||||
* There is no way to apply subproject
|
||||
* patch without looking at the index.
|
||||
*/
|
||||
patch->fragments = NULL;
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
size = xsize_t(st->st_size);
|
||||
alloc = size + 8192;
|
||||
buf = xmalloc(alloc);
|
||||
if (read_old_data(st, patch->old_name,
|
||||
&buf, &alloc, &size))
|
||||
return error("read of %s failed",
|
||||
patch->old_name);
|
||||
} else {
|
||||
if (read_old_data(st, patch->old_name, &buf))
|
||||
return error("read of %s failed", patch->old_name);
|
||||
}
|
||||
}
|
||||
|
||||
desc.size = size;
|
||||
desc.alloc = alloc;
|
||||
desc.buffer = buf;
|
||||
|
||||
if (apply_fragments(&desc, patch) < 0)
|
||||
if (apply_fragments(&buf, patch) < 0)
|
||||
return -1; /* note with --reject this succeeds. */
|
||||
|
||||
/* NUL terminate the result */
|
||||
if (desc.alloc <= desc.size)
|
||||
desc.buffer = xrealloc(desc.buffer, desc.size + 1);
|
||||
desc.buffer[desc.size] = 0;
|
||||
|
||||
patch->result = desc.buffer;
|
||||
patch->resultsize = desc.size;
|
||||
patch->result = strbuf_detach(&buf, &patch->resultsize);
|
||||
|
||||
if (0 < patch->is_delete && patch->resultsize)
|
||||
return error("removal patch leaves file contents");
|
||||
@@ -2226,9 +2128,26 @@ static int check_patch_list(struct patch *patch)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void show_index_list(struct patch *list)
|
||||
/* This function tries to read the sha1 from the current index */
|
||||
static int get_current_sha1(const char *path, unsigned char *sha1)
|
||||
{
|
||||
int pos;
|
||||
|
||||
if (read_cache() < 0)
|
||||
return -1;
|
||||
pos = cache_name_pos(path, strlen(path));
|
||||
if (pos < 0)
|
||||
return -1;
|
||||
hashcpy(sha1, active_cache[pos]->sha1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Build an index that contains the just the files needed for a 3way merge */
|
||||
static void build_fake_ancestor(struct patch *list, const char *filename)
|
||||
{
|
||||
struct patch *patch;
|
||||
struct index_state result = { 0 };
|
||||
int fd;
|
||||
|
||||
/* Once we start supporting the reverse patch, it may be
|
||||
* worth showing the new sha1 prefix, but until then...
|
||||
@@ -2236,24 +2155,36 @@ static void show_index_list(struct patch *list)
|
||||
for (patch = list; patch; patch = patch->next) {
|
||||
const unsigned char *sha1_ptr;
|
||||
unsigned char sha1[20];
|
||||
struct cache_entry *ce;
|
||||
const char *name;
|
||||
|
||||
name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
if (0 < patch->is_new)
|
||||
sha1_ptr = null_sha1;
|
||||
continue;
|
||||
else if (get_sha1(patch->old_sha1_prefix, sha1))
|
||||
die("sha1 information is lacking or useless (%s).",
|
||||
name);
|
||||
/* git diff has no index line for mode/type changes */
|
||||
if (!patch->lines_added && !patch->lines_deleted) {
|
||||
if (get_current_sha1(patch->new_name, sha1) ||
|
||||
get_current_sha1(patch->old_name, sha1))
|
||||
die("mode change for %s, which is not "
|
||||
"in current HEAD", name);
|
||||
sha1_ptr = sha1;
|
||||
} else
|
||||
die("sha1 information is lacking or useless "
|
||||
"(%s).", name);
|
||||
else
|
||||
sha1_ptr = sha1;
|
||||
|
||||
printf("%06o %s ",patch->old_mode, sha1_to_hex(sha1_ptr));
|
||||
if (line_termination && quote_c_style(name, NULL, NULL, 0))
|
||||
quote_c_style(name, NULL, stdout, 0);
|
||||
else
|
||||
fputs(name, stdout);
|
||||
putchar(line_termination);
|
||||
ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
|
||||
if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
|
||||
die ("Could not add %s to temporary index", name);
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT, 0666);
|
||||
if (fd < 0 || write_index(&result, fd) || close(fd))
|
||||
die ("Could not write temporary index to %s", filename);
|
||||
|
||||
discard_index(&result);
|
||||
}
|
||||
|
||||
static void stat_patch_list(struct patch *patch)
|
||||
@@ -2278,13 +2209,8 @@ static void numstat_patch_list(struct patch *patch)
|
||||
if (patch->is_binary)
|
||||
printf("-\t-\t");
|
||||
else
|
||||
printf("%d\t%d\t",
|
||||
patch->lines_added, patch->lines_deleted);
|
||||
if (line_termination && quote_c_style(name, NULL, NULL, 0))
|
||||
quote_c_style(name, NULL, stdout, 0);
|
||||
else
|
||||
fputs(name, stdout);
|
||||
putchar(line_termination);
|
||||
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
|
||||
write_name_quoted(name, stdout, line_termination);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2393,7 +2319,6 @@ static void remove_file(struct patch *patch, int rmdir_empty)
|
||||
if (update_index) {
|
||||
if (remove_file_from_cache(patch->old_name) < 0)
|
||||
die("unable to remove %s from index", patch->old_name);
|
||||
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
|
||||
}
|
||||
if (!cached) {
|
||||
if (S_ISGITLINK(patch->old_mode)) {
|
||||
@@ -2450,7 +2375,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
|
||||
static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
|
||||
{
|
||||
int fd;
|
||||
char *nbuf;
|
||||
struct strbuf nbuf;
|
||||
|
||||
if (S_ISGITLINK(mode)) {
|
||||
struct stat st;
|
||||
@@ -2469,23 +2394,16 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
nbuf = convert_to_working_tree(path, buf, &size);
|
||||
if (nbuf)
|
||||
buf = nbuf;
|
||||
|
||||
while (size) {
|
||||
int written = xwrite(fd, buf, size);
|
||||
if (written < 0)
|
||||
die("writing file %s: %s", path, strerror(errno));
|
||||
if (!written)
|
||||
die("out of space writing file %s", path);
|
||||
buf += written;
|
||||
size -= written;
|
||||
strbuf_init(&nbuf, 0);
|
||||
if (convert_to_working_tree(path, buf, size, &nbuf)) {
|
||||
size = nbuf.len;
|
||||
buf = nbuf.buf;
|
||||
}
|
||||
write_or_die(fd, buf, size);
|
||||
strbuf_release(&nbuf);
|
||||
|
||||
if (close(fd) < 0)
|
||||
die("closing file %s: %s", path, strerror(errno));
|
||||
if (nbuf)
|
||||
free(nbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2548,7 +2466,6 @@ static void create_file(struct patch *patch)
|
||||
mode = S_IFREG | 0644;
|
||||
create_one_file(path, mode, buf, size);
|
||||
add_index_file(path, mode, buf, size);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
}
|
||||
|
||||
/* phase zero is to remove, phase one is to create */
|
||||
@@ -2719,22 +2636,22 @@ static void prefix_patches(struct patch *p)
|
||||
|
||||
static int apply_patch(int fd, const char *filename, int inaccurate_eof)
|
||||
{
|
||||
unsigned long offset, size;
|
||||
char *buffer = read_patch_file(fd, &size);
|
||||
size_t offset;
|
||||
struct strbuf buf;
|
||||
struct patch *list = NULL, **listp = &list;
|
||||
int skipped_patch = 0;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
patch_input_file = filename;
|
||||
if (!buffer)
|
||||
return -1;
|
||||
read_patch_file(&buf, fd);
|
||||
offset = 0;
|
||||
while (size > 0) {
|
||||
while (offset < buf.len) {
|
||||
struct patch *patch;
|
||||
int nr;
|
||||
|
||||
patch = xcalloc(1, sizeof(*patch));
|
||||
patch->inaccurate_eof = inaccurate_eof;
|
||||
nr = parse_chunk(buffer + offset, size, patch);
|
||||
nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
|
||||
if (nr < 0)
|
||||
break;
|
||||
if (apply_in_reverse)
|
||||
@@ -2752,7 +2669,6 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
|
||||
skipped_patch++;
|
||||
}
|
||||
offset += nr;
|
||||
size -= nr;
|
||||
}
|
||||
|
||||
if (whitespace_error && (new_whitespace == error_on_whitespace))
|
||||
@@ -2775,8 +2691,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
|
||||
if (apply && write_out_results(list, skipped_patch))
|
||||
exit(1);
|
||||
|
||||
if (show_index_info)
|
||||
show_index_list(list);
|
||||
if (fake_ancestor)
|
||||
build_fake_ancestor(list, fake_ancestor);
|
||||
|
||||
if (diffstat)
|
||||
stat_patch_list(list);
|
||||
@@ -2787,7 +2703,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
|
||||
if (summary)
|
||||
summary_patch_list(list);
|
||||
|
||||
free(buffer);
|
||||
strbuf_release(&buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2884,9 +2800,11 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
|
||||
apply = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--index-info")) {
|
||||
if (!strcmp(arg, "--build-fake-ancestor")) {
|
||||
apply = 0;
|
||||
show_index_info = 1;
|
||||
if (++i >= argc)
|
||||
die ("need a filename");
|
||||
fake_ancestor = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-z")) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "exec_cmd.h"
|
||||
#include "pkt-line.h"
|
||||
#include "sideband.h"
|
||||
#include "attr.h"
|
||||
|
||||
static const char archive_usage[] = \
|
||||
"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
|
||||
@@ -29,7 +30,7 @@ static int run_remote_archiver(const char *remote, int argc,
|
||||
{
|
||||
char *url, buf[LARGE_PACKET_MAX];
|
||||
int fd[2], i, len, rv;
|
||||
pid_t pid;
|
||||
struct child_process *conn;
|
||||
const char *exec = "git-upload-archive";
|
||||
int exec_at = 0;
|
||||
|
||||
@@ -45,9 +46,7 @@ static int run_remote_archiver(const char *remote, int argc,
|
||||
}
|
||||
|
||||
url = xstrdup(remote);
|
||||
pid = git_connect(fd, url, exec, 0);
|
||||
if (pid < 0)
|
||||
return pid;
|
||||
conn = git_connect(fd, url, exec, 0);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (i == exec_at)
|
||||
@@ -75,11 +74,91 @@ static int run_remote_archiver(const char *remote, int argc,
|
||||
rv = recv_sideband("archive", fd[0], 1, 2);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
rv |= finish_connect(pid);
|
||||
rv |= finish_connect(conn);
|
||||
|
||||
return !!rv;
|
||||
}
|
||||
|
||||
static void format_subst(const struct commit *commit,
|
||||
const char *src, size_t len,
|
||||
struct strbuf *buf)
|
||||
{
|
||||
char *to_free = NULL;
|
||||
struct strbuf fmt;
|
||||
|
||||
if (src == buf->buf)
|
||||
to_free = strbuf_detach(buf, NULL);
|
||||
strbuf_init(&fmt, 0);
|
||||
for (;;) {
|
||||
const char *b, *c;
|
||||
|
||||
b = memmem(src, len, "$Format:", 8);
|
||||
if (!b || src + len < b + 9)
|
||||
break;
|
||||
c = memchr(b + 8, '$', len - 8);
|
||||
if (!c)
|
||||
break;
|
||||
|
||||
strbuf_reset(&fmt);
|
||||
strbuf_add(&fmt, b + 8, c - b - 8);
|
||||
|
||||
strbuf_add(buf, src, b - src);
|
||||
format_commit_message(commit, fmt.buf, buf);
|
||||
len -= c + 1 - src;
|
||||
src = c + 1;
|
||||
}
|
||||
strbuf_add(buf, src, len);
|
||||
strbuf_release(&fmt);
|
||||
free(to_free);
|
||||
}
|
||||
|
||||
static int convert_to_archive(const char *path,
|
||||
const void *src, size_t len,
|
||||
struct strbuf *buf,
|
||||
const struct commit *commit)
|
||||
{
|
||||
static struct git_attr *attr_export_subst;
|
||||
struct git_attr_check check[1];
|
||||
|
||||
if (!commit)
|
||||
return 0;
|
||||
|
||||
if (!attr_export_subst)
|
||||
attr_export_subst = git_attr("export-subst", 12);
|
||||
|
||||
check[0].attr = attr_export_subst;
|
||||
if (git_checkattr(path, ARRAY_SIZE(check), check))
|
||||
return 0;
|
||||
if (!ATTR_TRUE(check[0].value))
|
||||
return 0;
|
||||
|
||||
format_subst(commit, src, len, buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
|
||||
unsigned int mode, enum object_type *type,
|
||||
unsigned long *sizep,
|
||||
const struct commit *commit)
|
||||
{
|
||||
void *buffer;
|
||||
|
||||
buffer = read_sha1_file(sha1, type, sizep);
|
||||
if (buffer && S_ISREG(mode)) {
|
||||
struct strbuf buf;
|
||||
size_t size = 0;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
|
||||
convert_to_working_tree(path, buf.buf, buf.len, &buf);
|
||||
convert_to_archive(path, buf.buf, buf.len, &buf, commit);
|
||||
buffer = strbuf_detach(&buf, &size);
|
||||
*sizep = size;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static int init_archiver(const char *name, struct archiver *ar)
|
||||
{
|
||||
int rv = -1, i;
|
||||
@@ -109,7 +188,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
|
||||
const unsigned char *commit_sha1;
|
||||
time_t archive_time;
|
||||
struct tree *tree;
|
||||
struct commit *commit;
|
||||
const struct commit *commit;
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (get_sha1(name, sha1))
|
||||
@@ -142,6 +221,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
|
||||
}
|
||||
ar_args->tree = tree;
|
||||
ar_args->commit_sha1 = commit_sha1;
|
||||
ar_args->commit = commit;
|
||||
ar_args->time = archive_time;
|
||||
}
|
||||
|
||||
|
||||
@@ -1430,8 +1430,7 @@ static void get_commit_info(struct commit *commit,
|
||||
static void write_filename_info(const char *path)
|
||||
{
|
||||
printf("filename ");
|
||||
write_name_quoted(NULL, 0, path, 1, stdout);
|
||||
putchar('\n');
|
||||
write_name_quoted(path, stdout, '\n');
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2001,11 +2000,9 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
struct commit *commit;
|
||||
struct origin *origin;
|
||||
unsigned char head_sha1[20];
|
||||
char *buf;
|
||||
struct strbuf buf;
|
||||
const char *ident;
|
||||
int fd;
|
||||
time_t now;
|
||||
unsigned long fin_size;
|
||||
int size, len;
|
||||
struct cache_entry *ce;
|
||||
unsigned mode;
|
||||
@@ -2023,9 +2020,11 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
|
||||
origin = make_origin(commit, path);
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
if (!contents_from || strcmp("-", contents_from)) {
|
||||
struct stat st;
|
||||
const char *read_from;
|
||||
unsigned long fin_size;
|
||||
|
||||
if (contents_from) {
|
||||
if (stat(contents_from, &st) < 0)
|
||||
@@ -2038,19 +2037,16 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
read_from = path;
|
||||
}
|
||||
fin_size = xsize_t(st.st_size);
|
||||
buf = xmalloc(fin_size+1);
|
||||
mode = canon_mode(st.st_mode);
|
||||
switch (st.st_mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
fd = open(read_from, O_RDONLY);
|
||||
if (fd < 0)
|
||||
die("cannot open %s", read_from);
|
||||
if (read_in_full(fd, buf, fin_size) != fin_size)
|
||||
die("cannot read %s", read_from);
|
||||
if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
|
||||
die("cannot open or read %s", read_from);
|
||||
break;
|
||||
case S_IFLNK:
|
||||
if (readlink(read_from, buf, fin_size+1) != fin_size)
|
||||
if (readlink(read_from, buf.buf, buf.alloc) != fin_size)
|
||||
die("cannot readlink %s", read_from);
|
||||
buf.len = fin_size;
|
||||
break;
|
||||
default:
|
||||
die("unsupported file type %s", read_from);
|
||||
@@ -2059,26 +2055,14 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
else {
|
||||
/* Reading from stdin */
|
||||
contents_from = "standard input";
|
||||
buf = NULL;
|
||||
fin_size = 0;
|
||||
mode = 0;
|
||||
while (1) {
|
||||
ssize_t cnt = 8192;
|
||||
buf = xrealloc(buf, fin_size + cnt);
|
||||
cnt = xread(0, buf + fin_size, cnt);
|
||||
if (cnt < 0)
|
||||
die("read error %s from stdin",
|
||||
strerror(errno));
|
||||
if (!cnt)
|
||||
break;
|
||||
fin_size += cnt;
|
||||
}
|
||||
buf = xrealloc(buf, fin_size + 1);
|
||||
if (strbuf_read(&buf, 0, 0) < 0)
|
||||
die("read error %s from stdin", strerror(errno));
|
||||
}
|
||||
buf[fin_size] = 0;
|
||||
origin->file.ptr = buf;
|
||||
origin->file.size = fin_size;
|
||||
pretend_sha1_file(buf, fin_size, OBJ_BLOB, origin->blob_sha1);
|
||||
convert_to_git(path, buf.buf, buf.len, &buf);
|
||||
origin->file.ptr = buf.buf;
|
||||
origin->file.size = buf.len;
|
||||
pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
|
||||
commit->util = origin;
|
||||
|
||||
/*
|
||||
|
||||
@@ -268,23 +268,22 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
char *subject = NULL;
|
||||
unsigned long subject_len = 0;
|
||||
struct strbuf subject;
|
||||
const char *sub = " **** invalid ref ****";
|
||||
|
||||
strbuf_init(&subject, 0);
|
||||
|
||||
commit = lookup_commit(item->sha1);
|
||||
if (commit && !parse_commit(commit)) {
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
|
||||
&subject, &subject_len, 0,
|
||||
NULL, NULL, 0);
|
||||
sub = subject;
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit,
|
||||
&subject, 0, NULL, NULL, 0);
|
||||
sub = subject.buf;
|
||||
}
|
||||
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
|
||||
maxwidth, item->name,
|
||||
branch_get_color(COLOR_BRANCH_RESET),
|
||||
find_unique_abbrev(item->sha1, abbrev), sub);
|
||||
if (subject)
|
||||
free(subject);
|
||||
strbuf_release(&subject);
|
||||
} else {
|
||||
printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
|
||||
branch_get_color(COLOR_BRANCH_RESET));
|
||||
|
||||
365
builtin-bundle.c
365
builtin-bundle.c
@@ -1,11 +1,6 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "object.h"
|
||||
#include "commit.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "list-objects.h"
|
||||
#include "run-command.h"
|
||||
#include "bundle.h"
|
||||
|
||||
/*
|
||||
* Basic handler for bundle files to connect repositories via sneakernet.
|
||||
@@ -16,355 +11,6 @@
|
||||
|
||||
static const char *bundle_usage="git-bundle (create <bundle> <git-rev-list args> | verify <bundle> | list-heads <bundle> [refname]... | unbundle <bundle> [refname]... )";
|
||||
|
||||
static const char bundle_signature[] = "# v2 git bundle\n";
|
||||
|
||||
struct ref_list {
|
||||
unsigned int nr, alloc;
|
||||
struct ref_list_entry {
|
||||
unsigned char sha1[20];
|
||||
char *name;
|
||||
} *list;
|
||||
};
|
||||
|
||||
static void add_to_ref_list(const unsigned char *sha1, const char *name,
|
||||
struct ref_list *list)
|
||||
{
|
||||
if (list->nr + 1 >= list->alloc) {
|
||||
list->alloc = alloc_nr(list->nr + 1);
|
||||
list->list = xrealloc(list->list,
|
||||
list->alloc * sizeof(list->list[0]));
|
||||
}
|
||||
memcpy(list->list[list->nr].sha1, sha1, 20);
|
||||
list->list[list->nr].name = xstrdup(name);
|
||||
list->nr++;
|
||||
}
|
||||
|
||||
struct bundle_header {
|
||||
struct ref_list prerequisites;
|
||||
struct ref_list references;
|
||||
};
|
||||
|
||||
/* returns an fd */
|
||||
static int read_header(const char *path, struct bundle_header *header) {
|
||||
char buffer[1024];
|
||||
int fd;
|
||||
long fpos;
|
||||
FILE *ffd = fopen(path, "rb");
|
||||
|
||||
if (!ffd)
|
||||
return error("could not open '%s'", path);
|
||||
if (!fgets(buffer, sizeof(buffer), ffd) ||
|
||||
strcmp(buffer, bundle_signature)) {
|
||||
fclose(ffd);
|
||||
return error("'%s' does not look like a v2 bundle file", path);
|
||||
}
|
||||
while (fgets(buffer, sizeof(buffer), ffd)
|
||||
&& buffer[0] != '\n') {
|
||||
int is_prereq = buffer[0] == '-';
|
||||
int offset = is_prereq ? 1 : 0;
|
||||
int len = strlen(buffer);
|
||||
unsigned char sha1[20];
|
||||
struct ref_list *list = is_prereq ? &header->prerequisites
|
||||
: &header->references;
|
||||
char delim;
|
||||
|
||||
if (buffer[len - 1] == '\n')
|
||||
buffer[len - 1] = '\0';
|
||||
if (get_sha1_hex(buffer + offset, sha1)) {
|
||||
warning("unrecognized header: %s", buffer);
|
||||
continue;
|
||||
}
|
||||
delim = buffer[40 + offset];
|
||||
if (!isspace(delim) && (delim != '\0' || !is_prereq))
|
||||
die ("invalid header: %s", buffer);
|
||||
add_to_ref_list(sha1, isspace(delim) ?
|
||||
buffer + 41 + offset : "", list);
|
||||
}
|
||||
fpos = ftell(ffd);
|
||||
fclose(ffd);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return error("could not open '%s'", path);
|
||||
lseek(fd, fpos, SEEK_SET);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int list_refs(struct ref_list *r, int argc, const char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < r->nr; i++) {
|
||||
if (argc > 1) {
|
||||
int j;
|
||||
for (j = 1; j < argc; j++)
|
||||
if (!strcmp(r->list[i].name, argv[j]))
|
||||
break;
|
||||
if (j == argc)
|
||||
continue;
|
||||
}
|
||||
printf("%s %s\n", sha1_to_hex(r->list[i].sha1),
|
||||
r->list[i].name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PREREQ_MARK (1u<<16)
|
||||
|
||||
static int verify_bundle(struct bundle_header *header, int verbose)
|
||||
{
|
||||
/*
|
||||
* Do fast check, then if any prereqs are missing then go line by line
|
||||
* to be verbose about the errors
|
||||
*/
|
||||
struct ref_list *p = &header->prerequisites;
|
||||
struct rev_info revs;
|
||||
const char *argv[] = {NULL, "--all"};
|
||||
struct object_array refs;
|
||||
struct commit *commit;
|
||||
int i, ret = 0, req_nr;
|
||||
const char *message = "Repository lacks these prerequisite commits:";
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
for (i = 0; i < p->nr; i++) {
|
||||
struct ref_list_entry *e = p->list + i;
|
||||
struct object *o = parse_object(e->sha1);
|
||||
if (o) {
|
||||
o->flags |= PREREQ_MARK;
|
||||
add_pending_object(&revs, o, e->name);
|
||||
continue;
|
||||
}
|
||||
if (++ret == 1)
|
||||
error(message);
|
||||
error("%s %s", sha1_to_hex(e->sha1), e->name);
|
||||
}
|
||||
if (revs.pending.nr != p->nr)
|
||||
return ret;
|
||||
req_nr = revs.pending.nr;
|
||||
setup_revisions(2, argv, &revs, NULL);
|
||||
|
||||
memset(&refs, 0, sizeof(struct object_array));
|
||||
for (i = 0; i < revs.pending.nr; i++) {
|
||||
struct object_array_entry *e = revs.pending.objects + i;
|
||||
add_object_array(e->item, e->name, &refs);
|
||||
}
|
||||
|
||||
prepare_revision_walk(&revs);
|
||||
|
||||
i = req_nr;
|
||||
while (i && (commit = get_revision(&revs)))
|
||||
if (commit->object.flags & PREREQ_MARK)
|
||||
i--;
|
||||
|
||||
for (i = 0; i < req_nr; i++)
|
||||
if (!(refs.objects[i].item->flags & SHOWN)) {
|
||||
if (++ret == 1)
|
||||
error(message);
|
||||
error("%s %s", sha1_to_hex(refs.objects[i].item->sha1),
|
||||
refs.objects[i].name);
|
||||
}
|
||||
|
||||
for (i = 0; i < refs.nr; i++)
|
||||
clear_commit_marks((struct commit *)refs.objects[i].item, -1);
|
||||
|
||||
if (verbose) {
|
||||
struct ref_list *r;
|
||||
|
||||
r = &header->references;
|
||||
printf("The bundle contains %d ref%s\n",
|
||||
r->nr, (1 < r->nr) ? "s" : "");
|
||||
list_refs(r, 0, NULL);
|
||||
r = &header->prerequisites;
|
||||
printf("The bundle requires these %d ref%s\n",
|
||||
r->nr, (1 < r->nr) ? "s" : "");
|
||||
list_refs(r, 0, NULL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int list_heads(struct bundle_header *header, int argc, const char **argv)
|
||||
{
|
||||
return list_refs(&header->references, argc, argv);
|
||||
}
|
||||
|
||||
static int create_bundle(struct bundle_header *header, const char *path,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
static struct lock_file lock;
|
||||
int bundle_fd = -1;
|
||||
int bundle_to_stdout;
|
||||
const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
|
||||
const char **argv_pack = xmalloc(5 * sizeof(const char *));
|
||||
int i, ref_count = 0;
|
||||
char buffer[1024];
|
||||
struct rev_info revs;
|
||||
struct child_process rls;
|
||||
FILE *rls_fout;
|
||||
|
||||
bundle_to_stdout = !strcmp(path, "-");
|
||||
if (bundle_to_stdout)
|
||||
bundle_fd = 1;
|
||||
else
|
||||
bundle_fd = hold_lock_file_for_update(&lock, path, 1);
|
||||
|
||||
/* write signature */
|
||||
write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
|
||||
|
||||
/* init revs to list objects for pack-objects later */
|
||||
save_commit_buffer = 0;
|
||||
init_revisions(&revs, NULL);
|
||||
|
||||
/* write prerequisites */
|
||||
memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *));
|
||||
argv_boundary[0] = "rev-list";
|
||||
argv_boundary[1] = "--boundary";
|
||||
argv_boundary[2] = "--pretty=oneline";
|
||||
argv_boundary[argc + 2] = NULL;
|
||||
memset(&rls, 0, sizeof(rls));
|
||||
rls.argv = argv_boundary;
|
||||
rls.out = -1;
|
||||
rls.git_cmd = 1;
|
||||
if (start_command(&rls))
|
||||
return -1;
|
||||
rls_fout = fdopen(rls.out, "r");
|
||||
while (fgets(buffer, sizeof(buffer), rls_fout)) {
|
||||
unsigned char sha1[20];
|
||||
if (buffer[0] == '-') {
|
||||
write_or_die(bundle_fd, buffer, strlen(buffer));
|
||||
if (!get_sha1_hex(buffer + 1, sha1)) {
|
||||
struct object *object = parse_object(sha1);
|
||||
object->flags |= UNINTERESTING;
|
||||
add_pending_object(&revs, object, buffer);
|
||||
}
|
||||
} else if (!get_sha1_hex(buffer, sha1)) {
|
||||
struct object *object = parse_object(sha1);
|
||||
object->flags |= SHOWN;
|
||||
}
|
||||
}
|
||||
fclose(rls_fout);
|
||||
if (finish_command(&rls))
|
||||
return error("rev-list died");
|
||||
|
||||
/* write references */
|
||||
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||
if (argc > 1)
|
||||
return error("unrecognized argument: %s'", argv[1]);
|
||||
|
||||
for (i = 0; i < revs.pending.nr; i++) {
|
||||
struct object_array_entry *e = revs.pending.objects + i;
|
||||
unsigned char sha1[20];
|
||||
char *ref;
|
||||
|
||||
if (e->item->flags & UNINTERESTING)
|
||||
continue;
|
||||
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
|
||||
continue;
|
||||
/*
|
||||
* Make sure the refs we wrote out is correct; --max-count and
|
||||
* other limiting options could have prevented all the tips
|
||||
* from getting output.
|
||||
*
|
||||
* Non commit objects such as tags and blobs do not have
|
||||
* this issue as they are not affected by those extra
|
||||
* constraints.
|
||||
*/
|
||||
if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
|
||||
warning("ref '%s' is excluded by the rev-list options",
|
||||
e->name);
|
||||
free(ref);
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* If you run "git bundle create bndl v1.0..v2.0", the
|
||||
* name of the positive ref is "v2.0" but that is the
|
||||
* commit that is referenced by the tag, and not the tag
|
||||
* itself.
|
||||
*/
|
||||
if (hashcmp(sha1, e->item->sha1)) {
|
||||
/*
|
||||
* Is this the positive end of a range expressed
|
||||
* in terms of a tag (e.g. v2.0 from the range
|
||||
* "v1.0..v2.0")?
|
||||
*/
|
||||
struct commit *one = lookup_commit_reference(sha1);
|
||||
struct object *obj;
|
||||
|
||||
if (e->item == &(one->object)) {
|
||||
/*
|
||||
* Need to include e->name as an
|
||||
* independent ref to the pack-objects
|
||||
* input, so that the tag is included
|
||||
* in the output; otherwise we would
|
||||
* end up triggering "empty bundle"
|
||||
* error.
|
||||
*/
|
||||
obj = parse_object(sha1);
|
||||
obj->flags |= SHOWN;
|
||||
add_pending_object(&revs, obj, e->name);
|
||||
}
|
||||
free(ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
ref_count++;
|
||||
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
|
||||
write_or_die(bundle_fd, " ", 1);
|
||||
write_or_die(bundle_fd, ref, strlen(ref));
|
||||
write_or_die(bundle_fd, "\n", 1);
|
||||
free(ref);
|
||||
}
|
||||
if (!ref_count)
|
||||
die ("Refusing to create empty bundle.");
|
||||
|
||||
/* end header */
|
||||
write_or_die(bundle_fd, "\n", 1);
|
||||
|
||||
/* write pack */
|
||||
argv_pack[0] = "pack-objects";
|
||||
argv_pack[1] = "--all-progress";
|
||||
argv_pack[2] = "--stdout";
|
||||
argv_pack[3] = "--thin";
|
||||
argv_pack[4] = NULL;
|
||||
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(rls.in, "^", 1);
|
||||
write(rls.in, sha1_to_hex(object->sha1), 40);
|
||||
write(rls.in, "\n", 1);
|
||||
}
|
||||
if (finish_command(&rls))
|
||||
return error ("pack-objects died");
|
||||
close(bundle_fd);
|
||||
if (!bundle_to_stdout)
|
||||
commit_lock_file(&lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unbundle(struct bundle_header *header, int bundle_fd,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
const char *argv_index_pack[] = {"index-pack",
|
||||
"--fix-thin", "--stdin", NULL};
|
||||
struct child_process ip;
|
||||
|
||||
if (verify_bundle(header, 0))
|
||||
return -1;
|
||||
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);
|
||||
}
|
||||
|
||||
int cmd_bundle(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct bundle_header header;
|
||||
@@ -388,8 +34,8 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
if (strcmp(cmd, "create") &&
|
||||
(bundle_fd = read_header(bundle_file, &header)) < 0)
|
||||
if (strcmp(cmd, "create") && (bundle_fd =
|
||||
read_bundle_header(bundle_file, &header)) < 0)
|
||||
return 1;
|
||||
|
||||
if (!strcmp(cmd, "verify")) {
|
||||
@@ -401,7 +47,7 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
if (!strcmp(cmd, "list-heads")) {
|
||||
close(bundle_fd);
|
||||
return !!list_heads(&header, argc, argv);
|
||||
return !!list_bundle_refs(&header, argc, argv);
|
||||
}
|
||||
if (!strcmp(cmd, "create")) {
|
||||
if (nongit)
|
||||
@@ -410,7 +56,8 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
|
||||
} else if (!strcmp(cmd, "unbundle")) {
|
||||
if (nongit)
|
||||
die("Need a repository to unbundle.");
|
||||
return !!unbundle(&header, bundle_fd, argc, argv);
|
||||
return !!unbundle(&header, bundle_fd) ||
|
||||
list_bundle_refs(&header, argc, argv);
|
||||
} else
|
||||
usage(bundle_usage);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
|
||||
else if (ATTR_UNSET(value))
|
||||
value = "unspecified";
|
||||
|
||||
write_name_quoted("", 0, argv[i], 1, stdout);
|
||||
quote_c_style(argv[i], NULL, stdout, 0);
|
||||
printf(": %s: %s\n", argv[j+1], value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
*/
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "strbuf.h"
|
||||
#include "quote.h"
|
||||
#include "cache-tree.h"
|
||||
|
||||
@@ -67,9 +66,7 @@ static void write_tempfile_record(const char *name, int prefix_length)
|
||||
fputs(topath[checkout_stage], stdout);
|
||||
|
||||
putchar('\t');
|
||||
write_name_quoted("", 0, name + prefix_length,
|
||||
line_termination, stdout);
|
||||
putchar(line_termination);
|
||||
write_name_quoted(name + prefix_length, stdout, line_termination);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
topath[i][0] = 0;
|
||||
@@ -271,28 +268,28 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (read_from_stdin) {
|
||||
struct strbuf buf;
|
||||
struct strbuf buf, nbuf;
|
||||
|
||||
if (all)
|
||||
die("git-checkout-index: don't mix '--all' and '--stdin'");
|
||||
strbuf_init(&buf);
|
||||
while (1) {
|
||||
char *path_name;
|
||||
const char *p;
|
||||
|
||||
read_line(&buf, stdin, line_termination);
|
||||
if (buf.eof)
|
||||
break;
|
||||
if (line_termination && buf.buf[0] == '"')
|
||||
path_name = unquote_c_style(buf.buf, NULL);
|
||||
else
|
||||
path_name = buf.buf;
|
||||
p = prefix_path(prefix, prefix_length, path_name);
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_init(&nbuf, 0);
|
||||
while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
|
||||
const char *p;
|
||||
if (line_termination && buf.buf[0] == '"') {
|
||||
strbuf_reset(&nbuf);
|
||||
if (unquote_c_style(&nbuf, buf.buf, NULL))
|
||||
die("line is badly quoted");
|
||||
strbuf_swap(&buf, &nbuf);
|
||||
}
|
||||
p = prefix_path(prefix, prefix_length, buf.buf);
|
||||
checkout_file(p, prefix_length);
|
||||
if (p < path_name || p > path_name + strlen(path_name))
|
||||
if (p < buf.buf || p > buf.buf + buf.len)
|
||||
free((char *)p);
|
||||
if (path_name != buf.buf)
|
||||
free(path_name);
|
||||
}
|
||||
strbuf_release(&nbuf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
if (all)
|
||||
|
||||
@@ -14,36 +14,6 @@
|
||||
/*
|
||||
* FIXME! Share the code with "write-tree.c"
|
||||
*/
|
||||
static void init_buffer(char **bufp, unsigned int *sizep)
|
||||
{
|
||||
*bufp = xmalloc(BLOCKING);
|
||||
*sizep = 0;
|
||||
}
|
||||
|
||||
static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
|
||||
{
|
||||
char one_line[2048];
|
||||
va_list args;
|
||||
int len;
|
||||
unsigned long alloc, size, newsize;
|
||||
char *buf;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = vsnprintf(one_line, sizeof(one_line), fmt, args);
|
||||
va_end(args);
|
||||
size = *sizep;
|
||||
newsize = size + len + 1;
|
||||
alloc = (size + 32767) & ~32767;
|
||||
buf = *bufp;
|
||||
if (newsize > alloc) {
|
||||
alloc = (newsize + 32767) & ~32767;
|
||||
buf = xrealloc(buf, alloc);
|
||||
*bufp = buf;
|
||||
}
|
||||
*sizep = newsize - 1;
|
||||
memcpy(buf + size, one_line, len);
|
||||
}
|
||||
|
||||
static void check_valid(unsigned char *sha1, enum object_type expect)
|
||||
{
|
||||
enum object_type type = sha1_object_info(sha1, NULL);
|
||||
@@ -87,9 +57,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
int parents = 0;
|
||||
unsigned char tree_sha1[20];
|
||||
unsigned char commit_sha1[20];
|
||||
char comment[1000];
|
||||
char *buffer;
|
||||
unsigned int size;
|
||||
struct strbuf buffer;
|
||||
int encoding_is_utf8;
|
||||
|
||||
git_config(git_default_config);
|
||||
@@ -118,8 +86,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
/* Not having i18n.commitencoding is the same as having utf-8 */
|
||||
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
|
||||
|
||||
init_buffer(&buffer, &size);
|
||||
add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1));
|
||||
strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
|
||||
strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1));
|
||||
|
||||
/*
|
||||
* NOTE! This ordering means that the same exact tree merged with a
|
||||
@@ -127,26 +95,24 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
* if everything else stays the same.
|
||||
*/
|
||||
for (i = 0; i < parents; i++)
|
||||
add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i]));
|
||||
strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i]));
|
||||
|
||||
/* Person/date information */
|
||||
add_buffer(&buffer, &size, "author %s\n", git_author_info(1));
|
||||
add_buffer(&buffer, &size, "committer %s\n", git_committer_info(1));
|
||||
strbuf_addf(&buffer, "author %s\n", git_author_info(1));
|
||||
strbuf_addf(&buffer, "committer %s\n", git_committer_info(1));
|
||||
if (!encoding_is_utf8)
|
||||
add_buffer(&buffer, &size,
|
||||
"encoding %s\n", git_commit_encoding);
|
||||
add_buffer(&buffer, &size, "\n");
|
||||
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
|
||||
strbuf_addch(&buffer, '\n');
|
||||
|
||||
/* And add the comment */
|
||||
while (fgets(comment, sizeof(comment), stdin) != NULL)
|
||||
add_buffer(&buffer, &size, "%s", comment);
|
||||
if (strbuf_read(&buffer, 0, 0) < 0)
|
||||
die("git-commit-tree: read returned %s", strerror(errno));
|
||||
|
||||
/* And check the encoding */
|
||||
buffer[size] = '\0';
|
||||
if (encoding_is_utf8 && !is_utf8(buffer))
|
||||
if (encoding_is_utf8 && !is_utf8(buffer.buf))
|
||||
fprintf(stderr, commit_utf8_warn);
|
||||
|
||||
if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) {
|
||||
if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) {
|
||||
printf("%s\n", sha1_to_hex(commit_sha1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -165,15 +165,21 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int nongit = 0;
|
||||
char* value;
|
||||
setup_git_directory_gently(&nongit);
|
||||
const char *file = setup_git_directory_gently(&nongit);
|
||||
|
||||
while (1 < argc) {
|
||||
if (!strcmp(argv[1], "--int"))
|
||||
type = T_INT;
|
||||
else if (!strcmp(argv[1], "--bool"))
|
||||
type = T_BOOL;
|
||||
else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
|
||||
return git_config(show_all_config);
|
||||
else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) {
|
||||
if (argc != 2)
|
||||
usage(git_config_set_usage);
|
||||
if (git_config(show_all_config) < 0 && file && errno)
|
||||
die("unable to read config file %s: %s", file,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
else if (!strcmp(argv[1], "--global")) {
|
||||
char *home = getenv("HOME");
|
||||
if (home) {
|
||||
@@ -189,7 +195,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
||||
else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
|
||||
if (argc < 3)
|
||||
usage(git_config_set_usage);
|
||||
setenv(CONFIG_ENVIRONMENT, argv[2], 1);
|
||||
if (!is_absolute_path(argv[2]) && file)
|
||||
file = prefix_filename(file, strlen(file),
|
||||
argv[2]);
|
||||
else
|
||||
file = argv[2];
|
||||
setenv(CONFIG_ENVIRONMENT, file, 1);
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
@@ -3,26 +3,14 @@
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
static char *get_stdin(void)
|
||||
{
|
||||
size_t offset = 0;
|
||||
char *data = xmalloc(CHUNK_SIZE);
|
||||
|
||||
while (1) {
|
||||
ssize_t cnt = xread(0, data + offset, CHUNK_SIZE);
|
||||
if (cnt < 0)
|
||||
die("error reading standard input: %s",
|
||||
strerror(errno));
|
||||
if (cnt == 0) {
|
||||
data[offset] = 0;
|
||||
break;
|
||||
}
|
||||
offset += cnt;
|
||||
data = xrealloc(data, offset + CHUNK_SIZE);
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
if (strbuf_read(&buf, 0, 1024) < 0) {
|
||||
die("error reading standard input: %s", strerror(errno));
|
||||
}
|
||||
return data;
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static void show_new(enum object_type type, unsigned char *sha1_new)
|
||||
@@ -31,24 +19,19 @@ static void show_new(enum object_type type, unsigned char *sha1_new)
|
||||
find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
|
||||
}
|
||||
|
||||
static int update_ref(const char *action,
|
||||
static int update_ref_env(const char *action,
|
||||
const char *refname,
|
||||
unsigned char *sha1,
|
||||
unsigned char *oldval)
|
||||
{
|
||||
char msg[1024];
|
||||
char *rla = getenv("GIT_REFLOG_ACTION");
|
||||
static struct ref_lock *lock;
|
||||
const char *rla = getenv("GIT_REFLOG_ACTION");
|
||||
|
||||
if (!rla)
|
||||
rla = "(reflog update)";
|
||||
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
|
||||
lock = lock_any_ref_for_update(refname, oldval, 0);
|
||||
if (!lock)
|
||||
return 1;
|
||||
if (write_ref_sha1(lock, sha1, msg) < 0)
|
||||
return 1;
|
||||
return 0;
|
||||
if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
|
||||
warning("reflog message too long: %.*s...", 50, msg);
|
||||
return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
|
||||
}
|
||||
|
||||
static int update_local_ref(const char *name,
|
||||
@@ -78,7 +61,7 @@ static int update_local_ref(const char *name,
|
||||
}
|
||||
|
||||
if (get_sha1(name, sha1_old)) {
|
||||
char *msg;
|
||||
const char *msg;
|
||||
just_store:
|
||||
/* new ref */
|
||||
if (!strncmp(name, "refs/tags/", 10))
|
||||
@@ -88,7 +71,7 @@ static int update_local_ref(const char *name,
|
||||
fprintf(stderr, "* %s: storing %s\n",
|
||||
name, note);
|
||||
show_new(type, sha1_new);
|
||||
return update_ref(msg, name, sha1_new, NULL);
|
||||
return update_ref_env(msg, name, sha1_new, NULL);
|
||||
}
|
||||
|
||||
if (!hashcmp(sha1_old, sha1_new)) {
|
||||
@@ -102,7 +85,7 @@ static int update_local_ref(const char *name,
|
||||
if (!strncmp(name, "refs/tags/", 10)) {
|
||||
fprintf(stderr, "* %s: updating with %s\n", name, note);
|
||||
show_new(type, sha1_new);
|
||||
return update_ref("updating tag", name, sha1_new, NULL);
|
||||
return update_ref_env("updating tag", name, sha1_new, NULL);
|
||||
}
|
||||
|
||||
current = lookup_commit_reference(sha1_old);
|
||||
@@ -117,7 +100,7 @@ static int update_local_ref(const char *name,
|
||||
fprintf(stderr, "* %s: fast forward to %s\n",
|
||||
name, note);
|
||||
fprintf(stderr, " old..new: %s..%s\n", oldh, newh);
|
||||
return update_ref("fast forward", name, sha1_new, sha1_old);
|
||||
return update_ref_env("fast forward", name, sha1_new, sha1_old);
|
||||
}
|
||||
if (!force) {
|
||||
fprintf(stderr,
|
||||
@@ -131,7 +114,7 @@ static int update_local_ref(const char *name,
|
||||
"* %s: forcing update to non-fast forward %s\n",
|
||||
name, note);
|
||||
fprintf(stderr, " old...new: %s...%s\n", oldh, newh);
|
||||
return update_ref("forced-update", name, sha1_new, sha1_old);
|
||||
return update_ref_env("forced-update", name, sha1_new, sha1_old);
|
||||
}
|
||||
|
||||
static int append_fetch_head(FILE *fp,
|
||||
@@ -148,7 +131,7 @@ static int append_fetch_head(FILE *fp,
|
||||
|
||||
if (get_sha1(head, sha1))
|
||||
return error("Not a valid object name: %s", head);
|
||||
commit = lookup_commit_reference(sha1);
|
||||
commit = lookup_commit_reference_gently(sha1, 1);
|
||||
if (!commit)
|
||||
not_for_merge = 1;
|
||||
|
||||
@@ -239,19 +222,15 @@ static char *find_local_name(const char *remote_name, const char *refs,
|
||||
}
|
||||
if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
|
||||
const char *local_part = ref + len + 1;
|
||||
char *ret;
|
||||
int retlen;
|
||||
|
||||
if (!next)
|
||||
retlen = strlen(local_part);
|
||||
else
|
||||
retlen = next - local_part;
|
||||
ret = xmalloc(retlen + 1);
|
||||
memcpy(ret, local_part, retlen);
|
||||
ret[retlen] = 0;
|
||||
*force_p = single_force;
|
||||
*not_for_merge_p = not_for_merge;
|
||||
return ret;
|
||||
return xmemdupz(local_part, retlen);
|
||||
}
|
||||
ref = next;
|
||||
}
|
||||
|
||||
@@ -6,19 +6,18 @@
|
||||
#include "exec_cmd.h"
|
||||
#include "pack.h"
|
||||
#include "sideband.h"
|
||||
#include "fetch-pack.h"
|
||||
#include "run-command.h"
|
||||
|
||||
static int keep_pack;
|
||||
static int transfer_unpack_limit = -1;
|
||||
static int fetch_unpack_limit = -1;
|
||||
static int unpack_limit = 100;
|
||||
static int quiet;
|
||||
static int verbose;
|
||||
static int fetch_all;
|
||||
static int depth;
|
||||
static int no_progress;
|
||||
static struct fetch_pack_args args = {
|
||||
/* .uploadpack = */ "git-upload-pack",
|
||||
};
|
||||
|
||||
static const char fetch_pack_usage[] =
|
||||
"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
|
||||
static const char *uploadpack = "git-upload-pack";
|
||||
|
||||
#define COMPLETE (1U << 0)
|
||||
#define COMMON (1U << 1)
|
||||
@@ -180,7 +179,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
(use_sideband == 2 ? " side-band-64k" : ""),
|
||||
(use_sideband == 1 ? " side-band" : ""),
|
||||
(use_thin_pack ? " thin-pack" : ""),
|
||||
(no_progress ? " no-progress" : ""),
|
||||
(args.no_progress ? " no-progress" : ""),
|
||||
" ofs-delta");
|
||||
else
|
||||
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
|
||||
@@ -188,13 +187,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
}
|
||||
if (is_repository_shallow())
|
||||
write_shallow_commits(fd[1], 1);
|
||||
if (depth > 0)
|
||||
packet_write(fd[1], "deepen %d", depth);
|
||||
if (args.depth > 0)
|
||||
packet_write(fd[1], "deepen %d", args.depth);
|
||||
packet_flush(fd[1]);
|
||||
if (!fetching)
|
||||
return 1;
|
||||
|
||||
if (depth > 0) {
|
||||
if (args.depth > 0) {
|
||||
char line[1024];
|
||||
unsigned char sha1[20];
|
||||
int len;
|
||||
@@ -225,7 +224,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
retval = -1;
|
||||
while ((sha1 = get_rev())) {
|
||||
packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
|
||||
if (verbose)
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
|
||||
in_vain++;
|
||||
if (!(31 & ++count)) {
|
||||
@@ -243,7 +242,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
|
||||
do {
|
||||
ack = get_ack(fd[0], result_sha1);
|
||||
if (verbose && ack)
|
||||
if (args.verbose && ack)
|
||||
fprintf(stderr, "got ack %d %s\n", ack,
|
||||
sha1_to_hex(result_sha1));
|
||||
if (ack == 1) {
|
||||
@@ -262,7 +261,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
} while (ack);
|
||||
flushes--;
|
||||
if (got_continue && MAX_IN_VAIN < in_vain) {
|
||||
if (verbose)
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "giving up\n");
|
||||
break; /* give up */
|
||||
}
|
||||
@@ -270,7 +269,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
}
|
||||
done:
|
||||
packet_write(fd[1], "done\n");
|
||||
if (verbose)
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "done\n");
|
||||
if (retval != 0) {
|
||||
multi_ack = 0;
|
||||
@@ -279,7 +278,7 @@ done:
|
||||
while (flushes || multi_ack) {
|
||||
int ack = get_ack(fd[0], result_sha1);
|
||||
if (ack) {
|
||||
if (verbose)
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "got ack (%d) %s\n", ack,
|
||||
sha1_to_hex(result_sha1));
|
||||
if (ack == 1)
|
||||
@@ -317,7 +316,7 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag,
|
||||
static void mark_recent_complete_commits(unsigned long cutoff)
|
||||
{
|
||||
while (complete && cutoff <= complete->item->date) {
|
||||
if (verbose)
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Marking %s as complete\n",
|
||||
sha1_to_hex(complete->item->object.sha1));
|
||||
pop_most_recent_commit(&complete, COMPLETE);
|
||||
@@ -332,7 +331,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
|
||||
struct ref *ref, *next;
|
||||
struct ref *fastarray[32];
|
||||
|
||||
if (nr_match && !fetch_all) {
|
||||
if (nr_match && !args.fetch_all) {
|
||||
if (ARRAY_SIZE(fastarray) < nr_match)
|
||||
return_refs = xcalloc(nr_match, sizeof(struct ref *));
|
||||
else {
|
||||
@@ -348,8 +347,8 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
|
||||
if (!memcmp(ref->name, "refs/", 5) &&
|
||||
check_ref_format(ref->name + 5))
|
||||
; /* trash */
|
||||
else if (fetch_all &&
|
||||
(!depth || prefixcmp(ref->name, "refs/tags/") )) {
|
||||
else if (args.fetch_all &&
|
||||
(!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
|
||||
*newtail = ref;
|
||||
ref->next = NULL;
|
||||
newtail = &ref->next;
|
||||
@@ -365,7 +364,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
|
||||
free(ref);
|
||||
}
|
||||
|
||||
if (!fetch_all) {
|
||||
if (!args.fetch_all) {
|
||||
int i;
|
||||
for (i = 0; i < nr_match; i++) {
|
||||
ref = return_refs[i];
|
||||
@@ -408,7 +407,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
|
||||
}
|
||||
}
|
||||
|
||||
if (!depth) {
|
||||
if (!args.depth) {
|
||||
for_each_ref(mark_complete, NULL);
|
||||
if (cutoff)
|
||||
mark_recent_complete_commits(cutoff);
|
||||
@@ -442,7 +441,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
|
||||
o = lookup_object(remote);
|
||||
if (!o || !(o->flags & COMPLETE)) {
|
||||
retval = 0;
|
||||
if (!verbose)
|
||||
if (!args.verbose)
|
||||
continue;
|
||||
fprintf(stderr,
|
||||
"want %s (%s)\n", sha1_to_hex(remote),
|
||||
@@ -451,7 +450,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
|
||||
}
|
||||
|
||||
hashcpy(ref->new_sha1, local);
|
||||
if (!verbose)
|
||||
if (!args.verbose)
|
||||
continue;
|
||||
fprintf(stderr,
|
||||
"already have %s (%s)\n", sha1_to_hex(remote),
|
||||
@@ -460,55 +459,52 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static pid_t setup_sideband(int fd[2], int xd[2])
|
||||
static int sideband_demux(int fd, void *data)
|
||||
{
|
||||
pid_t side_pid;
|
||||
int *xd = data;
|
||||
|
||||
close(xd[1]);
|
||||
return recv_sideband("fetch-pack", xd[0], fd, 2);
|
||||
}
|
||||
|
||||
static void setup_sideband(int fd[2], int xd[2], struct async *demux)
|
||||
{
|
||||
if (!use_sideband) {
|
||||
fd[0] = xd[0];
|
||||
fd[1] = xd[1];
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
/* xd[] is talking with upload-pack; subprocess reads from
|
||||
* xd[0], spits out band#2 to stderr, and feeds us band#1
|
||||
* through our fd[0].
|
||||
* through demux->out.
|
||||
*/
|
||||
if (pipe(fd) < 0)
|
||||
die("fetch-pack: unable to set up pipe");
|
||||
side_pid = fork();
|
||||
if (side_pid < 0)
|
||||
demux->proc = sideband_demux;
|
||||
demux->data = xd;
|
||||
if (start_async(demux))
|
||||
die("fetch-pack: unable to fork off sideband demultiplexer");
|
||||
if (!side_pid) {
|
||||
/* subprocess */
|
||||
close(fd[0]);
|
||||
if (xd[0] != xd[1])
|
||||
close(xd[1]);
|
||||
if (recv_sideband("fetch-pack", xd[0], fd[1], 2))
|
||||
exit(1);
|
||||
exit(0);
|
||||
}
|
||||
close(xd[0]);
|
||||
close(fd[1]);
|
||||
fd[0] = demux->out;
|
||||
fd[1] = xd[1];
|
||||
return side_pid;
|
||||
}
|
||||
|
||||
static int get_pack(int xd[2])
|
||||
static int get_pack(int xd[2], char **pack_lockfile)
|
||||
{
|
||||
int status;
|
||||
pid_t pid, side_pid;
|
||||
struct async demux;
|
||||
int fd[2];
|
||||
const char *argv[20];
|
||||
char keep_arg[256];
|
||||
char hdr_arg[256];
|
||||
const char **av;
|
||||
int do_keep = keep_pack;
|
||||
int do_keep = args.keep_pack;
|
||||
struct child_process cmd;
|
||||
|
||||
side_pid = setup_sideband(fd, xd);
|
||||
setup_sideband(fd, xd, &demux);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.argv = argv;
|
||||
av = argv;
|
||||
*hdr_arg = 0;
|
||||
if (unpack_limit) {
|
||||
if (!args.keep_pack && unpack_limit) {
|
||||
struct pack_header header;
|
||||
|
||||
if (read_pack_header(fd[0], &header))
|
||||
@@ -522,13 +518,15 @@ static int get_pack(int xd[2])
|
||||
}
|
||||
|
||||
if (do_keep) {
|
||||
if (pack_lockfile)
|
||||
cmd.out = -1;
|
||||
*av++ = "index-pack";
|
||||
*av++ = "--stdin";
|
||||
if (!quiet && !no_progress)
|
||||
if (!args.quiet && !args.no_progress)
|
||||
*av++ = "-v";
|
||||
if (use_thin_pack)
|
||||
if (args.use_thin_pack)
|
||||
*av++ = "--fix-thin";
|
||||
if (keep_pack > 1 || unpack_limit) {
|
||||
if (args.lock_pack || unpack_limit) {
|
||||
int s = sprintf(keep_arg,
|
||||
"--keep=fetch-pack %d on ", getpid());
|
||||
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
|
||||
@@ -538,35 +536,32 @@ static int get_pack(int xd[2])
|
||||
}
|
||||
else {
|
||||
*av++ = "unpack-objects";
|
||||
if (quiet)
|
||||
if (args.quiet)
|
||||
*av++ = "-q";
|
||||
}
|
||||
if (*hdr_arg)
|
||||
*av++ = hdr_arg;
|
||||
*av++ = NULL;
|
||||
|
||||
pid = spawnv_git_cmd(argv, fd, NULL);
|
||||
if (pid < 0)
|
||||
cmd.in = fd[0];
|
||||
cmd.git_cmd = 1;
|
||||
if (start_command(&cmd))
|
||||
die("fetch-pack: unable to fork off %s", argv[0]);
|
||||
close(fd[1]);
|
||||
while (waitpid(pid, &status, 0) < 0) {
|
||||
if (errno != EINTR)
|
||||
die("waiting for %s: %s", argv[0], strerror(errno));
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
int code = WEXITSTATUS(status);
|
||||
if (code)
|
||||
die("%s died with error code %d", argv[0], code);
|
||||
return 0;
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
int sig = WTERMSIG(status);
|
||||
die("%s died of signal %d", argv[0], sig);
|
||||
}
|
||||
die("%s died of unnatural causes %d", argv[0], status);
|
||||
if (do_keep && pack_lockfile)
|
||||
*pack_lockfile = index_pack_lockfile(cmd.out);
|
||||
|
||||
if (finish_command(&cmd))
|
||||
die("%s failed", argv[0]);
|
||||
if (use_sideband && finish_async(&demux))
|
||||
die("error in sideband demultiplexer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fetch_pack(int fd[2], int nr_match, char **match)
|
||||
static struct ref *do_fetch_pack(int fd[2],
|
||||
int nr_match,
|
||||
char **match,
|
||||
char **pack_lockfile)
|
||||
{
|
||||
struct ref *ref;
|
||||
unsigned char sha1[20];
|
||||
@@ -575,18 +570,18 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
|
||||
if (is_repository_shallow() && !server_supports("shallow"))
|
||||
die("Server does not support shallow clients");
|
||||
if (server_supports("multi_ack")) {
|
||||
if (verbose)
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports multi_ack\n");
|
||||
multi_ack = 1;
|
||||
}
|
||||
#ifndef __MINGW32__
|
||||
if (server_supports("side-band-64k")) {
|
||||
if (verbose)
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports side-band-64k\n");
|
||||
use_sideband = 2;
|
||||
}
|
||||
else if (server_supports("side-band")) {
|
||||
if (verbose)
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports side-band\n");
|
||||
use_sideband = 1;
|
||||
}
|
||||
@@ -600,22 +595,17 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
|
||||
goto all_done;
|
||||
}
|
||||
if (find_common(fd, sha1, ref) < 0)
|
||||
if (keep_pack != 1)
|
||||
if (!args.keep_pack)
|
||||
/* When cloning, it is not unusual to have
|
||||
* no common commit.
|
||||
*/
|
||||
fprintf(stderr, "warning: no common commits\n");
|
||||
|
||||
if (get_pack(fd))
|
||||
if (get_pack(fd, pack_lockfile))
|
||||
die("git-fetch-pack: fetch failed.");
|
||||
|
||||
all_done:
|
||||
while (ref) {
|
||||
printf("%s %s\n",
|
||||
sha1_to_hex(ref->old_sha1), ref->name);
|
||||
ref = ref->next;
|
||||
}
|
||||
return 0;
|
||||
return ref;
|
||||
}
|
||||
|
||||
static int remove_duplicates(int nr_heads, char **heads)
|
||||
@@ -637,7 +627,6 @@ static int remove_duplicates(int nr_heads, char **heads)
|
||||
heads[dst] = heads[src];
|
||||
dst++;
|
||||
}
|
||||
heads[dst] = 0;
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -658,85 +647,117 @@ static int fetch_pack_config(const char *var, const char *value)
|
||||
|
||||
static struct lock_file lock;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void fetch_pack_setup(void)
|
||||
{
|
||||
int i, ret, nr_heads;
|
||||
char *dest = NULL, **heads;
|
||||
int fd[2];
|
||||
pid_t pid;
|
||||
struct stat st;
|
||||
|
||||
setup_git_directory();
|
||||
static int did_setup;
|
||||
if (did_setup)
|
||||
return;
|
||||
git_config(fetch_pack_config);
|
||||
|
||||
if (0 <= transfer_unpack_limit)
|
||||
unpack_limit = transfer_unpack_limit;
|
||||
else if (0 <= fetch_unpack_limit)
|
||||
unpack_limit = fetch_unpack_limit;
|
||||
did_setup = 1;
|
||||
}
|
||||
|
||||
int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, ret, nr_heads;
|
||||
struct ref *ref;
|
||||
char *dest = NULL, **heads;
|
||||
|
||||
nr_heads = 0;
|
||||
heads = NULL;
|
||||
for (i = 1; i < argc; i++) {
|
||||
char *arg = argv[i];
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (*arg == '-') {
|
||||
if (!prefixcmp(arg, "--upload-pack=")) {
|
||||
uploadpack = arg + 14;
|
||||
args.uploadpack = arg + 14;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--exec=")) {
|
||||
uploadpack = arg + 7;
|
||||
args.uploadpack = arg + 7;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
|
||||
quiet = 1;
|
||||
args.quiet = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
|
||||
keep_pack++;
|
||||
unpack_limit = 0;
|
||||
args.lock_pack = args.keep_pack;
|
||||
args.keep_pack = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--thin", arg)) {
|
||||
use_thin_pack = 1;
|
||||
args.use_thin_pack = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--all", arg)) {
|
||||
fetch_all = 1;
|
||||
args.fetch_all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-v", arg)) {
|
||||
verbose = 1;
|
||||
args.verbose = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--depth=")) {
|
||||
depth = strtol(arg + 8, NULL, 0);
|
||||
if (stat(git_path("shallow"), &st))
|
||||
st.st_mtime = 0;
|
||||
args.depth = strtol(arg + 8, NULL, 0);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--no-progress", arg)) {
|
||||
no_progress = 1;
|
||||
args.no_progress = 1;
|
||||
continue;
|
||||
}
|
||||
usage(fetch_pack_usage);
|
||||
}
|
||||
dest = arg;
|
||||
heads = argv + i + 1;
|
||||
dest = (char *)arg;
|
||||
heads = (char **)(argv + i + 1);
|
||||
nr_heads = argc - i - 1;
|
||||
break;
|
||||
}
|
||||
if (!dest)
|
||||
usage(fetch_pack_usage);
|
||||
pid = git_connect(fd, dest, uploadpack, verbose ? CONNECT_VERBOSE : 0);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
|
||||
ref = fetch_pack(&args, dest, nr_heads, heads, NULL);
|
||||
ret = !ref;
|
||||
|
||||
while (ref) {
|
||||
printf("%s %s\n",
|
||||
sha1_to_hex(ref->old_sha1), ref->name);
|
||||
ref = ref->next;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ref *fetch_pack(struct fetch_pack_args *my_args,
|
||||
const char *dest,
|
||||
int nr_heads,
|
||||
char **heads,
|
||||
char **pack_lockfile)
|
||||
{
|
||||
int i, ret;
|
||||
int fd[2];
|
||||
struct child_process *conn;
|
||||
struct ref *ref;
|
||||
struct stat st;
|
||||
|
||||
fetch_pack_setup();
|
||||
memcpy(&args, my_args, sizeof(args));
|
||||
if (args.depth > 0) {
|
||||
if (stat(git_path("shallow"), &st))
|
||||
st.st_mtime = 0;
|
||||
}
|
||||
|
||||
conn = git_connect(fd, (char *)dest, args.uploadpack,
|
||||
args.verbose ? CONNECT_VERBOSE : 0);
|
||||
if (heads && nr_heads)
|
||||
nr_heads = remove_duplicates(nr_heads, heads);
|
||||
ret = fetch_pack(fd, nr_heads, heads);
|
||||
ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
ret |= finish_connect(pid);
|
||||
ret = finish_connect(conn);
|
||||
|
||||
if (!ret && nr_heads) {
|
||||
/* If the heads to pull were given, we should have
|
||||
@@ -751,7 +772,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret && depth > 0) {
|
||||
if (!ret && args.depth > 0) {
|
||||
struct cache_time mtime;
|
||||
char *shallow = git_path("shallow");
|
||||
int fd;
|
||||
@@ -780,5 +801,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
return !!ret;
|
||||
if (ret)
|
||||
ref = NULL;
|
||||
|
||||
return ref;
|
||||
}
|
||||
586
builtin-fetch.c
Normal file
586
builtin-fetch.c
Normal file
@@ -0,0 +1,586 @@
|
||||
/*
|
||||
* "git fetch"
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
#include "builtin.h"
|
||||
#include "path-list.h"
|
||||
#include "remote.h"
|
||||
#include "transport.h"
|
||||
|
||||
static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack <upload-pack>] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth <depth>] [-v | --verbose] [<repository> <refspec>...]";
|
||||
|
||||
static int append, force, tags, no_tags, update_head_ok, verbose, quiet;
|
||||
static char *default_rla = NULL;
|
||||
static struct transport *transport;
|
||||
|
||||
static void unlock_pack(void)
|
||||
{
|
||||
if (transport)
|
||||
transport_unlock_pack(transport);
|
||||
}
|
||||
|
||||
static void unlock_pack_on_signal(int signo)
|
||||
{
|
||||
unlock_pack();
|
||||
signal(SIGINT, SIG_DFL);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
static void add_merge_config(struct ref **head,
|
||||
struct ref *remote_refs,
|
||||
struct branch *branch,
|
||||
struct ref ***tail)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < branch->merge_nr; i++) {
|
||||
struct ref *rm, **old_tail = *tail;
|
||||
struct refspec refspec;
|
||||
|
||||
for (rm = *head; rm; rm = rm->next) {
|
||||
if (branch_merge_matches(branch, i, rm->name)) {
|
||||
rm->merge = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rm)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Not fetched to a tracking branch? We need to fetch
|
||||
* it anyway to allow this branch's "branch.$name.merge"
|
||||
* to be honored by git-pull, but we do not have to
|
||||
* fail if branch.$name.merge is misconfigured to point
|
||||
* at a nonexisting branch. If we were indeed called by
|
||||
* git-pull, it will notice the misconfiguration because
|
||||
* there is no entry in the resulting FETCH_HEAD marked
|
||||
* for merging.
|
||||
*/
|
||||
refspec.src = branch->merge[i]->src;
|
||||
refspec.dst = NULL;
|
||||
refspec.pattern = 0;
|
||||
refspec.force = 0;
|
||||
get_fetch_map(remote_refs, &refspec, tail, 1);
|
||||
for (rm = *old_tail; rm; rm = rm->next)
|
||||
rm->merge = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static struct ref *get_ref_map(struct transport *transport,
|
||||
struct refspec *refs, int ref_count, int tags,
|
||||
int *autotags)
|
||||
{
|
||||
int i;
|
||||
struct ref *rm;
|
||||
struct ref *ref_map = NULL;
|
||||
struct ref **tail = &ref_map;
|
||||
|
||||
struct ref *remote_refs = transport_get_remote_refs(transport);
|
||||
|
||||
if (ref_count || tags) {
|
||||
for (i = 0; i < ref_count; i++) {
|
||||
get_fetch_map(remote_refs, &refs[i], &tail, 0);
|
||||
if (refs[i].dst && refs[i].dst[0])
|
||||
*autotags = 1;
|
||||
}
|
||||
/* Merge everything on the command line, but not --tags */
|
||||
for (rm = ref_map; rm; rm = rm->next)
|
||||
rm->merge = 1;
|
||||
if (tags) {
|
||||
struct refspec refspec;
|
||||
refspec.src = "refs/tags/";
|
||||
refspec.dst = "refs/tags/";
|
||||
refspec.pattern = 1;
|
||||
refspec.force = 0;
|
||||
get_fetch_map(remote_refs, &refspec, &tail, 0);
|
||||
}
|
||||
} else {
|
||||
/* Use the defaults */
|
||||
struct remote *remote = transport->remote;
|
||||
struct branch *branch = branch_get(NULL);
|
||||
int has_merge = branch_has_merge_config(branch);
|
||||
if (remote && (remote->fetch_refspec_nr || has_merge)) {
|
||||
for (i = 0; i < remote->fetch_refspec_nr; i++) {
|
||||
get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);
|
||||
if (remote->fetch[i].dst &&
|
||||
remote->fetch[i].dst[0])
|
||||
*autotags = 1;
|
||||
if (!i && !has_merge && ref_map &&
|
||||
!remote->fetch[0].pattern)
|
||||
ref_map->merge = 1;
|
||||
}
|
||||
/*
|
||||
* if the remote we're fetching from is the same
|
||||
* as given in branch.<name>.remote, we add the
|
||||
* ref given in branch.<name>.merge, too.
|
||||
*/
|
||||
if (has_merge &&
|
||||
!strcmp(branch->remote_name, remote->name))
|
||||
add_merge_config(&ref_map, remote_refs, branch, &tail);
|
||||
} else {
|
||||
ref_map = get_remote_ref(remote_refs, "HEAD");
|
||||
if (!ref_map)
|
||||
die("Couldn't find remote ref HEAD");
|
||||
ref_map->merge = 1;
|
||||
}
|
||||
}
|
||||
ref_remove_duplicates(ref_map);
|
||||
|
||||
return ref_map;
|
||||
}
|
||||
|
||||
static void show_new(enum object_type type, unsigned char *sha1_new)
|
||||
{
|
||||
fprintf(stderr, " %s: %s\n", typename(type),
|
||||
find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
|
||||
}
|
||||
|
||||
static int s_update_ref(const char *action,
|
||||
struct ref *ref,
|
||||
int check_old)
|
||||
{
|
||||
char msg[1024];
|
||||
char *rla = getenv("GIT_REFLOG_ACTION");
|
||||
static struct ref_lock *lock;
|
||||
|
||||
if (!rla)
|
||||
rla = default_rla;
|
||||
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
|
||||
lock = lock_any_ref_for_update(ref->name,
|
||||
check_old ? ref->old_sha1 : NULL, 0);
|
||||
if (!lock)
|
||||
return 1;
|
||||
if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_local_ref(struct ref *ref,
|
||||
const char *note,
|
||||
int verbose)
|
||||
{
|
||||
char oldh[41], newh[41];
|
||||
struct commit *current = NULL, *updated;
|
||||
enum object_type type;
|
||||
struct branch *current_branch = branch_get(NULL);
|
||||
|
||||
type = sha1_object_info(ref->new_sha1, NULL);
|
||||
if (type < 0)
|
||||
die("object %s not found", sha1_to_hex(ref->new_sha1));
|
||||
|
||||
if (!*ref->name) {
|
||||
/* Not storing */
|
||||
if (verbose) {
|
||||
fprintf(stderr, "* fetched %s\n", note);
|
||||
show_new(type, ref->new_sha1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
|
||||
if (verbose) {
|
||||
fprintf(stderr, "* %s: same as %s\n",
|
||||
ref->name, note);
|
||||
show_new(type, ref->new_sha1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (current_branch &&
|
||||
!strcmp(ref->name, current_branch->name) &&
|
||||
!(update_head_ok || is_bare_repository()) &&
|
||||
!is_null_sha1(ref->old_sha1)) {
|
||||
/*
|
||||
* If this is the head, and it's not okay to update
|
||||
* the head, and the old value of the head isn't empty...
|
||||
*/
|
||||
fprintf(stderr,
|
||||
" * %s: Cannot fetch into the current branch.\n",
|
||||
ref->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!is_null_sha1(ref->old_sha1) &&
|
||||
!prefixcmp(ref->name, "refs/tags/")) {
|
||||
fprintf(stderr, "* %s: updating with %s\n",
|
||||
ref->name, note);
|
||||
show_new(type, ref->new_sha1);
|
||||
return s_update_ref("updating tag", ref, 0);
|
||||
}
|
||||
|
||||
current = lookup_commit_reference_gently(ref->old_sha1, 1);
|
||||
updated = lookup_commit_reference_gently(ref->new_sha1, 1);
|
||||
if (!current || !updated) {
|
||||
char *msg;
|
||||
if (!strncmp(ref->name, "refs/tags/", 10))
|
||||
msg = "storing tag";
|
||||
else
|
||||
msg = "storing head";
|
||||
fprintf(stderr, "* %s: storing %s\n",
|
||||
ref->name, note);
|
||||
show_new(type, ref->new_sha1);
|
||||
return s_update_ref(msg, ref, 0);
|
||||
}
|
||||
|
||||
strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
|
||||
strcpy(newh, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
|
||||
|
||||
if (in_merge_bases(current, &updated, 1)) {
|
||||
fprintf(stderr, "* %s: fast forward to %s\n",
|
||||
ref->name, note);
|
||||
fprintf(stderr, " old..new: %s..%s\n", oldh, newh);
|
||||
return s_update_ref("fast forward", ref, 1);
|
||||
}
|
||||
if (!force && !ref->force) {
|
||||
fprintf(stderr,
|
||||
"* %s: not updating to non-fast forward %s\n",
|
||||
ref->name, note);
|
||||
fprintf(stderr,
|
||||
" old...new: %s...%s\n", oldh, newh);
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr,
|
||||
"* %s: forcing update to non-fast forward %s\n",
|
||||
ref->name, note);
|
||||
fprintf(stderr, " old...new: %s...%s\n", oldh, newh);
|
||||
return s_update_ref("forced-update", ref, 1);
|
||||
}
|
||||
|
||||
static void store_updated_refs(const char *url, struct ref *ref_map)
|
||||
{
|
||||
FILE *fp;
|
||||
struct commit *commit;
|
||||
int url_len, i, note_len;
|
||||
char note[1024];
|
||||
const char *what, *kind;
|
||||
struct ref *rm;
|
||||
|
||||
fp = fopen(git_path("FETCH_HEAD"), "a");
|
||||
for (rm = ref_map; rm; rm = rm->next) {
|
||||
struct ref *ref = NULL;
|
||||
|
||||
if (rm->peer_ref) {
|
||||
ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);
|
||||
strcpy(ref->name, rm->peer_ref->name);
|
||||
hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
|
||||
hashcpy(ref->new_sha1, rm->old_sha1);
|
||||
ref->force = rm->peer_ref->force;
|
||||
}
|
||||
|
||||
commit = lookup_commit_reference_gently(rm->old_sha1, 1);
|
||||
if (!commit)
|
||||
rm->merge = 0;
|
||||
|
||||
if (!strcmp(rm->name, "HEAD")) {
|
||||
kind = "";
|
||||
what = "";
|
||||
}
|
||||
else if (!prefixcmp(rm->name, "refs/heads/")) {
|
||||
kind = "branch";
|
||||
what = rm->name + 11;
|
||||
}
|
||||
else if (!prefixcmp(rm->name, "refs/tags/")) {
|
||||
kind = "tag";
|
||||
what = rm->name + 10;
|
||||
}
|
||||
else if (!prefixcmp(rm->name, "refs/remotes/")) {
|
||||
kind = "remote branch";
|
||||
what = rm->name + 13;
|
||||
}
|
||||
else {
|
||||
kind = "";
|
||||
what = rm->name;
|
||||
}
|
||||
|
||||
url_len = strlen(url);
|
||||
for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
|
||||
;
|
||||
url_len = i + 1;
|
||||
if (4 < i && !strncmp(".git", url + i - 3, 4))
|
||||
url_len = i - 3;
|
||||
|
||||
note_len = 0;
|
||||
if (*what) {
|
||||
if (*kind)
|
||||
note_len += sprintf(note + note_len, "%s ",
|
||||
kind);
|
||||
note_len += sprintf(note + note_len, "'%s' of ", what);
|
||||
}
|
||||
note_len += sprintf(note + note_len, "%.*s", url_len, url);
|
||||
fprintf(fp, "%s\t%s\t%s\n",
|
||||
sha1_to_hex(commit ? commit->object.sha1 :
|
||||
rm->old_sha1),
|
||||
rm->merge ? "" : "not-for-merge",
|
||||
note);
|
||||
|
||||
if (ref)
|
||||
update_local_ref(ref, note, verbose);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static int fetch_refs(struct transport *transport, struct ref *ref_map)
|
||||
{
|
||||
int ret = transport_fetch_refs(transport, ref_map);
|
||||
if (!ret)
|
||||
store_updated_refs(transport->url, ref_map);
|
||||
transport_unlock_pack(transport);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_existing(const char *refname, const unsigned char *sha1,
|
||||
int flag, void *cbdata)
|
||||
{
|
||||
struct path_list *list = (struct path_list *)cbdata;
|
||||
path_list_insert(refname, list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ref *find_non_local_tags(struct transport *transport,
|
||||
struct ref *fetch_map)
|
||||
{
|
||||
static struct path_list existing_refs = { NULL, 0, 0, 0 };
|
||||
struct path_list new_refs = { NULL, 0, 0, 1 };
|
||||
char *ref_name;
|
||||
int ref_name_len;
|
||||
unsigned char *ref_sha1;
|
||||
struct ref *tag_ref;
|
||||
struct ref *rm = NULL;
|
||||
struct ref *ref_map = NULL;
|
||||
struct ref **tail = &ref_map;
|
||||
struct ref *ref;
|
||||
|
||||
for_each_ref(add_existing, &existing_refs);
|
||||
for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
|
||||
if (prefixcmp(ref->name, "refs/tags"))
|
||||
continue;
|
||||
|
||||
ref_name = xstrdup(ref->name);
|
||||
ref_name_len = strlen(ref_name);
|
||||
ref_sha1 = ref->old_sha1;
|
||||
|
||||
if (!strcmp(ref_name + ref_name_len - 3, "^{}")) {
|
||||
ref_name[ref_name_len - 3] = 0;
|
||||
tag_ref = transport_get_remote_refs(transport);
|
||||
while (tag_ref) {
|
||||
if (!strcmp(tag_ref->name, ref_name)) {
|
||||
ref_sha1 = tag_ref->old_sha1;
|
||||
break;
|
||||
}
|
||||
tag_ref = tag_ref->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_list_has_path(&existing_refs, ref_name) &&
|
||||
!path_list_has_path(&new_refs, ref_name) &&
|
||||
lookup_object(ref->old_sha1)) {
|
||||
fprintf(stderr, "Auto-following %s\n",
|
||||
ref_name);
|
||||
|
||||
path_list_insert(ref_name, &new_refs);
|
||||
|
||||
rm = alloc_ref(strlen(ref_name) + 1);
|
||||
strcpy(rm->name, ref_name);
|
||||
rm->peer_ref = alloc_ref(strlen(ref_name) + 1);
|
||||
strcpy(rm->peer_ref->name, ref_name);
|
||||
hashcpy(rm->old_sha1, ref_sha1);
|
||||
|
||||
*tail = rm;
|
||||
tail = &rm->next;
|
||||
}
|
||||
free(ref_name);
|
||||
}
|
||||
|
||||
return ref_map;
|
||||
}
|
||||
|
||||
static int do_fetch(struct transport *transport,
|
||||
struct refspec *refs, int ref_count)
|
||||
{
|
||||
struct ref *ref_map, *fetch_map;
|
||||
struct ref *rm;
|
||||
int autotags = (transport->remote->fetch_tags == 1);
|
||||
if (transport->remote->fetch_tags == 2 && !no_tags)
|
||||
tags = 1;
|
||||
if (transport->remote->fetch_tags == -1)
|
||||
no_tags = 1;
|
||||
|
||||
if (!transport->get_refs_list || !transport->fetch)
|
||||
die("Don't know how to fetch from %s", transport->url);
|
||||
|
||||
/* if not appending, truncate FETCH_HEAD */
|
||||
if (!append)
|
||||
fclose(fopen(git_path("FETCH_HEAD"), "w"));
|
||||
|
||||
ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
|
||||
|
||||
for (rm = ref_map; rm; rm = rm->next) {
|
||||
if (rm->peer_ref)
|
||||
read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
|
||||
}
|
||||
|
||||
if (fetch_refs(transport, ref_map)) {
|
||||
free_refs(ref_map);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fetch_map = ref_map;
|
||||
|
||||
/* if neither --no-tags nor --tags was specified, do automated tag
|
||||
* following ... */
|
||||
if (!(tags || no_tags) && autotags) {
|
||||
ref_map = find_non_local_tags(transport, fetch_map);
|
||||
if (ref_map) {
|
||||
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
|
||||
fetch_refs(transport, ref_map);
|
||||
}
|
||||
free_refs(ref_map);
|
||||
}
|
||||
|
||||
free_refs(fetch_map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_option(const char *name, const char *value)
|
||||
{
|
||||
int r = transport_set_option(transport, name, value);
|
||||
if (r < 0)
|
||||
die("Option \"%s\" value \"%s\" is not valid for %s\n",
|
||||
name, value, transport->url);
|
||||
if (r > 0)
|
||||
warning("Option \"%s\" is ignored for %s\n",
|
||||
name, transport->url);
|
||||
}
|
||||
|
||||
int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct remote *remote;
|
||||
int i, j, rla_offset;
|
||||
static const char **refs = NULL;
|
||||
int ref_nr = 0;
|
||||
int cmd_len = 0;
|
||||
const char *depth = NULL, *upload_pack = NULL;
|
||||
int keep = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
cmd_len += strlen(arg);
|
||||
|
||||
if (arg[0] != '-')
|
||||
break;
|
||||
if (!strcmp(arg, "--append") || !strcmp(arg, "-a")) {
|
||||
append = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--upload-pack=")) {
|
||||
upload_pack = arg + 14;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--upload-pack")) {
|
||||
i++;
|
||||
if (i == argc)
|
||||
usage(fetch_usage);
|
||||
upload_pack = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
|
||||
force = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-tags")) {
|
||||
no_tags = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--tags") || !strcmp(arg, "-t")) {
|
||||
tags = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--keep") || !strcmp(arg, "-k")) {
|
||||
keep = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--update-head-ok") || !strcmp(arg, "-u")) {
|
||||
update_head_ok = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--depth=")) {
|
||||
depth = arg + 8;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--depth")) {
|
||||
i++;
|
||||
if (i == argc)
|
||||
usage(fetch_usage);
|
||||
depth = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--quiet")) {
|
||||
quiet = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
|
||||
verbose++;
|
||||
continue;
|
||||
}
|
||||
usage(fetch_usage);
|
||||
}
|
||||
|
||||
for (j = i; j < argc; j++)
|
||||
cmd_len += strlen(argv[j]);
|
||||
|
||||
default_rla = xmalloc(cmd_len + 5 + argc + 1);
|
||||
sprintf(default_rla, "fetch");
|
||||
rla_offset = strlen(default_rla);
|
||||
for (j = 1; j < argc; j++) {
|
||||
sprintf(default_rla + rla_offset, " %s", argv[j]);
|
||||
rla_offset += strlen(argv[j]) + 1;
|
||||
}
|
||||
|
||||
if (i == argc)
|
||||
remote = remote_get(NULL);
|
||||
else
|
||||
remote = remote_get(argv[i++]);
|
||||
|
||||
transport = transport_get(remote, remote->url[0]);
|
||||
if (verbose >= 2)
|
||||
transport->verbose = 1;
|
||||
if (quiet)
|
||||
transport->verbose = -1;
|
||||
if (upload_pack)
|
||||
set_option(TRANS_OPT_UPLOADPACK, upload_pack);
|
||||
if (keep)
|
||||
set_option(TRANS_OPT_KEEP, "yes");
|
||||
if (depth)
|
||||
set_option(TRANS_OPT_DEPTH, depth);
|
||||
|
||||
if (!transport->url)
|
||||
die("Where do you want to fetch from today?");
|
||||
|
||||
if (i < argc) {
|
||||
int j = 0;
|
||||
refs = xcalloc(argc - i + 1, sizeof(const char *));
|
||||
while (i < argc) {
|
||||
if (!strcmp(argv[i], "tag")) {
|
||||
char *ref;
|
||||
i++;
|
||||
ref = xmalloc(strlen(argv[i]) * 2 + 22);
|
||||
strcpy(ref, "refs/tags/");
|
||||
strcat(ref, argv[i]);
|
||||
strcat(ref, ":refs/tags/");
|
||||
strcat(ref, argv[i]);
|
||||
refs[j++] = ref;
|
||||
} else
|
||||
refs[j++] = argv[i];
|
||||
i++;
|
||||
}
|
||||
refs[j] = NULL;
|
||||
ref_nr = j;
|
||||
}
|
||||
|
||||
signal(SIGINT, unlock_pack_on_signal);
|
||||
atexit(unlock_pack);
|
||||
return do_fetch(transport, parse_ref_spec(ref_nr, refs), ref_nr);
|
||||
}
|
||||
@@ -142,12 +142,10 @@ static int handle_line(char *line)
|
||||
if (!strcmp(".", src) || !strcmp(src, origin)) {
|
||||
int len = strlen(origin);
|
||||
if (origin[0] == '\'' && origin[len - 1] == '\'') {
|
||||
char *new_origin = xmalloc(len - 1);
|
||||
memcpy(new_origin, origin + 1, len - 2);
|
||||
new_origin[len - 2] = 0;
|
||||
origin = new_origin;
|
||||
} else
|
||||
origin = xmemdupz(origin + 1, len - 2);
|
||||
} else {
|
||||
origin = xstrdup(origin);
|
||||
}
|
||||
} else {
|
||||
char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
|
||||
sprintf(new_origin, "%s of %s", origin, src);
|
||||
@@ -213,14 +211,11 @@ static void shortlog(const char *name, unsigned char *sha1,
|
||||
|
||||
bol += 2;
|
||||
eol = strchr(bol, '\n');
|
||||
|
||||
if (eol) {
|
||||
int len = eol - bol;
|
||||
oneline = xmalloc(len + 1);
|
||||
memcpy(oneline, bol, len);
|
||||
oneline[len] = 0;
|
||||
} else
|
||||
oneline = xmemdupz(bol, eol - bol);
|
||||
} else {
|
||||
oneline = xstrdup(bol);
|
||||
}
|
||||
append_to_list(&subjects, oneline, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ static struct {
|
||||
{ "objectsize", FIELD_ULONG },
|
||||
{ "objectname" },
|
||||
{ "tree" },
|
||||
{ "parent" }, /* NEEDSWORK: how to address 2nd and later parents? */
|
||||
{ "parent" },
|
||||
{ "numparent", FIELD_ULONG },
|
||||
{ "object" },
|
||||
{ "type" },
|
||||
@@ -87,7 +87,6 @@ static int used_atom_cnt, sort_atom_limit, need_tagged;
|
||||
static int parse_atom(const char *atom, const char *ep)
|
||||
{
|
||||
const char *sp;
|
||||
char *n;
|
||||
int i, at;
|
||||
|
||||
sp = atom;
|
||||
@@ -106,7 +105,16 @@ static int parse_atom(const char *atom, const char *ep)
|
||||
/* Is the atom a valid one? */
|
||||
for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
|
||||
int len = strlen(valid_atom[i].name);
|
||||
if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))
|
||||
/*
|
||||
* If the atom name has a colon, strip it and everything after
|
||||
* it off - it specifies the format for this entry, and
|
||||
* shouldn't be used for checking against the valid_atom
|
||||
* table.
|
||||
*/
|
||||
const char *formatp = strchr(sp, ':');
|
||||
if (!formatp || ep < formatp)
|
||||
formatp = ep;
|
||||
if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -120,10 +128,7 @@ static int parse_atom(const char *atom, const char *ep)
|
||||
(sizeof *used_atom) * used_atom_cnt);
|
||||
used_atom_type = xrealloc(used_atom_type,
|
||||
(sizeof(*used_atom_type) * used_atom_cnt));
|
||||
n = xmalloc(ep - atom + 1);
|
||||
memcpy(n, atom, ep - atom);
|
||||
n[ep-atom] = 0;
|
||||
used_atom[at] = n;
|
||||
used_atom[at] = xmemdupz(atom, ep - atom);
|
||||
used_atom_type[at] = valid_atom[i].cmp_type;
|
||||
return at;
|
||||
}
|
||||
@@ -262,24 +267,26 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
|
||||
}
|
||||
if (!strcmp(name, "numparent")) {
|
||||
char *s = xmalloc(40);
|
||||
v->ul = num_parents(commit);
|
||||
sprintf(s, "%lu", v->ul);
|
||||
v->s = s;
|
||||
v->ul = num_parents(commit);
|
||||
}
|
||||
else if (!strcmp(name, "parent")) {
|
||||
int num = num_parents(commit);
|
||||
int i;
|
||||
struct commit_list *parents;
|
||||
char *s = xmalloc(42 * num);
|
||||
char *s = xmalloc(41 * num + 1);
|
||||
v->s = s;
|
||||
for (i = 0, parents = commit->parents;
|
||||
parents;
|
||||
parents = parents->next, i = i + 42) {
|
||||
parents = parents->next, i = i + 41) {
|
||||
struct commit *parent = parents->item;
|
||||
strcpy(s+i, sha1_to_hex(parent->object.sha1));
|
||||
if (parents->next)
|
||||
s[i+40] = ' ';
|
||||
}
|
||||
if (!i)
|
||||
*s = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,54 +312,50 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un
|
||||
static const char *copy_line(const char *buf)
|
||||
{
|
||||
const char *eol = strchr(buf, '\n');
|
||||
char *line;
|
||||
int len;
|
||||
if (!eol)
|
||||
return "";
|
||||
len = eol - buf;
|
||||
line = xmalloc(len + 1);
|
||||
memcpy(line, buf, len);
|
||||
line[len] = 0;
|
||||
return line;
|
||||
return xmemdupz(buf, eol - buf);
|
||||
}
|
||||
|
||||
static const char *copy_name(const char *buf)
|
||||
{
|
||||
const char *eol = strchr(buf, '\n');
|
||||
const char *eoname = strstr(buf, " <");
|
||||
char *line;
|
||||
int len;
|
||||
if (!(eoname && eol && eoname < eol))
|
||||
return "";
|
||||
len = eoname - buf;
|
||||
line = xmalloc(len + 1);
|
||||
memcpy(line, buf, len);
|
||||
line[len] = 0;
|
||||
return line;
|
||||
const char *cp;
|
||||
for (cp = buf; *cp && *cp != '\n'; cp++) {
|
||||
if (!strncmp(cp, " <", 2))
|
||||
return xmemdupz(buf, cp - buf);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static const char *copy_email(const char *buf)
|
||||
{
|
||||
const char *email = strchr(buf, '<');
|
||||
const char *eoemail = strchr(email, '>');
|
||||
char *line;
|
||||
int len;
|
||||
if (!email || !eoemail)
|
||||
return "";
|
||||
eoemail++;
|
||||
len = eoemail - email;
|
||||
line = xmalloc(len + 1);
|
||||
memcpy(line, email, len);
|
||||
line[len] = 0;
|
||||
return line;
|
||||
return xmemdupz(email, eoemail + 1 - email);
|
||||
}
|
||||
|
||||
static void grab_date(const char *buf, struct atom_value *v)
|
||||
static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
|
||||
{
|
||||
const char *eoemail = strstr(buf, "> ");
|
||||
char *zone;
|
||||
unsigned long timestamp;
|
||||
long tz;
|
||||
enum date_mode date_mode = DATE_NORMAL;
|
||||
const char *formatp;
|
||||
|
||||
/*
|
||||
* We got here because atomname ends in "date" or "date<something>";
|
||||
* it's not possible that <something> is not ":<format>" because
|
||||
* parse_atom() wouldn't have allowed it, so we can assume that no
|
||||
* ":" means no format is specified, and use the default.
|
||||
*/
|
||||
formatp = strchr(atomname, ':');
|
||||
if (formatp != NULL) {
|
||||
formatp++;
|
||||
date_mode = parse_date_format(formatp);
|
||||
}
|
||||
|
||||
if (!eoemail)
|
||||
goto bad;
|
||||
@@ -362,7 +365,7 @@ static void grab_date(const char *buf, struct atom_value *v)
|
||||
tz = strtol(zone, NULL, 10);
|
||||
if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
|
||||
goto bad;
|
||||
v->s = xstrdup(show_date(timestamp, tz, 0));
|
||||
v->s = xstrdup(show_date(timestamp, tz, date_mode));
|
||||
v->ul = timestamp;
|
||||
return;
|
||||
bad:
|
||||
@@ -389,7 +392,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
|
||||
if (name[wholen] != 0 &&
|
||||
strcmp(name + wholen, "name") &&
|
||||
strcmp(name + wholen, "email") &&
|
||||
strcmp(name + wholen, "date"))
|
||||
prefixcmp(name + wholen, "date"))
|
||||
continue;
|
||||
if (!wholine)
|
||||
wholine = find_wholine(who, wholen, buf, sz);
|
||||
@@ -401,8 +404,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
|
||||
v->s = copy_name(wholine);
|
||||
else if (!strcmp(name + wholen, "email"))
|
||||
v->s = copy_email(wholine);
|
||||
else if (!strcmp(name + wholen, "date"))
|
||||
grab_date(wholine, v);
|
||||
else if (!prefixcmp(name + wholen, "date"))
|
||||
grab_date(wholine, v, name);
|
||||
}
|
||||
|
||||
/* For a tag or a commit object, if "creator" or "creatordate" is
|
||||
@@ -422,8 +425,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
|
||||
if (deref)
|
||||
name++;
|
||||
|
||||
if (!strcmp(name, "creatordate"))
|
||||
grab_date(wholine, v);
|
||||
if (!prefixcmp(name, "creatordate"))
|
||||
grab_date(wholine, v, name);
|
||||
else if (!strcmp(name, "creator"))
|
||||
v->s = copy_line(wholine);
|
||||
}
|
||||
|
||||
141
builtin-gc.c
141
builtin-gc.c
@@ -20,11 +20,13 @@ static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]";
|
||||
|
||||
static int pack_refs = 1;
|
||||
static int aggressive_window = -1;
|
||||
static int gc_auto_threshold = 6700;
|
||||
static int gc_auto_pack_limit = 20;
|
||||
|
||||
#define MAX_ADD 10
|
||||
static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
|
||||
static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
|
||||
static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL};
|
||||
static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
|
||||
static const char *argv_prune[] = {"prune", NULL};
|
||||
static const char *argv_rerere[] = {"rerere", "gc", NULL};
|
||||
|
||||
@@ -41,6 +43,14 @@ static int gc_config(const char *var, const char *value)
|
||||
aggressive_window = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "gc.auto")) {
|
||||
gc_auto_threshold = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "gc.autopacklimit")) {
|
||||
gc_auto_pack_limit = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
@@ -57,10 +67,107 @@ static void append_option(const char **cmd, const char *opt, int max_length)
|
||||
cmd[i] = NULL;
|
||||
}
|
||||
|
||||
static int too_many_loose_objects(void)
|
||||
{
|
||||
/*
|
||||
* Quickly check if a "gc" is needed, by estimating how
|
||||
* many loose objects there are. Because SHA-1 is evenly
|
||||
* distributed, we can check only one and get a reasonable
|
||||
* estimate.
|
||||
*/
|
||||
char path[PATH_MAX];
|
||||
const char *objdir = get_object_directory();
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
int auto_threshold;
|
||||
int num_loose = 0;
|
||||
int needed = 0;
|
||||
|
||||
if (gc_auto_threshold <= 0)
|
||||
return 0;
|
||||
|
||||
if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
|
||||
warning("insanely long object directory %.*s", 50, objdir);
|
||||
return 0;
|
||||
}
|
||||
dir = opendir(path);
|
||||
if (!dir)
|
||||
return 0;
|
||||
|
||||
auto_threshold = (gc_auto_threshold + 255) / 256;
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
|
||||
ent->d_name[38] != '\0')
|
||||
continue;
|
||||
if (++num_loose > auto_threshold) {
|
||||
needed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return needed;
|
||||
}
|
||||
|
||||
static int too_many_packs(void)
|
||||
{
|
||||
struct packed_git *p;
|
||||
int cnt;
|
||||
|
||||
if (gc_auto_pack_limit <= 0)
|
||||
return 0;
|
||||
|
||||
prepare_packed_git();
|
||||
for (cnt = 0, p = packed_git; p; p = p->next) {
|
||||
char path[PATH_MAX];
|
||||
size_t len;
|
||||
int keep;
|
||||
|
||||
if (!p->pack_local)
|
||||
continue;
|
||||
len = strlen(p->pack_name);
|
||||
if (PATH_MAX <= len + 1)
|
||||
continue; /* oops, give up */
|
||||
memcpy(path, p->pack_name, len-5);
|
||||
memcpy(path + len - 5, ".keep", 6);
|
||||
keep = access(p->pack_name, F_OK) && (errno == ENOENT);
|
||||
if (keep)
|
||||
continue;
|
||||
/*
|
||||
* Perhaps check the size of the pack and count only
|
||||
* very small ones here?
|
||||
*/
|
||||
cnt++;
|
||||
}
|
||||
return gc_auto_pack_limit <= cnt;
|
||||
}
|
||||
|
||||
static int need_to_gc(void)
|
||||
{
|
||||
/*
|
||||
* Setting gc.auto and gc.autopacklimit to 0 or negative can
|
||||
* disable the automatic gc.
|
||||
*/
|
||||
if (gc_auto_threshold <= 0 && gc_auto_pack_limit <= 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If there are too many loose objects, but not too many
|
||||
* packs, we run "repack -d -l". If there are too many packs,
|
||||
* we run "repack -A -d -l". Otherwise we tell the caller
|
||||
* there is no need.
|
||||
*/
|
||||
if (too_many_packs())
|
||||
append_option(argv_repack, "-A", MAX_ADD);
|
||||
else if (!too_many_loose_objects())
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
int prune = 0;
|
||||
int auto_gc = 0;
|
||||
char buf[80];
|
||||
|
||||
git_config(gc_config);
|
||||
@@ -82,12 +189,38 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* perhaps other parameters later... */
|
||||
if (!strcmp(arg, "--auto")) {
|
||||
auto_gc = 1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i != argc)
|
||||
usage(builtin_gc_usage);
|
||||
|
||||
if (auto_gc) {
|
||||
/*
|
||||
* Auto-gc should be least intrusive as possible.
|
||||
*/
|
||||
prune = 0;
|
||||
if (!need_to_gc())
|
||||
return 0;
|
||||
fprintf(stderr, "Packing your repository for optimum "
|
||||
"performance. You may also\n"
|
||||
"run \"git gc\" manually. See "
|
||||
"\"git help gc\" for more information.\n");
|
||||
} else {
|
||||
/*
|
||||
* Use safer (for shared repos) "-A" option to
|
||||
* repack when not pruning. Auto-gc makes its
|
||||
* own decision.
|
||||
*/
|
||||
if (prune)
|
||||
append_option(argv_repack, "-a", MAX_ADD);
|
||||
else
|
||||
append_option(argv_repack, "-A", MAX_ADD);
|
||||
}
|
||||
|
||||
if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
|
||||
return error(FAILED_RUN, argv_pack_refs[0]);
|
||||
|
||||
@@ -103,5 +236,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
|
||||
return error(FAILED_RUN, argv_rerere[0]);
|
||||
|
||||
if (auto_gc && too_many_loose_objects())
|
||||
warning("There are too many unreachable loose objects; "
|
||||
"run 'git prune' to remove them.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,78 @@ static int exec_grep(int argc, const char **argv)
|
||||
else die("maximum number of args exceeded"); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* If you send a singleton filename to grep, it does not give
|
||||
* the name of the file. GNU grep has "-H" but we would want
|
||||
* that behaviour in a portable way.
|
||||
*
|
||||
* So we keep two pathnames in argv buffer unsent to grep in
|
||||
* the main loop if we need to do more than one grep.
|
||||
*/
|
||||
static int flush_grep(struct grep_opt *opt,
|
||||
int argc, int arg0, const char **argv, int *kept)
|
||||
{
|
||||
int status;
|
||||
int count = argc - arg0;
|
||||
const char *kept_0 = NULL;
|
||||
|
||||
if (count <= 2) {
|
||||
/*
|
||||
* Because we keep at least 2 paths in the call from
|
||||
* the main loop (i.e. kept != NULL), and MAXARGS is
|
||||
* far greater than 2, this usually is a call to
|
||||
* conclude the grep. However, the user could attempt
|
||||
* to overflow the argv buffer by giving too many
|
||||
* options to leave very small number of real
|
||||
* arguments even for the call in the main loop.
|
||||
*/
|
||||
if (kept)
|
||||
die("insanely many options to grep");
|
||||
|
||||
/*
|
||||
* If we have two or more paths, we do not have to do
|
||||
* anything special, but we need to push /dev/null to
|
||||
* get "-H" behaviour of GNU grep portably but when we
|
||||
* are not doing "-l" nor "-L" nor "-c".
|
||||
*/
|
||||
if (count == 1 &&
|
||||
!opt->name_only &&
|
||||
!opt->unmatch_name_only &&
|
||||
!opt->count) {
|
||||
argv[argc++] = "/dev/null";
|
||||
argv[argc] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
else if (kept) {
|
||||
/*
|
||||
* Called because we found many paths and haven't finished
|
||||
* iterating over the cache yet. We keep two paths
|
||||
* for the concluding call. argv[argc-2] and argv[argc-1]
|
||||
* has the last two paths, so save the first one away,
|
||||
* replace it with NULL while sending the list to grep,
|
||||
* and recover them after we are done.
|
||||
*/
|
||||
*kept = 2;
|
||||
kept_0 = argv[argc-2];
|
||||
argv[argc-2] = NULL;
|
||||
argc -= 2;
|
||||
}
|
||||
|
||||
status = exec_grep(argc, argv);
|
||||
|
||||
if (kept_0) {
|
||||
/*
|
||||
* Then recover them. Now the last arg is beyond the
|
||||
* terminating NULL which is at argc, and the second
|
||||
* from the last is what we saved away in kept_0
|
||||
*/
|
||||
argv[arg0++] = kept_0;
|
||||
argv[arg0] = argv[argc+1];
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
{
|
||||
int i, nr, argc, hit, len, status;
|
||||
@@ -253,22 +325,12 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
push_arg(p->pattern);
|
||||
}
|
||||
|
||||
/*
|
||||
* To make sure we get the header printed out when we want it,
|
||||
* add /dev/null to the paths to grep. This is unnecessary
|
||||
* (and wrong) with "-l" or "-L", which always print out the
|
||||
* name anyway.
|
||||
*
|
||||
* GNU grep has "-H", but this is portable.
|
||||
*/
|
||||
if (!opt->name_only && !opt->unmatch_name_only)
|
||||
push_arg("/dev/null");
|
||||
|
||||
hit = 0;
|
||||
argc = nr;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
char *name;
|
||||
int kept;
|
||||
if (!S_ISREG(ntohl(ce->ce_mode)))
|
||||
continue;
|
||||
if (!pathspec_matches(paths, ce->name))
|
||||
@@ -283,10 +345,10 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
argv[argc++] = name;
|
||||
if (argc < MAXARGS && !ce_stage(ce))
|
||||
continue;
|
||||
status = exec_grep(argc, argv);
|
||||
status = flush_grep(opt, argc, nr, argv, &kept);
|
||||
if (0 < status)
|
||||
hit = 1;
|
||||
argc = nr;
|
||||
argc = nr + kept;
|
||||
if (ce_stage(ce)) {
|
||||
do {
|
||||
i++;
|
||||
@@ -296,7 +358,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
}
|
||||
}
|
||||
if (argc > nr) {
|
||||
status = exec_grep(argc, argv);
|
||||
status = flush_grep(opt, argc, nr, argv, NULL);
|
||||
if (0 < status)
|
||||
hit = 1;
|
||||
}
|
||||
|
||||
77
builtin-http-fetch.c
Normal file
77
builtin-http-fetch.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "cache.h"
|
||||
#include "walker.h"
|
||||
|
||||
int cmd_http_fetch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct walker *walker;
|
||||
int commits_on_stdin = 0;
|
||||
int commits;
|
||||
const char **write_ref = NULL;
|
||||
char **commit_id;
|
||||
const char *url;
|
||||
int arg = 1;
|
||||
int rc = 0;
|
||||
int get_tree = 0;
|
||||
int get_history = 0;
|
||||
int get_all = 0;
|
||||
int get_verbosely = 0;
|
||||
int get_recover = 0;
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
while (arg < argc && argv[arg][0] == '-') {
|
||||
if (argv[arg][1] == 't') {
|
||||
get_tree = 1;
|
||||
} else if (argv[arg][1] == 'c') {
|
||||
get_history = 1;
|
||||
} else if (argv[arg][1] == 'a') {
|
||||
get_all = 1;
|
||||
get_tree = 1;
|
||||
get_history = 1;
|
||||
} else if (argv[arg][1] == 'v') {
|
||||
get_verbosely = 1;
|
||||
} else if (argv[arg][1] == 'w') {
|
||||
write_ref = &argv[arg + 1];
|
||||
arg++;
|
||||
} else if (!strcmp(argv[arg], "--recover")) {
|
||||
get_recover = 1;
|
||||
} else if (!strcmp(argv[arg], "--stdin")) {
|
||||
commits_on_stdin = 1;
|
||||
}
|
||||
arg++;
|
||||
}
|
||||
if (argc < arg + 2 - commits_on_stdin) {
|
||||
usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
|
||||
return 1;
|
||||
}
|
||||
if (commits_on_stdin) {
|
||||
commits = walker_targets_stdin(&commit_id, &write_ref);
|
||||
} else {
|
||||
commit_id = (char **) &argv[arg++];
|
||||
commits = 1;
|
||||
}
|
||||
url = argv[arg];
|
||||
|
||||
walker = get_http_walker(url);
|
||||
walker->get_tree = get_tree;
|
||||
walker->get_history = get_history;
|
||||
walker->get_all = get_all;
|
||||
walker->get_verbosely = get_verbosely;
|
||||
walker->get_recover = get_recover;
|
||||
|
||||
rc = walker_fetch(walker, commits, commit_id, write_ref, url);
|
||||
|
||||
if (commits_on_stdin)
|
||||
walker_targets_free(commits, commit_id, write_ref);
|
||||
|
||||
if (walker->corrupt_object_found) {
|
||||
fprintf(stderr,
|
||||
"Some loose object were found to be corrupt, but they might be just\n"
|
||||
"a false '404 Not Found' error message sent with incorrect HTTP\n"
|
||||
"status code. Suggest running git-fsck.\n");
|
||||
}
|
||||
|
||||
walker_free(walker);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -140,7 +140,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
|
||||
* interpreted relative to the exec_dir
|
||||
*/
|
||||
template_dir = DEFAULT_GIT_TEMPLATE_DIR;
|
||||
if (template_dir[0] != '/' && template_dir[1] != ':') {
|
||||
if (!is_absolute_path(template_dir)) {
|
||||
const char *exec_path = git_exec_path();
|
||||
template_dir = prefix_path(exec_path, strlen(exec_path),
|
||||
template_dir);
|
||||
|
||||
@@ -437,6 +437,28 @@ static void gen_message_id(char *dest, unsigned int length, char *base)
|
||||
(int)(email_end - email_start - 1), email_start + 1);
|
||||
}
|
||||
|
||||
static const char *clean_message_id(const char *msg_id)
|
||||
{
|
||||
char ch;
|
||||
const char *a, *z, *m;
|
||||
|
||||
m = msg_id;
|
||||
while ((ch = *m) && (isspace(ch) || (ch == '<')))
|
||||
m++;
|
||||
a = m;
|
||||
z = NULL;
|
||||
while ((ch = *m)) {
|
||||
if (!isspace(ch) && (ch != '>'))
|
||||
z = m;
|
||||
m++;
|
||||
}
|
||||
if (!z)
|
||||
die("insane in-reply-to: %s", msg_id);
|
||||
if (++z == m)
|
||||
return a;
|
||||
return xmemdupz(a, z - a);
|
||||
}
|
||||
|
||||
int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct commit *commit;
|
||||
@@ -513,9 +535,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
endpos = strchr(committer, '>');
|
||||
if (!endpos)
|
||||
die("bogos committer info %s\n", committer);
|
||||
add_signoff = xmalloc(endpos - committer + 2);
|
||||
memcpy(add_signoff, committer, endpos - committer + 1);
|
||||
add_signoff[endpos - committer + 1] = 0;
|
||||
add_signoff = xmemdupz(committer, endpos - committer + 1);
|
||||
}
|
||||
else if (!strcmp(argv[i], "--attach")) {
|
||||
rev.mime_boundary = git_version_string;
|
||||
@@ -625,7 +645,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
if (numbered)
|
||||
rev.total = total + start_number - 1;
|
||||
rev.add_signoff = add_signoff;
|
||||
rev.ref_message_id = in_reply_to;
|
||||
if (in_reply_to)
|
||||
rev.ref_message_id = clean_message_id(in_reply_to);
|
||||
while (0 <= --nr) {
|
||||
int shown;
|
||||
commit = list[nr];
|
||||
@@ -763,13 +784,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
|
||||
sign = '-';
|
||||
|
||||
if (verbose) {
|
||||
char *buf = NULL;
|
||||
unsigned long buflen = 0;
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
|
||||
&buf, &buflen, 0, NULL, NULL, 0);
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit,
|
||||
&buf, 0, NULL, NULL, 0);
|
||||
printf("%c %s %s\n", sign,
|
||||
sha1_to_hex(commit->object.sha1), buf);
|
||||
free(buf);
|
||||
sha1_to_hex(commit->object.sha1), buf.buf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
else {
|
||||
printf("%c %s\n", sign,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "quote.h"
|
||||
#include "dir.h"
|
||||
#include "builtin.h"
|
||||
#include "tree.h"
|
||||
|
||||
static int abbrev;
|
||||
static int show_deleted;
|
||||
@@ -26,6 +27,7 @@ static int prefix_offset;
|
||||
static const char **pathspec;
|
||||
static int error_unmatch;
|
||||
static char *ps_matched;
|
||||
static const char *with_tree;
|
||||
|
||||
static const char *tag_cached = "";
|
||||
static const char *tag_unmerged = "";
|
||||
@@ -82,8 +84,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
||||
return;
|
||||
|
||||
fputs(tag, stdout);
|
||||
write_name_quoted("", 0, ent->name + offset, line_terminator, stdout);
|
||||
putchar(line_terminator);
|
||||
write_name_quoted(ent->name + offset, stdout, line_terminator);
|
||||
}
|
||||
|
||||
static void show_other_files(struct dir_struct *dir)
|
||||
@@ -206,21 +207,15 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
|
||||
|
||||
if (!show_stage) {
|
||||
fputs(tag, stdout);
|
||||
write_name_quoted("", 0, ce->name + offset,
|
||||
line_terminator, stdout);
|
||||
putchar(line_terminator);
|
||||
}
|
||||
else {
|
||||
printf("%s%06lo %s %u\t",
|
||||
} else {
|
||||
printf("%s%06o %s %d\t",
|
||||
tag,
|
||||
ntohl(ce->ce_mode),
|
||||
abbrev ? find_unique_abbrev(ce->sha1,abbrev)
|
||||
: sha1_to_hex(ce->sha1),
|
||||
ce_stage(ce));
|
||||
write_name_quoted("", 0, ce->name + offset,
|
||||
line_terminator, stdout);
|
||||
putchar(line_terminator);
|
||||
}
|
||||
write_name_quoted(ce->name + offset, stdout, line_terminator);
|
||||
}
|
||||
|
||||
static void show_files(struct dir_struct *dir, const char *prefix)
|
||||
@@ -247,6 +242,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
|
||||
continue;
|
||||
if (show_unmerged && !ce_stage(ce))
|
||||
continue;
|
||||
if (ce->ce_flags & htons(CE_UPDATE))
|
||||
continue;
|
||||
show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
|
||||
}
|
||||
}
|
||||
@@ -276,7 +273,8 @@ static void prune_cache(const char *prefix)
|
||||
|
||||
if (pos < 0)
|
||||
pos = -pos-1;
|
||||
active_cache += pos;
|
||||
memmove(active_cache, active_cache + pos,
|
||||
(active_nr - pos) * sizeof(struct cache_entry *));
|
||||
active_nr -= pos;
|
||||
first = 0;
|
||||
last = active_nr;
|
||||
@@ -295,7 +293,6 @@ static void prune_cache(const char *prefix)
|
||||
static const char *verify_pathspec(const char *prefix)
|
||||
{
|
||||
const char **p, *n, *prev;
|
||||
char *real_prefix;
|
||||
unsigned long max;
|
||||
|
||||
prev = NULL;
|
||||
@@ -322,14 +319,69 @@ static const char *verify_pathspec(const char *prefix)
|
||||
if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
|
||||
die("git-ls-files: cannot generate relative filenames containing '..'");
|
||||
|
||||
real_prefix = NULL;
|
||||
prefix_len = max;
|
||||
if (max) {
|
||||
real_prefix = xmalloc(max + 1);
|
||||
memcpy(real_prefix, prev, max);
|
||||
real_prefix[max] = 0;
|
||||
return max ? xmemdupz(prev, max) : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the tree specified with --with-tree option
|
||||
* (typically, HEAD) into stage #1 and then
|
||||
* squash them down to stage #0. This is used for
|
||||
* --error-unmatch to list and check the path patterns
|
||||
* that were given from the command line. We are not
|
||||
* going to write this index out.
|
||||
*/
|
||||
static void overlay_tree(const char *tree_name, const char *prefix)
|
||||
{
|
||||
struct tree *tree;
|
||||
unsigned char sha1[20];
|
||||
const char **match;
|
||||
struct cache_entry *last_stage0 = NULL;
|
||||
int i;
|
||||
|
||||
if (get_sha1(tree_name, sha1))
|
||||
die("tree-ish %s not found.", tree_name);
|
||||
tree = parse_tree_indirect(sha1);
|
||||
if (!tree)
|
||||
die("bad tree-ish %s", tree_name);
|
||||
|
||||
/* Hoist the unmerged entries up to stage #3 to make room */
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (!ce_stage(ce))
|
||||
continue;
|
||||
ce->ce_flags |= htons(CE_STAGEMASK);
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
static const char *(matchbuf[2]);
|
||||
matchbuf[0] = prefix;
|
||||
matchbuf [1] = NULL;
|
||||
match = matchbuf;
|
||||
} else
|
||||
match = NULL;
|
||||
if (read_tree(tree, 1, match))
|
||||
die("unable to read tree entries %s", tree_name);
|
||||
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
switch (ce_stage(ce)) {
|
||||
case 0:
|
||||
last_stage0 = ce;
|
||||
/* fallthru */
|
||||
default:
|
||||
continue;
|
||||
case 1:
|
||||
/*
|
||||
* If there is stage #0 entry for this, we do not
|
||||
* need to show it. We use CE_UPDATE bit to mark
|
||||
* such an entry.
|
||||
*/
|
||||
if (last_stage0 &&
|
||||
!strcmp(last_stage0->name, ce->name))
|
||||
ce->ce_flags |= htons(CE_UPDATE);
|
||||
}
|
||||
}
|
||||
return real_prefix;
|
||||
}
|
||||
|
||||
static const char ls_files_usage[] =
|
||||
@@ -452,6 +504,10 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
||||
error_unmatch = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--with-tree=")) {
|
||||
with_tree = arg + 12;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--abbrev=")) {
|
||||
abbrev = strtoul(arg+9, NULL, 10);
|
||||
if (abbrev && abbrev < MINIMUM_ABBREV)
|
||||
@@ -503,6 +559,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
||||
read_cache();
|
||||
if (prefix)
|
||||
prune_cache(prefix);
|
||||
if (with_tree) {
|
||||
/*
|
||||
* Basic sanity check; show-stages and show-unmerged
|
||||
* would not make any sense with this option.
|
||||
*/
|
||||
if (show_stage || show_unmerged)
|
||||
die("ls-files --with-tree is incompatible with -s or -u");
|
||||
overlay_tree(with_tree, prefix);
|
||||
}
|
||||
show_files(&dir, prefix);
|
||||
|
||||
if (ps_matched) {
|
||||
|
||||
@@ -112,10 +112,8 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
|
||||
abbrev ? find_unique_abbrev(sha1, abbrev)
|
||||
: sha1_to_hex(sha1));
|
||||
}
|
||||
write_name_quoted(base + chomp_prefix, baselen - chomp_prefix,
|
||||
pathname,
|
||||
line_termination, stdout);
|
||||
putchar(line_termination);
|
||||
write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,
|
||||
pathname, stdout, line_termination);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ static void cleanup_space(char *buf)
|
||||
}
|
||||
|
||||
static void decode_header(char *it, unsigned itsize);
|
||||
static char *header[MAX_HDR_PARSED] = {
|
||||
static const char *header[MAX_HDR_PARSED] = {
|
||||
"From","Subject","Date",
|
||||
};
|
||||
|
||||
|
||||
12
builtin-mv.c
12
builtin-mv.c
@@ -22,10 +22,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
|
||||
for (i = 0; i < count; i++) {
|
||||
int length = strlen(result[i]);
|
||||
if (length > 0 && result[i][length - 1] == '/') {
|
||||
char *without_slash = xmalloc(length);
|
||||
memcpy(without_slash, result[i], length - 1);
|
||||
without_slash[length - 1] = '\0';
|
||||
result[i] = without_slash;
|
||||
result[i] = xmemdupz(result[i], length - 1);
|
||||
}
|
||||
if (base_name) {
|
||||
const char *last_slash = strrchr(result[i], '/');
|
||||
@@ -276,11 +273,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
add_file_to_cache(path, verbose);
|
||||
}
|
||||
|
||||
for (i = 0; i < deleted.nr; i++) {
|
||||
const char *path = deleted.items[i].path;
|
||||
remove_file_from_cache(path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
}
|
||||
for (i = 0; i < deleted.nr; i++)
|
||||
remove_file_from_cache(deleted.items[i].path);
|
||||
|
||||
if (active_cache_changed) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
|
||||
@@ -15,13 +15,17 @@
|
||||
#include "list-objects.h"
|
||||
#include "progress.h"
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
static const char pack_usage[] = "\
|
||||
git-pack-objects [{ -q | --progress | --all-progress }] \n\
|
||||
[--max-pack-size=N] [--local] [--incremental] \n\
|
||||
[--window=N] [--window-memory=N] [--depth=N] \n\
|
||||
[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
|
||||
[--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
|
||||
[--stdout | base-name] [<ref-list | <object-list]";
|
||||
[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
|
||||
[--stdout | base-name] [--keep-unreachable] [<ref-list | <object-list]";
|
||||
|
||||
struct object_entry {
|
||||
struct pack_idx_entry idx;
|
||||
@@ -57,7 +61,7 @@ static struct object_entry **written_list;
|
||||
static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
|
||||
|
||||
static int non_empty;
|
||||
static int no_reuse_delta, no_reuse_object;
|
||||
static int no_reuse_delta, no_reuse_object, keep_unreachable;
|
||||
static int local;
|
||||
static int incremental;
|
||||
static int allow_ofs_delta;
|
||||
@@ -68,6 +72,7 @@ static int progress = 1;
|
||||
static int window = 10;
|
||||
static uint32_t pack_size_limit;
|
||||
static int depth = 50;
|
||||
static int delta_search_threads = 1;
|
||||
static int pack_to_stdout;
|
||||
static int num_preferred_base;
|
||||
static struct progress progress_state;
|
||||
@@ -78,7 +83,6 @@ static unsigned long delta_cache_size = 0;
|
||||
static unsigned long max_delta_cache_size = 0;
|
||||
static unsigned long cache_max_small_delta_size = 1000;
|
||||
|
||||
static unsigned long window_memory_usage = 0;
|
||||
static unsigned long window_memory_limit = 0;
|
||||
|
||||
/*
|
||||
@@ -1291,6 +1295,31 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
|
||||
static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define read_lock() pthread_mutex_lock(&read_mutex)
|
||||
#define read_unlock() pthread_mutex_unlock(&read_mutex)
|
||||
|
||||
static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define cache_lock() pthread_mutex_lock(&cache_mutex)
|
||||
#define cache_unlock() pthread_mutex_unlock(&cache_mutex)
|
||||
|
||||
static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define progress_lock() pthread_mutex_lock(&progress_mutex)
|
||||
#define progress_unlock() pthread_mutex_unlock(&progress_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define read_lock() (void)0
|
||||
#define read_unlock() (void)0
|
||||
#define cache_lock() (void)0
|
||||
#define cache_unlock() (void)0
|
||||
#define progress_lock() (void)0
|
||||
#define progress_unlock() (void)0
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We search for deltas _backwards_ in a list sorted by type and
|
||||
* by size, so that we see progressively smaller and smaller files.
|
||||
@@ -1300,7 +1329,7 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
|
||||
* one.
|
||||
*/
|
||||
static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
unsigned max_depth)
|
||||
unsigned max_depth, unsigned long *mem_usage)
|
||||
{
|
||||
struct object_entry *trg_entry = trg->entry;
|
||||
struct object_entry *src_entry = src->entry;
|
||||
@@ -1313,12 +1342,6 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
if (trg_entry->type != src_entry->type)
|
||||
return -1;
|
||||
|
||||
/* We do not compute delta to *create* objects we are not
|
||||
* going to pack.
|
||||
*/
|
||||
if (trg_entry->preferred_base)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* We do not bother to try a delta that we discarded
|
||||
* on an earlier try, but only when reusing delta data.
|
||||
@@ -1355,24 +1378,28 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
|
||||
/* Load data if not already done */
|
||||
if (!trg->data) {
|
||||
read_lock();
|
||||
trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
|
||||
read_unlock();
|
||||
if (!trg->data)
|
||||
die("object %s cannot be read",
|
||||
sha1_to_hex(trg_entry->idx.sha1));
|
||||
if (sz != trg_size)
|
||||
die("object %s inconsistent object length (%lu vs %lu)",
|
||||
sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
|
||||
window_memory_usage += sz;
|
||||
*mem_usage += sz;
|
||||
}
|
||||
if (!src->data) {
|
||||
read_lock();
|
||||
src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
|
||||
read_unlock();
|
||||
if (!src->data)
|
||||
die("object %s cannot be read",
|
||||
sha1_to_hex(src_entry->idx.sha1));
|
||||
if (sz != src_size)
|
||||
die("object %s inconsistent object length (%lu vs %lu)",
|
||||
sha1_to_hex(src_entry->idx.sha1), sz, src_size);
|
||||
window_memory_usage += sz;
|
||||
*mem_usage += sz;
|
||||
}
|
||||
if (!src->index) {
|
||||
src->index = create_delta_index(src->data, src_size);
|
||||
@@ -1382,7 +1409,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
warning("suboptimal pack - out of memory");
|
||||
return 0;
|
||||
}
|
||||
window_memory_usage += sizeof_delta_index(src->index);
|
||||
*mem_usage += sizeof_delta_index(src->index);
|
||||
}
|
||||
|
||||
delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
|
||||
@@ -1402,17 +1429,27 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
trg_entry->delta_size = delta_size;
|
||||
trg->depth = src->depth + 1;
|
||||
|
||||
/*
|
||||
* Handle memory allocation outside of the cache
|
||||
* accounting lock. Compiler will optimize the strangeness
|
||||
* away when THREADED_DELTA_SEARCH is not defined.
|
||||
*/
|
||||
if (trg_entry->delta_data)
|
||||
free(trg_entry->delta_data);
|
||||
cache_lock();
|
||||
if (trg_entry->delta_data) {
|
||||
delta_cache_size -= trg_entry->delta_size;
|
||||
free(trg_entry->delta_data);
|
||||
trg_entry->delta_data = NULL;
|
||||
}
|
||||
|
||||
if (delta_cacheable(src_size, trg_size, delta_size)) {
|
||||
trg_entry->delta_data = xrealloc(delta_buf, delta_size);
|
||||
delta_cache_size += trg_entry->delta_size;
|
||||
} else
|
||||
cache_unlock();
|
||||
trg_entry->delta_data = xrealloc(delta_buf, delta_size);
|
||||
} else {
|
||||
cache_unlock();
|
||||
free(delta_buf);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1429,68 +1466,60 @@ static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
|
||||
return m;
|
||||
}
|
||||
|
||||
static void free_unpacked(struct unpacked *n)
|
||||
static unsigned long free_unpacked(struct unpacked *n)
|
||||
{
|
||||
window_memory_usage -= sizeof_delta_index(n->index);
|
||||
unsigned long freed_mem = sizeof_delta_index(n->index);
|
||||
free_delta_index(n->index);
|
||||
n->index = NULL;
|
||||
if (n->data) {
|
||||
freed_mem += n->entry->size;
|
||||
free(n->data);
|
||||
n->data = NULL;
|
||||
window_memory_usage -= n->entry->size;
|
||||
}
|
||||
n->entry = NULL;
|
||||
n->depth = 0;
|
||||
return freed_mem;
|
||||
}
|
||||
|
||||
static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
static void find_deltas(struct object_entry **list, unsigned list_size,
|
||||
int window, int depth, unsigned *processed)
|
||||
{
|
||||
uint32_t i = nr_objects, idx = 0, count = 0, processed = 0;
|
||||
uint32_t i = list_size, idx = 0, count = 0;
|
||||
unsigned int array_size = window * sizeof(struct unpacked);
|
||||
struct unpacked *array;
|
||||
int max_depth;
|
||||
unsigned long mem_usage = 0;
|
||||
|
||||
if (!nr_objects)
|
||||
return;
|
||||
array = xmalloc(array_size);
|
||||
memset(array, 0, array_size);
|
||||
if (progress)
|
||||
start_progress(&progress_state, "Deltifying %u objects...", "", nr_result);
|
||||
|
||||
do {
|
||||
struct object_entry *entry = list[--i];
|
||||
struct unpacked *n = array + idx;
|
||||
int j;
|
||||
int j, max_depth, best_base = -1;
|
||||
|
||||
if (!entry->preferred_base)
|
||||
processed++;
|
||||
|
||||
if (progress)
|
||||
display_progress(&progress_state, processed);
|
||||
|
||||
if (entry->delta)
|
||||
/* This happens if we decided to reuse existing
|
||||
* delta from a pack. "!no_reuse_delta &&" is implied.
|
||||
*/
|
||||
continue;
|
||||
|
||||
if (entry->size < 50)
|
||||
continue;
|
||||
|
||||
if (entry->no_try_delta)
|
||||
continue;
|
||||
|
||||
free_unpacked(n);
|
||||
mem_usage -= free_unpacked(n);
|
||||
n->entry = entry;
|
||||
|
||||
while (window_memory_limit &&
|
||||
window_memory_usage > window_memory_limit &&
|
||||
mem_usage > window_memory_limit &&
|
||||
count > 1) {
|
||||
uint32_t tail = (idx + window - count) % window;
|
||||
free_unpacked(array + tail);
|
||||
mem_usage -= free_unpacked(array + tail);
|
||||
count--;
|
||||
}
|
||||
|
||||
/* We do not compute delta to *create* objects we are not
|
||||
* going to pack.
|
||||
*/
|
||||
if (entry->preferred_base)
|
||||
goto next;
|
||||
|
||||
progress_lock();
|
||||
(*processed)++;
|
||||
if (progress)
|
||||
display_progress(&progress_state, *processed);
|
||||
progress_unlock();
|
||||
|
||||
/*
|
||||
* If the current object is at pack edge, take the depth the
|
||||
* objects that depend on the current object into account
|
||||
@@ -1505,6 +1534,7 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
|
||||
j = window;
|
||||
while (--j > 0) {
|
||||
int ret;
|
||||
uint32_t other_idx = idx + j;
|
||||
struct unpacked *m;
|
||||
if (other_idx >= window)
|
||||
@@ -1512,8 +1542,11 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
m = array + other_idx;
|
||||
if (!m->entry)
|
||||
break;
|
||||
if (try_delta(n, m, max_depth) < 0)
|
||||
ret = try_delta(n, m, max_depth, &mem_usage);
|
||||
if (ret < 0)
|
||||
break;
|
||||
else if (ret > 0)
|
||||
best_base = other_idx;
|
||||
}
|
||||
|
||||
/* if we made n a delta, and if n is already at max
|
||||
@@ -1523,6 +1556,23 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
if (entry->delta && depth <= n->depth)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Move the best delta base up in the window, after the
|
||||
* currently deltified object, to keep it longer. It will
|
||||
* be the first base object to be attempted next.
|
||||
*/
|
||||
if (entry->delta) {
|
||||
struct unpacked swap = array[best_base];
|
||||
int dist = (window + idx - best_base) % window;
|
||||
int dst = best_base;
|
||||
while (dist--) {
|
||||
int src = (dst + 1) % window;
|
||||
array[dst] = array[src];
|
||||
dst = src;
|
||||
}
|
||||
array[dst] = swap;
|
||||
}
|
||||
|
||||
next:
|
||||
idx++;
|
||||
if (count + 1 < window)
|
||||
@@ -1531,9 +1581,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
idx = 0;
|
||||
} while (i > 0);
|
||||
|
||||
if (progress)
|
||||
stop_progress(&progress_state);
|
||||
|
||||
for (i = 0; i < window; ++i) {
|
||||
free_delta_index(array[i].index);
|
||||
free(array[i].data);
|
||||
@@ -1541,21 +1588,145 @@ static void find_deltas(struct object_entry **list, int window, int depth)
|
||||
free(array);
|
||||
}
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
|
||||
struct thread_params {
|
||||
pthread_t thread;
|
||||
struct object_entry **list;
|
||||
unsigned list_size;
|
||||
int window;
|
||||
int depth;
|
||||
unsigned *processed;
|
||||
};
|
||||
|
||||
static pthread_mutex_t data_request = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t data_ready = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t data_provider = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct thread_params *data_requester;
|
||||
|
||||
static void *threaded_find_deltas(void *arg)
|
||||
{
|
||||
struct thread_params *me = arg;
|
||||
|
||||
for (;;) {
|
||||
pthread_mutex_lock(&data_request);
|
||||
data_requester = me;
|
||||
pthread_mutex_unlock(&data_provider);
|
||||
pthread_mutex_lock(&data_ready);
|
||||
pthread_mutex_unlock(&data_request);
|
||||
|
||||
if (!me->list_size)
|
||||
return NULL;
|
||||
|
||||
find_deltas(me->list, me->list_size,
|
||||
me->window, me->depth, me->processed);
|
||||
}
|
||||
}
|
||||
|
||||
static void ll_find_deltas(struct object_entry **list, unsigned list_size,
|
||||
int window, int depth, unsigned *processed)
|
||||
{
|
||||
struct thread_params *target, p[delta_search_threads];
|
||||
int i, ret;
|
||||
unsigned chunk_size;
|
||||
|
||||
if (delta_search_threads <= 1) {
|
||||
find_deltas(list, list_size, window, depth, processed);
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&data_provider);
|
||||
pthread_mutex_lock(&data_ready);
|
||||
|
||||
for (i = 0; i < delta_search_threads; i++) {
|
||||
p[i].window = window;
|
||||
p[i].depth = depth;
|
||||
p[i].processed = processed;
|
||||
ret = pthread_create(&p[i].thread, NULL,
|
||||
threaded_find_deltas, &p[i]);
|
||||
if (ret)
|
||||
die("unable to create thread: %s", strerror(ret));
|
||||
}
|
||||
|
||||
/* this should be auto-tuned somehow */
|
||||
chunk_size = window * 1000;
|
||||
|
||||
do {
|
||||
unsigned sublist_size = chunk_size;
|
||||
if (sublist_size > list_size)
|
||||
sublist_size = list_size;
|
||||
|
||||
/* try to split chunks on "path" boundaries */
|
||||
while (sublist_size < list_size && list[sublist_size]->hash &&
|
||||
list[sublist_size]->hash == list[sublist_size-1]->hash)
|
||||
sublist_size++;
|
||||
|
||||
pthread_mutex_lock(&data_provider);
|
||||
target = data_requester;
|
||||
target->list = list;
|
||||
target->list_size = sublist_size;
|
||||
pthread_mutex_unlock(&data_ready);
|
||||
|
||||
list += sublist_size;
|
||||
list_size -= sublist_size;
|
||||
if (!sublist_size) {
|
||||
pthread_join(target->thread, NULL);
|
||||
i--;
|
||||
}
|
||||
} while (i);
|
||||
}
|
||||
|
||||
#else
|
||||
#define ll_find_deltas find_deltas
|
||||
#endif
|
||||
|
||||
static void prepare_pack(int window, int depth)
|
||||
{
|
||||
struct object_entry **delta_list;
|
||||
uint32_t i;
|
||||
uint32_t i, n, nr_deltas;
|
||||
|
||||
get_object_details();
|
||||
|
||||
if (!window || !depth)
|
||||
if (!nr_objects || !window || !depth)
|
||||
return;
|
||||
|
||||
delta_list = xmalloc(nr_objects * sizeof(*delta_list));
|
||||
for (i = 0; i < nr_objects; i++)
|
||||
delta_list[i] = objects + i;
|
||||
qsort(delta_list, nr_objects, sizeof(*delta_list), type_size_sort);
|
||||
find_deltas(delta_list, window+1, depth);
|
||||
nr_deltas = n = 0;
|
||||
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
struct object_entry *entry = objects + i;
|
||||
|
||||
if (entry->delta)
|
||||
/* This happens if we decided to reuse existing
|
||||
* delta from a pack. "!no_reuse_delta &&" is implied.
|
||||
*/
|
||||
continue;
|
||||
|
||||
if (entry->size < 50)
|
||||
continue;
|
||||
|
||||
if (entry->no_try_delta)
|
||||
continue;
|
||||
|
||||
if (!entry->preferred_base)
|
||||
nr_deltas++;
|
||||
|
||||
delta_list[n++] = entry;
|
||||
}
|
||||
|
||||
if (nr_deltas) {
|
||||
unsigned nr_done = 0;
|
||||
if (progress)
|
||||
start_progress(&progress_state,
|
||||
"Deltifying %u objects...", "",
|
||||
nr_deltas);
|
||||
qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
|
||||
ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
|
||||
if (progress)
|
||||
stop_progress(&progress_state);
|
||||
if (nr_done != nr_deltas)
|
||||
die("inconsistency with delta count");
|
||||
}
|
||||
free(delta_list);
|
||||
}
|
||||
|
||||
@@ -1591,6 +1762,17 @@ static int git_pack_config(const char *k, const char *v)
|
||||
cache_max_small_delta_size = git_config_int(k, v);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(k, "pack.threads")) {
|
||||
delta_search_threads = git_config_int(k, v);
|
||||
if (delta_search_threads < 1)
|
||||
die("invalid number of threads specified (%d)",
|
||||
delta_search_threads);
|
||||
#ifndef THREADED_DELTA_SEARCH
|
||||
if (delta_search_threads > 1)
|
||||
warning("no threads support, ignoring %s", k);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(k, v);
|
||||
}
|
||||
|
||||
@@ -1625,15 +1807,19 @@ static void read_object_list_from_stdin(void)
|
||||
}
|
||||
}
|
||||
|
||||
#define OBJECT_ADDED (1u<<20)
|
||||
|
||||
static void show_commit(struct commit *commit)
|
||||
{
|
||||
add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
|
||||
commit->object.flags |= OBJECT_ADDED;
|
||||
}
|
||||
|
||||
static void show_object(struct object_array_entry *p)
|
||||
{
|
||||
add_preferred_base_object(p->name);
|
||||
add_object_entry(p->item->sha1, p->item->type, p->name, 0);
|
||||
p->item->flags |= OBJECT_ADDED;
|
||||
}
|
||||
|
||||
static void show_edge(struct commit *commit)
|
||||
@@ -1641,6 +1827,86 @@ static void show_edge(struct commit *commit)
|
||||
add_preferred_base(commit->object.sha1);
|
||||
}
|
||||
|
||||
struct in_pack_object {
|
||||
off_t offset;
|
||||
struct object *object;
|
||||
};
|
||||
|
||||
struct in_pack {
|
||||
int alloc;
|
||||
int nr;
|
||||
struct in_pack_object *array;
|
||||
};
|
||||
|
||||
static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack)
|
||||
{
|
||||
in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->sha1, p);
|
||||
in_pack->array[in_pack->nr].object = object;
|
||||
in_pack->nr++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare the objects in the offset order, in order to emulate the
|
||||
* "git-rev-list --objects" output that produced the pack originally.
|
||||
*/
|
||||
static int ofscmp(const void *a_, const void *b_)
|
||||
{
|
||||
struct in_pack_object *a = (struct in_pack_object *)a_;
|
||||
struct in_pack_object *b = (struct in_pack_object *)b_;
|
||||
|
||||
if (a->offset < b->offset)
|
||||
return -1;
|
||||
else if (a->offset > b->offset)
|
||||
return 1;
|
||||
else
|
||||
return hashcmp(a->object->sha1, b->object->sha1);
|
||||
}
|
||||
|
||||
static void add_objects_in_unpacked_packs(struct rev_info *revs)
|
||||
{
|
||||
struct packed_git *p;
|
||||
struct in_pack in_pack;
|
||||
uint32_t i;
|
||||
|
||||
memset(&in_pack, 0, sizeof(in_pack));
|
||||
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
const unsigned char *sha1;
|
||||
struct object *o;
|
||||
|
||||
for (i = 0; i < revs->num_ignore_packed; i++) {
|
||||
if (matches_pack_name(p, revs->ignore_packed[i]))
|
||||
break;
|
||||
}
|
||||
if (revs->num_ignore_packed <= i)
|
||||
continue;
|
||||
if (open_pack_index(p))
|
||||
die("cannot open pack index");
|
||||
|
||||
ALLOC_GROW(in_pack.array,
|
||||
in_pack.nr + p->num_objects,
|
||||
in_pack.alloc);
|
||||
|
||||
for (i = 0; i < p->num_objects; i++) {
|
||||
sha1 = nth_packed_object_sha1(p, i);
|
||||
o = lookup_unknown_object(sha1);
|
||||
if (!(o->flags & OBJECT_ADDED))
|
||||
mark_in_pack_object(o, p, &in_pack);
|
||||
o->flags |= OBJECT_ADDED;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_pack.nr) {
|
||||
qsort(in_pack.array, in_pack.nr, sizeof(in_pack.array[0]),
|
||||
ofscmp);
|
||||
for (i = 0; i < in_pack.nr; i++) {
|
||||
struct object *o = in_pack.array[i].object;
|
||||
add_object_entry(o->sha1, o->type, "", 0);
|
||||
}
|
||||
}
|
||||
free(in_pack.array);
|
||||
}
|
||||
|
||||
static void get_object_list(int ac, const char **av)
|
||||
{
|
||||
struct rev_info revs;
|
||||
@@ -1672,6 +1938,9 @@ static void get_object_list(int ac, const char **av)
|
||||
prepare_revision_walk(&revs);
|
||||
mark_edges_uninteresting(revs.commits, &revs, show_edge);
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
|
||||
if (keep_unreachable)
|
||||
add_objects_in_unpacked_packs(&revs);
|
||||
}
|
||||
|
||||
static int adjust_perm(const char *path, mode_t mode)
|
||||
@@ -1750,6 +2019,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
usage(pack_usage);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--threads=")) {
|
||||
char *end;
|
||||
delta_search_threads = strtoul(arg+10, &end, 0);
|
||||
if (!arg[10] || *end || delta_search_threads < 1)
|
||||
usage(pack_usage);
|
||||
#ifndef THREADED_DELTA_SEARCH
|
||||
if (delta_search_threads > 1)
|
||||
warning("no threads support, "
|
||||
"ignoring %s", arg);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--depth=")) {
|
||||
char *end;
|
||||
depth = strtoul(arg+8, &end, 0);
|
||||
@@ -1789,6 +2070,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
use_internal_rev_list = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--keep-unreachable", arg)) {
|
||||
keep_unreachable = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--unpacked", arg) ||
|
||||
!prefixcmp(arg, "--unpacked=") ||
|
||||
!strcmp("--reflog", arg) ||
|
||||
|
||||
@@ -27,10 +27,8 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
|
||||
memcpy(pathname + len, de->d_name, 38);
|
||||
if (opts & DRY_RUN)
|
||||
printf("rm -f %s\n", pathname);
|
||||
else {
|
||||
if (unlink(pathname) < 0)
|
||||
error("unable to unlink %s", pathname);
|
||||
}
|
||||
else if (unlink(pathname) < 0)
|
||||
error("unable to unlink %s", pathname);
|
||||
}
|
||||
pathname[len] = 0;
|
||||
rmdir(pathname);
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
#include "run-command.h"
|
||||
#include "builtin.h"
|
||||
#include "remote.h"
|
||||
#include "transport.h"
|
||||
|
||||
static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
|
||||
static const char push_usage[] = "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
|
||||
|
||||
static int all, force, thin = 1, verbose;
|
||||
static int thin, verbose;
|
||||
static const char *receivepack;
|
||||
|
||||
static const char **refspec;
|
||||
@@ -43,80 +44,40 @@ static void set_refspecs(const char **refs, int nr)
|
||||
}
|
||||
}
|
||||
|
||||
static int do_push(const char *repo)
|
||||
static int do_push(const char *repo, int flags)
|
||||
{
|
||||
int i, errs;
|
||||
int common_argc;
|
||||
const char **argv;
|
||||
int argc;
|
||||
struct remote *remote = remote_get(repo);
|
||||
|
||||
if (!remote)
|
||||
die("bad repository '%s'", repo);
|
||||
|
||||
if (remote->receivepack) {
|
||||
char *rp = xmalloc(strlen(remote->receivepack) + 16);
|
||||
sprintf(rp, "--receive-pack=%s", remote->receivepack);
|
||||
receivepack = rp;
|
||||
}
|
||||
if (!refspec && !all && remote->push_refspec_nr) {
|
||||
if (!refspec
|
||||
&& !(flags & TRANSPORT_PUSH_ALL)
|
||||
&& remote->push_refspec_nr) {
|
||||
refspec = remote->push_refspec;
|
||||
refspec_nr = remote->push_refspec_nr;
|
||||
}
|
||||
|
||||
argv = xmalloc((refspec_nr + 10) * sizeof(char *));
|
||||
argv[0] = "dummy-send-pack";
|
||||
argc = 1;
|
||||
if (all)
|
||||
argv[argc++] = "--all";
|
||||
if (force)
|
||||
argv[argc++] = "--force";
|
||||
if (receivepack)
|
||||
argv[argc++] = receivepack;
|
||||
common_argc = argc;
|
||||
|
||||
errs = 0;
|
||||
for (i = 0; i < remote->uri_nr; i++) {
|
||||
for (i = 0; i < remote->url_nr; i++) {
|
||||
struct transport *transport =
|
||||
transport_get(remote, remote->url[i]);
|
||||
int err;
|
||||
int dest_argc = common_argc;
|
||||
int dest_refspec_nr = refspec_nr;
|
||||
const char **dest_refspec = refspec;
|
||||
const char *dest = remote->uri[i];
|
||||
const char *sender = "send-pack";
|
||||
if (!prefixcmp(dest, "http://") ||
|
||||
!prefixcmp(dest, "https://"))
|
||||
sender = "http-push";
|
||||
else {
|
||||
char *rem = xmalloc(strlen(remote->name) + 10);
|
||||
sprintf(rem, "--remote=%s", remote->name);
|
||||
argv[dest_argc++] = rem;
|
||||
if (thin)
|
||||
argv[dest_argc++] = "--thin";
|
||||
}
|
||||
argv[0] = sender;
|
||||
argv[dest_argc++] = dest;
|
||||
while (dest_refspec_nr--)
|
||||
argv[dest_argc++] = *dest_refspec++;
|
||||
argv[dest_argc] = NULL;
|
||||
if (receivepack)
|
||||
transport_set_option(transport,
|
||||
TRANS_OPT_RECEIVEPACK, receivepack);
|
||||
if (thin)
|
||||
transport_set_option(transport, TRANS_OPT_THIN, "yes");
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Pushing to %s\n", dest);
|
||||
err = run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
fprintf(stderr, "Pushing to %s\n", remote->url[i]);
|
||||
err = transport_push(transport, refspec_nr, refspec, flags);
|
||||
err |= transport_disconnect(transport);
|
||||
|
||||
if (!err)
|
||||
continue;
|
||||
|
||||
error("failed to push to '%s'", remote->uri[i]);
|
||||
switch (err) {
|
||||
case -ERR_RUN_COMMAND_FORK:
|
||||
error("unable to fork for %s", sender);
|
||||
case -ERR_RUN_COMMAND_EXEC:
|
||||
error("unable to exec %s", sender);
|
||||
break;
|
||||
case -ERR_RUN_COMMAND_WAITPID:
|
||||
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
|
||||
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
|
||||
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
|
||||
error("%s died with strange error", sender);
|
||||
}
|
||||
error("failed to push to '%s'", remote->url[i]);
|
||||
errs++;
|
||||
}
|
||||
return !!errs;
|
||||
@@ -125,6 +86,7 @@ static int do_push(const char *repo)
|
||||
int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
int flags = 0;
|
||||
const char *repo = NULL; /* default repository */
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
@@ -144,7 +106,11 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--all")) {
|
||||
all = 1;
|
||||
flags |= TRANSPORT_PUSH_ALL;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--dry-run")) {
|
||||
flags |= TRANSPORT_PUSH_DRY_RUN;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--tags")) {
|
||||
@@ -152,7 +118,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
|
||||
force = 1;
|
||||
flags |= TRANSPORT_PUSH_FORCE;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--thin")) {
|
||||
@@ -164,18 +130,18 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--receive-pack=")) {
|
||||
receivepack = arg;
|
||||
receivepack = arg + 15;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--exec=")) {
|
||||
receivepack = arg;
|
||||
receivepack = arg + 7;
|
||||
continue;
|
||||
}
|
||||
usage(push_usage);
|
||||
}
|
||||
set_refspecs(argv + i, argc - i);
|
||||
if (all && refspec)
|
||||
if ((flags & TRANSPORT_PUSH_ALL) && refspec)
|
||||
usage(push_usage);
|
||||
|
||||
return do_push(repo);
|
||||
return do_push(repo, flags);
|
||||
}
|
||||
|
||||
@@ -269,12 +269,6 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
|
||||
if (fclose(cb.newlog))
|
||||
status |= error("%s: %s", strerror(errno),
|
||||
newlog_path);
|
||||
#ifdef __MINGW32__
|
||||
/* rename fails if the destination exists */
|
||||
if (unlink(log_file))
|
||||
status |= error("cannot remove %s", log_file);
|
||||
else
|
||||
#endif
|
||||
if (rename(newlog_path, log_file)) {
|
||||
status |= error("cannot rename %s to %s",
|
||||
newlog_path, log_file);
|
||||
|
||||
@@ -66,40 +66,15 @@ static int write_rr(struct path_list *rr, int out_fd)
|
||||
return commit_lock_file(&write_lock);
|
||||
}
|
||||
|
||||
struct buffer {
|
||||
char *ptr;
|
||||
int nr, alloc;
|
||||
};
|
||||
|
||||
static void append_line(struct buffer *buffer, const char *line)
|
||||
{
|
||||
int len = strlen(line);
|
||||
|
||||
if (buffer->nr + len > buffer->alloc) {
|
||||
buffer->alloc = alloc_nr(buffer->nr + len);
|
||||
buffer->ptr = xrealloc(buffer->ptr, buffer->alloc);
|
||||
}
|
||||
memcpy(buffer->ptr + buffer->nr, line, len);
|
||||
buffer->nr += len;
|
||||
}
|
||||
|
||||
static void clear_buffer(struct buffer *buffer)
|
||||
{
|
||||
free(buffer->ptr);
|
||||
buffer->ptr = NULL;
|
||||
buffer->nr = buffer->alloc = 0;
|
||||
}
|
||||
|
||||
static int handle_file(const char *path,
|
||||
unsigned char *sha1, const char *output)
|
||||
{
|
||||
SHA_CTX ctx;
|
||||
char buf[1024];
|
||||
int hunk = 0, hunk_no = 0;
|
||||
struct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 };
|
||||
struct buffer *one = &minus, *two = +
|
||||
struct strbuf one, two;
|
||||
FILE *f = fopen(path, "r");
|
||||
FILE *out;
|
||||
FILE *out = NULL;
|
||||
|
||||
if (!f)
|
||||
return error("Could not open %s", path);
|
||||
@@ -110,51 +85,50 @@ static int handle_file(const char *path,
|
||||
fclose(f);
|
||||
return error("Could not write %s", output);
|
||||
}
|
||||
} else
|
||||
out = NULL;
|
||||
}
|
||||
|
||||
if (sha1)
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
strbuf_init(&one, 0);
|
||||
strbuf_init(&two, 0);
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
if (!prefixcmp(buf, "<<<<<<< "))
|
||||
hunk = 1;
|
||||
else if (!prefixcmp(buf, "======="))
|
||||
hunk = 2;
|
||||
else if (!prefixcmp(buf, ">>>>>>> ")) {
|
||||
int one_is_longer = (one->nr > two->nr);
|
||||
int common_len = one_is_longer ? two->nr : one->nr;
|
||||
int cmp = memcmp(one->ptr, two->ptr, common_len);
|
||||
int cmp = strbuf_cmp(&one, &two);
|
||||
|
||||
hunk_no++;
|
||||
hunk = 0;
|
||||
if ((cmp > 0) || ((cmp == 0) && one_is_longer)) {
|
||||
struct buffer *swap = one;
|
||||
one = two;
|
||||
two = swap;
|
||||
if (cmp > 0) {
|
||||
strbuf_swap(&one, &two);
|
||||
}
|
||||
if (out) {
|
||||
fputs("<<<<<<<\n", out);
|
||||
fwrite(one->ptr, one->nr, 1, out);
|
||||
fwrite(one.buf, one.len, 1, out);
|
||||
fputs("=======\n", out);
|
||||
fwrite(two->ptr, two->nr, 1, out);
|
||||
fwrite(two.buf, two.len, 1, out);
|
||||
fputs(">>>>>>>\n", out);
|
||||
}
|
||||
if (sha1) {
|
||||
SHA1_Update(&ctx, one->ptr, one->nr);
|
||||
SHA1_Update(&ctx, "\0", 1);
|
||||
SHA1_Update(&ctx, two->ptr, two->nr);
|
||||
SHA1_Update(&ctx, "\0", 1);
|
||||
SHA1_Update(&ctx, one.buf ? one.buf : "",
|
||||
one.len + 1);
|
||||
SHA1_Update(&ctx, two.buf ? two.buf : "",
|
||||
two.len + 1);
|
||||
}
|
||||
clear_buffer(one);
|
||||
clear_buffer(two);
|
||||
strbuf_reset(&one);
|
||||
strbuf_reset(&two);
|
||||
} else if (hunk == 1)
|
||||
append_line(one, buf);
|
||||
strbuf_addstr(&one, buf);
|
||||
else if (hunk == 2)
|
||||
append_line(two, buf);
|
||||
strbuf_addstr(&two, buf);
|
||||
else if (out)
|
||||
fputs(buf, out);
|
||||
}
|
||||
strbuf_release(&one);
|
||||
strbuf_release(&two);
|
||||
|
||||
fclose(f);
|
||||
if (out)
|
||||
@@ -415,18 +389,39 @@ static int is_rerere_enabled(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmd_rerere(int argc, const char **argv, const char *prefix)
|
||||
static int setup_rerere(struct path_list *merge_rr)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int i, fd = -1;
|
||||
int fd;
|
||||
|
||||
git_config(git_rerere_config);
|
||||
if (!is_rerere_enabled())
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
|
||||
fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
|
||||
read_rr(&merge_rr);
|
||||
read_rr(merge_rr);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int rerere(void)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int fd;
|
||||
|
||||
fd = setup_rerere(&merge_rr);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
return do_plain_rerere(&merge_rr, fd);
|
||||
}
|
||||
|
||||
int cmd_rerere(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int i, fd;
|
||||
|
||||
fd = setup_rerere(&merge_rr);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
if (argc < 2)
|
||||
return do_plain_rerere(&merge_rr, fd);
|
||||
|
||||
279
builtin-reset.c
Normal file
279
builtin-reset.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* "git reset" builtin command
|
||||
*
|
||||
* Copyright (c) 2007 Carlos Rica
|
||||
*
|
||||
* Based on git-reset.sh, which is
|
||||
*
|
||||
* Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "tag.h"
|
||||
#include "object.h"
|
||||
#include "commit.h"
|
||||
#include "run-command.h"
|
||||
#include "refs.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "tree.h"
|
||||
|
||||
static const char builtin_reset_usage[] =
|
||||
"git-reset [--mixed | --soft | --hard] [<commit-ish>] [ [--] <paths>...]";
|
||||
|
||||
static char *args_to_str(const char **argv)
|
||||
{
|
||||
char *buf = NULL;
|
||||
unsigned long len, space = 0, nr = 0;
|
||||
|
||||
for (; *argv; argv++) {
|
||||
len = strlen(*argv);
|
||||
ALLOC_GROW(buf, nr + 1 + len, space);
|
||||
if (nr)
|
||||
buf[nr++] = ' ';
|
||||
memcpy(buf + nr, *argv, len);
|
||||
nr += len;
|
||||
}
|
||||
ALLOC_GROW(buf, nr + 1, space);
|
||||
buf[nr] = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline int is_merge(void)
|
||||
{
|
||||
return !access(git_path("MERGE_HEAD"), F_OK);
|
||||
}
|
||||
|
||||
static int unmerged_files(void)
|
||||
{
|
||||
char b;
|
||||
ssize_t len;
|
||||
struct child_process cmd;
|
||||
const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL};
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.argv = argv_ls_files;
|
||||
cmd.git_cmd = 1;
|
||||
cmd.out = -1;
|
||||
|
||||
if (start_command(&cmd))
|
||||
die("Could not run sub-command: git ls-files");
|
||||
|
||||
len = xread(cmd.out, &b, 1);
|
||||
if (len < 0)
|
||||
die("Could not read output from git ls-files: %s",
|
||||
strerror(errno));
|
||||
finish_command(&cmd);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
|
||||
{
|
||||
int i = 0;
|
||||
const char *args[6];
|
||||
|
||||
args[i++] = "read-tree";
|
||||
args[i++] = "-v";
|
||||
args[i++] = "--reset";
|
||||
if (is_hard_reset)
|
||||
args[i++] = "-u";
|
||||
args[i++] = sha1_to_hex(sha1);
|
||||
args[i] = NULL;
|
||||
|
||||
return run_command_v_opt(args, RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static void print_new_head_line(struct commit *commit)
|
||||
{
|
||||
const char *hex, *dots = "...", *body;
|
||||
|
||||
hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
|
||||
if (!hex) {
|
||||
hex = sha1_to_hex(commit->object.sha1);
|
||||
dots = "";
|
||||
}
|
||||
printf("HEAD is now at %s%s", hex, dots);
|
||||
body = strstr(commit->buffer, "\n\n");
|
||||
if (body) {
|
||||
const char *eol;
|
||||
size_t len;
|
||||
body += 2;
|
||||
eol = strchr(body, '\n');
|
||||
len = eol ? eol - body : strlen(body);
|
||||
printf(" %.*s\n", (int) len, body);
|
||||
}
|
||||
else
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int update_index_refresh(void)
|
||||
{
|
||||
const char *argv_update_index[] = {"update-index", "--refresh", NULL};
|
||||
return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static void update_index_from_diff(struct diff_queue_struct *q,
|
||||
struct diff_options *opt, void *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* do_diff_cache() mangled the index */
|
||||
discard_cache();
|
||||
read_cache();
|
||||
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filespec *one = q->queue[i]->one;
|
||||
if (one->mode) {
|
||||
struct cache_entry *ce;
|
||||
ce = make_cache_entry(one->mode, one->sha1, one->path,
|
||||
0, 0);
|
||||
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
|
||||
ADD_CACHE_OK_TO_REPLACE);
|
||||
} else
|
||||
remove_file_from_cache(one->path);
|
||||
}
|
||||
}
|
||||
|
||||
static int read_from_tree(const char *prefix, const char **argv,
|
||||
unsigned char *tree_sha1)
|
||||
{
|
||||
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
|
||||
int index_fd;
|
||||
struct diff_options opt;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
|
||||
opt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
opt.format_callback = update_index_from_diff;
|
||||
|
||||
index_fd = hold_locked_index(lock, 1);
|
||||
read_cache();
|
||||
if (do_diff_cache(tree_sha1, &opt))
|
||||
return 1;
|
||||
diffcore_std(&opt);
|
||||
diff_flush(&opt);
|
||||
return write_cache(index_fd, active_cache, active_nr) ||
|
||||
close(index_fd) ||
|
||||
commit_locked_index(lock);
|
||||
}
|
||||
|
||||
static void prepend_reflog_action(const char *action, char *buf, size_t size)
|
||||
{
|
||||
const char *sep = ": ";
|
||||
const char *rla = getenv("GIT_REFLOG_ACTION");
|
||||
if (!rla)
|
||||
rla = sep = "";
|
||||
if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size)
|
||||
warning("Reflog action message too long: %.*s...", 50, buf);
|
||||
}
|
||||
|
||||
enum reset_type { MIXED, SOFT, HARD, NONE };
|
||||
static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
|
||||
|
||||
int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i = 1, reset_type = NONE, update_ref_status = 0;
|
||||
const char *rev = "HEAD";
|
||||
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
|
||||
*old_orig = NULL, sha1_old_orig[20];
|
||||
struct commit *commit;
|
||||
char *reflog_action, msg[1024];
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
reflog_action = args_to_str(argv);
|
||||
setenv("GIT_REFLOG_ACTION", reflog_action, 0);
|
||||
|
||||
if (i < argc) {
|
||||
if (!strcmp(argv[i], "--mixed")) {
|
||||
reset_type = MIXED;
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--soft")) {
|
||||
reset_type = SOFT;
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--hard")) {
|
||||
reset_type = HARD;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < argc && argv[i][0] != '-')
|
||||
rev = argv[i++];
|
||||
|
||||
if (get_sha1(rev, sha1))
|
||||
die("Failed to resolve '%s' as a valid ref.", rev);
|
||||
|
||||
commit = lookup_commit_reference(sha1);
|
||||
if (!commit)
|
||||
die("Could not parse object '%s'.", rev);
|
||||
hashcpy(sha1, commit->object.sha1);
|
||||
|
||||
if (i < argc && !strcmp(argv[i], "--"))
|
||||
i++;
|
||||
else if (i < argc && argv[i][0] == '-')
|
||||
usage(builtin_reset_usage);
|
||||
|
||||
/* git reset tree [--] paths... can be used to
|
||||
* load chosen paths from the tree into the index without
|
||||
* affecting the working tree nor HEAD. */
|
||||
if (i < argc) {
|
||||
if (reset_type == MIXED)
|
||||
warning("--mixed option is deprecated with paths.");
|
||||
else if (reset_type != NONE)
|
||||
die("Cannot do %s reset with paths.",
|
||||
reset_type_names[reset_type]);
|
||||
if (read_from_tree(prefix, argv + i, sha1))
|
||||
return 1;
|
||||
return update_index_refresh() ? 1 : 0;
|
||||
}
|
||||
if (reset_type == NONE)
|
||||
reset_type = MIXED; /* by default */
|
||||
|
||||
/* Soft reset does not touch the index file nor the working tree
|
||||
* at all, but requires them in a good order. Other resets reset
|
||||
* the index file to the tree object we are switching to. */
|
||||
if (reset_type == SOFT) {
|
||||
if (is_merge() || unmerged_files())
|
||||
die("Cannot do a soft reset in the middle of a merge.");
|
||||
}
|
||||
else if (reset_index_file(sha1, (reset_type == HARD)))
|
||||
die("Could not reset index file to revision '%s'.", rev);
|
||||
|
||||
/* Any resets update HEAD to the head being switched to,
|
||||
* saving the previous head in ORIG_HEAD before. */
|
||||
if (!get_sha1("ORIG_HEAD", sha1_old_orig))
|
||||
old_orig = sha1_old_orig;
|
||||
if (!get_sha1("HEAD", sha1_orig)) {
|
||||
orig = sha1_orig;
|
||||
prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg));
|
||||
update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
|
||||
}
|
||||
else if (old_orig)
|
||||
delete_ref("ORIG_HEAD", old_orig);
|
||||
prepend_reflog_action("updating HEAD", msg, sizeof(msg));
|
||||
update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
|
||||
|
||||
switch (reset_type) {
|
||||
case HARD:
|
||||
if (!update_ref_status)
|
||||
print_new_head_line(commit);
|
||||
break;
|
||||
case SOFT: /* Nothing else to do. */
|
||||
break;
|
||||
case MIXED: /* Report what has not been updated. */
|
||||
update_index_refresh();
|
||||
break;
|
||||
}
|
||||
|
||||
unlink(git_path("MERGE_HEAD"));
|
||||
unlink(git_path("rr-cache/MERGE_RR"));
|
||||
unlink(git_path("MERGE_MSG"));
|
||||
unlink(git_path("SQUASH_MSG"));
|
||||
|
||||
free(reflog_action);
|
||||
|
||||
return update_ref_status;
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "revision.h"
|
||||
#include "list-objects.h"
|
||||
#include "builtin.h"
|
||||
#include "log-tree.h"
|
||||
|
||||
/* bits #0-15 in revision.h */
|
||||
|
||||
@@ -38,7 +39,8 @@ static const char rev_list_usage[] =
|
||||
" --left-right\n"
|
||||
" special purpose:\n"
|
||||
" --bisect\n"
|
||||
" --bisect-vars"
|
||||
" --bisect-vars\n"
|
||||
" --bisect-all"
|
||||
;
|
||||
|
||||
static struct rev_info revs;
|
||||
@@ -74,19 +76,20 @@ static void show_commit(struct commit *commit)
|
||||
parents = parents->next;
|
||||
}
|
||||
}
|
||||
show_decorations(commit);
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
putchar(' ');
|
||||
else
|
||||
putchar('\n');
|
||||
|
||||
if (revs.verbose_header) {
|
||||
char *buf = NULL;
|
||||
unsigned long buflen = 0;
|
||||
pretty_print_commit(revs.commit_format, commit, ~0,
|
||||
&buf, &buflen,
|
||||
revs.abbrev, NULL, NULL, revs.date_mode);
|
||||
printf("%s%c", buf, hdr_termination);
|
||||
free(buf);
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
pretty_print_commit(revs.commit_format, commit,
|
||||
&buf, revs.abbrev, NULL, NULL, revs.date_mode);
|
||||
if (buf.len)
|
||||
printf("%s%c", buf.buf, hdr_termination);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
maybe_flush_or_die(stdout, "stdout");
|
||||
if (commit->parents) {
|
||||
@@ -189,7 +192,7 @@ static int count_interesting_parents(struct commit *commit)
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline int halfway(struct commit_list *p, int distance, int nr)
|
||||
static inline int halfway(struct commit_list *p, int nr)
|
||||
{
|
||||
/*
|
||||
* Don't short-cut something we are not going to return!
|
||||
@@ -202,8 +205,7 @@ static inline int halfway(struct commit_list *p, int distance, int nr)
|
||||
* 2 and 3 are halfway of 5.
|
||||
* 3 is halfway of 6 but 2 and 4 are not.
|
||||
*/
|
||||
distance *= 2;
|
||||
switch (distance - nr) {
|
||||
switch (2 * weight(p) - nr) {
|
||||
case -1: case 0: case 1:
|
||||
return 1;
|
||||
default:
|
||||
@@ -255,6 +257,81 @@ static void show_list(const char *debug, int counted, int nr,
|
||||
}
|
||||
#endif /* DEBUG_BISECT */
|
||||
|
||||
static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p, *best;
|
||||
int best_distance = -1;
|
||||
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (revs.prune_fn && !(flags & TREECHANGE))
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
if (distance > best_distance) {
|
||||
best = p;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
struct commit_dist {
|
||||
struct commit *commit;
|
||||
int distance;
|
||||
};
|
||||
|
||||
static int compare_commit_dist(const void *a_, const void *b_)
|
||||
{
|
||||
struct commit_dist *a, *b;
|
||||
|
||||
a = (struct commit_dist *)a_;
|
||||
b = (struct commit_dist *)b_;
|
||||
if (a->distance != b->distance)
|
||||
return b->distance - a->distance; /* desc sort */
|
||||
return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
|
||||
}
|
||||
|
||||
static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_dist *array = xcalloc(nr, sizeof(*array));
|
||||
int cnt, i;
|
||||
|
||||
for (p = list, cnt = 0; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (revs.prune_fn && !(flags & TREECHANGE))
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
array[cnt].commit = p->item;
|
||||
array[cnt].distance = distance;
|
||||
cnt++;
|
||||
}
|
||||
qsort(array, cnt, sizeof(*array), compare_commit_dist);
|
||||
for (p = list, i = 0; i < cnt; i++) {
|
||||
struct name_decoration *r = xmalloc(sizeof(*r) + 100);
|
||||
struct object *obj = &(array[i].commit->object);
|
||||
|
||||
sprintf(r->name, "dist=%d", array[i].distance);
|
||||
r->next = add_decoration(&name_decoration, obj, r);
|
||||
p->item = array[i].commit;
|
||||
p = p->next;
|
||||
}
|
||||
if (p)
|
||||
p->next = NULL;
|
||||
free(array);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* zero or positive weight is the number of interesting commits it can
|
||||
* reach, including itself. Especially, weight = 0 means it does not
|
||||
@@ -268,39 +345,13 @@ static void show_list(const char *debug, int counted, int nr,
|
||||
* unknown. After running count_distance() first, they will get zero
|
||||
* or positive distance.
|
||||
*/
|
||||
|
||||
static struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all)
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights,
|
||||
int find_all)
|
||||
{
|
||||
int n, nr, on_list, counted, distance;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
int *weights;
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
|
||||
show_list("bisection 2 entry", 0, 0, list);
|
||||
|
||||
/*
|
||||
* Count the number of total and tree-changing items on the
|
||||
* list, while reversing the list.
|
||||
*/
|
||||
for (nr = on_list = 0, last = NULL, p = list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING)
|
||||
continue;
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!revs.prune_fn || (flags & TREECHANGE))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
list = last;
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
counted = 0;
|
||||
|
||||
for (n = 0, p = list; p; p = p->next) {
|
||||
@@ -349,20 +400,14 @@ static struct commit_list *find_bisection(struct commit_list *list,
|
||||
for (p = list; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
n = weight(p);
|
||||
if (n != -2)
|
||||
if (weight(p) != -2)
|
||||
continue;
|
||||
distance = count_distance(p);
|
||||
weight_set(p, count_distance(p));
|
||||
clear_distance(list);
|
||||
weight_set(p, distance);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (halfway(p, distance, nr)) {
|
||||
p->next = NULL;
|
||||
*reaches = distance;
|
||||
free(weights);
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
counted++;
|
||||
}
|
||||
|
||||
@@ -399,37 +444,60 @@ static struct commit_list *find_bisection(struct commit_list *list,
|
||||
weight_set(p, weight(q));
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
distance = weight(p);
|
||||
if (halfway(p, distance, nr)) {
|
||||
p->next = NULL;
|
||||
*reaches = distance;
|
||||
free(weights);
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
/* Then find the best one */
|
||||
counted = -1;
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
if (!find_all)
|
||||
return best_bisection(list, nr);
|
||||
else
|
||||
return best_bisection_sorted(list, nr);
|
||||
}
|
||||
|
||||
static struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all,
|
||||
int find_all)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
int *weights;
|
||||
|
||||
show_list("bisection 2 entry", 0, 0, list);
|
||||
|
||||
/*
|
||||
* Count the number of total and tree-changing items on the
|
||||
* list, while reversing the list.
|
||||
*/
|
||||
for (nr = on_list = 0, last = NULL, p = list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (revs.prune_fn && !(flags & TREECHANGE))
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
if (distance > counted) {
|
||||
best = p;
|
||||
counted = distance;
|
||||
*reaches = weight(p);
|
||||
}
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!revs.prune_fn || (flags & TREECHANGE))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
list = last;
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights, find_all);
|
||||
if (best) {
|
||||
if (!find_all)
|
||||
best->next = NULL;
|
||||
*reaches = weight(best);
|
||||
}
|
||||
if (best)
|
||||
best->next = NULL;
|
||||
free(weights);
|
||||
return best;
|
||||
}
|
||||
@@ -457,6 +525,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
int i;
|
||||
int read_from_stdin = 0;
|
||||
int bisect_show_vars = 0;
|
||||
int bisect_find_all = 0;
|
||||
|
||||
git_config(git_default_config);
|
||||
init_revisions(&revs, prefix);
|
||||
@@ -479,6 +548,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
bisect_list = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--bisect-all")) {
|
||||
bisect_list = 1;
|
||||
bisect_find_all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--bisect-vars")) {
|
||||
bisect_list = 1;
|
||||
bisect_show_vars = 1;
|
||||
@@ -525,9 +599,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
if (bisect_list) {
|
||||
int reaches = reaches, all = all;
|
||||
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all);
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all,
|
||||
bisect_find_all);
|
||||
if (bisect_show_vars) {
|
||||
int cnt;
|
||||
char hex[41];
|
||||
if (!revs.commits)
|
||||
return 1;
|
||||
/*
|
||||
@@ -539,15 +615,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
* A bisect set of size N has (N-1) commits further
|
||||
* to test, as we already know one bad one.
|
||||
*/
|
||||
cnt = all-reaches;
|
||||
cnt = all - reaches;
|
||||
if (cnt < reaches)
|
||||
cnt = reaches;
|
||||
strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
|
||||
|
||||
if (bisect_find_all) {
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
printf("bisect_rev=%s\n"
|
||||
"bisect_nr=%d\n"
|
||||
"bisect_good=%d\n"
|
||||
"bisect_bad=%d\n"
|
||||
"bisect_all=%d\n",
|
||||
sha1_to_hex(revs.commits->item->object.sha1),
|
||||
hex,
|
||||
cnt - 1,
|
||||
all - reaches - 1,
|
||||
reaches - 1,
|
||||
|
||||
@@ -168,9 +168,7 @@ static void set_author_ident_env(const char *message)
|
||||
char *line, *pend, *email, *timestamp;
|
||||
|
||||
p += 7;
|
||||
line = xmalloc(eol + 1 - p);
|
||||
memcpy(line, p, eol - p);
|
||||
line[eol - p] = '\0';
|
||||
line = xmemdupz(p, eol - p);
|
||||
email = strchr(line, '<');
|
||||
if (!email)
|
||||
die ("Could not extract author email from %s",
|
||||
@@ -351,7 +349,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
die ("Error wrapping up %s", defmsg);
|
||||
fprintf(stderr, "Automatic %s failed. "
|
||||
"After resolving the conflicts,\n"
|
||||
"mark the corrected paths with 'git-add <paths>'\n"
|
||||
"mark the corrected paths with 'git add <paths>' "
|
||||
"and commit the result.\n", me);
|
||||
if (action == CHERRY_PICK) {
|
||||
fprintf(stderr, "When commiting, use the option "
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user