Merge branch 'master' into np/index-pack

* master: (90 commits)
  gitweb: Better support for non-CSS aware web browsers
  gitweb: Output also empty patches in "commitdiff" view
  gitweb: Use git-for-each-ref to generate list of heads and/or tags
  for-each-ref: "creator" and "creatordate" fields
  Add --global option to git-repo-config.
  pack-refs: Store the full name of the ref even when packing only tags.
  git-clone documentation didn't mention --origin as equivalent of -o
  Minor grammar fixes for git-diff-index.txt
  link_temp_to_file: call adjust_shared_perm() only when we created the directory
  Remove uneccessarily similar printf() from print_ref_list() in builtin-branch
  pack-objects doesn't create random pack names
  branch: work in subdirectories.
  gitweb: Use 's' regexp modifier to secure against filenames with LF
  gitweb: Secure against commit-ish/tree-ish with the same name as path
  gitweb: esc_html() author in blame
  git-svnimport: support for partial imports
  link_temp_to_file: don't leave the path truncated on adjust_shared_perm failure
  Move deny_non_fast_forwards handling completely into receive-pack.
  revision traversal: --unpacked does not limit commit list anymore.
  Continue traversal when rev-list --unpacked finds a packed commit.
  ...
This commit is contained in:
Junio C Hamano 2006-11-03 00:23:52 -08:00
commit 407e1d6e12
64 changed files with 1898 additions and 727 deletions

2
.gitignore vendored
View File

@ -75,6 +75,7 @@ git-name-rev
git-mv git-mv
git-pack-redundant git-pack-redundant
git-pack-objects git-pack-objects
git-pack-refs
git-parse-remote git-parse-remote
git-patch-id git-patch-id
git-peek-remote git-peek-remote
@ -106,6 +107,7 @@ git-shortlog
git-show git-show
git-show-branch git-show-branch
git-show-index git-show-index
git-show-ref
git-ssh-fetch git-ssh-fetch
git-ssh-pull git-ssh-pull
git-ssh-push git-ssh-push

View File

@ -71,12 +71,16 @@ core.preferSymlinkRefs::
expect HEAD to be a symbolic link. expect HEAD to be a symbolic link.
core.logAllRefUpdates:: core.logAllRefUpdates::
If true, `git-update-ref` will append a line to Updates to a ref <ref> is logged to the file
"$GIT_DIR/logs/<ref>" listing the new SHA1 and the date/time "$GIT_DIR/logs/<ref>", by appending the new and old
of the update. If the file does not exist it will be SHA1, the date/time and the reason of the update, but
created automatically. This information can be used to only when the file exists. If this configuration
determine what commit was the tip of a branch "2 days ago". variable is set to true, missing "$GIT_DIR/logs/<ref>"
This value is false by default (no logging). file is automatically created for branch heads.
This information can be used to determine what commit
was the tip of a branch "2 days ago". This value is
false by default (no automated creation of log files).
core.repositoryFormatVersion:: core.repositoryFormatVersion::
Internal variable identifying the repository format and layout Internal variable identifying the repository format and layout

View File

@ -75,6 +75,7 @@ OPTIONS
this option is used, neither the `origin` branch nor the this option is used, neither the `origin` branch nor the
default `remotes/origin` file is created. default `remotes/origin` file is created.
--origin <name>::
-o <name>:: -o <name>::
Instead of using the branch name 'origin' to keep track Instead of using the branch name 'origin' to keep track
of the upstream repository, use <name> instead. Note of the upstream repository, use <name> instead. Note

View File

@ -54,7 +54,7 @@ If '--cached' is specified, it allows you to ask:
For example, let's say that you have worked on your working directory, updated For example, let's say that you have worked on your working directory, updated
some files in the index and are ready to commit. You want to see exactly some files in the index and are ready to commit. You want to see exactly
*what* you are going to commit is without having to write a new tree *what* you are going to commit, without having to write a new tree
object and compare it that way, and to do that, you just do object and compare it that way, and to do that, you just do
git-diff-index --cached HEAD git-diff-index --cached HEAD
@ -68,7 +68,7 @@ matches my working directory. But doing a "git-diff-index" does:
-100644 blob 4161aecc6700a2eb579e842af0b7f22b98443f74 commit.c -100644 blob 4161aecc6700a2eb579e842af0b7f22b98443f74 commit.c
+100644 blob 4161aecc6700a2eb579e842af0b7f22b98443f74 git-commit.c +100644 blob 4161aecc6700a2eb579e842af0b7f22b98443f74 git-commit.c
You can trivially see that the above is a rename. You can see easily that the above is a rename.
In fact, "git-diff-index --cached" *should* always be entirely equivalent to In fact, "git-diff-index --cached" *should* always be entirely equivalent to
actually doing a "git-write-tree" and comparing that. Except this one is much actually doing a "git-write-tree" and comparing that. Except this one is much

View File

@ -47,9 +47,8 @@ base-name::
<base-name> to determine the name of the created file. <base-name> to determine the name of the created file.
When this option is used, the two files are written in When this option is used, the two files are written in
<base-name>-<SHA1>.{pack,idx} files. <SHA1> is a hash <base-name>-<SHA1>.{pack,idx} files. <SHA1> is a hash
of object names (currently in random order so it does of the sorted object names to make the resulting filename
not have any useful meaning) to make the resulting based on the pack content, and written to the standard
filename reasonably unique, and written to the standard
output of the command. output of the command.
--stdout:: --stdout::

View File

@ -3,19 +3,19 @@ git-repo-config(1)
NAME NAME
---- ----
git-repo-config - Get and set options in .git/config git-repo-config - Get and set repository or global options.
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git-repo-config' [type] name [value [value_regex]] 'git-repo-config' [--global] [type] name [value [value_regex]]
'git-repo-config' [type] --replace-all name [value [value_regex]] 'git-repo-config' [--global] [type] --replace-all name [value [value_regex]]
'git-repo-config' [type] --get name [value_regex] 'git-repo-config' [--global] [type] --get name [value_regex]
'git-repo-config' [type] --get-all name [value_regex] 'git-repo-config' [--global] [type] --get-all name [value_regex]
'git-repo-config' [type] --unset name [value_regex] 'git-repo-config' [--global] [type] --unset name [value_regex]
'git-repo-config' [type] --unset-all name [value_regex] 'git-repo-config' [--global] [type] --unset-all name [value_regex]
'git-repo-config' -l | --list 'git-repo-config' [--global] -l | --list
DESCRIPTION DESCRIPTION
----------- -----------
@ -41,8 +41,9 @@ This command will fail if:
. Can not write to .git/config, . Can not write to .git/config,
. no section was provided, . no section was provided,
. the section or key is invalid, . the section or key is invalid,
. you try to unset an option which does not exist, or . you try to unset an option which does not exist,
. you try to unset/set an option for which multiple lines match. . you try to unset/set an option for which multiple lines match, or
. you use --global option without $HOME being properly set.
OPTIONS OPTIONS
@ -64,14 +65,17 @@ OPTIONS
--get-regexp:: --get-regexp::
Like --get-all, but interprets the name as a regular expression. Like --get-all, but interprets the name as a regular expression.
--global::
Use global ~/.gitconfig file rather than the repository .git/config.
--unset:: --unset::
Remove the line matching the key from .git/config. Remove the line matching the key from config file.
--unset-all:: --unset-all::
Remove all matching lines from .git/config. Remove all matching lines from config file.
-l, --list:: -l, --list::
List all variables set in .git/config. List all variables set in config file.
ENVIRONMENT ENVIRONMENT
@ -79,6 +83,7 @@ ENVIRONMENT
GIT_CONFIG:: GIT_CONFIG::
Take the configuration from the given file instead of .git/config. Take the configuration from the given file instead of .git/config.
Using the "--global" option forces this to ~/.gitconfig.
GIT_CONFIG_LOCAL:: GIT_CONFIG_LOCAL::
Currently the same as $GIT_CONFIG; when Git will support global Currently the same as $GIT_CONFIG; when Git will support global

View File

@ -0,0 +1,156 @@
git-show-ref(1)
===============
NAME
----
git-show-ref - List references in a local repository
SYNOPSIS
--------
[verse]
'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
[-s|--hash] [--abbrev] [--tags] [--heads] [--] <pattern>...
DESCRIPTION
-----------
Displays references available in a local repository along with the associated
commit IDs. Results can be filtered using a pattern and tags can be
dereferenced into object IDs. Additionally, it can be used to test whether a
particular ref exists.
Use of this utility is encouraged in favor of directly accessing files under
in the `.git` directory.
OPTIONS
-------
-h, --head::
Show the HEAD reference.
--tags, --heads::
Limit to only "refs/heads" and "refs/tags", respectively. These
options are not mutually exclusive; when given both, references stored
in "refs/heads" and "refs/tags" are displayed.
-d, --dereference::
Dereference tags into object IDs as well. They will be shown with "^{}"
appended.
-s, --hash::
Only show the SHA1 hash, not the reference name. When also using
--dereference the dereferenced tag will still be shown after the SHA1.
--verify::
Enable stricter reference checking by requiring an exact ref path.
Aside from returning an error code of 1, it will also print an error
message if '--quiet' was not specified.
--abbrev, --abbrev=len::
Abbreviate the object name. When using `--hash`, you do
not have to say `--hash --abbrev`; `--hash=len` would do.
-q, --quiet::
Do not print any results to stdout. When combined with '--verify' this
can be used to silently check if a reference exists.
<pattern>::
Show references matching one or more patterns.
OUTPUT
------
The output is in the format: '<SHA-1 ID>' '<space>' '<reference name>'.
-----------------------------------------------------------------------------
$ git show-ref --head --dereference
832e76a9899f560a90ffd62ae2ce83bbeff58f54 HEAD
832e76a9899f560a90ffd62ae2ce83bbeff58f54 refs/heads/master
832e76a9899f560a90ffd62ae2ce83bbeff58f54 refs/heads/origin
3521017556c5de4159da4615a39fa4d5d2c279b5 refs/tags/v0.99.9c
6ddc0964034342519a87fe013781abf31c6db6ad refs/tags/v0.99.9c^{}
055e4ae3ae6eb344cbabf2a5256a49ea66040131 refs/tags/v1.0rc4
423325a2d24638ddcc82ce47be5e40be550f4507 refs/tags/v1.0rc4^{}
...
-----------------------------------------------------------------------------
When using --hash (and not --dereference) the output format is: '<SHA-1 ID>'
-----------------------------------------------------------------------------
$ git show-ref --heads --hash
2e3ba0114a1f52b47df29743d6915d056be13278
185008ae97960c8d551adcd9e23565194651b5d1
03adf42c988195b50e1a1935ba5fcbc39b2b029b
...
-----------------------------------------------------------------------------
EXAMPLE
-------
To show all references called "master", whether tags or heads or anything
else, and regardless of how deep in the reference naming hierarchy they are,
use:
-----------------------------------------------------------------------------
git show-ref master
-----------------------------------------------------------------------------
This will show "refs/heads/master" but also "refs/remote/other-repo/master",
if such references exists.
When using the '--verify' flag, the command requires an exact path:
-----------------------------------------------------------------------------
git show-ref --verify refs/heads/master
-----------------------------------------------------------------------------
will only match the exact branch called "master".
If nothing matches, gitlink:git-show-ref[1] will return an error code of 1,
and in the case of verification, it will show an error message.
For scripting, you can ask it to be quiet with the "--quiet" flag, which
allows you to do things like
-----------------------------------------------------------------------------
git-show-ref --quiet --verify -- "refs/heads/$headname" ||
echo "$headname is not a valid branch"
-----------------------------------------------------------------------------
to check whether a particular branch exists or not (notice how we don't
actually want to show any results, and we want to use the full refname for it
in order to not trigger the problem with ambiguous partial matches).
To show only tags, or only proper branch heads, use "--tags" and/or "--heads"
respectively (using both means that it shows tags and heads, but not other
random references under the refs/ subdirectory).
To do automatic tag object dereferencing, use the "-d" or "--dereference"
flag, so you can do
-----------------------------------------------------------------------------
git show-ref --tags --dereference
-----------------------------------------------------------------------------
to get a listing of all tags together with what they dereference.
SEE ALSO
--------
gitlink:git-ls-remote[1], gitlink:git-peek-remote[1]
AUTHORS
-------
Written by Linus Torvalds <torvalds@osdl.org>.
Man page by Jonas Fonseca <fonseca@diku.dk>.
GIT
---
Part of the gitlink:git[7] suite

View File

@ -7,7 +7,7 @@ git-update-ref - update the object name stored in a ref safely
SYNOPSIS SYNOPSIS
-------- --------
'git-update-ref' [-m <reason>] <ref> <newvalue> [<oldvalue>] 'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | <ref> <newvalue> [<oldvalue>])
DESCRIPTION DESCRIPTION
----------- -----------
@ -20,7 +20,9 @@ possibly dereferencing the symbolic refs, after verifying that
the current value of the <ref> matches <oldvalue>. the current value of the <ref> matches <oldvalue>.
E.g. `git-update-ref refs/heads/master <newvalue> <oldvalue>` E.g. `git-update-ref refs/heads/master <newvalue> <oldvalue>`
updates the master branch head to <newvalue> only if its current updates the master branch head to <newvalue> only if its current
value is <oldvalue>. value is <oldvalue>. You can specify 40 "0" or an empty string
as <oldvalue> to make sure that the ref you are creating does
not exist.
It also allows a "ref" file to be a symbolic pointer to another It also allows a "ref" file to be a symbolic pointer to another
ref file by starting with the four-byte header sequence of ref file by starting with the four-byte header sequence of
@ -49,6 +51,10 @@ for reading but not for writing (so we'll never write through a
ref symlink to some other tree, if you have copied a whole ref symlink to some other tree, if you have copied a whole
archive by creating a symlink tree). archive by creating a symlink tree).
With `-d` flag, it deletes the named <ref> after verifying it
still contains <oldvalue>.
Logging Updates Logging Updates
--------------- ---------------
If config parameter "core.logAllRefUpdates" is true or the file If config parameter "core.logAllRefUpdates" is true or the file

View File

@ -158,8 +158,8 @@ BASIC_CFLAGS =
BASIC_LDFLAGS = BASIC_LDFLAGS =
SCRIPT_SH = \ SCRIPT_SH = \
git-bisect.sh git-branch.sh git-checkout.sh \ git-bisect.sh git-checkout.sh \
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ git-clean.sh git-clone.sh git-commit.sh \
git-fetch.sh \ git-fetch.sh \
git-ls-remote.sh \ git-ls-remote.sh \
git-merge-one-file.sh git-parse-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \
@ -212,7 +212,7 @@ PROGRAMS = \
EXTRA_PROGRAMS = EXTRA_PROGRAMS =
BUILT_INS = \ BUILT_INS = \
git-format-patch$X git-show$X git-whatchanged$X \ git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
git-get-tar-commit-id$X \ git-get-tar-commit-id$X \
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS)) $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
@ -270,6 +270,7 @@ BUILTIN_OBJS = \
builtin-annotate.o \ builtin-annotate.o \
builtin-apply.o \ builtin-apply.o \
builtin-archive.o \ builtin-archive.o \
builtin-branch.o \
builtin-cat-file.o \ builtin-cat-file.o \
builtin-checkout-index.o \ builtin-checkout-index.o \
builtin-check-ref-format.o \ builtin-check-ref-format.o \
@ -310,7 +311,9 @@ BUILTIN_OBJS = \
builtin-update-ref.o \ builtin-update-ref.o \
builtin-upload-archive.o \ builtin-upload-archive.o \
builtin-verify-pack.o \ builtin-verify-pack.o \
builtin-write-tree.o builtin-write-tree.o \
builtin-show-ref.o \
builtin-pack-refs.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB) GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
EXTLIBS = -lz EXTLIBS = -lz

View File

