diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Documentation/git-clone.txt | 9 | ||||
-rw-r--r-- | Documentation/git-describe.txt | 79 | ||||
-rw-r--r-- | Documentation/git-rev-parse.txt | 7 | ||||
-rw-r--r-- | Documentation/git.txt | 3 | ||||
-rw-r--r-- | Documentation/tutorial.txt | 18 | ||||
-rwxr-xr-x | GIT-VERSION-GEN | 18 | ||||
-rw-r--r-- | Makefile | 23 | ||||
-rw-r--r-- | cache.h | 2 | ||||
-rw-r--r-- | connect.c | 40 | ||||
-rw-r--r-- | debian/changelog | 12 | ||||
-rw-r--r-- | describe.c | 163 | ||||
-rw-r--r-- | environment.c | 1 | ||||
-rwxr-xr-x | git-checkout.sh | 11 | ||||
-rwxr-xr-x | git-clone.sh | 33 | ||||
-rwxr-xr-x | git-merge-recursive.py | 38 | ||||
-rwxr-xr-x | git-whatchanged.sh | 10 | ||||
-rw-r--r-- | init-db.c | 32 | ||||
-rw-r--r-- | ls-tree.c | 22 | ||||
-rw-r--r-- | pack-redundant.c | 149 | ||||
-rw-r--r-- | receive-pack.c | 120 | ||||
-rw-r--r-- | rev-parse.c | 12 | ||||
-rw-r--r-- | send-pack.c | 59 | ||||
-rw-r--r-- | setup.c | 2 | ||||
-rw-r--r-- | sha1_file.c | 32 | ||||
-rwxr-xr-x | t/t6022-merge-rename.sh | 37 |
26 files changed, 750 insertions, 183 deletions
diff --git a/.gitignore b/.gitignore index 6bd508e4be..47d76f407e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +GIT-VERSION-FILE git git-add git-am diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 8410a6d381..f943f267da 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -8,7 +8,7 @@ git-clone - Clones a repository. SYNOPSIS -------- -'git-clone' [-l [-s]] [-q] [-n] [-u <upload-pack>] <repository> [<directory>] +'git-clone' [-l [-s]] [-q] [-n] [-o <name>] [-u <upload-pack>] <repository> [<directory>] DESCRIPTION ----------- @@ -56,6 +56,13 @@ OPTIONS -n:: No checkout of HEAD is performed after the clone is complete. +-o <name>:: + Instead of using the branch name 'origin' to keep track + of the upstream repository, use <name> instead. Note + that the shorthand name stored in `remotes/origin` is + not affected, but the local branch name to pull the + remote `master` branch into is. + --upload-pack <upload-pack>:: -u <upload-pack>:: When given, and the repository to clone from is handled diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt new file mode 100644 index 0000000000..0efe82ae1e --- /dev/null +++ b/Documentation/git-describe.txt @@ -0,0 +1,79 @@ +git-describe(1) +=============== + +NAME +---- +git-describe - Show the most recent tag that is reachable from a commit. + + +SYNOPSIS +-------- +'git-describe' [--all] [--tags] [--abbrev=<n>] <committish>... + +DESCRIPTION +----------- +The command finds the most recent tag that is reachable from a +commit, and if the commit itself is pointed at by the tag, shows +the tag. Otherwise, it suffixes the tag name with abbreviated +object name of the commit. + + +OPTIONS +------- +<committish>:: + The object name of the comittish. + +--all:: + Instead of using only the annotated tags, use any ref + found in `.git/refs/`. + +--tags:: + Instead of using only the annotated tags, use any tag + found in `.git/refs/tags`. + +--abbrev=<n>:: + Instead of using the default 8 hexadecimal digits as the + abbreviated object name, use <n> digits. + + +EXAMPLES +-------- + +With something like git.git current tree, I get: + + [torvalds@g5 git]$ git-describe parent + v1.0.4-g2414721b + +i.e. the current head of my "parent" branch is based on v1.0.4, +but since it has a few commits on top of that, it has added the +git hash of the thing to the end: "-g" + 8-char shorthand for +the commit `2414721b194453f058079d897d13c4e377f92dc6`. + +Doing a "git-describe" on a tag-name will just show the tag name: + + [torvalds@g5 git]$ git-describe v1.0.4 + v1.0.4 + +With --all, the command can use branch heads as references, so +the output shows the reference path as well: + + [torvalds@g5 git]$ git describe --all --abbrev=4 v1.0.5^2 + tags/v1.0.0-g975b + + [torvalds@g5 git]$ git describe --all HEAD^ + heads/lt/describe-g975b + + +Author +------ +Written by Linus Torvalds <torvalds@osdl.org>, but somewhat +butchered by Junio C Hamano <junkio@cox.net> + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. + +GIT +--- +Part of the gitlink:git[7] suite + diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 431b8f6e06..d638bfc202 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -68,10 +68,15 @@ OPTIONS Show all refs found in `$GIT_DIR/refs`. --show-prefix:: - When the command is invoked from a directory show the + When the command is invoked from a subdirectory, show the path of the current directory relative to the top-level directory. +--show-cdup:: + When the command is invoked from a subdirectory, show the + path of the top-level directory relative to the current + directory (typically a sequence of "../", or an empty string). + --since=datestring, --after=datestring:: Parses the date string, and outputs corresponding --max-age= parameter for git-rev-list command. diff --git a/Documentation/git.txt b/Documentation/git.txt index 5f068c2a1a..90c5bfaf64 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -139,6 +139,9 @@ Interrogation commands gitlink:git-cat-file[1]:: Provide content or type/size information for repository objects. +gitlink:git-describe[1]:: + Show the most recent tag that is reachable from a commit. + gitlink:git-diff-index[1]:: Compares content and mode of blobs between the index and repository. diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index 3a5c56e24e..edd91cbbe4 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -1695,6 +1695,24 @@ You still need to create UNIX user accounts for each developer, and put them in the same group. Make sure that the repository shared among these developers is writable by that group. +. Initializing the shared repository with `git-init-db --shared` +helps somewhat. + +. Run the following in the shared repository: ++ +------------ +$ chgrp -R $group repo.git +$ find repo.git -type d -print | xargs chmod ug+rwx,g+s +$ GIT_DIR=repo.git git repo-config core.sharedrepository true +------------ + +The above measures make sure that directories lazily created in +`$GIT_DIR` are writable by group members. You, as the +repository administrator, are still responsible to make sure +your developers belong to that shared repository group and set +their umask to a value no stricter than 027 (i.e. at least allow +reading and searching by group members). + You can implement finer grained branch policies using update hooks. There is a document ("control access to branches") in Documentation/howto by Carl Baldwin and JC outlining how to (1) diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN new file mode 100755 index 0000000000..196402ce66 --- /dev/null +++ b/GIT-VERSION-GEN @@ -0,0 +1,18 @@ +#!/bin/sh + +GVF=GIT-VERSION-FILE + +VN=$(git describe --abbrev=4 HEAD 2>/dev/null) || VN=v1.0.GIT +VN=$(expr "$VN" : v'\(.*\)') +if test -r $GVF +then + VC=$(sed -e 's/^GIT_VERSION = //' <$GVF) +else + VC=unset +fi +test "$VN" = "$VC" || { + echo >&2 "GIT_VERSION = $VN" + echo "GIT_VERSION = $VN" >$GVF +} + + @@ -55,7 +55,9 @@ all: # Define USE_STDEV below if you want git to care about the underlying device # change being considered an inode change from the update-cache perspective. -GIT_VERSION = 1.0.6 +GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE + @sh ./GIT-VERSION-GEN +-include GIT-VERSION-FILE # CFLAGS and LDFLAGS are for the users to override from the command line. @@ -135,7 +137,8 @@ PROGRAMS = \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ git-upload-pack$X git-verify-pack$X git-write-tree$X \ git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \ - git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X + git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ + git-describe$X # what 'all' will build and 'install' will install. ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X @@ -368,7 +371,7 @@ all: $(ALL_PROGRAMS) all: $(MAKE) -C templates -git$X: git.c $(LIB_FILE) Makefile +git$X: git.c $(LIB_FILE) $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \ $(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE) @@ -397,8 +400,12 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py git-cherry-pick: git-revert cp $< $@ -# format-patch records GIT_VERSION -git-format-patch: Makefile +# These can record GIT_VERSION +git$X git.spec \ + $(patsubst %.sh,%,$(SCRIPT_SH)) \ + $(patsubst %.perl,%,$(SCRIPT_PERL)) \ + $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ + : GIT-VERSION-FILE %.o: %.c $(CC) -o $*.o -c $(ALL_CFLAGS) $< @@ -474,7 +481,7 @@ install-doc: ### Maintainer's dist rules -git.spec: git.spec.in Makefile +git.spec: git.spec.in sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@ GIT_TARNAME=git-$(GIT_VERSION) @@ -509,4 +516,8 @@ clean: $(MAKE) -C Documentation/ clean $(MAKE) -C templates clean $(MAKE) -C t/ clean + rm -f GIT-VERSION-FILE + +.PHONY: all install clean +.PHONY: .FORCE-GIT-VERSION-FILE @@ -159,6 +159,7 @@ extern void rollback_index_file(struct cache_file *); extern int trust_executable_bit; extern int only_use_symrefs; extern int diff_rename_limit_default; +extern int shared_repository; #define GIT_REPO_VERSION 0 extern int repository_format_version; @@ -183,6 +184,7 @@ extern const unsigned char null_sha1[20]; int git_mkstemp(char *path, size_t n, const char *template); +int adjust_shared_perm(const char *path); int safe_create_leading_directories(char *path); char *safe_strncpy(char *, const char *, size_t); char *enter_repo(char *path, int strict); @@ -34,10 +34,6 @@ struct ref **get_remote_heads(int in, struct ref **list, die("protocol error: expected sha/ref, got '%s'", buffer); name = buffer + 41; - if (ignore_funny && 45 < len && !memcmp(name, "refs/", 5) && - check_ref_format(name + 5)) - continue; - name_len = strlen(name); if (len != name_len + 41) { if (server_capabilities) @@ -45,6 +41,10 @@ struct ref **get_remote_heads(int in, struct ref **list, server_capabilities = strdup(name + name_len + 1); } + if (ignore_funny && 45 < len && !memcmp(name, "refs/", 5) && + check_ref_format(name + 5)) + continue; + if (nr_match && !path_match(name, nr_match, match)) continue; ref = xcalloc(1, sizeof(*ref) + len - 40); @@ -561,7 +561,8 @@ int git_connect(int fd[2], char *url, const char *prog) { char command[1024]; char *host, *path = url; - char *colon = NULL; + char *end; + int c; int pipefd[2][2]; pid_t pid; enum protocol protocol = PROTO_LOCAL; @@ -571,15 +572,30 @@ int git_connect(int fd[2], char *url, const char *prog) *host = '\0'; protocol = get_protocol(url); host += 3; - path = strchr(host, '/'); - } - else { + c = '/'; + } else { host = url; - if ((colon = strchr(host, ':'))) { + c = ':'; + } + + if (host[0] == '[') { + end = strchr(host + 1, ']'); + if (end) { + *end = 0; + end++; + host++; + } else + end = host; + } else + end = host; + + path = strchr(end, c); + if (c == ':') { + if (path) { protocol = PROTO_SSH; - *colon = '\0'; - path = colon + 1; - } + *path++ = '\0'; + } else + path = host; } if (!path || !*path) diff --git a/debian/changelog b/debian/changelog index 88e5755f79..d7f7591079 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +git-core (1.0.GIT-0) unstable; urgency=low + + * Post GIT 1.0 development track. + + -- Junio C Hamano <junkio@cox.net> Wed, 21 Dec 2005 22:28:33 -0800 + +git-core (1.0.0.GIT-0) unstable; urgency=low + + * Post GIT 1.0.0 development track. + + -- Junio C Hamano <junkio@cox.net> Wed, 21 Dec 2005 12:12:05 -0800 + git-core (1.0.4-0) unstable; urgency=low * GIT 1.0.4. diff --git a/describe.c b/describe.c new file mode 100644 index 0000000000..84d96b5b82 --- /dev/null +++ b/describe.c @@ -0,0 +1,163 @@ +#include "cache.h" +#include "commit.h" +#include "tag.h" +#include "refs.h" + +#define SEEN (1u << 0) + +static const char describe_usage[] = +"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*"; + +static int all = 0; /* Default to annotated tags only */ +static int tags = 0; /* But allow any tags if --tags is specified */ + +#define DEFAULT_ABBREV 8 /* maybe too many */ +static int abbrev = DEFAULT_ABBREV; + +static int names = 0, allocs = 0; +static struct commit_name { + const struct commit *commit; + int prio; /* annotated tag = 2, tag = 1, head = 0 */ + char path[]; +} **name_array = NULL; + +static struct commit_name *match(struct commit *cmit) +{ + int i = names; + struct commit_name **p = name_array; + + while (i-- > 0) { + struct commit_name *n = *p++; + if (n->commit == cmit) + return n; + } + return NULL; +} + +static void add_to_known_names(const char *path, + const struct commit *commit, + int prio) +{ + int idx; + int len = strlen(path)+1; + struct commit_name *name = xmalloc(sizeof(struct commit_name) + len); + + name->commit = commit; + name->prio = prio; + memcpy(name->path, path, len); + idx = names; + if (idx >= allocs) { + allocs = (idx + 50) * 3 / 2; + name_array = xrealloc(name_array, allocs*sizeof(*name_array)); + } + name_array[idx] = name; + names = ++idx; +} + +static int get_name(const char *path, const unsigned char *sha1) +{ + struct commit *commit = lookup_commit_reference_gently(sha1, 1); + struct object *object; + int prio; + + if (!commit) + return 0; + object = parse_object(sha1); + /* If --all, then any refs are used. + * If --tags, then any tags are used. + * Otherwise only annotated tags are used. + */ + if (!strncmp(path, "refs/tags/", 10)) { + if (object->type == tag_type) + prio = 2; + else + prio = 1; + } + else + prio = 0; + + if (!all) { + if (!prio) + return 0; + if (!tags && prio < 2) + return 0; + } + add_to_known_names(all ? path + 5 : path + 10, commit, prio); + return 0; +} + +static int compare_names(const void *_a, const void *_b) +{ + struct commit_name *a = *(struct commit_name **)_a; + struct commit_name *b = *(struct commit_name **)_b; + unsigned long a_date = a->commit->date; + unsigned long b_date = b->commit->date; + + if (a->prio != b->prio) + return b->prio - a->prio; + return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1; +} + +static void describe(struct commit *cmit) +{ + struct commit_list *list; + static int initialized = 0; + struct commit_name *n; + + if (!initialized) { + initialized = 1; + for_each_ref(get_name); + qsort(name_array, names, sizeof(*name_array), compare_names); + } + + n = match(cmit); + if (n) { + printf("%s\n", n->path); + return; + } + + list = NULL; + commit_list_insert(cmit, &list); + while (list) { + struct commit *c = pop_most_recent_commit(&list, SEEN); + n = match(c); + if (n) { + printf("%s-g%s\n", n->path, + find_unique_abbrev(cmit->object.sha1, abbrev)); + return; + } + } +} + +int main(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + unsigned char sha1[20]; + struct commit *cmit; + + if (!strcmp(arg, "--all")) { + all = 1; + continue; + } + if (!strcmp(arg, "--tags")) { + tags = 1; + continue; + } + if (!strncmp(arg, "--abbrev=", 9)) { + abbrev = strtoul(arg + 9, NULL, 10); + if (abbrev < 4 || 40 <= abbrev) + abbrev = DEFAULT_ABBREV; + continue; + } + if (get_sha1(arg, sha1) < 0) + usage(describe_usage); + cmit = lookup_commit_reference(sha1); + if (!cmit) + usage(describe_usage); + describe(cmit); + } + return 0; +} diff --git a/environment.c b/environment.c index 0886ad38f9..0596fc647b 100644 --- a/environment.c +++ b/environment.c @@ -15,6 +15,7 @@ int trust_executable_bit = 1; int only_use_symrefs = 0; int repository_format_version = 0; char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8"; +int shared_repository = 0; static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file; diff --git a/git-checkout.sh b/git-checkout.sh index 36308d22c6..3bbd111773 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -1,6 +1,7 @@ #!/bin/sh USAGE='[-f] [-b <new_branch>] [<branch>] [<paths>...]' +SUBDIRECTORY_OK=Sometimes . git-sh-setup old=$(git-rev-parse HEAD) @@ -79,7 +80,7 @@ then # from a specific tree-ish; note that this is for # rescuing paths and is never meant to remove what # is not in the named tree-ish. - git-ls-tree -r "$new" "$@" | + git-ls-tree --full-name -r "$new" "$@" | git-update-index --index-info || exit $? fi git-checkout-index -f -u -- "$@" @@ -95,6 +96,14 @@ else fi fi +# We are switching branches and checking out trees, so +# we *NEED* to be at the toplevel. +cdup=$(git-rev-parse --show-cdup) +if test ! -z "$cdup" +then + cd "$cdup" +fi + [ -z "$new" ] && new=$old # If we don't have an old branch that we're switching to, diff --git a/git-clone.sh b/git-clone.sh index 280cc2e81e..377d59e62c 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -9,7 +9,7 @@ unset CDPATH usage() { - echo >&2 "Usage: $0 [-l [-s]] [-q] [-u <upload-pack>] [-n] <repo> [<dir>]" + echo >&2 "Usage: $0 [-l [-s]] [-q] [-u <upload-pack>] [-o <name>] [-n] <repo> [<dir>]" exit 1 } @@ -31,25 +31,11 @@ clone_dumb_http () { cd "$2" && clone_tmp='.git/clone-tmp' && mkdir -p "$clone_tmp" || exit 1 - http_fetch "$1/info/refs" "$clone_tmp/refs" && - http_fetch "$1/objects/info/packs" "$clone_tmp/packs" || { + http_fetch "$1/info/refs" "$clone_tmp/refs" || { echo >&2 "Cannot get remote repository information. Perhaps git-update-server-info needs to be run there?" exit 1; } - while read type name - do - case "$type" in - P) ;; - *) continue ;; - esac && - - idx=`expr "$name" : '\(.*\)\.pack'`.idx - http_fetch "$1/objects/pack/$name" ".git/objects/pack/$name" && - http_fetch "$1/objects/pack/$idx" ".git/objects/pack/$idx" && - git-verify-pack ".git/objects/pack/$idx" || exit 1 - done <"$clone_tmp/packs" - while read sha1 refname do name=`expr "$refname" : 'refs/\(.*\)'` && @@ -67,6 +53,7 @@ use_local=no local_shared=no no_checkout= upload_pack= +origin=origin while case "$#,$1" in 0,*) break ;; @@ -75,6 +62,14 @@ while *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) local_shared=yes; use_local=yes ;; *,-q|*,--quiet) quiet=-q ;; + 1,-o) usage;; + *,-o) + git-check-ref-format "$2" || { + echo >&2 "'$2' is not suitable for a branch name" + exit 1 + } + origin="$2"; shift + ;; 1,-u|1,--upload-pack) usage ;; *,-u|*,--upload-pack) shift @@ -208,14 +203,14 @@ then mkdir -p .git/remotes && echo >.git/remotes/origin \ "URL: $repo -Pull: $head_points_at:origin" && - cp ".git/refs/heads/$head_points_at" .git/refs/heads/origin && +Pull: $head_points_at:$origin" && + git-update-ref "refs/heads/$origin" $(git-rev-parse HEAD) && find .git/refs/heads -type f -print | while read ref do head=`expr "$ref" : '.git/refs/heads/\(.*\)'` && test "$head_points_at" = "$head" || - test "origin" = "$head" || + test "$origin" = "$head" || echo "Pull: ${head}:${head}" done >>.git/remotes/origin esac diff --git a/git-merge-recursive.py b/git-merge-recursive.py index f1320a6958..56c3641abb 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -283,12 +283,20 @@ def updateFileExt(sha, mode, path, updateCache, updateWd): def setIndexStages(path, oSHA1, oMode, aSHA1, aMode, - bSHA1, bMode): + bSHA1, bMode, + clear=True): + istring = [] + if clear: + istring.append("0 " + ("0" * 40) + "\t" + path + "\0") + if oMode: + istring.append("%o %s %d\t%s\0" % (oMode, oSHA1, 1, path)) + if aMode: + istring.append("%o %s %d\t%s\0" % (aMode, aSHA1, 2, path)) + if bMode: + istring.append("%o %s %d\t%s\0" % (bMode, bSHA1, 3, path)) + runProgram(['git-update-index', '-z', '--index-info'], - input="0 " + ("0" * 40) + "\t" + path + "\0" + \ - "%o %s %d\t%s\0" % (oMode, oSHA1, 1, path) + \ - "%o %s %d\t%s\0" % (aMode, aSHA1, 2, path) + \ - "%o %s %d\t%s\0" % (bMode, bSHA1, 3, path)) + input="".join(istring)) def removeFile(clean, path): updateCache = cacheOnly or clean @@ -570,7 +578,7 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB): continue ren1.processed = True - removeFile(True, ren1.srcName) + if ren2: # Renamed in 1 and renamed in 2 assert(ren1.srcName == ren2.srcName) @@ -598,13 +606,19 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB): 'adding as', dstName2, 'instead.') removeFile(False, ren2.dstName) else: - dstName2 = ren1.dstName + dstName2 = ren2.dstName + setIndexStages(dstName1, + None, None, + ren1.dstSha, ren1.dstMode, + None, None) + setIndexStages(dstName2, + None, None, + None, None, + ren2.dstSha, ren2.dstMode) - # NEEDSWORK: place dstNameA at stage 2 and dstNameB at stage 3 - # What about other stages??? - updateFile(False, ren1.dstSha, ren1.dstMode, dstName1) - updateFile(False, ren2.dstSha, ren2.dstMode, dstName2) else: + removeFile(True, ren1.srcName) + [resSha, resMode, clean, merge] = \ mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode, ren1.dstName, ren1.dstSha, ren1.dstMode, @@ -630,6 +644,8 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB): updateFile(clean, resSha, resMode, ren1.dstName) else: + removeFile(True, ren1.srcName) + # Renamed in 1, maybe changed in 2 if renamesA == renames1: stage = 3 diff --git a/git-whatchanged.sh b/git-whatchanged.sh index b170f74a94..80e2500e0e 100755 --- a/git-whatchanged.sh +++ b/git-whatchanged.sh @@ -4,9 +4,15 @@ USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-d SUBDIRECTORY_OK='Yes' . git-sh-setup +diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") +test -z "$diff_tree_flags" && + diff_tree_flags=$(git-repo-config --get whatchanged.difftree) +test -z "$diff_tree_flags" && + diff_tree_flags='-M --abbrev' + rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") && -diff_tree_args=$(git-rev-parse --sq --no-revs "$@") && +diff_tree_args=$(git-rev-parse --sq --no-revs --no-flags "$@") && eval "git-rev-list $rev_list_args" | -eval "git-diff-tree --stdin --pretty -r $diff_tree_args" | +eval "git-diff-tree --stdin --pretty -r $diff_tree_flags $diff_tree_args" | LESS="$LESS -S" ${PAGER:-less} @@ -9,7 +9,7 @@ #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates/" #endif -static void safe_create_dir(const char *dir) +static void safe_create_dir(const char *dir, int share) { if (mkdir(dir, 0777) < 0) { if (errno != EEXIST) { @@ -17,6 +17,8 @@ static void safe_create_dir(const char *dir) exit(1); } } + else if (share && adjust_shared_perm(dir)) + die("Could not make %s writable by group\n", dir); } static int copy_file(const char *dst, const char *src, int mode) @@ -32,6 +34,10 @@ static int copy_file(const char *dst, const char *src, int mode) } status = copy_fd(fdi, fdo); close(fdo); + + if (!status && adjust_shared_perm(dst)) + return -1; + return status; } @@ -48,7 +54,7 @@ static void copy_templates_1(char *path, int baselen, * with the way the namespace under .git/ is organized, should * be really carefully chosen. */ - safe_create_dir(path); + safe_create_dir(path, 1); while ((de = readdir(dir)) != NULL) { struct stat st_git, st_template; int namelen; @@ -176,11 +182,11 @@ static void create_default_files(const char *git_dir, char *template_path) * Create .git/refs/{heads,tags} */ strcpy(path + len, "refs"); - safe_create_dir(path); + safe_create_dir(path, 1); strcpy(path + len, "refs/heads"); - safe_create_dir(path); + safe_create_dir(path, 1); strcpy(path + len, "refs/tags"); - safe_create_dir(path); + safe_create_dir(path, 1); /* First copy the templates -- we might have the default * config file there, in which case we would want to read @@ -220,7 +226,7 @@ static void create_default_files(const char *git_dir, char *template_path) } static const char init_db_usage[] = -"git-init-db [--template=<template-directory>]"; +"git-init-db [--template=<template-directory>] [--shared]"; /* * If you want to, you can share the DB area with any number of branches. @@ -239,6 +245,8 @@ int main(int argc, char **argv) char *arg = argv[1]; if (!strncmp(arg, "--template=", 11)) template_dir = arg+11; + else if (!strcmp(arg, "--shared")) + shared_repository = 1; else die(init_db_usage); } @@ -251,7 +259,7 @@ int main(int argc, char **argv) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; fprintf(stderr, "defaulting to local storage area\n"); } - safe_create_dir(git_dir); + safe_create_dir(git_dir, 0); /* Check to see if the repository version is right. * Note that a newly created repository does not have @@ -270,10 +278,14 @@ int main(int argc, char **argv) path = xmalloc(len + 40); memcpy(path, sha1_dir, len); - safe_create_dir(sha1_dir); + safe_create_dir(sha1_dir, 1); strcpy(path+len, "/pack"); - safe_create_dir(path); + safe_create_dir(path, 1); strcpy(path+len, "/info"); - safe_create_dir(path); + safe_create_dir(path, 1); + + if (shared_repository) + git_config_set("core.sharedRepository", "true"); + return 0; } @@ -15,9 +15,11 @@ static int line_termination = '\n'; #define LS_NAME_ONLY 8 static int ls_options = 0; const char **pathspec; +static int chomp_prefix = 0; +static const char *prefix; static const char ls_tree_usage[] = - "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] <tree-ish> [path...]"; + "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] <tree-ish> [path...]"; static int show_recursive(const char *base, int baselen, const char *pathname) { @@ -49,7 +51,8 @@ static int show_recursive(const char *base, int baselen, const char *pathname) } } -static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) +static int show_tree(unsigned char *sha1, const char *base, int baselen, + const char *pathname, unsigned mode, int stage) { int retval = 0; const char *type = "blob"; @@ -65,21 +68,28 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c else if (ls_options & LS_TREE_ONLY) return 0; + if (chomp_prefix && + (baselen < chomp_prefix || memcmp(prefix, base, chomp_prefix))) + return 0; + if (!(ls_options & LS_NAME_ONLY)) printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1)); - write_name_quoted(base, baselen, pathname, line_termination, stdout); + write_name_quoted(base + chomp_prefix, baselen - chomp_prefix, + pathname, + line_termination, stdout); putchar(line_termination); return retval; } int main(int argc, const char **argv) { - const char *prefix; unsigned char sha1[20]; char *buf; unsigned long size; prefix = setup_git_directory(); + if (prefix && *prefix) + chomp_prefix = strlen(prefix); while (1 < argc && argv[1][0] == '-') { switch (argv[1][1]) { case 'z': @@ -100,6 +110,10 @@ int main(int argc, const char **argv) ls_options |= LS_NAME_ONLY; break; } + if (!strcmp(argv[1]+2, "full-name")) { + chomp_prefix = 0; + break; + } /* otherwise fallthru */ default: usage(ls_tree_usage); diff --git a/pack-redundant.c b/pack-redundant.c index 0a43278924..1869b38b72 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -8,6 +8,8 @@ #include "cache.h" +#define BLKSIZE 512 + static const char pack_redundant_usage[] = "git-pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>"; @@ -33,29 +35,32 @@ static struct pack_list { struct pll { struct pll *next; struct pack_list *pl; - size_t pl_size; }; static struct llist_item *free_nodes = NULL; +static inline void llist_item_put(struct llist_item *item) +{ + item->next = free_nodes; + free_nodes = item; +} + static inline struct llist_item *llist_item_get() { struct llist_item *new; if ( free_nodes ) { new = free_nodes; free_nodes = free_nodes->next; - } else - new = xmalloc(sizeof(struct llist_item)); - + } else { + int i = 1; + new = xmalloc(sizeof(struct llist_item) * BLKSIZE); + for(;i < BLKSIZE; i++) { + llist_item_put(&new[i]); + } + } return new; } -static inline void llist_item_put(struct llist_item *item) -{ - item->next = free_nodes; - free_nodes = item; -} - static void llist_free(struct llist *list) { while((list->back = list->front)) { @@ -270,77 +275,58 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) } } -static void pll_insert(struct pll **pll, struct pll **hint_table) +void pll_free(struct pll *l) { - struct pll *prev; - int i = (*pll)->pl_size - 1; - - if (hint_table[i] == NULL) { - hint_table[i--] = *pll; - for (; i >= 0; --i) { - if (hint_table[i] != NULL) - break; + struct pll *old; + struct pack_list *opl; + + while (l) { + old = l; + while (l->pl) { + opl = l->pl; + l->pl = opl->next; + free(opl); } - if (hint_table[i] == NULL) /* no elements in list */ - die("Why did this happen?"); + l = l->next; + free(old); } - - prev = hint_table[i]; - while (prev->next && prev->next->pl_size < (*pll)->pl_size) - prev = prev->next; - - (*pll)->next = prev->next; - prev->next = *pll; } /* all the permutations have to be free()d at the same time, * since they refer to each other */ -static struct pll * get_all_permutations(struct pack_list *list) +static struct pll * get_permutations(struct pack_list *list, int n) { - struct pll *subset, *pll, *new_pll = NULL; /*silence warning*/ - static struct pll **hint = NULL; - if (hint == NULL) - hint = xcalloc(pack_list_size(list), sizeof(struct pll *)); - - if (list == NULL) + struct pll *subset, *ret = NULL, *new_pll = NULL, *pll; + + if (list == NULL || pack_list_size(list) < n || n == 0) return NULL; - if (list->next == NULL) { - new_pll = xmalloc(sizeof(struct pll)); - hint[0] = new_pll; - new_pll->next = NULL; - new_pll->pl = list; - new_pll->pl_size = 1; - return new_pll; + if (n == 1) { + while (list) { + new_pll = xmalloc(sizeof(pll)); + new_pll->pl = NULL; + pack_list_insert(&new_pll->pl, list); + new_pll->next = ret; + ret = new_pll; + list = list->next; + } + return ret; } - pll = subset = get_all_permutations(list->next); - while (pll) { - if (pll->pl->pack == list->pack) { - pll = pll->next; - continue; + while (list->next) { + subset = get_permutations(list->next, n - 1); + while (subset) { + new_pll = xmalloc(sizeof(pll)); + new_pll->pl = subset->pl; + pack_list_insert(&new_pll->pl, list); + new_pll->next = ret; + ret = new_pll; + subset = subset->next; } - new_pll = xmalloc(sizeof(struct pll)); - - new_pll->pl = xmalloc(sizeof(struct pack_list)); - memcpy(new_pll->pl, list, sizeof(struct pack_list)); - new_pll->pl->next = pll->pl; - new_pll->pl_size = pll->pl_size + 1; - - pll_insert(&new_pll, hint); - - pll = pll->next; - } - /* add ourself */ - new_pll = xmalloc(sizeof(struct pll)); - new_pll->pl = xmalloc(sizeof(struct pack_list)); - memcpy(new_pll->pl, list, sizeof(struct pack_list)); - new_pll->pl->next = NULL; - new_pll->pl_size = 1; - pll_insert(&new_pll, hint); - - return hint[0]; + list = list->next; + } + return ret; } static int is_superset(struct pack_list *pl, struct llist *list) @@ -428,6 +414,7 @@ static void minimize(struct pack_list **min) struct pll *perm, *perm_all, *perm_ok = NULL, *new_perm; struct llist *missing; size_t min_perm_size = (size_t)-1, perm_size; + int n; pl = local_packs; while (pl) { @@ -441,8 +428,7 @@ static void minimize(struct pack_list **min) missing = llist_copy(all_objects); pl = unique; while (pl) { - llist_sorted_difference_inplace(missing, - pl->all_objects); + llist_sorted_difference_inplace(missing, pl->all_objects); pl = pl->next; } @@ -453,19 +439,21 @@ static void minimize(struct pack_list **min) } /* find the permutations which contain all missing objects */ - perm_all = perm = get_all_permutations(non_unique); - while (perm) { - if (perm_ok && perm->pl_size > perm_ok->pl_size) - break; /* ignore all larger permutations */ - if (is_superset(perm->pl, missing)) { - new_perm = xmalloc(sizeof(struct pll)); - memcpy(new_perm, perm, sizeof(struct pll)); - new_perm->next = perm_ok; - perm_ok = new_perm; + for (n = 1; n <= pack_list_size(non_unique) && !perm_ok; n++) { + perm_all = perm = get_permutations(non_unique, n); + while (perm) { + if (is_superset(perm->pl, missing)) { + new_perm = xmalloc(sizeof(struct pll)); + memcpy(new_perm, perm, sizeof(struct pll)); + new_perm->next = perm_ok; + perm_ok = new_perm; + } + perm = perm->next; } - perm = perm->next; + if (perm_ok) + break; + pll_free(perm_all); } - if (perm_ok == NULL) die("Internal error: No complete sets found!\n"); @@ -537,6 +525,7 @@ static void scan_alt_odb_packs(void) alt->all_objects); local = local->next; } + llist_sorted_difference_inplace(all_objects, alt->all_objects); alt = alt->next; } } diff --git a/receive-pack.c b/receive-pack.c index 92878ecac3..e79de917b5 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -8,20 +8,33 @@ static const char receive_pack_usage[] = "git-receive-pack <git-dir>"; static const char unpacker[] = "git-unpack-objects"; +static int report_status = 0; + +static char capabilities[] = "report-status"; +static int capabilities_sent = 0; + static int show_ref(const char *path, const unsigned char *sha1) { - packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); + if (capabilities_sent) + packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); + else + packet_write(1, "%s %s%c%s\n", + sha1_to_hex(sha1), path, 0, capabilities); + capabilities_sent = 1; return 0; } static void write_head_info(void) { for_each_ref(show_ref); + if (!capabilities_sent) + show_ref("capabilities^{}", null_sha1); + } struct command { struct command *next; - unsigned char updated; + const char *error_string; unsigned char old_sha1[20]; unsigned char new_sha1[20]; char ref_name[0]; @@ -71,33 +84,37 @@ static int run_update_hook(const char *refname, case 0: return 0; case -ERR_RUN_COMMAND_FORK: - die("hook fork failed"); + return error("hook fork failed"); case -ERR_RUN_COMMAND_EXEC: - die("hook execute failed"); + return error("hook execute failed"); case -ERR_RUN_COMMAND_WAITPID: - die("waitpid failed"); + return error("waitpid failed"); case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: - die("waitpid is confused"); + return error("waitpid is confused"); case -ERR_RUN_COMMAND_WAITPID_SIGNAL: - fprintf(stderr, "%s died of signal\n", update_hook); - return -1; + return error("%s died of signal\n", update_hook); case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - die("%s died strangely", update_hook); + return error("%s died strangely", update_hook); default: error("%s exited with error code %d", update_hook, -code); return -code; } } -static int update(const char *name, - unsigned char *old_sha1, unsigned char *new_sha1) +static int update(struct command *cmd) { + const char *name = cmd->ref_name; + unsigned char *old_sha1 = cmd->old_sha1; + unsigned char *new_sha1 = cmd->new_sha1; char new_hex[60], *old_hex, *lock_name; int newfd, namelen, written; - if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) + cmd->error_string = NULL; + if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) { + cmd->error_string = "funny refname"; return error("refusing to create funny ref '%s' locally", name); + } namelen = strlen(name); lock_name = xmalloc(namelen + 10); @@ -106,16 +123,19 @@ static int update(const char *name, strcpy(new_hex, sha1_to_hex(new_sha1)); old_hex = sha1_to_hex(old_sha1); - if (!has_sha1_file(new_sha1)) + if (!has_sha1_file(new_sha1)) { + cmd->error_string = "bad pack"; return error("unpack should have generated %s, " "but I can't find it!", new_hex); - + } safe_create_leading_directories(lock_name); newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (newfd < 0) + 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'; @@ -127,18 +147,22 @@ static int update(const char *name, 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)) { unlink(lock_name); + cmd->error_string = "hook declined"; return error("hook declined to update %s\n", name); } else if (rename(lock_name, name) < 0) { unlink(lock_name); + cmd->error_string = "can't rename"; return error("unable to replace %s", name); } else { @@ -158,7 +182,7 @@ static void run_update_post_hook(struct command *cmd) if (access(update_post_hook, X_OK) < 0) return; for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) { - if (!cmd_p->updated) + if (cmd_p->error_string) continue; argc++; } @@ -166,7 +190,7 @@ static void run_update_post_hook(struct command *cmd) argv[0] = update_post_hook; for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) { - if (!cmd_p->updated) + if (cmd_p->error_string) continue; argv[argc] = xmalloc(strlen(cmd_p->ref_name) + 1); strcpy(argv[argc], cmd_p->ref_name); @@ -185,8 +209,7 @@ static void execute_commands(void) struct command *cmd = commands; while (cmd) { - cmd->updated = !update(cmd->ref_name, - cmd->old_sha1, cmd->new_sha1); + update(cmd); cmd = cmd->next; } run_update_post_hook(commands); @@ -199,7 +222,8 @@ static void read_head_info(void) static char line[1000]; unsigned char old_sha1[20], new_sha1[20]; struct command *cmd; - int len; + char *refname; + int len, reflen; len = packet_read_line(0, line, sizeof(line)); if (!len) @@ -211,38 +235,66 @@ static void read_head_info(void) line[81] != ' ' || get_sha1_hex(line, old_sha1) || get_sha1_hex(line + 41, new_sha1)) - die("protocol error: expected old/new/ref, got '%s'", line); + die("protocol error: expected old/new/ref, got '%s'", + line); + + refname = line + 82; + reflen = strlen(refname); + if (reflen + 82 < len) { + if (strstr(refname + reflen + 1, "report-status")) + report_status = 1; + } cmd = xmalloc(sizeof(struct command) + len - 80); memcpy(cmd->old_sha1, old_sha1, 20); memcpy(cmd->new_sha1, new_sha1, 20); memcpy(cmd->ref_name, line + 82, len - 81); + cmd->error_string = "n/a (unpacker error)"; cmd->next = NULL; *p = cmd; p = &cmd->next; } } -static void unpack(void) +static const char *unpack(int *error_code) { int code = run_command(unpacker, NULL); + + *error_code = 0; switch (code) { case 0: - return; + return NULL; case -ERR_RUN_COMMAND_FORK: - die("unpack fork failed"); + return "unpack fork failed"; case -ERR_RUN_COMMAND_EXEC: - die("unpack execute failed"); + return "unpack execute failed"; case -ERR_RUN_COMMAND_WAITPID: - die("waitpid failed"); + return "waitpid failed"; case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: - die("waitpid is confused"); + return "waitpid is confused"; case -ERR_RUN_COMMAND_WAITPID_SIGNAL: - die("%s died of signal", unpacker); + return "unpacker died of signal"; case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - die("%s died strangely", unpacker); + return "unpacker died strangely"; default: - die("%s exited with error code %d", unpacker, -code); + *error_code = -code; + return "unpacker exited with error code"; + } +} + +static void report(const char *unpack_status) +{ + struct command *cmd; + packet_write(1, "unpack %s\n", + unpack_status ? unpack_status : "ok"); + for (cmd = commands; cmd; cmd = cmd->next) { + if (!cmd->error_string) + packet_write(1, "ok %s\n", + cmd->ref_name); + else + packet_write(1, "ng %s %s\n", + cmd->ref_name, cmd->error_string); } + packet_flush(1); } int main(int argc, char **argv) @@ -275,8 +327,12 @@ int main(int argc, char **argv) read_head_info(); if (commands) { - unpack(); - execute_commands(); + int code; + const char *unpack_status = unpack(&code); + if (!unpack_status) + execute_commands(); + if (report_status) + report(unpack_status); } return 0; } diff --git a/rev-parse.c b/rev-parse.c index bb4949ad70..0c951af0d4 100644 --- a/rev-parse.c +++ b/rev-parse.c @@ -216,6 +216,18 @@ int main(int argc, char **argv) puts(prefix); continue; } + if (!strcmp(arg, "--show-cdup")) { + const char *pfx = prefix; + while (pfx) { + pfx = strchr(pfx, '/'); + if (pfx) { + pfx++; + printf("../"); + } + } + putchar('\n'); + continue; + } if (!strcmp(arg, "--git-dir")) { const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); static char cwd[PATH_MAX]; diff --git a/send-pack.c b/send-pack.c index 0d41f9a178..cd36193444 100644 --- a/send-pack.c +++ b/send-pack.c @@ -176,16 +176,53 @@ static void get_local_heads(void) for_each_ref(one_local_ref); } +static int receive_status(int in) +{ + char line[1000]; + int ret = 0; + int len = packet_read_line(in, line, sizeof(line)); + if (len < 10 || memcmp(line, "unpack ", 7)) { + fprintf(stderr, "did not receive status back\n"); + return -1; + } + if (memcmp(line, "unpack ok\n", 10)) { + fputs(line, stderr); + ret = -1; + } + while (1) { + len = packet_read_line(in, line, sizeof(line)); + if (!len) + break; + if (len < 3 || + (memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) { + fprintf(stderr, "protocol error: %s\n", line); + ret = -1; + break; + } + if (!memcmp(line, "ok", 2)) + continue; + fputs(line, stderr); + ret = -1; + } + return ret; +} + static int send_pack(int in, int out, int nr_refspec, char **refspec) { struct ref *ref; int new_refs; int ret = 0; + int ask_for_status_report = 0; + int expect_status_report = 0; /* No funny business with the matcher */ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, 1); get_local_heads(); + /* Does the other end support the reporting? */ + if (server_supports("report-status")) + ask_for_status_report = 1; + /* match them up */ if (!remote_tail) remote_tail = &remote_refs; @@ -260,7 +297,17 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec) new_refs++; strcpy(old_hex, sha1_to_hex(ref->old_sha1)); new_hex = sha1_to_hex(ref->new_sha1); - packet_write(out, "%s %s %s", old_hex, new_hex, ref->name); + + if (ask_for_status_report) { + packet_write(out, "%s %s %s%c%s", + old_hex, new_hex, ref->name, 0, + "report-status"); + ask_for_status_report = 0; + expect_status_report = 1; + } + else + packet_write(out, "%s %s %s", + old_hex, new_hex, ref->name); fprintf(stderr, "updating '%s'", ref->name); if (strcmp(ref->name, ref->peer_ref->name)) fprintf(stderr, " using '%s'", ref->peer_ref->name); @@ -270,9 +317,15 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec) packet_flush(out); if (new_refs) pack_objects(out, remote_refs); - else if (ret == 0) - fprintf(stderr, "Everything up-to-date\n"); close(out); + + if (expect_status_report) { + if (receive_status(in)) + ret = -4; + } + + if (!new_refs && ret == 0) + fprintf(stderr, "Everything up-to-date\n"); return ret; } @@ -180,6 +180,8 @@ int check_repository_format_version(const char *var, const char *value) { if (strcmp(var, "core.repositoryformatversion") == 0) repository_format_version = git_config_int(var, value); + else if (strcmp(var, "core.sharedrepository") == 0) + shared_repository = git_config_bool(var, value); return 0; } diff --git a/sha1_file.c b/sha1_file.c index 6b7577dbc4..8bebbb255f 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -48,6 +48,29 @@ int get_sha1_hex(const char *hex, unsigned char *sha1) return 0; } +int adjust_shared_perm(const char *path) +{ + struct stat st; + int mode; + + if (!shared_repository) + return 0; + if (lstat(path, &st) < 0) + return -1; + mode = st.st_mode; + if (mode & S_IRUSR) + mode |= S_IRGRP; + if (mode & S_IWUSR) + mode |= S_IWGRP; + if (mode & S_IXUSR) + mode |= S_IXGRP; + if (S_ISDIR(mode)) + mode |= S_ISGID; + if (chmod(path, mode) < 0) + return -2; + return 0; +} + int safe_create_leading_directories(char *path) { char *pos = path; @@ -59,11 +82,16 @@ int safe_create_leading_directories(char *path) if (!pos) break; *pos = 0; - if (mkdir(path, 0777) < 0) + if (mkdir(path, 0777) < 0) { if (errno != EEXIST) { *pos = '/'; return -1; } + } + else if (adjust_shared_perm(path)) { + *pos = '/'; + return -2; + } *pos++ = '/'; } return 0; @@ -1255,6 +1283,8 @@ static int link_temp_to_file(const char *tmpfile, char *filename) if (dir) { *dir = 0; mkdir(filename, 0777); + if (adjust_shared_perm(filename)) + return -2; *dir = '/'; if (!link(tmpfile, filename)) return 0; diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh index 153b9e49f2..1292cafd7f 100755 --- a/t/t6022-merge-rename.sh +++ b/t/t6022-merge-rename.sh @@ -161,4 +161,41 @@ test_expect_success 'pull unrenaming branch into renaming one' \ } ' +test_expect_success 'pull conflicting renames' \ +' + git reset --hard + git show-branch + git pull . blue && { + echo "BAD: should have conflicted" + exit 1 + } + test "$(git ls-files -u A | wc -l)" -eq 1 || { + echo "BAD: should have left a stage" + exit 1 + } + test "$(git ls-files -u B | wc -l)" -eq 1 || { + echo "BAD: should have left a stage" + exit 1 + } + test "$(git ls-files -u C | wc -l)" -eq 1 || { + echo "BAD: should have left a stage" + exit 1 + } + test "$(git ls-files -s N | wc -l)" -eq 1 || { + echo "BAD: should have merged N" + exit 1 + } + sed -ne "/^g/{ + p + q + }" B | grep red || { + echo "BAD: should have listed our change first" + exit 1 + } + test "$(git diff white N | wc -l)" -eq 0 || { + echo "BAD: should have taken colored branch" + exit 1 + } +' + test_done |