@ -19,7 +19,9 @@
#include "xdiff-interface.h" #include "xdiff-interface.h"
#include "quote.h" #include "quote.h"
#ifndef DEBUG
#define DEBUG 0 #define DEBUG 0
#endif
static const char blame_usage[] = static const char blame_usage[] =
"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-S <revs-file>] [--] file [commit]\n" "git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-S <revs-file>] [--] file [commit]\n"
@ -232,6 +234,9 @@ static void print_map(struct commit *cmit, struct commit *other)
util2->num_lines ? util->num_lines : util2->num_lines; util2->num_lines ? util->num_lines : util2->num_lines;
int num; int num;
if (print_map == NULL)
; /* to avoid "unused function" warning */
for (i = 0; i < max; i++) { for (i = 0; i < max; i++) {
printf("i: %d ", i); printf("i: %d ", i);
num = -1; num = -1;

221
builtin-branch.c Normal file
View File

@ -0,0 +1,221 @@
/*
* Builtin "git branch"
*
* Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
* Based on git-branch.sh by Junio C Hamano.
*/
#include "cache.h"
#include "refs.h"
#include "commit.h"
#include "builtin.h"
static const char builtin_branch_usage[] =
"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r]";
static const char *head;
static unsigned char head_sha1[20];
static int in_merge_bases(const unsigned char *sha1,
struct commit *rev1,
struct commit *rev2)
{
struct commit_list *bases, *b;
int ret = 0;
bases = get_merge_bases(rev1, rev2, 1);
for (b = bases; b; b = b->next) {
if (!hashcmp(sha1, b->item->object.sha1)) {
ret = 1;
break;
}
}
free_commit_list(bases);
return ret;
}
static void delete_branches(int argc, const char **argv, int force)
{
struct commit *rev, *head_rev;
unsigned char sha1[20];
char *name;
int i;
head_rev = lookup_commit_reference(head_sha1);
for (i = 0; i < argc; i++) {
if (!strcmp(head, argv[i]))
die("Cannot delete the branch you are currently on.");
name = xstrdup(mkpath("refs/heads/%s", argv[i]));
if (!resolve_ref(name, sha1, 1, NULL))
die("Branch '%s' not found.", argv[i]);
rev = lookup_commit_reference(sha1);
if (!rev || !head_rev)
die("Couldn't look up commit objects.");
/* This checks whether the merge bases of branch and
* HEAD contains branch -- which means that the HEAD
* contains everything in both.
*/
if (!force &&
!in_merge_bases(sha1, rev, head_rev)) {
fprintf(stderr,
"The branch '%s' is not a strict subset of your current HEAD.\n"
"If you are sure you want to delete it, run 'git branch -D %s'.\n",
argv[i], argv[i]);
exit(1);
}
if (delete_ref(name, sha1))
printf("Error deleting branch '%s'\n", argv[i]);
else
printf("Deleted branch %s.\n", argv[i]);
free(name);
}
}
static int ref_index, ref_alloc;
static char **ref_list;
static int append_ref(const char *refname, const unsigned char *sha1, int flags,
void *cb_data)
{
if (ref_index >= ref_alloc) {
ref_alloc = alloc_nr(ref_alloc);
ref_list = xrealloc(ref_list, ref_alloc * sizeof(char *));
}
ref_list[ref_index++] = xstrdup(refname);
return 0;
}
static int ref_cmp(const void *r1, const void *r2)
{
return strcmp(*(char **)r1, *(char **)r2);
}
static void print_ref_list(int remote_only)
{
int i;
char c;
if (remote_only)
for_each_remote_ref(append_ref, NULL);
else
for_each_branch_ref(append_ref, NULL);
qsort(ref_list, ref_index, sizeof(char *), ref_cmp);
for (i = 0; i < ref_index; i++) {
c = ' ';
if (!strcmp(ref_list[i], head))
c = '*';
printf("%c %s\n", c, ref_list[i]);
}
}
static void create_branch(const char *name, const char *start,
int force, int reflog)
{
struct ref_lock *lock;
struct commit *commit;
unsigned char sha1[20];
char ref[PATH_MAX], msg[PATH_MAX + 20];
snprintf(ref, sizeof ref, "refs/heads/%s", name);
if (check_ref_format(ref))
die("'%s' is not a valid branch name.", name);
if (resolve_ref(ref, sha1, 1, NULL)) {
if (!force)
die("A branch named '%s' already exists.", name);
else if (!strcmp(head, name))
die("Cannot force update the current branch.");
}
if (get_sha1(start, sha1) ||
(commit = lookup_commit_reference(sha1)) == NULL)
die("Not a valid branch point: '%s'.", start);
hashcpy(sha1, commit->object.sha1);
lock = lock_any_ref_for_update(ref, NULL);
if (!lock)
die("Failed to lock ref for update: %s.", strerror(errno));
if (reflog) {
log_all_ref_updates = 1;
snprintf(msg, sizeof msg, "branch: Created from %s", start);
}
if (write_ref_sha1(lock, sha1, msg) < 0)
die("Failed to write ref: %s.", strerror(errno));
}
int cmd_branch(int argc, const char **argv, const char *prefix)
{
int delete = 0, force_delete = 0, force_create = 0, remote_only = 0;
int reflog = 0;
int i;
git_config(git_default_config);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "--")) {
i++;
break;
}
if (!strcmp(arg, "-d")) {
delete = 1;
continue;
}
if (!strcmp(arg, "-D")) {
delete = 1;
force_delete = 1;
continue;
}
if (!strcmp(arg, "-f")) {
force_create = 1;
continue;
}
if (!strcmp(arg, "-r")) {
remote_only = 1;
continue;
}
if (!strcmp(arg, "-l")) {
reflog = 1;
continue;
}
usage(builtin_branch_usage);
}
head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
if (!head)
die("Failed to resolve HEAD as a valid ref.");
if (strncmp(head, "refs/heads/", 11))
die("HEAD not found below refs/heads!");
head += 11;
if (delete)
delete_branches(argc - i, argv + i, force_delete);
else if (i == argc)
print_ref_list(remote_only);
else if (i == argc - 1)
create_branch(argv[i], head, force_create, reflog);
else if (i == argc - 2)
create_branch(argv[i], argv[i + 1], force_create, reflog);
else
usage(builtin_branch_usage);
return 0;
}

View File

@ -249,7 +249,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
FILE *in = stdin; FILE *in = stdin;
const char *sep = ""; const char *sep = "";
unsigned char head_sha1[20]; unsigned char head_sha1[20];
const char *head, *current_branch; const char *current_branch;
git_config(fmt_merge_msg_config); git_config(fmt_merge_msg_config);
@ -277,10 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
usage(fmt_merge_msg_usage); usage(fmt_merge_msg_usage);
/* get current branch */ /* get current branch */
head = xstrdup(git_path("HEAD")); current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
current_branch = resolve_ref(head, head_sha1, 1);
current_branch += strlen(head) - 4;
free((char *)head);
if (!strncmp(current_branch, "refs/heads/", 11)) if (!strncmp(current_branch, "refs/heads/", 11))
current_branch += 11; current_branch += 11;

View File

@ -59,6 +59,8 @@ static struct {
{ "taggername" }, { "taggername" },
{ "taggeremail" }, { "taggeremail" },
{ "taggerdate", FIELD_TIME }, { "taggerdate", FIELD_TIME },
{ "creator" },
{ "creatordate", FIELD_TIME },
{ "subject" }, { "subject" },
{ "body" }, { "body" },
{ "contents" }, { "contents" },
@ -401,6 +403,29 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
else if (!strcmp(name + wholen, "date")) else if (!strcmp(name + wholen, "date"))
grab_date(wholine, v); grab_date(wholine, v);
} }
/* For a tag or a commit object, if "creator" or "creatordate" is
* requested, do something special.
*/
if (strcmp(who, "tagger") && strcmp(who, "committer"))
return; /* "author" for commit object is not wanted */
if (!wholine)
wholine = find_wholine(who, wholen, buf, sz);
if (!wholine)
return;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i];
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
if (deref)
name++;
if (!strcmp(name, "creatordate"))
grab_date(wholine, v);
else if (!strcmp(name, "creator"))
v->s = copy_line(wholine);
}
} }
static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body) static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body)
@ -585,24 +610,27 @@ static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
*v = &ref->value[atom]; *v = &ref->value[atom];
} }
static struct refinfo **grab_array; struct grab_ref_cbdata {
static const char **grab_pattern; struct refinfo **grab_array;
static int *grab_cnt; const char **grab_pattern;
int grab_cnt;
};
/* /*
* A call-back given to for_each_ref(). It is unfortunate that we * A call-back given to for_each_ref(). It is unfortunate that we
* need to use global variables to pass extra information to this * need to use global variables to pass extra information to this
* function. * function.
*/ */
static int grab_single_ref(const char *refname, const unsigned char *sha1) static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct grab_ref_cbdata *cb = cb_data;
struct refinfo *ref; struct refinfo *ref;
int cnt; int cnt;
if (*grab_pattern) { if (*cb->grab_pattern) {
const char **pattern; const char **pattern;
int namelen = strlen(refname); int namelen = strlen(refname);
for (pattern = grab_pattern; *pattern; pattern++) { for (pattern = cb->grab_pattern; *pattern; pattern++) {
const char *p = *pattern; const char *p = *pattern;
int plen = strlen(p); int plen = strlen(p);
@ -626,25 +654,14 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1)
ref->refname = xstrdup(refname); ref->refname = xstrdup(refname);
hashcpy(ref->objectname, sha1); hashcpy(ref->objectname, sha1);
cnt = *grab_cnt; cnt = cb->grab_cnt;
grab_array = xrealloc(grab_array, sizeof(*grab_array) * (cnt + 1)); cb->grab_array = xrealloc(cb->grab_array,
grab_array[cnt++] = ref; sizeof(*cb->grab_array) * (cnt + 1));
*grab_cnt = cnt; cb->grab_array[cnt++] = ref;
cb->grab_cnt = cnt;
return 0; return 0;
} }
static struct refinfo **grab_refs(const char **pattern, int *cnt)
{
/* Sheesh, we really should make for-each-ref to take
* callback data.
*/
*cnt = 0;
grab_pattern = pattern;
grab_cnt = cnt;
for_each_ref(grab_single_ref);
return grab_array;
}
static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b) static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
{ {
struct atom_value *va, *vb; struct atom_value *va, *vb;
@ -784,6 +801,7 @@ int cmd_for_each_ref(int ac, const char **av, char *prefix)
int maxcount = 0; int maxcount = 0;
int quote_style = -1; /* unspecified yet */ int quote_style = -1; /* unspecified yet */
struct refinfo **refs; struct refinfo **refs;
struct grab_ref_cbdata cbdata;
for (i = 1; i < ac; i++) { for (i = 1; i < ac; i++) {
const char *arg = av[i]; const char *arg = av[i];
@ -855,7 +873,11 @@ int cmd_for_each_ref(int ac, const char **av, char *prefix)
verify_format(format); verify_format(format);
refs = grab_refs(av + i, &num_refs); memset(&cbdata, 0, sizeof(cbdata));
cbdata.grab_pattern = av + i;
for_each_ref(grab_single_ref, &cbdata);
refs = cbdata.grab_array;
num_refs = cbdata.grab_cnt;
for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
if (used_atom[i][0] == '*') { if (used_atom[i][0] == '*') {

View File

@ -218,8 +218,8 @@ static void create_default_files(const char *git_dir, const char *template_path)
* branch, if it does not exist yet. * branch, if it does not exist yet.
*/ */
strcpy(path + len, "HEAD"); strcpy(path + len, "HEAD");
if (read_ref(path, sha1) < 0) { if (read_ref("HEAD", sha1) < 0) {
if (create_symref(path, "refs/heads/master") < 0) if (create_symref("HEAD", "refs/heads/master") < 0)
exit(1); exit(1);
} }

View File

@ -171,8 +171,11 @@ static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
static int get_patch_id(struct commit *commit, struct diff_options *options, static int get_patch_id(struct commit *commit, struct diff_options *options,
unsigned char *sha1) unsigned char *sha1)
{ {
diff_tree_sha1(commit->parents->item->object.sha1, commit->object.sha1, if (commit->parents)
"", options); diff_tree_sha1(commit->parents->item->object.sha1,
commit->object.sha1, "", options);
else
diff_root_tree_sha1(commit->object.sha1, "", options);
diffcore_std(options); diffcore_std(options);
return diff_flush_patch_id(options, sha1); return diff_flush_patch_id(options, sha1);
} }
@ -437,3 +440,109 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
return 0; return 0;
} }
static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
{
unsigned char sha1[20];
if (get_sha1(arg, sha1) == 0) {
struct commit *commit = lookup_commit_reference(sha1);
if (commit) {
commit->object.flags |= flags;
add_pending_object(revs, &commit->object, arg);
return 0;
}
}
return -1;
}
static const char cherry_usage[] =
"git-cherry [-v] <upstream> [<head>] [<limit>]";
int cmd_cherry(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
struct diff_options patch_id_opts;
struct commit *commit;
struct commit_list *list = NULL;
const char *upstream;
const char *head = "HEAD";
const char *limit = NULL;
int verbose = 0;
if (argc > 1 && !strcmp(argv[1], "-v")) {
verbose = 1;
argc--;
argv++;
}
switch (argc) {
case 4:
limit = argv[3];
/* FALLTHROUGH */
case 3:
head = argv[2];
/* FALLTHROUGH */
case 2:
upstream = argv[1];
break;
default:
usage(cherry_usage);
}
init_revisions(&revs, prefix);
revs.diff = 1;
revs.combine_merges = 0;
revs.ignore_merges = 1;
revs.diffopt.recursive = 1;
if (add_pending_commit(head, &revs, 0))
die("Unknown commit %s", head);
if (add_pending_commit(upstream, &revs, UNINTERESTING))
die("Unknown commit %s", upstream);
/* Don't say anything if head and upstream are the same. */
if (revs.pending.nr == 2) {
struct object_array_entry *o = revs.pending.objects;
if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
return 0;
}
get_patch_ids(&revs, &patch_id_opts, prefix);
if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
die("Unknown commit %s", limit);
/* reverse the list of commits */
prepare_revision_walk(&revs);
while ((commit = get_revision(&revs)) != NULL) {
/* ignore merges */
if (commit->parents && commit->parents->next)
continue;
commit_list_insert(commit, &list);
}
while (list) {
unsigned char sha1[20];
char sign = '+';
commit = list->item;
if (!get_patch_id(commit, &patch_id_opts, sha1) &&
lookup_object(sha1))
sign = '-';
if (verbose) {
static char buf[16384];
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
buf, sizeof(buf), 0, NULL, NULL, 0);
printf("%c %s %s\n", sign,
sha1_to_hex(commit->object.sha1), buf);
}
else {
printf("%c %s\n", sign,
sha1_to_hex(commit->object.sha1));
}
list = list->next;
}
return 0;
}

View File

@ -75,11 +75,10 @@ copy_data:
} }
} }
static int tags_only; static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
static int name_ref(const char *path, const unsigned char *sha1)
{ {
struct object *o = parse_object(sha1); struct object *o = parse_object(sha1);
int tags_only = *(int*)cb_data;
int deref = 0; int deref = 0;
if (tags_only && strncmp(path, "refs/tags/", 10)) if (tags_only && strncmp(path, "refs/tags/", 10))
@ -131,6 +130,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
{ {
struct object_array revs = { 0, 0, NULL }; struct object_array revs = { 0, 0, NULL };
int as_is = 0, all = 0, transform_stdin = 0; int as_is = 0, all = 0, transform_stdin = 0;
int tags_only = 0;
git_config(git_default_config); git_config(git_default_config);
@ -186,7 +186,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
add_object_array((struct object *)commit, *argv, &revs); add_object_array((struct object *)commit, *argv, &revs);
} }
for_each_ref(name_ref); for_each_ref(name_ref, &tags_only);
if (transform_stdin) { if (transform_stdin) {
char buffer[2048]; char buffer[2048];

107
builtin-pack-refs.c Normal file
View File

@ -0,0 +1,107 @@
#include "cache.h"
#include "refs.h"
static const char builtin_pack_refs_usage[] =
"git-pack-refs [--all] [--prune]";
struct ref_to_prune {
struct ref_to_prune *next;
unsigned char sha1[20];
char name[FLEX_ARRAY];
};
struct pack_refs_cb_data {
int prune;
int all;
struct ref_to_prune *ref_to_prune;
FILE *refs_file;
};
static int do_not_prune(int flags)
{
/* If it is already packed or if it is a symref,
* do not prune it.
*/
return (flags & (REF_ISSYMREF|REF_ISPACKED));
}
static int handle_one_ref(const char *path, const unsigned char *sha1,
int flags, void *cb_data)
{
struct pack_refs_cb_data *cb = cb_data;
if (!cb->all && strncmp(path, "refs/tags/", 10))
return 0;
/* Do not pack the symbolic refs */
if (!(flags & REF_ISSYMREF))
fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
if (cb->prune && !do_not_prune(flags)) {
int namelen = strlen(path) + 1;
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
hashcpy(n->sha1, sha1);
strcpy(n->name, path);
n->next = cb->ref_to_prune;
cb->ref_to_prune = n;
}
return 0;
}
/* make sure nobody touched the ref, and unlink */
static void prune_ref(struct ref_to_prune *r)
{
struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
if (lock) {
unlink(git_path("%s", r->name));
unlock_ref(lock);
}
}
static void prune_refs(struct ref_to_prune *r)
{
while (r) {
prune_ref(r);
r = r->next;
}
}
static struct lock_file packed;
int cmd_pack_refs(int argc, const char **argv, const char *prefix)
{
int fd, i;
struct pack_refs_cb_data cbdata;
memset(&cbdata, 0, sizeof(cbdata));
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--prune")) {
cbdata.prune = 1;
continue;
}
if (!strcmp(arg, "--all")) {
cbdata.all = 1;
continue;
}
/* perhaps other parameters later... */
break;
}
if (i != argc)
usage(builtin_pack_refs_usage);
fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1);
cbdata.refs_file = fdopen(fd, "w");
if (!cbdata.refs_file)
die("unable to create ref-pack file structure (%s)",
strerror(errno));
for_each_ref(handle_one_ref, &cbdata);
fflush(cbdata.refs_file);
fsync(fd);
fclose(cbdata.refs_file);
if (commit_lock_file(&packed) < 0)
die("unable to overwrite old ref-pack file (%s)", strerror(errno));
if (cbdata.prune)
prune_refs(cbdata.ref_to_prune);
return 0;
}

View File

@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_info *revs)
} }
} }
static int add_one_ref(const char *path, const unsigned char *sha1) static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct object *object = parse_object(sha1); struct object *object = parse_object(sha1);
if (!object) if (!object)
@ -240,7 +240,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
revs.tree_objects = 1; revs.tree_objects = 1;
/* Add all external refs */ /* Add all external refs */
for_each_ref(add_one_ref); for_each_ref(add_one_ref, NULL);
/* Add all refs from the index file */ /* Add all refs from the index file */
add_cache_refs(); add_cache_refs();

View File

@ -27,7 +27,7 @@ static void add_refspec(const char *ref)
refspec_nr = nr; refspec_nr = nr;
} }
static int expand_one_ref(const char *ref, const unsigned char *sha1) static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
{ {
/* Ignore the "refs/" at the beginning of the refname */ /* Ignore the "refs/" at the beginning of the refname */
ref += 5; ref += 5;
@ -51,7 +51,7 @@ static void expand_refspecs(void)
} }
if (!tags) if (!tags)
return; return;
for_each_ref(expand_one_ref); for_each_ref(expand_one_ref, NULL);
} }
static void set_refspecs(const char **refs, int nr) static void set_refspecs(const char **refs, int nr)

View File

@ -3,7 +3,7 @@
#include <regex.h> #include <regex.h>
static const char git_config_set_usage[] = static const char git_config_set_usage[] =
"git-repo-config [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list"; "git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
static char *key; static char *key;
static regex_t *key_regexp; static regex_t *key_regexp;
@ -139,7 +139,16 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
type = T_BOOL; type = T_BOOL;
else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
return git_config(show_all_config); return git_config(show_all_config);
else else if (!strcmp(argv[1], "--global")) {
char *home = getenv("HOME");
if (home) {
char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
setenv("GIT_CONFIG", user_config, 1);
free(user_config);
} else {
die("$HOME not set");
}
} else
break; break;
argc--; argc--;
argv++; argv++;

View File

@ -137,7 +137,7 @@ static void show_default(void)
} }
} }
static int show_reference(const char *refname, const unsigned char *sha1) static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
show_rev(NORMAL, sha1, refname); show_rev(NORMAL, sha1, refname);
return 0; return 0;
@ -299,19 +299,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue; continue;
} }
if (!strcmp(arg, "--all")) { if (!strcmp(arg, "--all")) {
for_each_ref(show_reference); for_each_ref(show_reference, NULL);
continue; continue;
} }
if (!strcmp(arg, "--branches")) { if (!strcmp(arg, "--branches")) {
for_each_branch_ref(show_reference); for_each_branch_ref(show_reference, NULL);
continue; continue;
} }
if (!strcmp(arg, "--tags")) { if (!strcmp(arg, "--tags")) {
for_each_tag_ref(show_reference); for_each_tag_ref(show_reference, NULL);
continue; continue;
} }
if (!strcmp(arg, "--remotes")) { if (!strcmp(arg, "--remotes")) {
for_each_remote_ref(show_reference); for_each_remote_ref(show_reference, NULL);
continue; continue;
} }
if (!strcmp(arg, "--show-prefix")) { if (!strcmp(arg, "--show-prefix")) {

View File

@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, int top)
compare_ref_name); compare_ref_name);
} }
static int append_ref(const char *refname, const unsigned char *sha1) static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct commit *commit = lookup_commit_reference_gently(sha1, 1); struct commit *commit = lookup_commit_reference_gently(sha1, 1);
int i; int i;
@ -369,7 +369,7 @@ static int append_ref(const char *refname, const unsigned char *sha1)
return 0; return 0;
} }
static int append_head_ref(const char *refname, const unsigned char *sha1) static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
unsigned char tmp[20]; unsigned char tmp[20];
int ofs = 11; int ofs = 11;
@ -380,14 +380,14 @@ static int append_head_ref(const char *refname, const unsigned char *sha1)
*/ */
if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1)) if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
ofs = 5; ofs = 5;
return append_ref(refname + ofs, sha1); return append_ref(refname + ofs, sha1, flag, cb_data);
} }
static int append_tag_ref(const char *refname, const unsigned char *sha1) static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
if (strncmp(refname, "refs/tags/", 10)) if (strncmp(refname, "refs/tags/", 10))
return 0; return 0;
return append_ref(refname + 5, sha1); return append_ref(refname + 5, sha1, flag, cb_data);
} }
static const char *match_ref_pattern = NULL; static const char *match_ref_pattern = NULL;
@ -401,7 +401,7 @@ static int count_slash(const char *s)
return cnt; return cnt;
} }
static int append_matching_ref(const char *refname, const unsigned char *sha1) static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
/* we want to allow pattern hold/<asterisk> to show all /* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show * branches under refs/heads/hold/, and v0.99.9? to show
@ -417,41 +417,39 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1)
if (fnmatch(match_ref_pattern, tail, 0)) if (fnmatch(match_ref_pattern, tail, 0))
return 0; return 0;
if (!strncmp("refs/heads/", refname, 11)) if (!strncmp("refs/heads/", refname, 11))
return append_head_ref(refname, sha1); return append_head_ref(refname, sha1, flag, cb_data);
if (!strncmp("refs/tags/", refname, 10)) if (!strncmp("refs/tags/", refname, 10))
return append_tag_ref(refname, sha1); return append_tag_ref(refname, sha1, flag, cb_data);
return append_ref(refname, sha1); return append_ref(refname, sha1, flag, cb_data);
} }
static void snarf_refs(int head, int tag) static void snarf_refs(int head, int tag)
{ {
if (head) { if (head) {
int orig_cnt = ref_name_cnt; int orig_cnt = ref_name_cnt;
for_each_ref(append_head_ref); for_each_ref(append_head_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt); sort_ref_range(orig_cnt, ref_name_cnt);
} }
if (tag) { if (tag) {
int orig_cnt = ref_name_cnt; int orig_cnt = ref_name_cnt;
for_each_ref(append_tag_ref); for_each_ref(append_tag_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt); sort_ref_range(orig_cnt, ref_name_cnt);
} }
} }
static int rev_is_head(char *head_path, int headlen, char *name, static int rev_is_head(char *head, int headlen, char *name,
unsigned char *head_sha1, unsigned char *sha1) unsigned char *head_sha1, unsigned char *sha1)
{ {
int namelen; if ((!head[0]) ||
if ((!head_path[0]) ||
(head_sha1 && sha1 && hashcmp(head_sha1, sha1))) (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
return 0; return 0;
namelen = strlen(name); if (!strncmp(head, "refs/heads/", 11))
if ((headlen < namelen) || head += 11;
memcmp(head_path + headlen - namelen, name, namelen)) if (!strncmp(name, "refs/heads/", 11))
return 0; name += 11;
if (headlen == namelen || else if (!strncmp(name, "heads/", 6))
head_path[headlen - namelen - 1] == '/') name += 6;
return 1; return !strcmp(head, name);
return 0;
} }
static int show_merge_base(struct commit_list *seen, int num_rev) static int show_merge_base(struct commit_list *seen, int num_rev)
@ -495,7 +493,7 @@ static void append_one_rev(const char *av)
{ {
unsigned char revkey[20]; unsigned char revkey[20];
if (!get_sha1(av, revkey)) { if (!get_sha1(av, revkey)) {
append_ref(av, revkey); append_ref(av, revkey, 0, NULL);
return; return;
} }
if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
@ -503,7 +501,7 @@ static void append_one_rev(const char *av)
int saved_matches = ref_name_cnt; int saved_matches = ref_name_cnt;
match_ref_pattern = av; match_ref_pattern = av;
match_ref_slash = count_slash(av); match_ref_slash = count_slash(av);
for_each_ref(append_matching_ref); for_each_ref(append_matching_ref, NULL);
if (saved_matches == ref_name_cnt && if (saved_matches == ref_name_cnt &&
ref_name_cnt < MAX_REVS) ref_name_cnt < MAX_REVS)
error("no matching refs with %s", av); error("no matching refs with %s", av);
@ -559,9 +557,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
int all_heads = 0, all_tags = 0; int all_heads = 0, all_tags = 0;
int all_mask, all_revs; int all_mask, all_revs;
int lifo = 1; int lifo = 1;
char head_path[128]; char head[128];
const char *head_path_p; const char *head_p;
int head_path_len; int head_len;
unsigned char head_sha1[20]; unsigned char head_sha1[20];
int merge_base = 0; int merge_base = 0;
int independent = 0; int independent = 0;
@ -638,31 +636,31 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
ac--; av++; ac--; av++;
} }
head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1); head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
if (head_path_p) { if (head_p) {
head_path_len = strlen(head_path_p); head_len = strlen(head_p);
memcpy(head_path, head_path_p, head_path_len + 1); memcpy(head, head_p, head_len + 1);
} }
else { else {
head_path_len = 0; head_len = 0;
head_path[0] = 0; head[0] = 0;
} }
if (with_current_branch && head_path_p) { if (with_current_branch && head_p) {
int has_head = 0; int has_head = 0;
for (i = 0; !has_head && i < ref_name_cnt; i++) { for (i = 0; !has_head && i < ref_name_cnt; i++) {
/* We are only interested in adding the branch /* We are only interested in adding the branch
* HEAD points at. * HEAD points at.
*/ */
if (rev_is_head(head_path, if (rev_is_head(head,
head_path_len, head_len,
ref_name[i], ref_name[i],
head_sha1, NULL)) head_sha1, NULL))
has_head++; has_head++;
} }
if (!has_head) { if (!has_head) {
int pfxlen = strlen(git_path("refs/heads/")); int pfxlen = strlen("refs/heads/");
append_one_rev(head_path + pfxlen); append_one_rev(head + pfxlen);
} }
} }
@ -713,8 +711,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (1 < num_rev || extra < 0) { if (1 < num_rev || extra < 0) {
for (i = 0; i < num_rev; i++) { for (i = 0; i < num_rev; i++) {
int j; int j;
int is_head = rev_is_head(head_path, int is_head = rev_is_head(head,
head_path_len, head_len,
ref_name[i], ref_name[i],
head_sha1, head_sha1,
rev[i]->object.sha1); rev[i]->object.sha1);

147
builtin-show-ref.c Normal file
View File

@ -0,0 +1,147 @@
#include "cache.h"
#include "refs.h"
#include "object.h"
#include "tag.h"
static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*]";
static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
static const char **pattern;
static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
{
struct object *obj;
const char *hex;
if (tags_only || heads_only) {
int match;
match = heads_only && !strncmp(refname, "refs/heads/", 11);
match |= tags_only && !strncmp(refname, "refs/tags/", 10);
if (!match)
return 0;
}
if (pattern) {
int reflen = strlen(refname);
const char **p = pattern, *m;
while ((m = *p++) != NULL) {
int len = strlen(m);
if (len > reflen)
continue;
if (memcmp(m, refname + reflen - len, len))
continue;
if (len == reflen)
goto match;
/* "--verify" requires an exact match */
if (verify)
continue;
if (refname[reflen - len - 1] == '/')
goto match;
}
return 0;
}
match:
found_match++;
obj = parse_object(sha1);
if (!obj) {
if (quiet)
return 0;
die("git-show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1));
}
if (quiet)
return 0;
hex = find_unique_abbrev(sha1, abbrev);
if (hash_only)
printf("%s\n", hex);
else
printf("%s %s\n", hex, refname);
if (deref_tags && obj->type == OBJ_TAG) {
obj = deref_tag(obj, refname, 0);
hex = find_unique_abbrev(obj->sha1, abbrev);
printf("%s %s^{}\n", hex, refname);
}
return 0;
}
int cmd_show_ref(int argc, const char **argv, const char *prefix)
{
int i;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (*arg != '-') {
pattern = argv + i;
break;
}
if (!strcmp(arg, "--")) {
pattern = argv + i + 1;
if (!*pattern)
pattern = NULL;
break;
}
if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) {
quiet = 1;
continue;
}
if (!strcmp(arg, "-h") || !strcmp(arg, "--head")) {
show_head = 1;
continue;
}
if (!strcmp(arg, "-d") || !strcmp(arg, "--dereference")) {
deref_tags = 1;
continue;
}
if (!strcmp(arg, "-s") || !strcmp(arg, "--hash")) {
hash_only = 1;
continue;
}
if (!strncmp(arg, "--hash=", 7) ||
(!strncmp(arg, "--abbrev", 8) &&
(arg[8] == '=' || arg[8] == '\0'))) {
if (arg[3] != 'h' && !arg[8])
/* --abbrev only */
abbrev = DEFAULT_ABBREV;
else {
/* --hash= or --abbrev= */
char *end;
if (arg[3] == 'h') {
hash_only = 1;
arg += 7;
}
else
arg += 9;
abbrev = strtoul(arg, &end, 10);
if (*end || abbrev > 40)
usage(show_ref_usage);
if (abbrev < MINIMUM_ABBREV)
abbrev = MINIMUM_ABBREV;
}
continue;
}
if (!strcmp(arg, "--verify")) {
verify = 1;
continue;
}
if (!strcmp(arg, "--tags")) {
tags_only = 1;
continue;
}
if (!strcmp(arg, "--heads")) {
heads_only = 1;
continue;
}
usage(show_ref_usage);
}
if (show_head)
head_ref(show_ref, NULL);
for_each_ref(show_ref, NULL);
if (!found_match) {
if (verify && !quiet)
die("No match");
return 1;
}
return 0;
}

View File

@ -1,5 +1,6 @@
#include "builtin.h" #include "builtin.h"
#include "cache.h" #include "cache.h"
#include "refs.h"
static const char git_symbolic_ref_usage[] = static const char git_symbolic_ref_usage[] =
"git-symbolic-ref name [ref]"; "git-symbolic-ref name [ref]";
@ -7,15 +8,14 @@ static const char git_symbolic_ref_usage[] =
static void check_symref(const char *HEAD) static void check_symref(const char *HEAD)
{ {
unsigned char sha1[20]; unsigned char sha1[20];
const char *git_HEAD = xstrdup(git_path("%s", HEAD)); int flag;
const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0); const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
if (git_refs_heads_master) {
/* we want to strip the .git/ part */ if (!refs_heads_master)
int pfxlen = strlen(git_HEAD) - strlen(HEAD);
puts(git_refs_heads_master + pfxlen);
}
else
die("No such ref: %s", HEAD); die("No such ref: %s", HEAD);
else if (!(flag & REF_ISSYMREF))
die("ref %s is not a symbolic ref", HEAD);
puts(refs_heads_master);
} }
int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
@ -26,7 +26,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
check_symref(argv[1]); check_symref(argv[1]);
break; break;
case 3: case 3:
create_symref(xstrdup(git_path("%s", argv[1])), argv[2]); create_symref(argv[1], argv[2]);
break; break;
default: default:
usage(git_symbolic_ref_usage); usage(git_symbolic_ref_usage);

View File

@ -22,7 +22,7 @@ static SHA_CTX ctx;
* Make sure at least "min" bytes are available in the buffer, and * Make sure at least "min" bytes are available in the buffer, and
* return the pointer to the buffer. * return the pointer to the buffer.
*/ */
static void * fill(int min) static void *fill(int min)
{ {
if (min <= len) if (min <= len)
return buffer + offset; return buffer + offset;
@ -30,7 +30,7 @@ static void * fill(int min)
die("cannot fill %d bytes", min); die("cannot fill %d bytes", min);
if (offset) { if (offset) {
SHA1_Update(&ctx, buffer, offset); SHA1_Update(&ctx, buffer, offset);
memcpy(buffer, buffer + offset, len); memmove(buffer, buffer + offset, len);
offset = 0; offset = 0;
} }
do { do {

View File

@ -406,9 +406,9 @@ static int unresolve_one(const char *path)
static void read_head_pointers(void) static void read_head_pointers(void)
{ {
if (read_ref(git_path("HEAD"), head_sha1)) if (read_ref("HEAD", head_sha1))
die("No HEAD -- no initial commit yet?\n"); die("No HEAD -- no initial commit yet?\n");
if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) { if (read_ref("MERGE_HEAD", merge_head_sha1)) {
fprintf(stderr, "Not in the middle of a merge.\n"); fprintf(stderr, "Not in the middle of a merge.\n");
exit(0); exit(0);
} }
@ -445,7 +445,7 @@ static int do_reupdate(int ac, const char **av,
int has_head = 1; int has_head = 1;
const char **pathspec = get_pathspec(prefix, av + 1); const char **pathspec = get_pathspec(prefix, av + 1);
if (read_ref(git_path("HEAD"), head_sha1)) if (read_ref("HEAD", head_sha1))
/* If there is no HEAD, that means it is an initial /* If there is no HEAD, that means it is an initial
* commit. Update everything in the index. * commit. Update everything in the index.
*/ */

View File

@ -3,15 +3,16 @@
#include "builtin.h" #include "builtin.h"
static const char git_update_ref_usage[] = static const char git_update_ref_usage[] =
"git-update-ref <refname> <value> [<oldval>] [-m <reason>]"; "git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])";
int cmd_update_ref(int argc, const char **argv, const char *prefix) int cmd_update_ref(int argc, const char **argv, const char *prefix)
{ {
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
struct ref_lock *lock; struct ref_lock *lock;
unsigned char sha1[20], oldsha1[20]; unsigned char sha1[20], oldsha1[20];
int i; int i, delete;
delete = 0;
setup_ident(); setup_ident();
git_config(git_default_config); git_config(git_default_config);
@ -26,6 +27,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("Refusing to perform update with \\n in message."); die("Refusing to perform update with \\n in message.");
continue; continue;
} }
if (!strcmp("-d", argv[i])) {
delete = 1;
continue;
}
if (!refname) { if (!refname) {
refname = argv[i]; refname = argv[i];
continue; continue;
@ -44,11 +49,18 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (get_sha1(value, sha1)) if (get_sha1(value, sha1))
die("%s: not a valid SHA1", value); die("%s: not a valid SHA1", value);
if (delete) {
if (oldval)
usage(git_update_ref_usage);
return delete_ref(refname, sha1);
}
hashclr(oldsha1); hashclr(oldsha1);
if (oldval && get_sha1(oldval, oldsha1)) if (oldval && *oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval); die("%s: not a valid old SHA1", oldval);
lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0); lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL);
if (!lock) if (!lock)
return 1; return 1;
if (write_ref_sha1(lock, sha1, msg) < 0) if (write_ref_sha1(lock, sha1, msg) < 0)

View File

@ -17,9 +17,11 @@ extern int cmd_add(int argc, const char **argv, const char *prefix);
extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_apply(int argc, const char **argv, const char *prefix); extern int cmd_apply(int argc, const char **argv, const char *prefix);
extern int cmd_archive(int argc, const char **argv, const char *prefix); extern int cmd_archive(int argc, const char **argv, const char *prefix);
extern int cmd_branch(int argc, const char **argv, const char *prefix);
extern int cmd_cat_file(int argc, const char **argv, const char *prefix); extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix); extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
extern int cmd_commit_tree(int argc, const char **argv, const char *prefix); extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
extern int cmd_count_objects(int argc, const char **argv, const char *prefix); extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
extern int cmd_diff_files(int argc, const char **argv, const char *prefix); extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
@ -65,5 +67,7 @@ extern int cmd_version(int argc, const char **argv, const char *prefix);
extern int cmd_whatchanged(int argc, const char **argv, const char *prefix); extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
extern int cmd_write_tree(int argc, const char **argv, const char *prefix); extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
extern int cmd_verify_pack(int argc, const char **argv, const char *prefix); extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
#endif #endif

View File

@ -2,7 +2,9 @@
#include "tree.h" #include "tree.h"
#include "cache-tree.h" #include "cache-tree.h"
#ifndef DEBUG
#define DEBUG 0 #define DEBUG 0
#endif
struct cache_tree *cache_tree(void) struct cache_tree *cache_tree(void)
{ {

View File

@ -179,6 +179,7 @@ struct lock_file {
extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
extern int commit_lock_file(struct lock_file *); extern int commit_lock_file(struct lock_file *);
extern void rollback_lock_file(struct lock_file *); extern void rollback_lock_file(struct lock_file *);
extern int delete_ref(const char *, unsigned char *sha1);
/* Environment bits from configuration mechanism */ /* Environment bits from configuration mechanism */
extern int use_legacy_headers; extern int use_legacy_headers;
@ -188,7 +189,6 @@ extern int prefer_symlink_refs;
extern int log_all_ref_updates; extern int log_all_ref_updates;
extern int warn_ambiguous_refs; extern int warn_ambiguous_refs;
extern int shared_repository; extern int shared_repository;
extern int deny_non_fast_forwards;
extern const char *apply_default_whitespace; extern const char *apply_default_whitespace;
extern int zlib_compression_level; extern int zlib_compression_level;
@ -289,9 +289,9 @@ extern int get_sha1(const char *str, unsigned char *sha1);
extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1); extern int read_ref(const char *filename, unsigned char *sha1);
extern const char *resolve_ref(const char *path, unsigned char *sha1, int); extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
extern int create_symref(const char *git_HEAD, const char *refs_heads_master); extern int create_symref(const char *ref, const char *refs_heads_master);
extern int validate_symref(const char *git_HEAD); extern int validate_symref(const char *ref);
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2); extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2); extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);

View File

@ -53,7 +53,7 @@ static void add_to_known_names(const char *path,
names = ++idx; names = ++idx;
} }
static int get_name(const char *path, const unsigned char *sha1) static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct commit *commit = lookup_commit_reference_gently(sha1, 1); struct commit *commit = lookup_commit_reference_gently(sha1, 1);
struct object *object; struct object *object;
@ -113,7 +113,7 @@ static void describe(const char *arg, int last_one)
if (!initialized) { if (!initialized) {
initialized = 1; initialized = 1;
for_each_ref(get_name); for_each_ref(get_name, NULL);
qsort(name_array, names, sizeof(*name_array), compare_names); qsort(name_array, names, sizeof(*name_array), compare_names);
} }

2
diff.h
View File

@ -102,6 +102,8 @@ extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
const char *base, struct diff_options *opt); const char *base, struct diff_options *opt);
extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new, extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
const char *base, struct diff_options *opt); const char *base, struct diff_options *opt);
extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
struct diff_options *opt);
struct combine_diff_path { struct combine_diff_path {
struct combine_diff_path *next; struct combine_diff_path *next;

View File

@ -20,7 +20,6 @@ int warn_ambiguous_refs = 1;
int repository_format_version; int repository_format_version;
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8"; char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
int shared_repository = PERM_UMASK; int shared_repository = PERM_UMASK;
int deny_non_fast_forwards = 0;
const char *apply_default_whitespace; const char *apply_default_whitespace;
int zlib_compression_level = Z_DEFAULT_COMPRESSION; int zlib_compression_level = Z_DEFAULT_COMPRESSION;
int pager_in_use; int pager_in_use;

View File

@ -42,7 +42,7 @@ static void rev_list_push(struct commit *commit, int mark)
} }
} }
static int rev_list_insert_ref(const char *path, const unsigned char *sha1) static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct object *o = deref_tag(parse_object(sha1), path, 0); struct object *o = deref_tag(parse_object(sha1), path, 0);
@ -143,7 +143,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
unsigned in_vain = 0; unsigned in_vain = 0;
int got_continue = 0; int got_continue = 0;
for_each_ref(rev_list_insert_ref); for_each_ref(rev_list_insert_ref, NULL);
fetching = 0; fetching = 0;
for ( ; refs ; refs = refs->next) { for ( ; refs ; refs = refs->next) {
@ -254,7 +254,7 @@ done:
static struct commit_list *complete; static struct commit_list *complete;
static int mark_complete(const char *path, const unsigned char *sha1) static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct object *o = parse_object(sha1); struct object *o = parse_object(sha1);
@ -366,7 +366,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
} }
} }
for_each_ref(mark_complete); for_each_ref(mark_complete, NULL);
if (cutoff) if (cutoff)
mark_recent_complete_commits(cutoff); mark_recent_complete_commits(cutoff);

View File

@ -201,7 +201,7 @@ static int interpret_target(char *target, unsigned char *sha1)
return -1; return -1;
} }
static int mark_complete(const char *path, const unsigned char *sha1) static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct commit *commit = lookup_commit_reference_gently(sha1, 1); struct commit *commit = lookup_commit_reference_gently(sha1, 1);
if (commit) { if (commit) {
@ -266,7 +266,7 @@ int pull(int targets, char **target, const char **write_ref,
if (!write_ref || !write_ref[i]) if (!write_ref || !write_ref[i])
continue; continue;
lock[i] = lock_ref_sha1(write_ref[i], NULL, 0); lock[i] = lock_ref_sha1(write_ref[i], NULL);
if (!lock[i]) { if (!lock[i]) {
error("Can't lock ref %s", write_ref[i]); error("Can't lock ref %s", write_ref[i]);
goto unlock_and_fail; goto unlock_and_fail;
@ -274,7 +274,7 @@ int pull(int targets, char **target, const char **write_ref,
} }
if (!get_recover) if (!get_recover)
for_each_ref(mark_complete); for_each_ref(mark_complete, NULL);
for (i = 0; i < targets; i++) { for (i = 0; i < targets; i++) {
if (interpret_target(target[i], &sha1[20 * i])) { if (interpret_target(target[i], &sha1[20 * i])) {

View File

@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path)
static int default_refs; static int default_refs;
static int fsck_handle_ref(const char *refname, const unsigned char *sha1) static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct object *obj; struct object *obj;
@ -424,7 +424,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
static void get_default_heads(void) static void get_default_heads(void)
{ {
for_each_ref(fsck_handle_ref); for_each_ref(fsck_handle_ref, NULL);
/* /*
* Not having any default heads isn't really fatal, but * Not having any default heads isn't really fatal, but
@ -458,15 +458,14 @@ static void fsck_object_dir(const char *path)
static int fsck_head_link(void) static int fsck_head_link(void)
{ {
unsigned char sha1[20]; unsigned char sha1[20];
const char *git_HEAD = xstrdup(git_path("HEAD")); int flag;
const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1); const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag);
int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */
if (!git_refs_heads_master) if (!head_points_at || !(flag & REF_ISSYMREF))
return error("HEAD is not a symbolic ref"); return error("HEAD is not a symbolic ref");
if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11)) if (strncmp(head_points_at, "refs/heads/", 11))
return error("HEAD points to something strange (%s)", return error("HEAD points to something strange (%s)",
git_refs_heads_master + pfxlen); head_points_at);
if (is_null_sha1(sha1)) if (is_null_sha1(sha1))
return error("HEAD: not a valid git pointer"); return error("HEAD: not a valid git pointer");
return 0; return 0;

View File

@ -1,140 +0,0 @@
#!/bin/sh
USAGE='[-l] [-f] <branchname> [<start-point>] | (-d | -D) <branchname> | [-r]'
LONG_USAGE='If no arguments, show available branches and mark current branch with a star.
If one argument, create a new branch <branchname> based off of current HEAD.
If two arguments, create a new branch <branchname> based off of <start-point>.'
SUBDIRECTORY_OK='Yes'
. git-sh-setup
headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||')
delete_branch () {
option="$1"
shift
for branch_name
do
case ",$headref," in
",$branch_name,")
die "Cannot delete the branch you are on." ;;
,,)
die "What branch are you on anyway?" ;;
esac
branch=$(cat "$GIT_DIR/refs/heads/$branch_name") &&
branch=$(git-rev-parse --verify "$branch^0") ||
die "Seriously, what branch are you talking about?"
case "$option" in
-D)
;;
*)
mbs=$(git-merge-base -a "$branch" HEAD | tr '\012' ' ')
case " $mbs " in
*' '$branch' '*)
# the merge base of branch and HEAD contains branch --
# which means that the HEAD contains everything in both.
;;
*)
echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD.
If you are sure you want to delete it, run 'git branch -D $branch_name'."
exit 1
;;
esac
;;
esac
rm -f "$GIT_DIR/logs/refs/heads/$branch_name"
rm -f "$GIT_DIR/refs/heads/$branch_name"
echo "Deleted branch $branch_name."
done
exit 0
}
ls_remote_branches () {
git-rev-parse --symbolic --all |
sed -ne 's|^refs/\(remotes/\)|\1|p' |
sort
}
force=
create_log=
while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac
do
case "$1" in
-d | -D)
delete_branch "$@"
exit
;;
-r)
ls_remote_branches
exit
;;
-f)
force="$1"
;;
-l)
create_log="yes"
;;
--)
shift
break
;;
-*)
usage
;;
esac
shift
done
case "$#" in
0)
git-rev-parse --symbolic --branches |
sort |
while read ref
do
if test "$headref" = "$ref"
then
pfx='*'
else
pfx=' '
fi
echo "$pfx $ref"
done
exit 0 ;;
1)
head=HEAD ;;
2)
head="$2^0" ;;
esac
branchname="$1"
rev=$(git-rev-parse --verify "$head") || exit
git-check-ref-format "heads/$branchname" ||
die "we do not like '$branchname' as a branch name."
if [ -d "$GIT_DIR/refs/heads/$branchname" ]
then
for refdir in `cd "$GIT_DIR" && \
find "refs/heads/$branchname" -type d | sort -r`
do
rmdir "$GIT_DIR/$refdir" || \
die "Could not delete '$refdir', there may still be a ref there."
done
fi
if [ -e "$GIT_DIR/refs/heads/$branchname" ]
then
if test '' = "$force"
then
die "$branchname already exists."
elif test "$branchname" = "$headref"
then
die "cannot force-update the current branch."
fi
fi
if test "$create_log" = 'yes'
then
mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$branchname")
touch "$GIT_DIR/logs/refs/heads/$branchname"
fi
git update-ref -m "branch: Created from $head" "refs/heads/$branchname" $rev

View File

@ -22,7 +22,7 @@ while [ "$#" != "0" ]; do
shift shift
[ -z "$newbranch" ] && [ -z "$newbranch" ] &&
die "git checkout: -b needs a branch name" die "git checkout: -b needs a branch name"
[ -e "$GIT_DIR/refs/heads/$newbranch" ] && git-show-ref --verify --quiet -- "refs/heads/$newbranch" &&
die "git checkout: branch $newbranch already exists" die "git checkout: branch $newbranch already exists"
git-check-ref-format "heads/$newbranch" || git-check-ref-format "heads/$newbranch" ||
die "git checkout: we do not like '$newbranch' as a branch name." die "git checkout: we do not like '$newbranch' as a branch name."
@ -51,7 +51,8 @@ while [ "$#" != "0" ]; do
fi fi
new="$rev" new="$rev"
new_name="$arg^0" new_name="$arg^0"
if [ -f "$GIT_DIR/refs/heads/$arg" ]; then if git-show-ref --verify --quiet -- "refs/heads/$arg"
then
branch="$arg" branch="$arg"
fi fi
elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null) elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)

View File

@ -441,7 +441,7 @@ then
elif test "$use_commit" != "" elif test "$use_commit" != ""
then then
git-cat-file commit "$use_commit" | sed -e '1,/^$/d' git-cat-file commit "$use_commit" | sed -e '1,/^$/d'
elif test -f "$GIT_DIR/MERGE_HEAD" && test -f "$GIT_DIR/MERGE_MSG" elif test -f "$GIT_DIR/MERGE_MSG"
then then
cat "$GIT_DIR/MERGE_MSG" cat "$GIT_DIR/MERGE_MSG"
elif test -f "$GIT_DIR/SQUASH_MSG" elif test -f "$GIT_DIR/SQUASH_MSG"
@ -522,15 +522,15 @@ then
PARENTS=$(git-cat-file commit HEAD | PARENTS=$(git-cat-file commit HEAD |
sed -n -e '/^$/q' -e 's/^parent /-p /p') sed -n -e '/^$/q' -e 's/^parent /-p /p')
fi fi
current=$(git-rev-parse --verify HEAD) current="$(git-rev-parse --verify HEAD)"
else else
if [ -z "$(git-ls-files)" ]; then if [ -z "$(git-ls-files)" ]; then
echo >&2 Nothing to commit echo >&2 Nothing to commit
exit 1 exit 1
fi fi
PARENTS="" PARENTS=""
current=
rloga='commit (initial)' rloga='commit (initial)'
current=''
fi fi
if test -z "$no_edit" if test -z "$no_edit"
@ -606,8 +606,8 @@ then
fi && fi &&
commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) && commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) && rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
git-update-ref -m "$rloga: $rlogm" HEAD $commit $current && git-update-ref -m "$rloga: $rlogm" HEAD $commit "$current" &&
rm -f -- "$GIT_DIR/MERGE_HEAD" && rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
if test -f "$NEXT_INDEX" if test -f "$NEXT_INDEX"
then then
mv "$NEXT_INDEX" "$THIS_INDEX" mv "$NEXT_INDEX" "$THIS_INDEX"

View File

@ -147,15 +147,15 @@ update_local_ref () {
[ "$verbose" ] && echo >&2 " $label_: $newshort_" [ "$verbose" ] && echo >&2 " $label_: $newshort_"
return 0 return 0
fi fi
oldshort_=$(git-rev-parse --short "$1" 2>/dev/null) oldshort_=$(git show-ref --hash --abbrev "$1" 2>/dev/null)
mkdir -p "$(dirname "$GIT_DIR/$1")"
case "$1" in case "$1" in
refs/tags/*) refs/tags/*)
# Tags need not be pointing at commits so there # Tags need not be pointing at commits so there
# is no way to guarantee "fast-forward" anyway. # is no way to guarantee "fast-forward" anyway.
if test -f "$GIT_DIR/$1" if test -n "$oldshort_"
then then
if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2" if now_=$(git show-ref --hash "$1") && test "$now_" = "$2"
then then
[ "$verbose" ] && echo >&2 "* $1: same as $3" [ "$verbose" ] && echo >&2 "* $1: same as $3"
[ "$verbose" ] && echo >&2 " $label_: $newshort_" ||: [ "$verbose" ] && echo >&2 " $label_: $newshort_" ||:
@ -427,7 +427,7 @@ case "$no_tags$tags" in
sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' | sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' |
while read sha1 name while read sha1 name
do do
test -f "$GIT_DIR/$name" && continue git-show-ref --verify --quiet -- $name && continue
git-check-ref-format "$name" || { git-check-ref-format "$name" || {
echo >&2 "warning: tag ${name} ignored" echo >&2 "warning: tag ${name} ignored"
continue continue

View File

@ -145,9 +145,18 @@ git-read-tree -m -u --aggressive $base $head $next &&
result=$(git-write-tree 2>/dev/null) || { result=$(git-write-tree 2>/dev/null) || {
echo >&2 "Simple $me fails; trying Automatic $me." echo >&2 "Simple $me fails; trying Automatic $me."
git-merge-index -o git-merge-one-file -a || { git-merge-index -o git-merge-one-file -a || {
mv -f .msg "$GIT_DIR/MERGE_MSG"
{
echo '
Conflicts:
'
git ls-files --unmerged |
sed -e 's/^[^ ]* / /' |
uniq
} >>"$GIT_DIR/MERGE_MSG"
echo >&2 "Automatic $me failed. After resolving the conflicts," echo >&2 "Automatic $me failed. After resolving the conflicts,"
echo >&2 "mark the corrected paths with 'git-update-index <paths>'" echo >&2 "mark the corrected paths with 'git-update-index <paths>'"
echo >&2 "and commit with 'git commit -F .msg'" echo >&2 "and commit the result."
case "$me" in case "$me" in
cherry-pick) cherry-pick)
echo >&2 "You may choose to use the following when making" echo >&2 "You may choose to use the following when making"

View File

@ -31,7 +31,7 @@ $SIG{'PIPE'}="IGNORE";
$ENV{'TZ'}="UTC"; $ENV{'TZ'}="UTC";
our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T, our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
$opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F); $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F,$opt_P);
sub usage() { sub usage() {
print STDERR <<END; print STDERR <<END;
@ -39,17 +39,19 @@ Usage: ${\basename $0} # fetch/update GIT from SVN
[-o branch-for-HEAD] [-h] [-v] [-l max_rev] [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
[-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname] [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
[-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg] [-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
[-m] [-M regex] [-A author_file] [-S] [-F] [SVN_URL] [-m] [-M regex] [-A author_file] [-S] [-F] [-P project_name] [SVN_URL]
END END
exit(1); exit(1);
} }
getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:Suv") or usage(); getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:uv") or usage();
usage if $opt_h; usage if $opt_h;
my $tag_name = $opt_t || "tags"; my $tag_name = $opt_t || "tags";
my $trunk_name = $opt_T || "trunk"; my $trunk_name = $opt_T || "trunk";
my $branch_name = $opt_b || "branches"; my $branch_name = $opt_b || "branches";
my $project_name = $opt_P || "";
$project_name = "/" . $project_name if ($project_name);
@ARGV == 1 or @ARGV == 2 or usage(); @ARGV == 1 or @ARGV == 2 or usage();
@ -427,6 +429,20 @@ sub get_ignore($$$$$) {
} }
} }
sub project_path($$)
{
my ($path, $project) = @_;
$path = "/".$path unless ($path =~ m#^\/#) ;
return $1 if ($path =~ m#^$project\/(.*)$#);
$path =~ s#\.#\\\.#g;
$path =~ s#\+#\\\+#g;
return "/" if ($project =~ m#^$path.*$#);
return undef;
}
sub split_path($$) { sub split_path($$) {
my($rev,$path) = @_; my($rev,$path) = @_;
my $branch; my $branch;
@ -446,7 +462,11 @@ sub split_path($$) {
print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path}); print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path});
return () return ()
} }
$path = "/" if $path eq ""; if ($path eq "") {
$path = "/";
} elsif ($project_name) {
$path = project_path($path, $project_name);
}
return ($branch,$path); return ($branch,$path);
} }
@ -898,6 +918,7 @@ sub commit_all {
while(my($path,$action) = each %$changed_paths) { while(my($path,$action) = each %$changed_paths) {
($branch,$path) = split_path($revision,$path); ($branch,$path) = split_path($revision,$path);
next if not defined $branch; next if not defined $branch;
next if not defined $path;
$done{$branch}{$path} = $action; $done{$branch}{$path} = $action;
} }
while(($branch,$changed_paths) = each %done) { while(($branch,$changed_paths) = each %done) {

View File

@ -47,8 +47,10 @@ do
-d) -d)
shift shift
tag_name="$1" tag_name="$1"
rm "$GIT_DIR/refs/tags/$tag_name" && \ tag=$(git-show-ref --verify --hash -- "refs/tags/$tag_name") ||
echo "Deleted tag $tag_name." die "Seriously, what tag are you talking about?"
git-update-ref -m 'tag: delete' -d "refs/tags/$tag_name" "$tag" &&
echo "Deleted tag $tag_name."
exit $? exit $?
;; ;;
-*) -*)
@ -63,8 +65,11 @@ done
name="$1" name="$1"
[ "$name" ] || usage [ "$name" ] || usage
if [ -e "$GIT_DIR/refs/tags/$name" -a -z "$force" ]; then prev=0000000000000000000000000000000000000000
die "tag '$name' already exists" if git-show-ref --verify --quiet -- "refs/tags/$name"
then
test -n "$force" || die "tag '$name' already exists"
prev=`git rev-parse "refs/tags/$name"`
fi fi
shift shift
git-check-ref-format "tags/$name" || git-check-ref-format "tags/$name" ||
@ -107,6 +112,5 @@ if [ "$annotate" ]; then
object=$(git-mktag < "$GIT_DIR"/TAG_TMP) object=$(git-mktag < "$GIT_DIR"/TAG_TMP)
fi fi
leading=`expr "refs/tags/$name" : '\(.*\)/'` && git update-ref "refs/tags/$name" "$object" "$prev"
mkdir -p "$GIT_DIR/$leading" &&
echo $object > "$GIT_DIR/refs/tags/$name"

4
git.c
View File

@ -222,9 +222,11 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "annotate", cmd_annotate, }, { "annotate", cmd_annotate, },
{ "apply", cmd_apply }, { "apply", cmd_apply },
{ "archive", cmd_archive }, { "archive", cmd_archive },
{ "branch", cmd_branch, RUN_SETUP },
{ "cat-file", cmd_cat_file, RUN_SETUP }, { "cat-file", cmd_cat_file, RUN_SETUP },
{ "checkout-index", cmd_checkout_index, RUN_SETUP }, { "checkout-index", cmd_checkout_index, RUN_SETUP },
{ "check-ref-format", cmd_check_ref_format }, { "check-ref-format", cmd_check_ref_format },
{ "cherry", cmd_cherry, RUN_SETUP },
{ "commit-tree", cmd_commit_tree, RUN_SETUP }, { "commit-tree", cmd_commit_tree, RUN_SETUP },
{ "count-objects", cmd_count_objects, RUN_SETUP }, { "count-objects", cmd_count_objects, RUN_SETUP },
{ "diff", cmd_diff, RUN_SETUP | USE_PAGER }, { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
@ -269,6 +271,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER }, { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
{ "write-tree", cmd_write_tree, RUN_SETUP }, { "write-tree", cmd_write_tree, RUN_SETUP },
{ "verify-pack", cmd_verify_pack }, { "verify-pack", cmd_verify_pack },
{ "show-ref", cmd_show_ref, RUN_SETUP },
{ "pack-refs", cmd_pack_refs, RUN_SETUP },
}; };
int i; int i;

View File

@ -554,12 +554,17 @@ sub esc_url {
} }
# replace invalid utf8 character with SUBSTITUTION sequence # replace invalid utf8 character with SUBSTITUTION sequence
sub esc_html { sub esc_html ($;%) {
my $str = shift; my $str = shift;
my %opts = @_;
$str = to_utf8($str); $str = to_utf8($str);
$str = escapeHTML($str); $str = escapeHTML($str);
$str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file) $str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file)
$str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1) $str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1)
if ($opts{'-nbsp'}) {
$str =~ s/ /&nbsp;/g;
}
return $str; return $str;
} }
@ -784,7 +789,7 @@ sub format_diff_line {
$diff_class = " incomplete"; $diff_class = " incomplete";
} }
$line = untabify($line); $line = untabify($line);
return "<div class=\"diff$diff_class\">" . esc_html($line) . "</div>\n"; return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
} }
## ---------------------------------------------------------------------- ## ----------------------------------------------------------------------
@ -860,7 +865,7 @@ sub git_get_hash_by_path {
close $fd or return undef; close $fd or return undef;
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/;
if (defined $type && $type ne $2) { if (defined $type && $type ne $2) {
# type doesn't match # type doesn't match
return undef; return undef;
@ -1111,7 +1116,9 @@ sub parse_commit {
@commit_lines = @$commit_text; @commit_lines = @$commit_text;
} else { } else {
local $/ = "\0"; local $/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", "--max-count=1", $commit_id open my $fd, "-|", git_cmd(), "rev-list",
"--header", "--parents", "--max-count=1",
$commit_id, "--"
or return; or return;
@commit_lines = split '\n', <$fd>; @commit_lines = split '\n', <$fd>;
close $fd or return; close $fd or return;
@ -1275,7 +1282,7 @@ sub parse_ls_tree_line ($;%) {
my %res; my %res;
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/; $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
$res{'mode'} = $1; $res{'mode'} = $1;
$res{'type'} = $2; $res{'type'} = $2;
@ -1292,47 +1299,88 @@ sub parse_ls_tree_line ($;%) {
## ...................................................................... ## ......................................................................
## parse to array of hashes functions ## parse to array of hashes functions
sub git_get_refs_list { sub git_get_heads_list {
my $type = shift || ""; my $limit = shift;
my %refs; my @headslist;
my @reflist;
my @refs; open my $fd, '-|', git_cmd(), 'for-each-ref',
open my $fd, "-|", $GIT, "peek-remote", "$projectroot/$project/" ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',
'--format=%(objectname) %(refname) %(subject)%00%(committer)',
'refs/heads'
or return; or return;
while (my $line = <$fd>) { while (my $line = <$fd>) {
chomp $line; my %ref_item;
if ($line =~ m/^([0-9a-fA-F]{40})\trefs\/($type\/?([^\^]+))(\^\{\})?$/) {
if (defined $refs{$1}) {
push @{$refs{$1}}, $2;
} else {
$refs{$1} = [ $2 ];
}
if (! $4) { # unpeeled, direct reference chomp $line;
push @refs, { hash => $1, name => $3 }; # without type my ($refinfo, $committerinfo) = split(/\0/, $line);
} elsif ($3 eq $refs[-1]{'name'}) { my ($hash, $name, $title) = split(' ', $refinfo, 3);
# most likely a tag is followed by its peeled my ($committer, $epoch, $tz) =
# (deref) one, and when that happens we know the ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
# previous one was of type 'tag'. $name =~ s!^refs/heads/!!;
$refs[-1]{'type'} = "tag";
} $ref_item{'name'} = $name;
$ref_item{'id'} = $hash;
$ref_item{'title'} = $title || '(no commit message)';
$ref_item{'epoch'} = $epoch;
if ($epoch) {
$ref_item{'age'} = age_string(time - $ref_item{'epoch'});
} else {
$ref_item{'age'} = "unknown";
} }
push @headslist, \%ref_item;
} }
close $fd; close $fd;
foreach my $ref (@refs) { return wantarray ? @headslist : \@headslist;
my $ref_file = $ref->{'name'}; }
my $ref_id = $ref->{'hash'};
my $type = $ref->{'type'} || git_get_type($ref_id) || next; sub git_get_tags_list {
my %ref_item = parse_ref($ref_file, $ref_id, $type); my $limit = shift;
my @tagslist;
push @reflist, \%ref_item; open my $fd, '-|', git_cmd(), 'for-each-ref',
($limit ? '--count='.($limit+1) : ()), '--sort=-creatordate',
'--format=%(objectname) %(objecttype) %(refname) '.
'%(*objectname) %(*objecttype) %(subject)%00%(creator)',
'refs/tags'
or return;
while (my $line = <$fd>) {
my %ref_item;
chomp $line;
my ($refinfo, $creatorinfo) = split(/\0/, $line);
my ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6);
my ($creator, $epoch, $tz) =
($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/);
$name =~ s!^refs/tags/!!;
$ref_item{'type'} = $type;
$ref_item{'id'} = $id;
$ref_item{'name'} = $name;
if ($type eq "tag") {
$ref_item{'subject'} = $title;
$ref_item{'reftype'} = $reftype;
$ref_item{'refid'} = $refid;
} else {
$ref_item{'reftype'} = $type;
$ref_item{'refid'} = $id;
}
if ($type eq "tag" || $type eq "commit") {
$ref_item{'epoch'} = $epoch;
if ($epoch) {
$ref_item{'age'} = age_string(time - $ref_item{'epoch'});
} else {
$ref_item{'age'} = "unknown";
}
}
push @tagslist, \%ref_item;
} }
# sort refs by age close $fd;
@reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
return (\@reflist, \%refs); return wantarray ? @tagslist : \@tagslist;
} }
## ---------------------------------------------------------------------- ## ----------------------------------------------------------------------
@ -1943,19 +1991,19 @@ sub git_difftree_body {
print "</td>\n"; print "</td>\n";
print "<td>$mode_chnge</td>\n"; print "<td>$mode_chnge</td>\n";
print "<td class=\"link\">"; print "<td class=\"link\">";
if ($diff{'to_id'} ne $diff{'from_id'}) { # modified if ($action eq 'commitdiff') {
if ($action eq 'commitdiff') { # link to patch
# link to patch $patchno++;
$patchno++; print $cgi->a({-href => "#patch$patchno"}, "patch") .
print $cgi->a({-href => "#patch$patchno"}, "patch"); " | ";
} else { } elsif ($diff{'to_id'} ne $diff{'from_id'}) {
print $cgi->a({-href => href(action=>"blobdiff", # "commit" view and modified file (not onlu mode changed)
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, print $cgi->a({-href => href(action=>"blobdiff",
hash_base=>$hash, hash_parent_base=>$parent, hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
file_name=>$diff{'file'})}, hash_base=>$hash, hash_parent_base=>$parent,
"diff"); file_name=>$diff{'file'})},
} "diff") .
print " | "; " | ";
} }
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'})}, hash_base=>$hash, file_name=>$diff{'file'})},
@ -1986,19 +2034,19 @@ sub git_difftree_body {
-class => "list"}, esc_html($diff{'from_file'})) . -class => "list"}, esc_html($diff{'from_file'})) .
" with " . (int $diff{'similarity'}) . "% similarity$mode_chng]</span></td>\n" . " with " . (int $diff{'similarity'}) . "% similarity$mode_chng]</span></td>\n" .
"<td class=\"link\">"; "<td class=\"link\">";
if ($diff{'to_id'} ne $diff{'from_id'}) { if ($action eq 'commitdiff') {
if ($action eq 'commitdiff') { # link to patch
# link to patch $patchno++;
$patchno++; print $cgi->a({-href => "#patch$patchno"}, "patch") .
print $cgi->a({-href => "#patch$patchno"}, "patch"); " | ";
} else { } elsif ($diff{'to_id'} ne $diff{'from_id'}) {
print $cgi->a({-href => href(action=>"blobdiff", # "commit" view and modified file (not only pure rename or copy)
hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, print $cgi->a({-href => href(action=>"blobdiff",
hash_base=>$hash, hash_parent_base=>$parent, hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, hash_base=>$hash, hash_parent_base=>$parent,
"diff"); file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
} "diff") .
print " | "; " | ";
} }
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'}, print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'from_file'})}, hash_base=>$parent, file_name=>$diff{'from_file'})},
@ -2049,13 +2097,6 @@ sub git_patchset_body {
} }
$patch_idx++; $patch_idx++;
# for now, no extended header, hence we skip empty patches
# companion to next LINE if $in_header;
if ($diffinfo->{'from_id'} eq $diffinfo->{'to_id'}) { # no change
$in_header = 1;
next LINE;
}
if ($diffinfo->{'status'} eq "A") { # added if ($diffinfo->{'status'} eq "A") { # added
print "<div class=\"diff_info\">" . file_type($diffinfo->{'to_mode'}) . ":" . print "<div class=\"diff_info\">" . file_type($diffinfo->{'to_mode'}) . ":" .
$cgi->a({-href => href(action=>"blob", hash_base=>$hash, $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
@ -2262,8 +2303,7 @@ sub git_tags_body {
for (my $i = $from; $i <= $to; $i++) { for (my $i = $from; $i <= $to; $i++) {
my $entry = $taglist->[$i]; my $entry = $taglist->[$i];
my %tag = %$entry; my %tag = %$entry;
my $comment_lines = $tag{'comment'}; my $comment = $tag{'subject'};
my $comment = shift @$comment_lines;
my $comment_short; my $comment_short;
if (defined $comment) { if (defined $comment) {
$comment_short = chop_str($comment, 30, 5); $comment_short = chop_str($comment, 30, 5);
@ -2296,7 +2336,7 @@ sub git_tags_body {
$cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'}); $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'});
if ($tag{'reftype'} eq "commit") { if ($tag{'reftype'} eq "commit") {
print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") .
" | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'refid'})}, "log"); " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log");
} elsif ($tag{'reftype'} eq "blob") { } elsif ($tag{'reftype'} eq "blob") {
print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw"); print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw");
} }
@ -2321,23 +2361,23 @@ sub git_heads_body {
my $alternate = 1; my $alternate = 1;
for (my $i = $from; $i <= $to; $i++) { for (my $i = $from; $i <= $to; $i++) {
my $entry = $headlist->[$i]; my $entry = $headlist->[$i];
my %tag = %$entry; my %ref = %$entry;
my $curr = $tag{'id'} eq $head; my $curr = $ref{'id'} eq $head;
if ($alternate) { if ($alternate) {
print "<tr class=\"dark\">\n"; print "<tr class=\"dark\">\n";
} else { } else {
print "<tr class=\"light\">\n"; print "<tr class=\"light\">\n";
} }
$alternate ^= 1; $alternate ^= 1;
print "<td><i>$tag{'age'}</i></td>\n" . print "<td><i>$ref{'age'}</i></td>\n" .
($tag{'id'} eq $head ? "<td class=\"current_head\">" : "<td>") . ($curr ? "<td class=\"current_head\">" : "<td>") .
$cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'}), $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'}),
-class => "list name"},esc_html($tag{'name'})) . -class => "list name"},esc_html($ref{'name'})) .
"</td>\n" . "</td>\n" .
"<td class=\"link\">" . "<td class=\"link\">" .
$cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'})}, "shortlog") . " | " .
$cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log") . " | " . $cgi->a({-href => href(action=>"log", hash=>$ref{'name'})}, "log") . " | " .
$cgi->a({-href => href(action=>"tree", hash=>$tag{'name'}, hash_base=>$tag{'name'})}, "tree") . $cgi->a({-href => href(action=>"tree", hash=>$ref{'name'}, hash_base=>$ref{'name'})}, "tree") .
"</td>\n" . "</td>\n" .
"</tr>"; "</tr>";
} }
@ -2487,18 +2527,9 @@ sub git_summary {
my $owner = git_get_project_owner($project); my $owner = git_get_project_owner($project);
my ($reflist, $refs) = git_get_refs_list(); my $refs = git_get_references();
my @taglist = git_get_tags_list(15);
my @taglist; my @headlist = git_get_heads_list(15);
my @headlist;
foreach my $ref (@$reflist) {
if ($ref->{'name'} =~ s!^heads/!!) {
push @headlist, $ref;
} else {
$ref->{'name'} =~ s!^tags/!!;
push @taglist, $ref;
}
}
git_header_html(); git_header_html();
git_print_page_nav('summary','', $head); git_print_page_nav('summary','', $head);
@ -2529,7 +2560,7 @@ sub git_summary {
} }
open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17", open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17",
git_get_head_hash($project) git_get_head_hash($project), "--"
or die_error(undef, "Open git-rev-list failed"); or die_error(undef, "Open git-rev-list failed");
my @revlist = map { chomp; $_ } <$fd>; my @revlist = map { chomp; $_ } <$fd>;
close $fd; close $fd;
@ -2659,7 +2690,7 @@ HTML
print "<tr class=\"$rev_color[$current_color]\">\n"; print "<tr class=\"$rev_color[$current_color]\">\n";
if ($group_size) { if ($group_size) {
print "<td class=\"sha1\""; print "<td class=\"sha1\"";
print " title=\"$author, $date\""; print " title=\"". esc_html($author) . ", $date\"";
print " rowspan=\"$group_size\"" if ($group_size > 1); print " rowspan=\"$group_size\"" if ($group_size > 1);
print ">"; print ">";
print $cgi->a({-href => href(action=>"commit", print $cgi->a({-href => href(action=>"commit",
@ -2790,9 +2821,9 @@ sub git_tags {
git_print_page_nav('','', $head,undef,$head); git_print_page_nav('','', $head,undef,$head);
git_print_header_div('summary', $project); git_print_header_div('summary', $project);
my ($taglist) = git_get_refs_list("tags"); my @tagslist = git_get_tags_list();
if (@$taglist) { if (@tagslist) {
git_tags_body($taglist); git_tags_body(\@tagslist);
} }
git_footer_html(); git_footer_html();
} }
@ -2803,9 +2834,9 @@ sub git_heads {
git_print_page_nav('','', $head,undef,$head); git_print_page_nav('','', $head,undef,$head);
git_print_header_div('summary', $project); git_print_header_div('summary', $project);
my ($headlist) = git_get_refs_list("heads"); my @headslist = git_get_heads_list();
if (@$headlist) { if (@headslist) {
git_heads_body($headlist, $head); git_heads_body(\@headslist, $head);
} }
git_footer_html(); git_footer_html();
} }
@ -2918,7 +2949,7 @@ sub git_blob {
$nr++; $nr++;
$line = untabify($line); $line = untabify($line);
printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n", printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
$nr, $nr, $nr, esc_html($line); $nr, $nr, $nr, esc_html($line, -nbsp=>1);
} }
close $fd close $fd
or print "Reading blob failed.\n"; or print "Reading blob failed.\n";
@ -3072,7 +3103,7 @@ sub git_log {
my $refs = git_get_references(); my $refs = git_get_references();
my $limit = sprintf("--max-count=%i", (100 * ($page+1))); my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash, "--"
or die_error(undef, "Open git-rev-list failed"); or die_error(undef, "Open git-rev-list failed");
my @revlist = map { chomp; $_ } <$fd>; my @revlist = map { chomp; $_ } <$fd>;
close $fd; close $fd;
@ -3130,7 +3161,7 @@ sub git_commit {
$parent = "--root"; $parent = "--root";
} }
open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id", open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
@diff_opts, $parent, $hash @diff_opts, $parent, $hash, "--"
or die_error(undef, "Open git-diff-tree failed"); or die_error(undef, "Open git-diff-tree failed");
my @difftree = map { chomp; $_ } <$fd>; my @difftree = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading git-diff-tree failed"); close $fd or die_error(undef, "Reading git-diff-tree failed");
@ -3235,7 +3266,8 @@ sub git_blobdiff {
if (defined $hash_base && defined $hash_parent_base) { if (defined $hash_base && defined $hash_parent_base) {
if (defined $file_name) { if (defined $file_name) {
# read raw output # read raw output
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base, open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
$hash_parent_base, $hash_base,
"--", $file_name "--", $file_name
or die_error(undef, "Open git-diff-tree failed"); or die_error(undef, "Open git-diff-tree failed");
@difftree = map { chomp; $_ } <$fd>; @difftree = map { chomp; $_ } <$fd>;
@ -3249,7 +3281,8 @@ sub git_blobdiff {
# try to find filename from $hash # try to find filename from $hash
# read filtered raw output # read filtered raw output
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
$hash_parent_base, $hash_base, "--"
or die_error(undef, "Open git-diff-tree failed"); or die_error(undef, "Open git-diff-tree failed");
@difftree = @difftree =
# ':100644 100644 03b21826... 3b93d5e7... M ls-files.c' # ':100644 100644 03b21826... 3b93d5e7... M ls-files.c'
@ -3319,7 +3352,8 @@ sub git_blobdiff {
} }
# open patch output # open patch output
open $fd, "-|", git_cmd(), "diff", '-p', @diff_opts, $hash_parent, $hash open $fd, "-|", git_cmd(), "diff", '-p', @diff_opts,
$hash_parent, $hash, "--"
or die_error(undef, "Open git-diff failed"); or die_error(undef, "Open git-diff failed");
} else { } else {
die_error('404 Not Found', "Missing one of the blob diff parameters") die_error('404 Not Found', "Missing one of the blob diff parameters")
@ -3450,8 +3484,8 @@ sub git_commitdiff {
my @difftree; my @difftree;
if ($format eq 'html') { if ($format eq 'html') {
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
"--no-commit-id", "--no-commit-id", "--patch-with-raw", "--full-index",
"--patch-with-raw", "--full-index", $hash_parent, $hash $hash_parent, $hash, "--"
or die_error(undef, "Open git-diff-tree failed"); or die_error(undef, "Open git-diff-tree failed");
while (chomp(my $line = <$fd>)) { while (chomp(my $line = <$fd>)) {
@ -3462,7 +3496,7 @@ sub git_commitdiff {
} elsif ($format eq 'plain') { } elsif ($format eq 'plain') {
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
'-p', $hash_parent, $hash '-p', $hash_parent, $hash, "--"
or die_error(undef, "Open git-diff-tree failed"); or die_error(undef, "Open git-diff-tree failed");
} else { } else {
@ -3639,7 +3673,9 @@ sub git_search {
my $alternate = 1; my $alternate = 1;
if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') { if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') {
$/ = "\0"; $/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", $hash or next; open my $fd, "-|", git_cmd(), "rev-list",
"--header", "--parents", $hash, "--"
or next;
while (my $commit_text = <$fd>) { while (my $commit_text = <$fd>) {
if (!grep m/$searchtext/i, $commit_text) { if (!grep m/$searchtext/i, $commit_text) {
next; next;
@ -3785,7 +3821,7 @@ sub git_shortlog {
my $refs = git_get_references(); my $refs = git_get_references();
my $limit = sprintf("--max-count=%i", (100 * ($page+1))); my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash, "--"
or die_error(undef, "Open git-rev-list failed"); or die_error(undef, "Open git-rev-list failed");
my @revlist = map { chomp; $_ } <$fd>; my @revlist = map { chomp; $_ } <$fd>;
close $fd; close $fd;
@ -3813,7 +3849,8 @@ sub git_shortlog {
sub git_rss { sub git_rss {
# http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150", git_get_head_hash($project) open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150",
git_get_head_hash($project), "--"
or die_error(undef, "Open git-rev-list failed"); or die_error(undef, "Open git-rev-list failed");
my @revlist = map { chomp; $_ } <$fd>; my @revlist = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading git-rev-list failed"); close $fd or die_error(undef, "Reading git-rev-list failed");
@ -3837,7 +3874,7 @@ XML
} }
my %cd = parse_date($co{'committer_epoch'}); my %cd = parse_date($co{'committer_epoch'});
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
$co{'parent'}, $co{'id'} $co{'parent'}, $co{'id'}, "--"
or next; or next;
my @difftree = map { chomp; $_ } <$fd>; my @difftree = map { chomp; $_ } <$fd>;
close $fd close $fd

View File

@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
static struct ref *local_refs, **local_tail; static struct ref *local_refs, **local_tail;
static struct ref *remote_refs, **remote_tail; static struct ref *remote_refs, **remote_tail;
static int one_local_ref(const char *refname, const unsigned char *sha1) static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct ref *ref; struct ref *ref;
int len = strlen(refname) + 1; int len = strlen(refname) + 1;
@ -1913,7 +1913,7 @@ static void one_remote_ref(char *refname)
static void get_local_heads(void) static void get_local_heads(void)
{ {
local_tail = &local_refs; local_tail = &local_refs;
for_each_ref(one_local_ref); for_each_ref(one_local_ref, NULL);
} }
static void get_dav_remote_heads(void) static void get_dav_remote_heads(void)

View File

@ -272,7 +272,7 @@ buffer_gets( buffer_t * b, char **s )
n = b->bytes - start; n = b->bytes - start;
if (n) if (n)
memcpy( b->buf, b->buf + start, n ); memmove(b->buf, b->buf + start, n);
b->offset -= start; b->offset -= start;
b->bytes = n; b->bytes = n;
start = 0; start = 0;

View File

@ -252,26 +252,6 @@ int log_tree_diff_flush(struct rev_info *opt)
return 1; return 1;
} }
static int diff_root_tree(struct rev_info *opt,
const unsigned char *new, const char *base)
{
int retval;
void *tree;
struct tree_desc empty, real;
tree = read_object_with_reference(new, tree_type, &real.size, NULL);
if (!tree)
die("unable to read root tree (%s)", sha1_to_hex(new));
real.buf = tree;
empty.buf = "";
empty.size = 0;
retval = diff_tree(&empty, &real, base, &opt->diffopt);
free(tree);
log_tree_diff_flush(opt);
return retval;
}
static int do_diff_combined(struct rev_info *opt, struct commit *commit) static int do_diff_combined(struct rev_info *opt, struct commit *commit)
{ {
unsigned const char *sha1 = commit->object.sha1; unsigned const char *sha1 = commit->object.sha1;
@ -297,8 +277,10 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
/* Root commit? */ /* Root commit? */
parents = commit->parents; parents = commit->parents;
if (!parents) { if (!parents) {
if (opt->show_root_diff) if (opt->show_root_diff) {
diff_root_tree(opt, sha1, ""); diff_root_tree_sha1(sha1, "", &opt->diffopt);
log_tree_diff_flush(opt);
}
return !opt->loginfo; return !opt->loginfo;
} }

View File

@ -209,7 +209,7 @@ static int quote_c_style_counted(const char *name, int namelen,
if (!ch) if (!ch)
break; break;
if ((ch < ' ') || (ch == '"') || (ch == '\\') || if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
(ch == 0177)) { (ch >= 0177)) {
needquote = 1; needquote = 1;
switch (ch) { switch (ch) {
case '\a': EMITQ(); ch = 'a'; break; case '\a': EMITQ(); ch = 'a'; break;

View File

@ -9,12 +9,26 @@ static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
static const char *unpacker[] = { "unpack-objects", NULL }; static const char *unpacker[] = { "unpack-objects", NULL };
static int deny_non_fast_forwards = 0;
static int report_status; static int report_status;
static char capabilities[] = "report-status"; static char capabilities[] = "report-status";
static int capabilities_sent; static int capabilities_sent;
static int show_ref(const char *path, const unsigned char *sha1) static int receive_pack_config(const char *var, const char *value)
{
git_default_config(var, value);
if (strcmp(var, "receive.denynonfastforwards") == 0)
{
deny_non_fast_forwards = git_config_bool(var, value);
return 0;
}
return 0;
}
static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{ {
if (capabilities_sent) if (capabilities_sent)
packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
@ -27,9 +41,9 @@ static int show_ref(const char *path, const unsigned char *sha1)
static void write_head_info(void) static void write_head_info(void)
{ {
for_each_ref(show_ref); for_each_ref(show_ref, NULL);
if (!capabilities_sent) if (!capabilities_sent)
show_ref("capabilities^{}", null_sha1); show_ref("capabilities^{}", null_sha1, 0, NULL);
} }
@ -43,34 +57,6 @@ struct command {
static struct command *commands; static struct command *commands;
static int is_all_zeroes(const char *hex)
{
int i;
for (i = 0; i < 40; i++)
if (*hex++ != '0')
return 0;
return 1;
}
static int verify_old_ref(const char *name, char *hex_contents)
{
int fd, ret;
char buffer[60];
if (is_all_zeroes(hex_contents))
return 0;
fd = open(name, O_RDONLY);
if (fd < 0)
return -1;
ret = read(fd, buffer, 40);
close(fd);
if (ret != 40)
return -1;
if (memcmp(buffer, hex_contents, 40))
return -1;
return 0;
}
static char update_hook[] = "hooks/update"; static char update_hook[] = "hooks/update";
static int run_update_hook(const char *refname, static int run_update_hook(const char *refname,
@ -107,8 +93,8 @@ static int update(struct command *cmd)
const char *name = cmd->ref_name; const char *name = cmd->ref_name;
unsigned char *old_sha1 = cmd->old_sha1; unsigned char *old_sha1 = cmd->old_sha1;
unsigned char *new_sha1 = cmd->new_sha1; unsigned char *new_sha1 = cmd->new_sha1;
char new_hex[60], *old_hex, *lock_name; char new_hex[41], old_hex[41];
int newfd, namelen, written; struct ref_lock *lock;
cmd->error_string = NULL; cmd->error_string = NULL;
if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) { if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) {
@ -117,13 +103,8 @@ static int update(struct command *cmd)
name); name);
} }
namelen = strlen(name);
lock_name = xmalloc(namelen + 10);
memcpy(lock_name, name, namelen);
memcpy(lock_name + namelen, ".lock", 6);
strcpy(new_hex, sha1_to_hex(new_sha1)); strcpy(new_hex, sha1_to_hex(new_sha1));
old_hex = sha1_to_hex(old_sha1); strcpy(old_hex, sha1_to_hex(old_sha1));
if (!has_sha1_file(new_sha1)) { if (!has_sha1_file(new_sha1)) {
cmd->error_string = "bad pack"; cmd->error_string = "bad pack";
return error("unpack should have generated %s, " return error("unpack should have generated %s, "
@ -144,47 +125,20 @@ static int update(struct command *cmd)
return error("denying non-fast forward;" return error("denying non-fast forward;"
" you should pull first"); " you should pull first");
} }
safe_create_leading_directories(lock_name);
newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (newfd < 0) {
cmd->error_string = "can't lock";
return error("unable to create %s (%s)",
lock_name, strerror(errno));
}
/* Write the ref with an ending '\n' */
new_hex[40] = '\n';
new_hex[41] = 0;
written = write(newfd, new_hex, 41);
/* Remove the '\n' again */
new_hex[40] = 0;
close(newfd);
if (written != 41) {
unlink(lock_name);
cmd->error_string = "can't write";
return error("unable to write %s", lock_name);
}
if (verify_old_ref(name, old_hex) < 0) {
unlink(lock_name);
cmd->error_string = "raced";
return error("%s changed during push", name);
}
if (run_update_hook(name, old_hex, new_hex)) { if (run_update_hook(name, old_hex, new_hex)) {
unlink(lock_name);
cmd->error_string = "hook declined"; cmd->error_string = "hook declined";
return error("hook declined to update %s", name); return error("hook declined to update %s", name);
} }
else if (rename(lock_name, name) < 0) {
unlink(lock_name); lock = lock_any_ref_for_update(name, old_sha1);
cmd->error_string = "can't rename"; if (!lock) {
return error("unable to replace %s", name); cmd->error_string = "failed to lock";
} return error("failed to lock %s", name);
else {
fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
return 0;
} }
write_ref_sha1(lock, new_sha1, "push");
fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
return 0;
} }
static char update_post_hook[] = "hooks/post-update"; static char update_post_hook[] = "hooks/post-update";
@ -333,9 +287,12 @@ int main(int argc, char **argv)
if (!dir) if (!dir)
usage(receive_pack_usage); usage(receive_pack_usage);
if(!enter_repo(dir, 0)) if (!enter_repo(dir, 0))
die("'%s': unable to chdir or not a git archive", dir); die("'%s': unable to chdir or not a git archive", dir);
setup_ident();
git_config(receive_pack_config);
write_head_info(); write_head_info();
/* EOF */ /* EOF */

612
refs.c
View File

@ -3,15 +3,193 @@
#include <errno.h> #include <errno.h>
struct ref_list {
struct ref_list *next;
unsigned char flag; /* ISSYMREF? ISPACKED? */
unsigned char sha1[20];
char name[FLEX_ARRAY];
};
static const char *parse_ref_line(char *line, unsigned char *sha1)
{
/*
* 42: the answer to everything.
*
* In this case, it happens to be the answer to
* 40 (length of sha1 hex representation)
* +1 (space in between hex and name)
* +1 (newline at the end of the line)
*/
int len = strlen(line) - 42;
if (len <= 0)
return NULL;
if (get_sha1_hex(line, sha1) < 0)
return NULL;
if (!isspace(line[40]))
return NULL;
line += 41;
if (isspace(*line))
return NULL;
if (line[len] != '\n')
return NULL;
line[len] = 0;
return line;
}
static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
int flag, struct ref_list *list)
{
int len;
struct ref_list **p = &list, *entry;
/* Find the place to insert the ref into.. */
while ((entry = *p) != NULL) {
int cmp = strcmp(entry->name, name);
if (cmp > 0)
break;
/* Same as existing entry? */
if (!cmp)
return list;
p = &entry->next;
}
/* Allocate it and add it in.. */
len = strlen(name) + 1;
entry = xmalloc(sizeof(struct ref_list) + len);
hashcpy(entry->sha1, sha1);
memcpy(entry->name, name, len);
entry->flag = flag;
entry->next = *p;
*p = entry;
return list;
}
/*
* Future: need to be in "struct repository"
* when doing a full libification.
*/
struct cached_refs {
char did_loose;
char did_packed;
struct ref_list *loose;
struct ref_list *packed;
} cached_refs;
static void free_ref_list(struct ref_list *list)
{
struct ref_list *next;
for ( ; list; list = next) {
next = list->next;
free(list);
}
}
static void invalidate_cached_refs(void)
{
struct cached_refs *ca = &cached_refs;
if (ca->did_loose && ca->loose)
free_ref_list(ca->loose);
if (ca->did_packed && ca->packed)
free_ref_list(ca->packed);
ca->loose = ca->packed = NULL;
ca->did_loose = ca->did_packed = 0;
}
static struct ref_list *get_packed_refs(void)
{
if (!cached_refs.did_packed) {
struct ref_list *refs = NULL;
FILE *f = fopen(git_path("packed-refs"), "r");
if (f) {
struct ref_list *list = NULL;
char refline[PATH_MAX];
while (fgets(refline, sizeof(refline), f)) {
unsigned char sha1[20];
const char *name = parse_ref_line(refline, sha1);
if (!name)
continue;
list = add_ref(name, sha1, REF_ISPACKED, list);
}
fclose(f);
refs = list;
}
cached_refs.packed = refs;
cached_refs.did_packed = 1;
}
return cached_refs.packed;
}
static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
{
DIR *dir = opendir(git_path("%s", base));
if (dir) {
struct dirent *de;
int baselen = strlen(base);
char *ref = xmalloc(baselen + 257);
memcpy(ref, base, baselen);
if (baselen && base[baselen-1] != '/')
ref[baselen++] = '/';
while ((de = readdir(dir)) != NULL) {
unsigned char sha1[20];
struct stat st;
int flag;
int namelen;
if (de->d_name[0] == '.')
continue;
namelen = strlen(de->d_name);
if (namelen > 255)
continue;
if (has_extension(de->d_name, ".lock"))
continue;
memcpy(ref + baselen, de->d_name, namelen+1);
if (stat(git_path("%s", ref), &st) < 0)
continue;
if (S_ISDIR(st.st_mode)) {
list = get_ref_dir(ref, list);
continue;
}
if (!resolve_ref(ref, sha1, 1, &flag)) {
error("%s points nowhere!", ref);
continue;
}
list = add_ref(ref, sha1, flag, list);
}
free(ref);
closedir(dir);
}
return list;
}
static struct ref_list *get_loose_refs(void)
{
if (!cached_refs.did_loose) {
cached_refs.loose = get_ref_dir("refs", NULL);
cached_refs.did_loose = 1;
}
return cached_refs.loose;
}
/* We allow "recursive" symbolic refs. Only within reason, though */ /* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5 #define MAXDEPTH 5
const char *resolve_ref(const char *path, unsigned char *sha1, int reading) const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
{ {
int depth = MAXDEPTH, len; int depth = MAXDEPTH, len;
char buffer[256]; char buffer[256];
static char ref_buffer[256];
if (flag)
*flag = 0;
for (;;) { for (;;) {
const char *path = git_path("%s", ref);
struct stat st; struct stat st;
char *buf; char *buf;
int fd; int fd;
@ -27,17 +205,31 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
* reading. * reading.
*/ */
if (lstat(path, &st) < 0) { if (lstat(path, &st) < 0) {
struct ref_list *list = get_packed_refs();
while (list) {
if (!strcmp(ref, list->name)) {
hashcpy(sha1, list->sha1);
if (flag)
*flag |= REF_ISPACKED;
return ref;
}
list = list->next;
}
if (reading || errno != ENOENT) if (reading || errno != ENOENT)
return NULL; return NULL;
hashclr(sha1); hashclr(sha1);
return path; return ref;
} }
/* Follow "normalized" - ie "refs/.." symlinks by hand */ /* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) { if (S_ISLNK(st.st_mode)) {
len = readlink(path, buffer, sizeof(buffer)-1); len = readlink(path, buffer, sizeof(buffer)-1);
if (len >= 5 && !memcmp("refs/", buffer, 5)) { if (len >= 5 && !memcmp("refs/", buffer, 5)) {
path = git_path("%.*s", len, buffer); buffer[len] = 0;
strcpy(ref_buffer, buffer);
ref = ref_buffer;
if (flag)
*flag |= REF_ISSYMREF;
continue; continue;
} }
} }
@ -68,19 +260,24 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
while (len && isspace(*buf)) while (len && isspace(*buf))
buf++, len--; buf++, len--;
while (len && isspace(buf[len-1])) while (len && isspace(buf[len-1]))
buf[--len] = 0; len--;
path = git_path("%.*s", len, buf); buf[len] = 0;
memcpy(ref_buffer, buf, len + 1);
ref = ref_buffer;
if (flag)
*flag |= REF_ISSYMREF;
} }
if (len < 40 || get_sha1_hex(buffer, sha1)) if (len < 40 || get_sha1_hex(buffer, sha1))
return NULL; return NULL;
return path; return ref;
} }
int create_symref(const char *git_HEAD, const char *refs_heads_master) int create_symref(const char *ref_target, const char *refs_heads_master)
{ {
const char *lockpath; const char *lockpath;
char ref[1000]; char ref[1000];
int fd, len, written; int fd, len, written;
const char *git_HEAD = git_path("%s", ref_target);
#ifndef NO_SYMLINK_HEAD #ifndef NO_SYMLINK_HEAD
if (prefer_symlink_refs) { if (prefer_symlink_refs) {
@ -118,104 +315,101 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master)
return 0; return 0;
} }
int read_ref(const char *filename, unsigned char *sha1) int read_ref(const char *ref, unsigned char *sha1)
{ {
if (resolve_ref(filename, sha1, 1)) if (resolve_ref(ref, sha1, 1, NULL))
return 0; return 0;
return -1; return -1;
} }
static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim) static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
void *cb_data)
{ {
int retval = 0; int retval;
DIR *dir = opendir(git_path("%s", base)); struct ref_list *packed = get_packed_refs();
struct ref_list *loose = get_loose_refs();
if (dir) { while (packed && loose) {
struct dirent *de; struct ref_list *entry;
int baselen = strlen(base); int cmp = strcmp(packed->name, loose->name);
char *path = xmalloc(baselen + 257); if (!cmp) {
packed = packed->next;
if (!strncmp(base, "./", 2)) { continue;
base += 2;
baselen -= 2;
} }
memcpy(path, base, baselen); if (cmp > 0) {
if (baselen && base[baselen-1] != '/') entry = loose;
path[baselen++] = '/'; loose = loose->next;
} else {
while ((de = readdir(dir)) != NULL) { entry = packed;
unsigned char sha1[20]; packed = packed->next;
struct stat st;
int namelen;
if (de->d_name[0] == '.')
continue;
namelen = strlen(de->d_name);
if (namelen > 255)
continue;
if (has_extension(de->d_name, ".lock"))
continue;
memcpy(path + baselen, de->d_name, namelen+1);
if (stat(git_path("%s", path), &st) < 0)
continue;
if (S_ISDIR(st.st_mode)) {
retval = do_for_each_ref(path, fn, trim);
if (retval)
break;
continue;
}
if (read_ref(git_path("%s", path), sha1) < 0) {
error("%s points nowhere!", path);
continue;
}
if (!has_sha1_file(sha1)) {
error("%s does not point to a valid "
"commit object!", path);
continue;
}
retval = fn(path + trim, sha1);
if (retval)
break;
} }
free(path); if (strncmp(base, entry->name, trim))
closedir(dir); continue;
if (is_null_sha1(entry->sha1))
continue;
if (!has_sha1_file(entry->sha1)) {
error("%s does not point to a valid object!", entry->name);
continue;
}
retval = fn(entry->name + trim, entry->sha1,
entry->flag, cb_data);
if (retval)
return retval;
} }
return retval;
}
int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) packed = packed ? packed : loose;
{ while (packed) {
unsigned char sha1[20]; if (!strncmp(base, packed->name, trim)) {
if (!read_ref(git_path("HEAD"), sha1)) retval = fn(packed->name + trim, packed->sha1,
return fn("HEAD", sha1); packed->flag, cb_data);
if (retval)
return retval;
}
packed = packed->next;
}
return 0; return 0;
} }
int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)) int head_ref(each_ref_fn fn, void *cb_data)
{ {
return do_for_each_ref("refs", fn, 0); unsigned char sha1[20];
int flag;
if (resolve_ref("HEAD", sha1, 1, &flag))
return fn("HEAD", sha1, flag, cb_data);
return 0;
} }
int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)) int for_each_ref(each_ref_fn fn, void *cb_data)
{ {
return do_for_each_ref("refs/tags", fn, 10); return do_for_each_ref("refs/", fn, 0, cb_data);
} }
int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)) int for_each_tag_ref(each_ref_fn fn, void *cb_data)
{ {
return do_for_each_ref("refs/heads", fn, 11); return do_for_each_ref("refs/tags/", fn, 10, cb_data);
} }
int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)) int for_each_branch_ref(each_ref_fn fn, void *cb_data)
{ {
return do_for_each_ref("refs/remotes", fn, 13); return do_for_each_ref("refs/heads/", fn, 11, cb_data);
} }
int for_each_remote_ref(each_ref_fn fn, void *cb_data)
{
return do_for_each_ref("refs/remotes/", fn, 13, cb_data);
}
/* NEEDSWORK: This is only used by ssh-upload and it should go; the
* caller should do resolve_ref or read_ref like everybody else. Or
* maybe everybody else should use get_ref_sha1() instead of doing
* read_ref().
*/
int get_ref_sha1(const char *ref, unsigned char *sha1) int get_ref_sha1(const char *ref, unsigned char *sha1)
{ {
if (check_ref_format(ref)) if (check_ref_format(ref))
return -1; return -1;
return read_ref(git_path("refs/%s", ref), sha1); return read_ref(mkpath("refs/%s", ref), sha1);
} }
/* /*
@ -273,22 +467,13 @@ int check_ref_format(const char *ref)
static struct ref_lock *verify_lock(struct ref_lock *lock, static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist) const unsigned char *old_sha1, int mustexist)
{ {
char buf[40]; if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
int nr, fd = open(lock->ref_file, O_RDONLY); error("Can't verify ref %s", lock->ref_name);
if (fd < 0 && (mustexist || errno != ENOENT)) {
error("Can't verify ref %s", lock->ref_file);
unlock_ref(lock);
return NULL;
}
nr = read(fd, buf, 40);
close(fd);
if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) {
error("Can't verify ref %s", lock->ref_file);
unlock_ref(lock); unlock_ref(lock);
return NULL; return NULL;
} }
if (hashcmp(lock->old_sha1, old_sha1)) { if (hashcmp(lock->old_sha1, old_sha1)) {
error("Ref %s is at %s but expected %s", lock->ref_file, error("Ref %s is at %s but expected %s", lock->ref_name,
sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1)); sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
unlock_ref(lock); unlock_ref(lock);
return NULL; return NULL;
@ -296,54 +481,223 @@ static struct ref_lock *verify_lock(struct ref_lock *lock,
return lock; return lock;
} }
static struct ref_lock *lock_ref_sha1_basic(const char *path, static int remove_empty_dir_recursive(char *path, int len)
int plen,
const unsigned char *old_sha1, int mustexist)
{ {
const char *orig_path = path; DIR *dir = opendir(path);
struct dirent *e;
int ret = 0;
if (!dir)
return -1;
if (path[len-1] != '/')
path[len++] = '/';
while ((e = readdir(dir)) != NULL) {
struct stat st;
int namlen;
if ((e->d_name[0] == '.') &&
((e->d_name[1] == 0) ||
((e->d_name[1] == '.') && e->d_name[2] == 0)))
continue; /* "." and ".." */
namlen = strlen(e->d_name);
if ((len + namlen < PATH_MAX) &&
strcpy(path + len, e->d_name) &&
!lstat(path, &st) &&
S_ISDIR(st.st_mode) &&
!remove_empty_dir_recursive(path, len + namlen))
continue; /* happy */
/* path too long, stat fails, or non-directory still exists */
ret = -1;
break;
}
closedir(dir);
if (!ret) {
path[len] = 0;
ret = rmdir(path);
}
return ret;
}
static int remove_empty_directories(char *file)
{
/* we want to create a file but there is a directory there;
* if that is an empty directory (or a directory that contains
* only empty directories), remove them.
*/
char path[PATH_MAX];
int len = strlen(file);
if (len >= PATH_MAX) /* path too long ;-) */
return -1;
strcpy(path, file);
return remove_empty_dir_recursive(path, len);
}
static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
{
char *ref_file;
const char *orig_ref = ref;
struct ref_lock *lock; struct ref_lock *lock;
struct stat st; struct stat st;
int last_errno = 0;
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
lock = xcalloc(1, sizeof(struct ref_lock)); lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1; lock->lock_fd = -1;
plen = strlen(path) - plen; ref = resolve_ref(ref, lock->old_sha1, mustexist, flag);
path = resolve_ref(path, lock->old_sha1, mustexist); if (!ref && errno == EISDIR) {
if (!path) { /* we are trying to lock foo but we used to
int last_errno = errno; * have foo/bar which now does not exist;
error("unable to resolve reference %s: %s", * it is normal for the empty directory 'foo'
orig_path, strerror(errno)); * to remain.
unlock_ref(lock); */
errno = last_errno; ref_file = git_path("%s", orig_ref);
return NULL; if (remove_empty_directories(ref_file)) {
last_errno = errno;
error("there are still refs under '%s'", orig_ref);
goto error_return;
}
ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
} }
if (!ref) {
last_errno = errno;
error("unable to resolve reference %s: %s",
orig_ref, strerror(errno));
goto error_return;
}
if (is_null_sha1(lock->old_sha1)) {
/* The ref did not exist and we are creating it.
* Make sure there is no existing ref that is packed
* whose name begins with our refname, nor a ref whose
* name is a proper prefix of our refname.
*/
int namlen = strlen(ref); /* e.g. 'foo/bar' */
struct ref_list *list = get_packed_refs();
while (list) {
/* list->name could be 'foo' or 'foo/bar/baz' */
int len = strlen(list->name);
int cmplen = (namlen < len) ? namlen : len;
const char *lead = (namlen < len) ? list->name : ref;
if (!strncmp(ref, list->name, cmplen) &&
lead[cmplen] == '/') {
error("'%s' exists; cannot create '%s'",
list->name, ref);
goto error_return;
}
list = list->next;
}
}
lock->lk = xcalloc(1, sizeof(struct lock_file)); lock->lk = xcalloc(1, sizeof(struct lock_file));
lock->ref_file = xstrdup(path); lock->ref_name = xstrdup(ref);
lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen)); lock->log_file = xstrdup(git_path("logs/%s", ref));
lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; ref_file = git_path("%s", ref);
lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
if (safe_create_leading_directories(lock->ref_file)) if (safe_create_leading_directories(ref_file)) {
die("unable to create directory for %s", lock->ref_file); last_errno = errno;
lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file, 1); error("unable to create directory for %s", ref_file);
goto error_return;
}
lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
error_return:
unlock_ref(lock);
errno = last_errno;
return NULL;
} }
struct ref_lock *lock_ref_sha1(const char *ref, struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
const unsigned char *old_sha1, int mustexist)
{ {
char refpath[PATH_MAX];
if (check_ref_format(ref)) if (check_ref_format(ref))
return NULL; return NULL;
return lock_ref_sha1_basic(git_path("refs/%s", ref), strcpy(refpath, mkpath("refs/%s", ref));
5 + strlen(ref), old_sha1, mustexist); return lock_ref_sha1_basic(refpath, old_sha1, NULL);
} }
struct ref_lock *lock_any_ref_for_update(const char *ref, struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
const unsigned char *old_sha1, int mustexist)
{ {
return lock_ref_sha1_basic(git_path("%s", ref), return lock_ref_sha1_basic(ref, old_sha1, NULL);
strlen(ref), old_sha1, mustexist); }
static struct lock_file packlock;
static int repack_without_ref(const char *refname)
{
struct ref_list *list, *packed_ref_list;
int fd;
int found = 0;
packed_ref_list = get_packed_refs();
for (list = packed_ref_list; list; list = list->next) {
if (!strcmp(refname, list->name)) {
found = 1;
break;
}
}
if (!found)
return 0;
memset(&packlock, 0, sizeof(packlock));
fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
if (fd < 0)
return error("cannot delete '%s' from packed refs", refname);
for (list = packed_ref_list; list; list = list->next) {
char line[PATH_MAX + 100];
int len;
if (!strcmp(refname, list->name))
continue;
len = snprintf(line, sizeof(line), "%s %s\n",
sha1_to_hex(list->sha1), list->name);
/* this should not happen but just being defensive */
if (len > sizeof(line))
die("too long a refname '%s'", list->name);
write_or_die(fd, line, len);
}
return commit_lock_file(&packlock);
}
int delete_ref(const char *refname, unsigned char *sha1)
{
struct ref_lock *lock;
int err, i, ret = 0, flag = 0;
lock = lock_ref_sha1_basic(refname, sha1, &flag);
if (!lock)
return 1;
if (!(flag & REF_ISPACKED)) {
/* loose */
i = strlen(lock->lk->filename) - 5; /* .lock */
lock->lk->filename[i] = 0;
err = unlink(lock->lk->filename);
if (err) {
ret = 1;
error("unlink(%s) failed: %s",
lock->lk->filename, strerror(errno));
}
lock->lk->filename[i] = '.';
}
/* removing the loose one could have resurrected an earlier
* packed one. Also, if it was not loose we need to repack
* without it.
*/
ret |= repack_without_ref(refname);
err = unlink(lock->log_file);
if (err && errno != ENOENT)
fprintf(stderr, "warning: unlink(%s) failed: %s",
lock->log_file, strerror(errno));
invalidate_cached_refs();
unlock_ref(lock);
return ret;
} }
void unlock_ref(struct ref_lock *lock) void unlock_ref(struct ref_lock *lock)
@ -354,7 +708,7 @@ void unlock_ref(struct ref_lock *lock)
if (lock->lk) if (lock->lk)
rollback_lock_file(lock->lk); rollback_lock_file(lock->lk);
} }
free(lock->ref_file); free(lock->ref_name);
free(lock->log_file); free(lock->log_file);
free(lock); free(lock);
} }
@ -367,7 +721,8 @@ static int log_ref_write(struct ref_lock *lock,
char *logrec; char *logrec;
const char *committer; const char *committer;
if (log_all_ref_updates) { if (log_all_ref_updates &&
!strncmp(lock->ref_name, "refs/heads/", 11)) {
if (safe_create_leading_directories(lock->log_file) < 0) if (safe_create_leading_directories(lock->log_file) < 0)
return error("unable to create directory for %s", return error("unable to create directory for %s",
lock->log_file); lock->log_file);
@ -376,10 +731,20 @@ static int log_ref_write(struct ref_lock *lock,
logfd = open(lock->log_file, oflags, 0666); logfd = open(lock->log_file, oflags, 0666);
if (logfd < 0) { if (logfd < 0) {
if (!log_all_ref_updates && errno == ENOENT) if (!(oflags & O_CREAT) && errno == ENOENT)
return 0; return 0;
return error("Unable to append to %s: %s",
lock->log_file, strerror(errno)); if ((oflags & O_CREAT) && errno == EISDIR) {
if (remove_empty_directories(lock->log_file)) {
return error("There are still logs under '%s'",
lock->log_file);
}
logfd = open(lock->log_file, oflags, 0666);
}
if (logfd < 0)
return error("Unable to append to %s: %s",
lock->log_file, strerror(errno));
} }
committer = git_committer_info(1); committer = git_committer_info(1);
@ -426,12 +791,13 @@ int write_ref_sha1(struct ref_lock *lock,
unlock_ref(lock); unlock_ref(lock);
return -1; return -1;
} }
invalidate_cached_refs();
if (log_ref_write(lock, sha1, logmsg) < 0) { if (log_ref_write(lock, sha1, logmsg) < 0) {
unlock_ref(lock); unlock_ref(lock);
return -1; return -1;
} }
if (commit_lock_file(lock->lk)) { if (commit_lock_file(lock->lk)) {
error("Couldn't set %s", lock->ref_file); error("Couldn't set %s", lock->ref_name);
unlock_ref(lock); unlock_ref(lock);
return -1; return -1;
} }
@ -440,7 +806,7 @@ int write_ref_sha1(struct ref_lock *lock,
return 0; return 0;
} }
int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1)
{ {
const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
char *tz_c; char *tz_c;
@ -473,7 +839,7 @@ int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
if (!lastgt) if (!lastgt)
die("Log %s is corrupt.", logfile); die("Log %s is corrupt.", logfile);
date = strtoul(lastgt + 1, &tz_c, 10); date = strtoul(lastgt + 1, &tz_c, 10);
if (date <= at_time) { if (date <= at_time || cnt == 0) {
if (lastrec) { if (lastrec) {
if (get_sha1_hex(lastrec, logged_sha1)) if (get_sha1_hex(lastrec, logged_sha1))
die("Log %s is corrupt.", logfile); die("Log %s is corrupt.", logfile);
@ -504,6 +870,8 @@ int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
return 0; return 0;
} }
lastrec = rec; lastrec = rec;
if (cnt > 0)
cnt--;
} }
rec = logdata; rec = logdata;

21
refs.h
View File

@ -2,7 +2,7 @@
#define REFS_H #define REFS_H
struct ref_lock { struct ref_lock {
char *ref_file; char *ref_name;
char *log_file; char *log_file;
struct lock_file *lk; struct lock_file *lk;
unsigned char old_sha1[20]; unsigned char old_sha1[20];
@ -14,20 +14,23 @@ struct ref_lock {
* Calls the specified function for each ref file until it returns nonzero, * Calls the specified function for each ref file until it returns nonzero,
* and returns the value * and returns the value
*/ */
extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1)); #define REF_ISSYMREF 01
extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)); #define REF_ISPACKED 02
extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)); typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)); extern int head_ref(each_ref_fn, void *);
extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)); extern int for_each_ref(each_ref_fn, void *);
extern int for_each_tag_ref(each_ref_fn, void *);
extern int for_each_branch_ref(each_ref_fn, void *);
extern int for_each_remote_ref(each_ref_fn, void *);
/** Reads the refs file specified into sha1 **/ /** Reads the refs file specified into sha1 **/
extern int get_ref_sha1(const char *ref, unsigned char *sha1); extern int get_ref_sha1(const char *ref, unsigned char *sha1);
/** Locks a "refs/" ref returning the lock on success and NULL on failure. **/ /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist); extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
/** Locks any ref (for 'HEAD' type refs). */ /** Locks any ref (for 'HEAD' type refs). */
extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist); extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1);
/** Release any lock taken but not written. **/ /** Release any lock taken but not written. **/
extern void unlock_ref(struct ref_lock *lock); extern void unlock_ref(struct ref_lock *lock);
@ -36,7 +39,7 @@ extern void unlock_ref(struct ref_lock *lock);
extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg); extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
/** Reads log for the value of ref during at_time. **/ /** Reads log for the value of ref during at_time. **/
extern int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1); extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1);
/** Returns 0 if target has the right format for a ref. **/ /** Returns 0 if target has the right format for a ref. **/
extern int check_ref_format(const char *target); extern int check_ref_format(const char *target);

View File

@ -418,9 +418,6 @@ static void limit_list(struct rev_info *revs)
if (revs->max_age != -1 && (commit->date < revs->max_age)) if (revs->max_age != -1 && (commit->date < revs->max_age))
obj->flags |= UNINTERESTING; obj->flags |= UNINTERESTING;
if (revs->unpacked &&
has_sha1_pack(obj->sha1, revs->ignore_packed))
obj->flags |= UNINTERESTING;
add_parents_to_list(revs, commit, &list); add_parents_to_list(revs, commit, &list);
if (obj->flags & UNINTERESTING) { if (obj->flags & UNINTERESTING) {
mark_parents_uninteresting(commit); mark_parents_uninteresting(commit);
@ -468,7 +465,7 @@ static void limit_list(struct rev_info *revs)
static int all_flags; static int all_flags;
static struct rev_info *all_revs; static struct rev_info *all_revs;
static int handle_one_ref(const char *path, const unsigned char *sha1) static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct object *object = get_reference(all_revs, path, sha1, all_flags); struct object *object = get_reference(all_revs, path, sha1, all_flags);
add_pending_object(all_revs, object, ""); add_pending_object(all_revs, object, "");
@ -479,7 +476,7 @@ static void handle_all(struct rev_info *revs, unsigned flags)
{ {
all_revs = revs; all_revs = revs;
all_flags = flags; all_flags = flags;
for_each_ref(handle_one_ref); for_each_ref(handle_one_ref, NULL);
} }
static int add_parents_only(struct rev_info *revs, const char *arg, int flags) static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
@ -1015,7 +1012,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
add_pending_object(revs, object, def); add_pending_object(revs, object, def);
} }
if (revs->topo_order || revs->unpacked) if (revs->topo_order)
revs->limited = 1; revs->limited = 1;
if (revs->prune_data) { if (revs->prune_data) {
@ -1149,17 +1146,18 @@ struct commit *get_revision(struct rev_info *revs)
* that we'd otherwise have done in limit_list(). * that we'd otherwise have done in limit_list().
*/ */
if (!revs->limited) { if (!revs->limited) {
if ((revs->unpacked && if (revs->max_age != -1 &&
has_sha1_pack(commit->object.sha1, (commit->date < revs->max_age))
revs->ignore_packed)) ||
(revs->max_age != -1 &&
(commit->date < revs->max_age)))
continue; continue;
add_parents_to_list(revs, commit, &revs->commits); add_parents_to_list(revs, commit, &revs->commits);
} }
if (commit->object.flags & SHOWN) if (commit->object.flags & SHOWN)
continue; continue;
if (revs->unpacked && has_sha1_pack(commit->object.sha1,
revs->ignore_packed))
continue;
/* We want to show boundary commits only when their /* We want to show boundary commits only when their
* children are shown. When path-limiter is in effect, * children are shown. When path-limiter is in effect,
* rewrite_parents() drops some commits from getting shown, * rewrite_parents() drops some commits from getting shown,

View File

@ -215,7 +215,7 @@ static int ref_newer(const unsigned char *new_sha1,
static struct ref *local_refs, **local_tail; static struct ref *local_refs, **local_tail;
static struct ref *remote_refs, **remote_tail; static struct ref *remote_refs, **remote_tail;
static int one_local_ref(const char *refname, const unsigned char *sha1) static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct ref *ref; struct ref *ref;
int len = strlen(refname) + 1; int len = strlen(refname) + 1;
@ -230,7 +230,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1)
static void get_local_heads(void) static void get_local_heads(void)
{ {
local_tail = &local_refs; local_tail = &local_refs;
for_each_ref(one_local_ref); for_each_ref(one_local_ref, NULL);
} }
static int receive_status(int in) static int receive_status(int in)

View File

@ -7,7 +7,7 @@
/* refs */ /* refs */
static FILE *info_ref_fp; static FILE *info_ref_fp;
static int add_info_ref(const char *path, const unsigned char *sha1) static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{ {
struct object *o = parse_object(sha1); struct object *o = parse_object(sha1);
@ -34,7 +34,7 @@ static int update_info_refs(int force)
info_ref_fp = fopen(path1, "w"); info_ref_fp = fopen(path1, "w");
if (!info_ref_fp) if (!info_ref_fp)
return error("unable to update %s", path0); return error("unable to update %s", path0);
for_each_ref(add_info_ref); for_each_ref(add_info_ref, NULL);
fclose(info_ref_fp); fclose(info_ref_fp);
rename(path1, path0); rename(path1, path0);
free(path0); free(path0);

View File

@ -244,8 +244,6 @@ int check_repository_format_version(const char *var, const char *value)
repository_format_version = git_config_int(var, value); repository_format_version = git_config_int(var, value);
else if (strcmp(var, "core.sharedrepository") == 0) else if (strcmp(var, "core.sharedrepository") == 0)
shared_repository = git_config_perm(var, value); shared_repository = git_config_perm(var, value);
else if (strcmp(var, "receive.denynonfastforwards") == 0)
deny_non_fast_forwards = git_config_bool(var, value);
return 0; return 0;
} }

View File

@ -1417,9 +1417,10 @@ static int link_temp_to_file(const char *tmpfile, const char *filename)
dir = strrchr(filename, '/'); dir = strrchr(filename, '/');
if (dir) { if (dir) {
*dir = 0; *dir = 0;
mkdir(filename, 0777); if (!mkdir(filename, 0777) && adjust_shared_perm(filename)) {
if (adjust_shared_perm(filename)) *dir = '/';
return -2; return -2;
}
*dir = '/'; *dir = '/';
if (!link(tmpfile, filename)) if (!link(tmpfile, filename))
return 0; return 0;

View File

@ -247,26 +247,25 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
NULL NULL
}; };
static const char *warning = "warning: refname '%.*s' is ambiguous.\n"; static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
const char **p, *pathname; const char **p, *ref;
char *real_path = NULL; char *real_ref = NULL;
int refs_found = 0, am; int refs_found = 0;
unsigned long at_time = (unsigned long)-1; int at, reflog_len;
unsigned char *this_result; unsigned char *this_result;
unsigned char sha1_from_ref[20]; unsigned char sha1_from_ref[20];
if (len == 40 && !get_sha1_hex(str, sha1)) if (len == 40 && !get_sha1_hex(str, sha1))
return 0; return 0;
/* At a given period of time? "@{2 hours ago}" */ /* basic@{time or number} format to query ref-log */
for (am = 1; am < len - 1; am++) { reflog_len = at = 0;
if (str[am] == '@' && str[am+1] == '{' && str[len-1] == '}') { if (str[len-1] == '}') {
int date_len = len - am - 3; for (at = 1; at < len - 1; at++) {
char *date_spec = xmalloc(date_len + 1); if (str[at] == '@' && str[at+1] == '{') {
strlcpy(date_spec, str + am + 2, date_len + 1); reflog_len = (len-1) - (at+2);
at_time = approxidate(date_spec); len = at;
free(date_spec); break;
len = am; }
break;
} }
} }
@ -276,10 +275,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
for (p = fmt; *p; p++) { for (p = fmt; *p; p++) {
this_result = refs_found ? sha1_from_ref : sha1; this_result = refs_found ? sha1_from_ref : sha1;
pathname = resolve_ref(git_path(*p, len, str), this_result, 1); ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);
if (pathname) { if (ref) {
if (!refs_found++) if (!refs_found++)
real_path = xstrdup(pathname); real_ref = xstrdup(ref);
if (!warn_ambiguous_refs) if (!warn_ambiguous_refs)
break; break;
} }
@ -291,14 +290,25 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
if (warn_ambiguous_refs && refs_found > 1) if (warn_ambiguous_refs && refs_found > 1)
fprintf(stderr, warning, len, str); fprintf(stderr, warning, len, str);
if (at_time != (unsigned long)-1) { if (reflog_len) {
read_ref_at( /* Is it asking for N-th entry, or approxidate? */
real_path + strlen(git_path(".")) - 1, int nth, i;
at_time, unsigned long at_time;
sha1); for (i = nth = 0; 0 <= nth && i < reflog_len; i++) {
char ch = str[at+2+i];
if ('0' <= ch && ch <= '9')
nth = nth * 10 + ch - '0';
else
nth = -1;
}
if (0 <= nth)
at_time = 0;
else
at_time = approxidate(str + at + 2);
read_ref_at(real_ref, at_time, nth, sha1);
} }
free(real_path); free(real_ref);
return 0; return 0;
} }

View File

@ -30,11 +30,8 @@ rm -f .git/$m
test_expect_success \ test_expect_success \
"fail to create $n" \ "fail to create $n" \
"touch .git/$n_dir "touch .git/$n_dir
git-update-ref $n $A >out 2>err git-update-ref $n $A >out 2>err"'
test "'$? = 1 && test $? != 0'
test "" = "$(cat out)" &&
grep "error: unable to resolve reference" err &&
grep '"$n err"
rm -f .git/$n_dir out err rm -f .git/$n_dir out err
test_expect_success \ test_expect_success \

View File

@ -17,13 +17,10 @@ test_expect_success \
git-commit -m "Initial commit." && git-commit -m "Initial commit." &&
HEAD=$(git-rev-parse --verify HEAD)' HEAD=$(git-rev-parse --verify HEAD)'
test_expect_success \
'git branch --help should return success now.' \
'git-branch --help'
test_expect_failure \ test_expect_failure \
'git branch --help should not have created a bogus branch' \ 'git branch --help should not have created a bogus branch' \
'test -f .git/refs/heads/--help' 'git-branch --help </dev/null >/dev/null 2>/dev/null || :
test -f .git/refs/heads/--help'
test_expect_success \ test_expect_success \
'git branch abc should create a branch' \ 'git branch abc should create a branch' \
@ -34,7 +31,7 @@ test_expect_success \
'git-branch a/b/c && test -f .git/refs/heads/a/b/c' 'git-branch a/b/c && test -f .git/refs/heads/a/b/c'
cat >expect <<EOF cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from HEAD 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
EOF EOF
test_expect_success \ test_expect_success \
'git branch -l d/e/f should create a branch and a log' \ 'git branch -l d/e/f should create a branch and a log' \

99
t/t3210-pack-refs.sh Executable file
View File

@ -0,0 +1,99 @@
#!/bin/sh
#
# Copyright (c) 2005 Amos Waterland
# Copyright (c) 2006 Christian Couder
#
test_description='git pack-refs should not change the branch semantic
This test runs git pack-refs and git show-ref and checks that the branch
semantic is still the same.
'
. ./test-lib.sh
echo '[core] logallrefupdates = true' >>.git/config
test_expect_success \
'prepare a trivial repository' \
'echo Hello > A &&
git-update-index --add A &&
git-commit -m "Initial commit." &&
HEAD=$(git-rev-parse --verify HEAD)'
SHA1=
test_expect_success \
'see if git show-ref works as expected' \
'git-branch a &&
SHA1=`cat .git/refs/heads/a` &&
echo "$SHA1 refs/heads/a" >expect &&
git-show-ref a >result &&
diff expect result'
test_expect_success \
'see if a branch still exists when packed' \
'git-branch b &&
git-pack-refs --all &&
rm .git/refs/heads/b &&
echo "$SHA1 refs/heads/b" >expect &&
git-show-ref b >result &&
diff expect result'
test_expect_failure \
'git branch c/d should barf if branch c exists' \
'git-branch c &&
git-pack-refs --all &&
rm .git/refs/heads/c &&
git-branch c/d'
test_expect_success \
'see if a branch still exists after git pack-refs --prune' \
'git-branch e &&
git-pack-refs --all --prune &&
echo "$SHA1 refs/heads/e" >expect &&
git-show-ref e >result &&
diff expect result'
test_expect_failure \
'see if git pack-refs --prune remove ref files' \
'git-branch f &&
git-pack-refs --all --prune &&
ls .git/refs/heads/f'
test_expect_success \
'git branch g should work when git branch g/h has been deleted' \
'git-branch g/h &&
git-pack-refs --all --prune &&
git-branch -d g/h &&
git-branch g &&
git-pack-refs --all &&
git-branch -d g'
test_expect_failure \
'git branch i/j/k should barf if branch i exists' \
'git-branch i &&
git-pack-refs --all --prune &&
git-branch i/j/k'
test_expect_success \
'test git branch k after branch k/l/m and k/lm have been deleted' \
'git-branch k/l &&
git-branch k/lm &&
git-branch -d k/l &&
git-branch k/l/m &&
git-branch -d k/l/m &&
git-branch -d k/lm &&
git-branch k'
test_expect_success \
'test git branch n after some branch deletion and pruning' \
'git-branch n/o &&
git-branch n/op &&
git-branch -d n/o &&
git-branch n/o/p &&
git-branch -d n/op &&
git-pack-refs --all --prune &&
git-branch -d n/o/p &&
git-branch n'
test_done

View File

@ -215,6 +215,24 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
return retval; return retval;
} }
int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_options *opt)
{
int retval;
void *tree;
struct tree_desc empty, real;
tree = read_object_with_reference(new, tree_type, &real.size, NULL);
if (!tree)
die("unable to read root tree (%s)", sha1_to_hex(new));
real.buf = tree;
empty.size = 0;
empty.buf = "";
retval = diff_tree(&empty, &real, base, opt);
free(tree);
return retval;
}
static int count_paths(const char **paths) static int count_paths(const char **paths)
{ {
int i = 0; int i = 0;

View File

@ -420,7 +420,7 @@ static void receive_needs(void)
} }
} }
static int send_ref(const char *refname, const unsigned char *sha1) static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{ {
static const char *capabilities = "multi_ack thin-pack side-band side-band-64k ofs-delta"; static const char *capabilities = "multi_ack thin-pack side-band side-band-64k ofs-delta";
struct object *o = parse_object(sha1); struct object *o = parse_object(sha1);
@ -448,8 +448,8 @@ static int send_ref(const char *refname, const unsigned char *sha1)
static void upload_pack(void) static void upload_pack(void)
{ {
reset_timeout(); reset_timeout();
head_ref(send_ref); head_ref(send_ref, NULL);
for_each_ref(send_ref); for_each_ref(send_ref, NULL);
packet_flush(1); packet_flush(1);
receive_needs(); receive_needs();
if (want_obj.nr) { if (want_obj.nr) {

View File

@ -41,10 +41,8 @@ void wt_status_prepare(struct wt_status *s)
s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0; s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0;
head = resolve_ref(git_path("HEAD"), sha1, 0); head = resolve_ref("HEAD", sha1, 0, NULL);
s->branch = head ? s->branch = head ? xstrdup(head) : NULL;
strdup(head + strlen(get_git_dir()) + 1) :
NULL;
s->reference = "HEAD"; s->reference = "HEAD";
s->amend = 0; s->amend = 0;