From 88cd621deedd2aab8f0a4c6ea3afed7269e66d0c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junio@twinsun.com>
Date: Fri, 30 Sep 2005 14:02:47 -0700
Subject: Consolidate null_sha1[].
Signed-off-by: Junio C Hamano <junio@twinsun.com>
---
cache.h | 1 +
diff-files.c | 1 -
diff.c | 3 +--
sha1_file.c | 2 ++
4 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/cache.h b/cache.h
index 52a45f9c9a..b8e3d9bfac 100644
--- a/cache.h
+++ b/cache.h
@@ -189,6 +189,7 @@ extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)
extern char *sha1_file_name(const unsigned char *sha1);
extern char *sha1_pack_name(const unsigned char *sha1);
extern char *sha1_pack_index_name(const unsigned char *sha1);
+extern const unsigned char null_sha1[20];
int git_mkstemp(char *path, size_t n, const char *template);
diff --git a/diff-files.c b/diff-files.c
index e8db3d2d69..5e598322ff 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -34,7 +34,6 @@ static void show_modified(int oldmode, int mode,
int main(int argc, const char **argv)
{
- static const unsigned char null_sha1[20] = { 0, };
const char **pathspec;
const char *prefix = setup_git_directory();
int entries, i;
diff --git a/diff.c b/diff.c
index 9bded28729..7d06b035ae 100644
--- a/diff.c
+++ b/diff.c
@@ -10,7 +10,6 @@
#include "diffcore.h"
static const char *diff_opts = "-pu";
-static unsigned char null_sha1[20] = { 0, };
static int use_size_cache;
@@ -414,7 +413,7 @@ void diff_free_filespec_data(struct diff_filespec *s)
static void prep_temp_blob(struct diff_tempfile *temp,
void *blob,
unsigned long size,
- unsigned char *sha1,
+ const unsigned char *sha1,
int mode)
{
int fd;
diff --git a/sha1_file.c b/sha1_file.c
index 1e847a891a..895c1fab6f 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -20,6 +20,8 @@
#endif
#endif
+const unsigned char null_sha1[20] = { 0, };
+
static unsigned int sha1_file_open_flag = O_NOATIME;
static unsigned hexval(char c)
--
cgit v1.2.1
From 1fea629f794cda57cc161979dab903ec7460cc7c Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Fri, 30 Sep 2005 23:25:23 -0700
Subject: [PATCH] Flag empty patches as errors
A patch that contains no actual diff, and that doesn't change any
meta-data is bad. It shouldn't be a patch at all, and git-apply shouldn't
just accept it.
This caused a corrupted patch to be silently applied as an empty change in
the kernel, because the corruption ended up making the patch look empty.
An example of such a patch is one that contains the patch header, but
where the initial fragment header (the "@@ -nr,.." line) is missing,
causing us to not parse any fragments.
The real "patch" program will also flag such patches as bad, with the
message
patch: **** Only garbage was found in the patch input.
and we should do likewise.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
apply.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/apply.c b/apply.c
index 964df2db10..f8862722fd 100644
--- a/apply.c
+++ b/apply.c
@@ -723,6 +723,16 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
return offset;
}
+static inline int metadata_changes(struct patch *patch)
+{
+ return patch->is_rename > 0 ||
+ patch->is_copy > 0 ||
+ patch->is_new > 0 ||
+ patch->is_delete ||
+ (patch->old_mode && patch->new_mode &&
+ patch->old_mode != patch->new_mode);
+}
+
static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
{
int hdrsize, patchsize;
@@ -733,6 +743,9 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
+ if (!patchsize && !metadata_changes(patch))
+ die("patch with only garbage at line %d", linenr);
+
return offset + hdrsize + patchsize;
}
--
cgit v1.2.1
From f8d839ad992d92e1e31d7a557c198371d1fb7692 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 1 Oct 2005 11:58:43 -0700
Subject: Honor user's umask.
Fix the last two holdouts that forced mode bits stricter than the user's umask.
Noticed by Wolfgang Denk and fixed by Linus.
[jc: applied the same fix to mailsplit just for the sake of consistency.]
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
index.c | 2 +-
mailsplit.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/index.c b/index.c
index 87fc7b0387..bdde65f75c 100644
--- a/index.c
+++ b/index.c
@@ -29,7 +29,7 @@ int hold_index_file_for_update(struct cache_file *cf, const char *path)
signal(SIGINT, remove_lock_file_on_signal);
atexit(remove_lock_file);
}
- return open(cf->lockfile, O_RDWR | O_CREAT | O_EXCL, 0600);
+ return open(cf->lockfile, O_RDWR | O_CREAT | O_EXCL, 0666);
}
int commit_index_file(struct cache_file *cf)
diff --git a/mailsplit.c b/mailsplit.c
index a3238c20da..7afea1aaca 100644
--- a/mailsplit.c
+++ b/mailsplit.c
@@ -128,7 +128,7 @@ int main(int argc, char **argv)
unsigned long len = parse_email(map, size);
assert(len <= size);
sprintf(name, "%04d", ++nr);
- fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (fd < 0) {
perror(name);
exit(1);
--
cgit v1.2.1
From 38ec15a973a1f075f0d94d130b0ef279562921cd Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 1 Oct 2005 12:01:07 -0700
Subject: Honor extractor's umask in git-tar-tree.
The archive generated with git-tar-tree had 0755 and 0644 mode bits.
This inconvenienced the extractor with umask 002 by robbing g+w bit
unconditionally. Just write it out with loose permissions bits and
let the umask of the extractor do its job.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
tar-tree.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tar-tree.c b/tar-tree.c
index 2716ae3eb1..970c4bb54e 100644
--- a/tar-tree.c
+++ b/tar-tree.c
@@ -353,6 +353,8 @@ static void traverse_tree(void *buffer, unsigned long size,
if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
die("corrupt 'tree' file");
+ if (S_ISDIR(mode) || S_ISREG(mode))
+ mode |= (mode & 0100) ? 0777 : 0666;
buffer = sha1 + 20;
size -= namelen + 20;
--
cgit v1.2.1
From 37f15d50c93398eac90370cfe07315905501bdad Mon Sep 17 00:00:00 2001
From: Martin Langhoff <martin@catalyst.net.nz>
Date: Fri, 30 Sep 2005 19:15:12 +1200
Subject: [PATCH] archimport: Actually cope with merges from "remote"
repositories. Plus: Nicer messages.
archimport was refusing to import commits that had merges from repositories
that it didn't know about. Fixed.
Also brings in nicer messages.
Signed-off-by: Martin Langhoff <martin@catalyst.net.nz>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
git-archimport.perl | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/git-archimport.perl b/git-archimport.perl
index 3749b8b572..980e827b27 100755
--- a/git-archimport.perl
+++ b/git-archimport.perl
@@ -228,10 +228,12 @@ foreach my $ps (@psets) {
# skip commits already in repo
#
if (ptag($ps->{id})) {
- $opt_v && print "Skipping already imported: $ps->{id}\n";
+ $opt_v && print " * Skipping already imported: $ps->{id}\n";
next;
}
+ print " * Starting to work on $ps->{id}\n";
+
#
# create the branch if needed
#
@@ -675,6 +677,10 @@ sub find_parents {
# that branch.
#
foreach my $branch (keys %branches) {
+
+ # check that we actually know about the branch
+ next unless -e "$git_dir/refs/heads/$branch";
+
my $mergebase = `git-merge-base $branch $ps->{branch}`;
die "Cannot find merge base for $branch and $ps->{branch}" if $?;
chomp $mergebase;
--
cgit v1.2.1
From 94c23343dce0f556392fe8bbbba1b38cd37da481 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 30 Sep 2005 01:48:57 -0700
Subject: Pass CVSps generated A U Thor <author@domain.xz> intact.
Alexey Nezhdanov updated CVSps to generate author-name and
author-email information in its output.
If the input looks like it has that already properly formatted,
use that without our own munging.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
git-cvsimport.perl | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 565f4f1b32..f35c0d045b 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -510,7 +510,7 @@ unless($pid) {
my $state = 0;
-my($patchset,$date,$author,$branch,$ancestor,$tag,$logmsg);
+my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
my(@old,@new);
my $commit = sub {
my $pid;
@@ -591,11 +591,11 @@ my $commit = sub {
}
exec("env",
- "GIT_AUTHOR_NAME=$author",
- "GIT_AUTHOR_EMAIL=$author",
+ "GIT_AUTHOR_NAME=$author_name",
+ "GIT_AUTHOR_EMAIL=$author_email",
"GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
- "GIT_COMMITTER_NAME=$author",
- "GIT_COMMITTER_EMAIL=$author",
+ "GIT_COMMITTER_NAME=$author_name",
+ "GIT_COMMITTER_EMAIL=$author_email",
"GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
"git-commit-tree", $tree,@par);
die "Cannot exec git-commit-tree: $!\n";
@@ -638,7 +638,7 @@ my $commit = sub {
print $out "object $cid\n".
"type commit\n".
"tag $xtag\n".
- "tagger $author <$author>\n"
+ "tagger $author_name <$author_email>\n"
or die "Cannot create tag object $xtag: $!\n";
close($out)
or die "Cannot create tag object $xtag: $!\n";
@@ -683,7 +683,11 @@ while(<CVS>) {
$state=3;
} elsif($state == 3 and s/^Author:\s+//) {
s/\s+$//;
- $author = $_;
+ if (/^(.*?)\s+<(.*)>/) {
+ ($author_name, $author_email) = ($1, $2);
+ } else {
+ $author_name = $author_email = $_;
+ }
$state = 4;
} elsif($state == 4 and s/^Branch:\s+//) {
s/\s+$//;
--
cgit v1.2.1
From 49a0f240f7be05728f97903efd97ad7898ff6d08 Mon Sep 17 00:00:00 2001
From: Nick Hengeveld <nickh@reactrix.com>
Date: Wed, 28 Sep 2005 10:14:04 -0700
Subject: [PATCH] HTTP partial transfer support for object, pack, and index
transfers
HTTP partial transfer support for object, pack, and index transfers
[jc: this should not be placed in "master" -- it does not have any
fixes requested on the list.]
Signed-off-by: Nick Hengeveld <nickh@reactrix.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
http-fetch.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 161 insertions(+), 23 deletions(-)
diff --git a/http-fetch.c b/http-fetch.c
index 0566a9125c..778d508243 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -13,8 +13,12 @@
#define curl_global_init(a) do { /* nothing */ } while(0)
#endif
+#define PREV_BUF_SIZE 4096
+#define RANGE_HEADER_SIZE 30
+
static CURL *curl;
static struct curl_slist *no_pragma_header;
+static struct curl_slist *no_range_header;
static char curl_errorstr[CURL_ERROR_SIZE];
static char *initial_base;
@@ -87,12 +91,37 @@ void prefetch(unsigned char *sha1)
{
}
+int relink_or_rename(char *old, char *new) {
+ int ret;
+
+ ret = link(old, new);
+ if (ret < 0) {
+ /* Same Coda hack as in write_sha1_file(sha1_file.c) */
+ ret = errno;
+ if (ret == EXDEV && !rename(old, new))
+ return 0;
+ }
+ unlink(old);
+ if (ret) {
+ if (ret != EEXIST)
+ return ret;
+ }
+
+ return 0;
+}
+
static int got_alternates = 0;
static int fetch_index(struct alt_base *repo, unsigned char *sha1)
{
char *filename;
char *url;
+ char tmpfile[PATH_MAX];
+ int ret;
+ long prev_posn = 0;
+ char range[RANGE_HEADER_SIZE];
+ struct curl_slist *range_header = NULL;
+ CURLcode curl_result;
FILE *indexfile;
@@ -108,7 +137,8 @@ static int fetch_index(struct alt_base *repo, unsigned char *sha1)
repo->base, sha1_to_hex(sha1));
filename = sha1_pack_index_name(sha1);
- indexfile = fopen(filename, "w");
+ snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
+ indexfile = fopen(tmpfile, "a");
if (!indexfile)
return error("Unable to open local file %s for pack index",
filename);
@@ -119,13 +149,36 @@ static int fetch_index(struct alt_base *repo, unsigned char *sha1)
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_pragma_header);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr);
- if (curl_easy_perform(curl)) {
+ /* If there is data present from a previous transfer attempt,
+ resume where it left off */
+ prev_posn = ftell(indexfile);
+ if (prev_posn>0) {
+ if (get_verbosely)
+ fprintf(stderr,
+ "Resuming fetch of index for pack %s at byte %ld\n",
+ sha1_to_hex(sha1), prev_posn);
+ sprintf(range, "Range: bytes=%ld-", prev_posn);
+ range_header = curl_slist_append(range_header, range);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, range_header);
+ }
+
+ /* Clear out the Range: header after performing the request, so
+ other curl requests don't inherit inappropriate header data */
+ curl_result = curl_easy_perform(curl);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_range_header);
+ if (curl_result != 0) {
fclose(indexfile);
return error("Unable to get pack index %s\n%s", url,
curl_errorstr);
}
fclose(indexfile);
+
+ ret = relink_or_rename(tmpfile, filename);
+ if (ret)
+ return error("unable to write index filename %s: %s",
+ filename, strerror(ret));
+
return 0;
}
@@ -306,6 +359,12 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
struct packed_git **lst;
FILE *packfile;
char *filename;
+ char tmpfile[PATH_MAX];
+ int ret;
+ long prev_posn = 0;
+ char range[RANGE_HEADER_SIZE];
+ struct curl_slist *range_header = NULL;
+ CURLcode curl_result;
if (fetch_indices(repo))
return -1;
@@ -325,7 +384,8 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
repo->base, sha1_to_hex(target->sha1));
filename = sha1_pack_name(target->sha1);
- packfile = fopen(filename, "w");
+ snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
+ packfile = fopen(tmpfile, "a");
if (!packfile)
return error("Unable to open local file %s for pack",
filename);
@@ -336,7 +396,24 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_pragma_header);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr);
- if (curl_easy_perform(curl)) {
+ /* If there is data present from a previous transfer attempt,
+ resume where it left off */
+ prev_posn = ftell(packfile);
+ if (prev_posn>0) {
+ if (get_verbosely)
+ fprintf(stderr,
+ "Resuming fetch of pack %s at byte %ld\n",
+ sha1_to_hex(target->sha1), prev_posn);
+ sprintf(range, "Range: bytes=%ld-", prev_posn);
+ range_header = curl_slist_append(range_header, range);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, range_header);
+ }
+
+ /* Clear out the Range: header after performing the request, so
+ other curl requests don't inherit inappropriate header data */
+ curl_result = curl_easy_perform(curl);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_range_header);
+ if (curl_result != 0) {
fclose(packfile);
return error("Unable to get pack file %s\n%s", url,
curl_errorstr);
@@ -344,6 +421,11 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
fclose(packfile);
+ ret = relink_or_rename(tmpfile, filename);
+ if (ret)
+ return error("unable to write pack filename %s: %s",
+ filename, strerror(ret));
+
lst = &repo->packs;
while (*lst != target)
lst = &((*lst)->next);
@@ -360,14 +442,29 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
char *filename = sha1_file_name(sha1);
unsigned char real_sha1[20];
char tmpfile[PATH_MAX];
+ char prevfile[PATH_MAX];
int ret;
char *url;
char *posn;
+ int prevlocal;
+ unsigned char prev_buf[PREV_BUF_SIZE];
+ ssize_t prev_read = 0;
+ long prev_posn = 0;
+ char range[RANGE_HEADER_SIZE];
+ struct curl_slist *range_header = NULL;
+ CURLcode curl_result;
+
+ snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
+ snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
+ unlink(prevfile);
+ rename(tmpfile, prevfile);
+ unlink(tmpfile);
+
+ local = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
- snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX",
- get_object_directory());
+ /* Note: if another instance starts now, it will turn our new
+ tmpfile into its prevfile. */
- local = mkstemp(tmpfile);
if (local < 0)
return error("Couldn't create temporary file %s for %s: %s\n",
tmpfile, filename, strerror(errno));
@@ -396,8 +493,57 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
curl_easy_setopt(curl, CURLOPT_URL, url);
- if (curl_easy_perform(curl)) {
- unlink(filename);
+ /* If a previous temp file is present, process what was already
+ fetched. */
+ prevlocal = open(prevfile, O_RDONLY);
+ if (prevlocal != -1) {
+ do {
+ prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
+ if (prev_read>0) {
+ if (fwrite_sha1_file(prev_buf,
+ 1,
+ prev_read,
+ NULL) == prev_read) {
+ prev_posn += prev_read;
+ } else {
+ prev_read = -1;
+ }
+ }
+ } while (prev_read > 0);
+ close(prevlocal);
+ }
+ unlink(prevfile);
+
+ /* Reset inflate/SHA1 if there was an error reading the previous temp
+ file; also rewind to the beginning of the local file. */
+ if (prev_read == -1) {
+ memset(&stream, 0, sizeof(stream));
+ inflateInit(&stream);
+ SHA1_Init(&c);
+ if (prev_posn>0) {
+ prev_posn = 0;
+ lseek(local, SEEK_SET, 0);
+ }
+ }
+
+ /* If we have successfully processed data from a previous fetch
+ attempt, only fetch the data we don't already have. */
+ if (prev_posn>0) {
+ if (get_verbosely)
+ fprintf(stderr,
+ "Resuming fetch of object %s at byte %ld\n",
+ hex, prev_posn);
+ sprintf(range, "Range: bytes=%ld-", prev_posn);
+ range_header = curl_slist_append(range_header, range);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, range_header);
+ }
+
+ /* Clear out the Range: header after performing the request, so
+ other curl requests don't inherit inappropriate header data */
+ curl_result = curl_easy_perform(curl);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_range_header);
+ if (curl_result != 0) {
+ unlink(tmpfile);
return error("%s", curl_errorstr);
}
@@ -413,20 +559,11 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
unlink(tmpfile);
return error("File %s has bad hash\n", hex);
}
- ret = link(tmpfile, filename);
- if (ret < 0) {
- /* Same Coda hack as in write_sha1_file(sha1_file.c) */
- ret = errno;
- if (ret == EXDEV && !rename(tmpfile, filename))
- goto out;
- }
- unlink(tmpfile);
- if (ret) {
- if (ret != EEXIST)
- return error("unable to write sha1 filename %s: %s",
- filename, strerror(ret));
- }
- out:
+ ret = relink_or_rename(tmpfile, filename);
+ if (ret)
+ return error("unable to write sha1 filename %s: %s",
+ filename, strerror(ret));
+
pull_say("got %s\n", hex);
return 0;
}
@@ -519,6 +656,7 @@ int main(int argc, char **argv)
curl = curl_easy_init();
no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
+ no_range_header = curl_slist_append(no_range_header, "Range:");
curl_ssl_verify = getenv("GIT_SSL_NO_VERIFY") ? 0 : 1;
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);
--
cgit v1.2.1
From 271421cd345a9b5e2fc7e30e672d2c998f50e1dc Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 30 Sep 2005 00:07:39 -0700
Subject: Update partial HTTP transfers.
Add the sanity checks discussed on the list with Nick Hengeveld in
<20050927000931.GA15615@reactrix.com>.
* unlink of previous and rename from temp to previous can fail for
reasons other than benign ones (missing previous and missing temp).
Report these failures when we encounter them, to make diagnosing
problems easier.
* when rewinding the partially written result, make sure to
truncate the file.
Also verify the pack after downloading by calling
verify_packfile().
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
http-fetch.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/http-fetch.c b/http-fetch.c
index 778d508243..e8ac9959ff 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -1,6 +1,6 @@
#include "cache.h"
#include "commit.h"
-
+#include "pack.h"
#include "fetch.h"
#include <curl/curl.h>
@@ -431,6 +431,8 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
lst = &((*lst)->next);
*lst = (*lst)->next;
+ if (verify_pack(target, 0))
+ return -1;
install_packed_git(target);
return 0;
@@ -456,9 +458,13 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
- unlink(prevfile);
- rename(tmpfile, prevfile);
- unlink(tmpfile);
+
+ if (unlink(prevfile) && (errno != ENOENT))
+ return error("Failed to unlink %s (%s)",
+ prevfile, strerror(errno));
+ if (rename(tmpfile, prevfile) && (errno != ENOENT))
+ return error("Failed to rename %s to %s (%s)",
+ tmpfile, prevfile, strerror(errno));
local = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
@@ -523,6 +529,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
if (prev_posn>0) {
prev_posn = 0;
lseek(local, SEEK_SET, 0);
+ ftruncate(local, 0);
}
}
--
cgit v1.2.1
From 4fa2197e614950279527b14825d1e9572454a48c Mon Sep 17 00:00:00 2001
From: Nick Hengeveld <nickh@reactrix.com>
Date: Fri, 30 Sep 2005 16:27:47 -0700
Subject: [PATCH] HTTP partial transfer support fix.
Don't unlink the temp file when an object transfer fails, so next attempt
will pick up where the failed transfer left off
Signed-off-by: Nick Hengeveld <nickh@reactrix.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
http-fetch.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/http-fetch.c b/http-fetch.c
index e8ac9959ff..71a8c60b56 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -550,7 +550,6 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
curl_result = curl_easy_perform(curl);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_range_header);
if (curl_result != 0) {
- unlink(tmpfile);
return error("%s", curl_errorstr);
}
--
cgit v1.2.1
From ed1aadf1b0cb4afc7cb0aaf4cec4f5ad84e3307d Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Thu, 29 Sep 2005 14:35:15 -0700
Subject: [PATCH] git fetch --tags
You can do
git fetch --tags <linus-kernel-repo>
and it should fetch all my tags automatically.
[jc: The original by Linus fetched and overwrote branch heads with
--all, which felt dangerous and wrong, so I removed it. Also this
version does not use any refs that resulted as --tags for later
merge. ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
git-fetch.sh | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/git-fetch.sh b/git-fetch.sh
index 27407c1d35..61da6a9e31 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -5,6 +5,7 @@
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+tags=
append=
force=
update_head_ok=
@@ -17,6 +18,9 @@ do
-f|--f|--fo|--for|--forc|--force)
force=t
;;
+ -t|--t|--ta|--tag|--tags)
+ tags=t
+ ;;
-u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\
--update-he|--update-hea|--update-head|--update-head-|\
--update-head-o|--update-head-ok)
@@ -158,7 +162,26 @@ case "$update_head_ok" in
;;
esac
-for ref in $(get_remote_refs_for_fetch "$@")
+# If --tags (and later --heads or --all) is specified, then we are
+# not talking about defaults stored in Pull: line of remotes or
+# branches file, and just fetch those and refspecs explicitly given.
+# Otherwise we do what we always did.
+
+reflist=$(get_remote_refs_for_fetch "$@")
+if test "$tags"
+then
+ taglist=$(git-ls-remote --tags "$remote" | awk '{ print "."$2":"$2 }')
+ if test "$#" -gt 1
+ then
+ # remote URL plus explicit refspecs; we need to merge them.
+ reflist="$reflist $taglist"
+ else
+ # No explicit refspecs; fetch tags only.
+ reflist=$taglist
+ fi
+fi
+
+for ref in $reflist
do
refs="$refs $ref"
--
cgit v1.2.1
From 9b143c6e155a8eead165b2a813b533e0f3e0018a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 25 Sep 2005 19:30:24 -0700
Subject: Teach update-ref about a symbolic ref stored in a textfile.
A symbolic ref is a regular file whose contents is "ref:", followed by
optional leading whitespaces, followed by a GIT_DIR relative pathname,
followed by optional trailing whitespaces (the optional whitespaces
are unconditionally removed, so you cannot have leading nor trailing
whitespaces). This can be used in place of a traditional symbolic
link .git/HEAD that usually points at "refs/heads/master". You can
instead have a regular file .git/HEAD whose contents is
"ref: refs/heads/master".
[jc: currently the code does not enforce the symbolic ref to begin with
refs/, unlike the symbolic link case. It may be worthwhile to require
either case to begin with refs/ and not have any /./ nor /../ in them.]
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
update-ref.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/update-ref.c b/update-ref.c
index 1863b82324..6919cead4b 100644
--- a/update-ref.c
+++ b/update-ref.c
@@ -13,6 +13,7 @@ static const char *resolve_ref(const char *path, unsigned char *sha1)
for (;;) {
struct stat st;
+ char *buf;
int fd;
if (--depth < 0)
@@ -44,7 +45,19 @@ static const char *resolve_ref(const char *path, unsigned char *sha1)
return NULL;
len = read(fd, buffer, sizeof(buffer)-1);
close(fd);
- break;
+
+ /*
+ * Is it a symbolic ref?
+ */
+ if (len < 4 || memcmp("ref:", buffer, 4))
+ break;
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ while (len && isspace(buf[len-1]))
+ buf[--len] = 0;
+ path = git_path("%.*s", len, buf);
}
if (len < 40 || get_sha1_hex(buffer, sha1))
return NULL;
--
cgit v1.2.1
From ca8db1424d1808a1f78bc9905efd267f7c154d8e Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Sun, 25 Sep 2005 09:59:37 -0700
Subject: [PATCH] Allow reading "symbolic refs" that point to other refs
This extends the ref reading to understand a "symbolic ref": a ref file
that starts with "ref: " and points to another ref file, and thus
introduces the notion of ref aliases.
This is in preparation of allowing HEAD to eventually not be a symlink,
but one of these symbolic refs instead.
[jc: Linus originally required the prefix to be "ref: " five bytes
and nothing else, but I changed it to allow and strip any number of
leading whitespaces to match what update-ref.c does.]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
cache.h | 1 +
refs.c | 73 +++++++++++++++++++++++++++++++++----------------------------
sha1_name.c | 17 +-------------
3 files changed, 42 insertions(+), 49 deletions(-)
diff --git a/cache.h b/cache.h
index b8e3d9bfac..958c96e14d 100644
--- a/cache.h
+++ b/cache.h
@@ -229,6 +229,7 @@ extern int has_pack_index(const unsigned char *sha1);
extern int get_sha1(const char *str, 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 int read_ref(const char *filename, unsigned char *sha1);
/* General helper functions */
extern void usage(const char *err) NORETURN;
diff --git a/refs.c b/refs.c
index 161018097d..d4f3612487 100644
--- a/refs.c
+++ b/refs.c
@@ -2,17 +2,43 @@
#include "cache.h"
#include <errno.h>
+#include <ctype.h>
-static int read_ref(const char *refname, unsigned char *sha1)
+/* We allow "recursive" symbolic refs. Only within reason, though */
+#define MAXDEPTH 5
+
+int read_ref(const char *filename, unsigned char *sha1)
{
- int ret = -1;
- int fd = open(git_path("%s", refname), O_RDONLY);
+ int depth = 0;
+ int ret = -1, fd;
+
+ while ((fd = open(filename, O_RDONLY)) >= 0) {
+ char buffer[256];
+ int len = read(fd, buffer, sizeof(buffer)-1);
- if (fd >= 0) {
- char buffer[60];
- if (read(fd, buffer, sizeof(buffer)) >= 40)
- ret = get_sha1_hex(buffer, sha1);
close(fd);
+ if (len < 0)
+ break;
+
+ buffer[len] = 0;
+ while (len && isspace(buffer[len-1]))
+ buffer[--len] = 0;
+
+ if (!strncmp(buffer, "ref:", 4)) {
+ char *buf;
+ if (depth > MAXDEPTH)
+ break;
+ depth++;
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ filename = git_path("%.*s", len, buf);
+ continue;
+ }
+ if (len >= 40)
+ ret = get_sha1_hex(buffer, sha1);
+ break;
}
return ret;
}
@@ -54,7 +80,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u
break;
continue;
}
- if (read_ref(path, sha1) < 0)
+ if (read_ref(git_path("%s", path), sha1) < 0)
continue;
if (!has_sha1_file(sha1))
continue;
@@ -71,7 +97,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u
int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
{
unsigned char sha1[20];
- if (!read_ref("HEAD", sha1))
+ if (!read_ref(git_path("HEAD"), sha1))
return fn("HEAD", sha1);
return 0;
}
@@ -101,33 +127,14 @@ static char *ref_lock_file_name(const char *ref)
return ret;
}
-static int read_ref_file(const char *filename, unsigned char *sha1) {
- int fd = open(filename, O_RDONLY);
- char hex[41];
- if (fd < 0) {
- return error("Couldn't open %s\n", filename);
- }
- if ((read(fd, hex, 41) < 41) ||
- (hex[40] != '\n') ||
- get_sha1_hex(hex, sha1)) {
- error("Couldn't read a hash from %s\n", filename);
- close(fd);
- return -1;
- }
- close(fd);
- return 0;
-}
-
int get_ref_sha1(const char *ref, unsigned char *sha1)
{
- char *filename;
- int retval;
+ const char *filename;
+
if (check_ref_format(ref))
return -1;
- filename = ref_file_name(ref);
- retval = read_ref_file(filename, sha1);
- free(filename);
- return retval;
+ filename = git_path("refs/%s", ref);
+ return read_ref(filename, sha1);
}
static int lock_ref_file(const char *filename, const char *lock_filename,
@@ -140,7 +147,7 @@ static int lock_ref_file(const char *filename, const char *lock_filename,
return error("Couldn't open lock file for %s: %s",
filename, strerror(errno));
}
- retval = read_ref_file(filename, current_sha1);
+ retval = read_ref(filename, current_sha1);
if (old_sha1) {
if (retval) {
close(fd);
diff --git a/sha1_name.c b/sha1_name.c
index b4fed924f7..57e6cd3bdf 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -119,21 +119,6 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1)
return -1;
}
-static int get_sha1_file(const char *path, unsigned char *result)
-{
- char buffer[60];
- int fd = open(path, O_RDONLY);
- int len;
-
- if (fd < 0)
- return -1;
- len = read(fd, buffer, sizeof(buffer));
- close(fd);
- if (len < 40)
- return -1;
- return get_sha1_hex(buffer, result);
-}
-
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
{
static const char *prefix[] = {
@@ -150,7 +135,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
for (p = prefix; *p; p++) {
char *pathname = git_path("%s/%.*s", *p, len, str);
- if (!get_sha1_file(pathname, sha1))
+ if (!read_ref(pathname, sha1))
return 0;
}
--
cgit v1.2.1
From a876ed83be5467d6075da8a16306724cb1babc2a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junio@twinsun.com>
Date: Fri, 30 Sep 2005 14:08:25 -0700
Subject: Use resolve_ref() to implement read_ref().
Symbolic refs are understood by resolve_ref(), so existing read_ref()
users will automatically understand them as well.
Signed-off-by: Junio C Hamano <junio@twinsun.com>
---
cache.h | 1 +
refs.c | 89 ++++++++++++++++++++++++++++++++++++++++++------------------
update-ref.c | 62 +-----------------------------------------
3 files changed, 64 insertions(+), 88 deletions(-)
diff --git a/cache.h b/cache.h
index 958c96e14d..63823c3529 100644
--- a/cache.h
+++ b/cache.h
@@ -230,6 +230,7 @@ extern int get_sha1(const char *str, 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 int read_ref(const char *filename, unsigned char *sha1);
+extern const char *resolve_ref(const char *path, unsigned char *sha1, int);
/* General helper functions */
extern void usage(const char *err) NORETURN;
diff --git a/refs.c b/refs.c
index d4f3612487..6aa6aec82d 100644
--- a/refs.c
+++ b/refs.c
@@ -7,40 +7,75 @@
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
-int read_ref(const char *filename, unsigned char *sha1)
+const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
{
- int depth = 0;
- int ret = -1, fd;
+ int depth = MAXDEPTH, len;
+ char buffer[256];
- while ((fd = open(filename, O_RDONLY)) >= 0) {
- char buffer[256];
- int len = read(fd, buffer, sizeof(buffer)-1);
+ for (;;) {
+ struct stat st;
+ char *buf;
+ int fd;
- close(fd);
- if (len < 0)
- break;
+ if (--depth < 0)
+ return NULL;
- buffer[len] = 0;
- while (len && isspace(buffer[len-1]))
- buffer[--len] = 0;
+ /* Special case: non-existing file.
+ * Not having the refs/heads/new-branch is OK
+ * if we are writing into it, so is .git/HEAD
+ * that points at refs/heads/master still to be
+ * born. It is NOT OK if we are resolving for
+ * reading.
+ */
+ if (lstat(path, &st) < 0) {
+ if (reading || errno != ENOENT)
+ return NULL;
+ memset(sha1, 0, 20);
+ return path;
+ }
- if (!strncmp(buffer, "ref:", 4)) {
- char *buf;
- if (depth > MAXDEPTH)
- break;
- depth++;
- buf = buffer + 4;
- len -= 4;
- while (len && isspace(*buf))
- buf++, len--;
- filename = git_path("%.*s", len, buf);
- continue;
+ /* Follow "normalized" - ie "refs/.." symlinks by hand */
+ if (S_ISLNK(st.st_mode)) {
+ len = readlink(path, buffer, sizeof(buffer)-1);
+ if (len >= 5 && !memcmp("refs/", buffer, 5)) {
+ path = git_path("%.*s", len, buffer);
+ continue;
+ }
}
- if (len >= 40)
- ret = get_sha1_hex(buffer, sha1);
- break;
+
+ /*
+ * Anything else, just open it and try to use it as
+ * a ref
+ */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+ len = read(fd, buffer, sizeof(buffer)-1);
+ close(fd);
+
+ /*
+ * Is it a symbolic ref?
+ */
+ if (len < 4 || memcmp("ref:", buffer, 4))
+ break;
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ while (len && isspace(buf[len-1]))
+ buf[--len] = 0;
+ path = git_path("%.*s", len, buf);
}
- return ret;
+ if (len < 40 || get_sha1_hex(buffer, sha1))
+ return NULL;
+ return path;
+}
+
+int read_ref(const char *filename, unsigned char *sha1)
+{
+ if (resolve_ref(filename, sha1, 1))
+ return 0;
+ return -1;
}
static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1))
diff --git a/update-ref.c b/update-ref.c
index 6919cead4b..4a1704c1a5 100644
--- a/update-ref.c
+++ b/update-ref.c
@@ -4,66 +4,6 @@
static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]";
-#define MAXDEPTH 5
-
-static const char *resolve_ref(const char *path, unsigned char *sha1)
-{
- int depth = MAXDEPTH, len;
- char buffer[256];
-
- for (;;) {
- struct stat st;
- char *buf;
- int fd;
-
- if (--depth < 0)
- return NULL;
-
- /* Special case: non-existing file */
- if (lstat(path, &st) < 0) {
- if (errno != ENOENT)
- return NULL;
- memset(sha1, 0, 20);
- return path;
- }
-
- /* Follow "normalized" - ie "refs/.." symlinks by hand */
- if (S_ISLNK(st.st_mode)) {
- len = readlink(path, buffer, sizeof(buffer)-1);
- if (len >= 5 && !memcmp("refs/", buffer, 5)) {
- path = git_path("%.*s", len, buffer);
- continue;
- }
- }
-
- /*
- * Anything else, just open it and try to use it as
- * a ref
- */
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return NULL;
- len = read(fd, buffer, sizeof(buffer)-1);
- close(fd);
-
- /*
- * Is it a symbolic ref?
- */
- if (len < 4 || memcmp("ref:", buffer, 4))
- break;
- buf = buffer + 4;
- len -= 4;
- while (len && isspace(*buf))
- buf++, len--;
- while (len && isspace(buf[len-1]))
- buf[--len] = 0;
- path = git_path("%.*s", len, buf);
- }
- if (len < 40 || get_sha1_hex(buffer, sha1))
- return NULL;
- return path;
-}
-
static int re_verify(const char *path, unsigned char *oldsha1, unsigned char *currsha1)
{
char buf[40];
@@ -97,7 +37,7 @@ int main(int argc, char **argv)
if (oldval && get_sha1(oldval, oldsha1) < 0)
die("%s: not a valid old SHA1", oldval);
- path = resolve_ref(git_path("%s", refname), currsha1);
+ path = resolve_ref(git_path("%s", refname), currsha1, !!oldval);
if (!path)
die("No such ref: %s", refname);
--
cgit v1.2.1
From 8098a178b26dc7a158d129a092a5b78da6d12b72 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junio@twinsun.com>
Date: Fri, 30 Sep 2005 14:26:57 -0700
Subject: Add git-symbolic-ref
This adds the counterpart of git-update-ref that lets you read
and create "symbolic refs". By default it uses a symbolic link
to represent ".git/HEAD -> refs/heads/master", but it can be compiled
to use the textfile symbolic ref.
The places that did 'readlink .git/HEAD' and 'ln -s refs/heads/blah
.git/HEAD' have been converted to use new git-symbolic-ref command, so
that they can deal with either implementation.
Signed-off-by: Junio C Hamano <junio@twinsun.com>
---
.gitignore | 1 +
Makefile | 2 +-
cache.h | 2 ++
fsck-objects.c | 28 +++++++------------
git-bisect.sh | 7 +++--
git-branch.sh | 6 ++--
git-checkout.sh | 3 +-
git-commit.sh | 17 ++++++------
git-sh-setup.sh | 7 +++--
git-status.sh | 4 +--
init-db.c | 10 +++----
refs.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++
setup.c | 16 ++++++-----
show-branch.c | 13 ++++++---
symbolic-ref.c | 34 +++++++++++++++++++++++
t/t5400-send-pack.sh | 6 ++--
16 files changed, 176 insertions(+), 57 deletions(-)
create mode 100644 symbolic-ref.c
diff --git a/.gitignore b/.gitignore
index c3eb9543ff..e90e2c3503 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,6 +82,7 @@ git-ssh-push
git-ssh-upload
git-status
git-stripspace
+git-symbolic-ref
git-tag
git-tar-tree
git-unpack-file
diff --git a/Makefile b/Makefile
index e943954cf8..5648296a8d 100644
--- a/Makefile
+++ b/Makefile
@@ -116,7 +116,7 @@ PROGRAMS = \
git-ssh-upload git-tar-tree git-unpack-file \
git-unpack-objects git-update-index git-update-server-info \
git-upload-pack git-verify-pack git-write-tree \
- git-update-ref \
+ git-update-ref git-symbolic-ref \
$(SIMPLE_PROGRAMS)
# Backward compatibility -- to be removed after 1.0
diff --git a/cache.h b/cache.h
index 63823c3529..ec2a1610b2 100644
--- a/cache.h
+++ b/cache.h
@@ -231,6 +231,8 @@ extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1);
extern const char *resolve_ref(const char *path, unsigned char *sha1, int);
+extern int create_symref(const char *git_HEAD, const char *refs_heads_master);
+extern int validate_symref(const char *git_HEAD);
/* General helper functions */
extern void usage(const char *err) NORETURN;
diff --git a/fsck-objects.c b/fsck-objects.c
index 247edf0529..65cec7d12b 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -402,25 +402,17 @@ static void fsck_object_dir(const char *path)
static int fsck_head_link(void)
{
- int fd, count;
- char hex[40];
unsigned char sha1[20];
- static char path[PATH_MAX], link[PATH_MAX];
- const char *git_dir = get_git_dir();
-
- snprintf(path, sizeof(path), "%s/HEAD", git_dir);
- if (readlink(path, link, sizeof(link)) < 0)
- return error("HEAD is not a symlink");
- if (strncmp("refs/heads/", link, 11))
- return error("HEAD points to something strange (%s)", link);
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return error("HEAD: %s", strerror(errno));
- count = read(fd, hex, sizeof(hex));
- close(fd);
- if (count < 0)
- return error("HEAD: %s", strerror(errno));
- if (count < 40 || get_sha1_hex(hex, sha1))
+ const char *git_HEAD = strdup(git_path("HEAD"));
+ const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1);
+ int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */
+
+ if (!git_refs_heads_master)
+ return error("HEAD is not a symbolic ref");
+ if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11))
+ return error("HEAD points to something strange (%s)",
+ git_refs_heads_master + pfxlen);
+ if (!memcmp(null_sha1, sha1, 20))
return error("HEAD: not a valid git pointer");
return 0;
}
diff --git a/git-bisect.sh b/git-bisect.sh
index 8dc77c991c..1ab2f187dc 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -38,7 +38,8 @@ bisect_start() {
# Verify HEAD. If we were bisecting before this, reset to the
# top-of-line master first!
#
- head=$(readlink $GIT_DIR/HEAD) || die "Bad HEAD - I need a symlink"
+ head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
+ die "Bad HEAD - I need a symbolic ref"
case "$head" in
refs/heads/bisect*)
git checkout master || exit
@@ -46,7 +47,7 @@ bisect_start() {
refs/heads/*)
;;
*)
- die "Bad HEAD - strange symlink"
+ die "Bad HEAD - strange symbolic ref"
;;
esac
@@ -135,7 +136,7 @@ bisect_next() {
echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
git checkout new-bisect || exit
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
- ln -sf refs/heads/bisect "$GIT_DIR/HEAD"
+ GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
git-show-branch "$rev"
}
diff --git a/git-branch.sh b/git-branch.sh
index dcec2a9f2f..074229c206 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -14,7 +14,8 @@ If two arguments, create a new branch <branchname> based off of <start-point>.
delete_branch () {
option="$1" branch_name="$2"
- headref=$(readlink "$GIT_DIR/HEAD" | sed -e 's|^refs/heads/||')
+ headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
+ sed -e 's|^refs/heads/||')
case ",$headref," in
",$branch_name,")
die "Cannot delete the branch you are on." ;;
@@ -67,7 +68,8 @@ done
case "$#" in
0)
- headref=$(readlink "$GIT_DIR/HEAD" | sed -e 's|^refs/heads/||')
+ headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
+ sed -e 's|^refs/heads/||')
git-rev-parse --symbolic --all |
sed -ne 's|^refs/heads/||p' |
sort |
diff --git a/git-checkout.sh b/git-checkout.sh
index 37afcdda30..c3825904b6 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -71,7 +71,8 @@ if [ "$?" -eq 0 ]; then
echo $new > "$GIT_DIR/refs/heads/$newbranch"
branch="$newbranch"
fi
- [ "$branch" ] && ln -sf "refs/heads/$branch" "$GIT_DIR/HEAD"
+ [ "$branch" ] &&
+ GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
rm -f "$GIT_DIR/MERGE_HEAD"
else
exit 1
diff --git a/git-commit.sh b/git-commit.sh
index 18b259c708..1206c20c2e 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -153,15 +153,8 @@ if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
fi >>.editmsg
PARENTS="-p HEAD"
-if [ ! -r "$GIT_DIR/HEAD" ]; then
- if [ -z "$(git-ls-files)" ]; then
- echo Nothing to commit 1>&2
- exit 1
- fi
- PARENTS=""
- current=
-else
- current=$(git-rev-parse --verify HEAD)
+if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
+then
if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
fi
@@ -194,6 +187,12 @@ else
export GIT_AUTHOR_EMAIL
export GIT_AUTHOR_DATE
fi
+else
+ if [ -z "$(git-ls-files)" ]; then
+ echo Nothing to commit 1>&2
+ exit 1
+ fi
+ PARENTS=""
fi
git-status >>.editmsg
if [ "$?" != "0" -a ! -f $GIT_DIR/MERGE_HEAD ]
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 55db795843..a0172686a9 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -13,10 +13,13 @@
unset CDPATH
die() {
- echo "$@" >&2
+ echo >&2 "$@"
exit 1
}
-[ -h "$GIT_DIR/HEAD" ] &&
+case "$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD 2>/dev/null)" in
+refs/*) : ;;
+*) false ;;
+esac &&
[ -d "$GIT_DIR/refs" ] &&
[ -d "$GIT_OBJECT_DIRECTORY/00" ]
diff --git a/git-status.sh b/git-status.sh
index 621fa49d2b..ca9a15459f 100755
--- a/git-status.sh
+++ b/git-status.sh
@@ -31,7 +31,7 @@ report () {
[ "$header" ]
}
-branch=`readlink "$GIT_DIR/HEAD"`
+branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD)
case "$branch" in
refs/heads/master) ;;
*) echo "# On branch $branch" ;;
@@ -39,7 +39,7 @@ esac
git-update-index --refresh >/dev/null 2>&1
-if test -f "$GIT_DIR/HEAD"
+if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
then
git-diff-index -M --cached HEAD |
sed 's/^://' |
diff --git a/init-db.c b/init-db.c
index da2bc8f42b..aabc09f4e1 100644
--- a/init-db.c
+++ b/init-db.c
@@ -166,6 +166,7 @@ static void create_default_files(const char *git_dir,
{
unsigned len = strlen(git_dir);
static char path[PATH_MAX];
+ unsigned char sha1[20];
if (len > sizeof(path)-50)
die("insane git directory %s", git_dir);
@@ -186,15 +187,14 @@ static void create_default_files(const char *git_dir,
/*
* Create the default symlink from ".git/HEAD" to the "master"
- * branch
+ * branch, if it does not exist yet.
*/
strcpy(path + len, "HEAD");
- if (symlink("refs/heads/master", path) < 0) {
- if (errno != EEXIST) {
- perror(path);
+ if (read_ref(path, sha1) < 0) {
+ if (create_symref(path, "refs/heads/master") < 0)
exit(1);
- }
}
+ path[len] = 0;
copy_templates(path, len, template_path);
}
diff --git a/refs.c b/refs.c
index 6aa6aec82d..2aac90ca54 100644
--- a/refs.c
+++ b/refs.c
@@ -7,6 +7,50 @@
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
+#ifndef USE_SYMLINK_HEAD
+#define USE_SYMLINK_HEAD 1
+#endif
+
+int validate_symref(const char *path)
+{
+ struct stat st;
+ char *buf, buffer[256];
+ int len, fd;
+
+ if (lstat(path, &st) < 0)
+ return -1;
+
+ /* Make sure it is a "refs/.." symlink */
+ if (S_ISLNK(st.st_mode)) {
+ len = readlink(path, buffer, sizeof(buffer)-1);
+ if (len >= 5 && !memcmp("refs/", buffer, 5))
+ return 0;
+ return -1;
+ }
+
+ /*
+ * Anything else, just open it and try to see if it is a symbolic ref.
+ */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ len = read(fd, buffer, sizeof(buffer)-1);
+ close(fd);
+
+ /*
+ * Is it a symbolic ref?
+ */
+ if (len < 4 || memcmp("ref:", buffer, 4))
+ return -1;
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ if (len >= 5 && !memcmp("refs/", buffer, 5))
+ return 0;
+ return -1;
+}
+
const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
{
int depth = MAXDEPTH, len;
@@ -71,6 +115,39 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
return path;
}
+int create_symref(const char *git_HEAD, const char *refs_heads_master)
+{
+#if USE_SYMLINK_HEAD
+ unlink(git_HEAD);
+ return symlink(refs_heads_master, git_HEAD);
+#else
+ const char *lockpath;
+ char ref[1000];
+ int fd, len, written;
+
+ len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
+ if (sizeof(ref) <= len) {
+ error("refname too long: %s", refs_heads_master);
+ return -1;
+ }
+ lockpath = mkpath("%s.lock", git_HEAD);
+ fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ written = write(fd, ref, len);
+ close(fd);
+ if (written != len) {
+ unlink(lockpath);
+ error("Unable to write to %s", lockpath);
+ return -2;
+ }
+ if (rename(lockpath, git_HEAD) < 0) {
+ unlink(lockpath);
+ error("Unable to create %s", git_HEAD);
+ return -3;
+ }
+ return 0;
+#endif
+}
+
int read_ref(const char *filename, unsigned char *sha1)
{
if (resolve_ref(filename, sha1, 1))
diff --git a/setup.c b/setup.c
index 9e20160d94..c487d7eb9d 100644
--- a/setup.c
+++ b/setup.c
@@ -76,18 +76,20 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
* Test it it looks like we're at the top
* level git directory. We want to see a
*
- * - a HEAD symlink and a refs/ directory under ".git"
* - either a .git/objects/ directory _or_ the proper
* GIT_OBJECT_DIRECTORY environment variable
+ * - a refs/ directory under ".git"
+ * - either a HEAD symlink or a HEAD file that is formatted as
+ * a proper "ref:".
*/
static int is_toplevel_directory(void)
{
- struct stat st;
-
- return !lstat(".git/HEAD", &st) &&
- S_ISLNK(st.st_mode) &&
- !access(".git/refs/", X_OK) &&
- (getenv(DB_ENVIRONMENT) || !access(".git/objects/", X_OK));
+ if (access(".git/refs/", X_OK) ||
+ access(getenv(DB_ENVIRONMENT) ?
+ getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) ||
+ validate_symref(".git/HEAD"))
+ return 0;
+ return 1;
}
const char *setup_git_directory(void)
diff --git a/show-branch.c b/show-branch.c
index 5778a594f4..8429c171cf 100644
--- a/show-branch.c
+++ b/show-branch.c
@@ -349,6 +349,7 @@ int main(int ac, char **av)
int all_heads = 0, all_tags = 0;
int all_mask, all_revs, shown_merge_point;
char head_path[128];
+ const char *head_path_p;
int head_path_len;
unsigned char head_sha1[20];
int merge_base = 0;
@@ -430,11 +431,15 @@ int main(int ac, char **av)
if (0 <= extra)
join_revs(&list, &seen, num_rev, extra);
- head_path_len = readlink(".git/HEAD", head_path, sizeof(head_path)-1);
- if ((head_path_len < 0) || get_sha1("HEAD", head_sha1))
+ head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
+ if (head_path_p) {
+ head_path_len = strlen(head_path_p);
+ memcpy(head_path, head_path_p, head_path_len + 1);
+ }
+ else {
+ head_path_len = 0;
head_path[0] = 0;
- else
- head_path[head_path_len] = 0;
+ }
if (merge_base)
return show_merge_base(seen, num_rev);
diff --git a/symbolic-ref.c b/symbolic-ref.c
new file mode 100644
index 0000000000..af087d211d
--- /dev/null
+++ b/symbolic-ref.c
@@ -0,0 +1,34 @@
+#include "cache.h"
+
+static const char git_symbolic_ref_usage[] =
+"git-symbolic-ref name [ref]";
+
+static int check_symref(const char *HEAD)
+{
+ unsigned char sha1[20];
+ const char *git_HEAD = strdup(git_path("%s", HEAD));
+ const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);
+ if (git_refs_heads_master) {
+ /* we want to strip the .git/ part */
+ int pfxlen = strlen(git_HEAD) - strlen(HEAD);
+ puts(git_refs_heads_master + pfxlen);
+ }
+ else
+ die("No such ref: %s", HEAD);
+}
+
+int main(int argc, const char **argv)
+{
+ setup_git_directory();
+ switch (argc) {
+ case 2:
+ check_symref(argv[1]);
+ break;
+ case 3:
+ create_symref(strdup(git_path("%s", argv[1])), argv[2]);
+ break;
+ default:
+ usage(git_symbolic_ref_usage);
+ }
+ return 0;
+}
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 59ce77b6b4..1a4d2f2f13 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -20,12 +20,12 @@ test_expect_success setup '
commit=$(echo "Commit #$i" | git-commit-tree $tree -p $parent) &&
parent=$commit || return 1
done &&
- echo "$commit" >.git/HEAD &&
+ git-update-ref HEAD "$commit" &&
git-clone -l ./. victim &&
cd victim &&
git-log &&
cd .. &&
- echo $zero >.git/HEAD &&
+ git-update-ref HEAD "$zero" &&
parent=$zero &&
for i in $cnt
do
@@ -33,7 +33,7 @@ test_expect_success setup '
commit=$(echo "Rebase #$i" | git-commit-tree $tree -p $parent) &&
parent=$commit || return 1
done &&
- echo "$commit" >.git/HEAD &&
+ git-update-ref HEAD "$commit" &&
echo Rebase &&
git-log'
--
cgit v1.2.1
From 455a7f3275d264f6e66045b92c83747ec461dda5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junio@twinsun.com>
Date: Fri, 30 Sep 2005 13:31:16 -0700
Subject: More portability.
- The location of openssl development files got customizable.
- The location of iconv development files got customizable.
- Pass $TAR down to t5000 test so that the user can override with
'gmake TAR=gtar'.
- Solaris 'bc' does not seem to grok "define abs()". There is no
reason to use bc there -- expr would do.
Signed-off-by: Junio C Hamano <junio@twinsun.com>
---
Makefile | 20 +++++++++++++++++---
t/Makefile | 1 +
t/t5000-tar-tree.sh | 6 +++---
t/t6002-rev-list-bisect.sh | 29 +++++++++++++----------------
4 files changed, 34 insertions(+), 22 deletions(-)
diff --git a/Makefile b/Makefile
index 5648296a8d..e7f3d1ffca 100644
--- a/Makefile
+++ b/Makefile
@@ -200,18 +200,32 @@ endif
ifndef NO_OPENSSL
LIB_OBJS += epoch.o
OPENSSL_LIBSSL = -lssl
+ ifdef OPENSSLDIR
+ # Again this may be problematic -- gcc does not always want -R.
+ CFLAGS += -I$(OPENSSLDIR)/include
+ OPENSSL_LINK = -L$(OPENSSLDIR)/lib -R$(OPENSSLDIR)/lib
+ else
+ OPENSSL_LINK =
+ endif
else
DEFINES += '-DNO_OPENSSL'
MOZILLA_SHA1 = 1
OPENSSL_LIBSSL =
endif
ifdef NEEDS_SSL_WITH_CRYPTO
- LIB_4_CRYPTO = -lcrypto -lssl
+ LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto -lssl
else
- LIB_4_CRYPTO = -lcrypto
+ LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto
endif
ifdef NEEDS_LIBICONV
- LIB_4_ICONV = -liconv
+ ifdef ICONVDIR
+ # Again this may be problematic -- gcc does not always want -R.
+ CFLAGS += -I$(ICONVDIR)/include
+ ICONV_LINK = -L$(ICONVDIR)/lib -R$(ICONVDIR)/lib
+ else
+ ICONV_LINK =
+ endif
+ LIB_4_ICONV = $(ICONV_LINK) -liconv
else
LIB_4_ICONV =
endif
diff --git a/t/Makefile b/t/Makefile
index a6b80882ce..e71da7782e 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -5,6 +5,7 @@
#GIT_TEST_OPTS=--verbose --debug
SHELL_PATH ?= $(SHELL)
+TAR ?= $(TAR)
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 6bf34066e1..5dffb8e1e5 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -50,7 +50,7 @@ test_expect_success \
test_expect_success \
'validate file modification time' \
- 'TZ=GMT tar tvf b.tar a/a |
+ 'TZ=GMT $TAR tvf b.tar a/a |
awk \{print\ \$4,\ \(length\(\$5\)\<7\)\ ?\ \$5\":00\"\ :\ \$5\} \
>b.mtime &&
echo "2005-05-27 22:00:00" >expected.mtime &&
@@ -63,7 +63,7 @@ test_expect_success \
test_expect_success \
'extract tar archive' \
- '(cd b && tar xf -) <b.tar'
+ '(cd b && $TAR xf -) <b.tar'
test_expect_success \
'validate filenames' \
@@ -80,7 +80,7 @@ test_expect_success \
test_expect_success \
'extract tar archive with prefix' \
- '(cd c && tar xf -) <c.tar'
+ '(cd c && $TAR xf -) <c.tar'
test_expect_success \
'validate filenames with prefix' \
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index d0a4ff29c9..42fcbc60ca 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -7,20 +7,6 @@ test_description='Tests git-rev-list --bisect functionality'
. ./test-lib.sh
. ../t6000lib.sh # t6xxx specific functions
-bc_expr()
-{
-bc <<EOF
-scale=1
-define abs(x) {
- if (x>=0) { return (x); } else { return (-x); }
-}
-define floor(x) {
- save=scale; scale=0; result=x/1; scale=save; return (result);
-}
-$*
-EOF
-}
-
# usage: test_bisection max-diff bisect-option head ^prune...
#
# e.g. test_bisection 1 --bisect l1 ^l0
@@ -35,8 +21,19 @@ test_bisection_diff()
_head=$1
shift 1
_bisection_size=$(git-rev-list $_bisection "$@" | wc -l)
- [ -n "$_list_size" -a -n "$_bisection_size" ] || error "test_bisection_diff failed"
- test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" "[ $(bc_expr "floor(abs($_list_size/2)-$_bisection_size)") -le $_max_diff ]"
+ [ -n "$_list_size" -a -n "$_bisection_size" ] ||
+ error "test_bisection_diff failed"
+
+ # Test if bisection size is close to half of list size within
+ # tolerance.
+ #
+ _bisect_err=`expr $_list_size - $_bisection_size \* 2`
+ test "$_bisect_err" -lt 0 && _bisect_err=`expr 0 - $_bisect_err`
+ _bisect_err=`expr $_bisect_err / 2` ; # floor
+
+ test_expect_success \
+ "bisection diff $_bisect_option $_head $* <= $_max_diff" \
+ 'test $_bisect_err -le $_max_diff'
}
date >path0
--
cgit v1.2.1
From 5d1a5c02e8ac1c16688ea4a44512245f25a49f8a Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Sat, 1 Oct 2005 13:24:27 -0700
Subject: [PATCH] Better error reporting for "git status"
Instead of "git status" ignoring (and hiding) potential errors from the
"git-update-index" call, make it exit if it fails, and show the error.
In order to do this, use the "-q" flag (to ignore not-up-to-date files)
and add a new "--unmerged" flag that allows unmerged entries in the index
without any errors.
This also avoids marking the index "changed" if an entry isn't actually
modified, and makes sure that we exit with an understandable error message
if the index is corrupt or unreadable. "read_cache()" no longer returns an
error for the caller to check.
Finally, make die() and usage() exit with recognizable error codes, if we
ever want to check the failure reason in scripts.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
git-status.sh | 2 +-
read-cache.c | 14 +++++++++-----
update-index.c | 16 ++++++++++++----
usage.c | 4 ++--
4 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/git-status.sh b/git-status.sh
index ca9a15459f..44398d760c 100755
--- a/git-status.sh
+++ b/git-status.sh
@@ -37,7 +37,7 @@ refs/heads/master) ;;
*) echo "# On branch $branch" ;;
esac
-git-update-index --refresh >/dev/null 2>&1
+git-update-index -q --unmerged --refresh || exit
if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
then
diff --git a/read-cache.c b/read-cache.c
index 0e345bdb2f..d2aebdd6bc 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -464,11 +464,15 @@ int read_cache(void)
errno = EBUSY;
if (active_cache)
- return error("more than one cachefile");
+ return active_nr;
+
errno = ENOENT;
fd = open(get_index_file(), O_RDONLY);
- if (fd < 0)
- return (errno == ENOENT) ? 0 : error("open failed");
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return 0;
+ die("index file open failed (%s)", strerror(errno));
+ }
size = 0; // avoid gcc warning
map = MAP_FAILED;
@@ -480,7 +484,7 @@ int read_cache(void)
}
close(fd);
if (map == MAP_FAILED)
- return error("mmap failed");
+ die("index file mmap failed (%s)", strerror(errno));
hdr = map;
if (verify_hdr(hdr, size) < 0)
@@ -501,7 +505,7 @@ int read_cache(void)
unmap:
munmap(map, size);
errno = EINVAL;
- return error("verify header failed");
+ die("index file corrupt");
}
#define WRITE_BUFFER_SIZE 8192
diff --git a/update-index.c b/update-index.c
index 01eaa1a984..ade1ee72eb 100644
--- a/update-index.c
+++ b/update-index.c
@@ -13,7 +13,7 @@
* like "git-update-index *" and suddenly having all the object
* files be revision controlled.
*/
-static int allow_add = 0, allow_remove = 0, allow_replace = 0, not_new = 0, quiet = 0, info_only = 0;
+static int allow_add = 0, allow_remove = 0, allow_replace = 0, allow_unmerged = 0, not_new = 0, quiet = 0, info_only = 0;
static int force_remove;
/* Three functions to allow overloaded pointer return; see linux/err.h */
@@ -135,7 +135,7 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce)
changed = ce_match_stat(ce, &st);
if (!changed)
- return ce;
+ return NULL;
if (ce_modified(ce, &st))
return ERR_PTR(-EINVAL);
@@ -156,16 +156,20 @@ static int refresh_cache(void)
struct cache_entry *ce, *new;
ce = active_cache[i];
if (ce_stage(ce)) {
- printf("%s: needs merge\n", ce->name);
- has_errors = 1;
while ((i < active_nr) &&
! strcmp(active_cache[i]->name, ce->name))
i++;
i--;
+ if (allow_unmerged)
+ continue;
+ printf("%s: needs merge\n", ce->name);
+ has_errors = 1;
continue;
}
new = refresh_entry(ce);
+ if (!new)
+ continue;
if (IS_ERR(new)) {
if (not_new && PTR_ERR(new) == -ENOENT)
continue;
@@ -335,6 +339,10 @@ int main(int argc, const char **argv)
allow_remove = 1;
continue;
}
+ if (!strcmp(path, "--unmerged")) {
+ allow_unmerged = 1;
+ continue;
+ }
if (!strcmp(path, "--refresh")) {
has_errors |= refresh_cache();
continue;
diff --git a/usage.c b/usage.c
index 86211c9141..dfa87fe119 100644
--- a/usage.c
+++ b/usage.c
@@ -15,7 +15,7 @@ static void report(const char *prefix, const char *err, va_list params)
void usage(const char *err)
{
fprintf(stderr, "usage: %s\n", err);
- exit(1);
+ exit(129);
}
void die(const char *err, ...)
@@ -25,7 +25,7 @@ void die(const char *err, ...)
va_start(params, err);
report("fatal: ", err, params);
va_end(params);
- exit(1);
+ exit(128);
}
int error(const char *err, ...)
--
cgit v1.2.1
From 5cd5ace72bf8944c920a20a7b98c82f7ed663d8a Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Sat, 1 Oct 2005 13:39:47 -0700
Subject: [PATCH] Re-instate index file write optimization
This makes "git-update-index" avoid the new index file write if it didn't
make any changes to the index.
It still doesn't make things like "git status" be read-only operations in
general, but if the index file doesn't need refreshing, it now will at
least avoid making unnecessary changes.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
update-index.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/update-index.c b/update-index.c
index ade1ee72eb..b825a11d2f 100644
--- a/update-index.c
+++ b/update-index.c
@@ -391,9 +391,11 @@ int main(int argc, const char **argv)
update_one(buf.buf, prefix, prefix_length);
}
}
- if (write_cache(newfd, active_cache, active_nr) ||
- commit_index_file(&cache_file))
- die("Unable to write new cachefile");
+ if (active_cache_changed) {
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_index_file(&cache_file))
+ die("Unable to write new cachefile");
+ }
return has_errors ? 1 : 0;
}
--
cgit v1.2.1
From 18c5a52537173b12bd78ce25c6dd524d147e87a7 Mon Sep 17 00:00:00 2001
From: Han Boetes <han@mijncomputer.nl>
Date: Sat, 1 Oct 2005 08:23:26 +0200
Subject: [PATCH] git on OpenBSD
iconv is installed in /usr/local.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Makefile b/Makefile
index e7f3d1ffca..79cafd7171 100644
--- a/Makefile
+++ b/Makefile
@@ -175,6 +175,10 @@ endif
ifneq (,$(findstring arm,$(shell uname -m)))
ARM_SHA1 = YesPlease
endif
+ifeq ($(shell uname -s),OpenBSD)
+ NEEDS_LIBICONV = YesPlease
+ PLATFORM_DEFINES += -I/usr/local/include -L/usr/local/lib
+endif
ifndef NO_CURL
ifdef CURLDIR
--
cgit v1.2.1
From 0842acff57f386ba749c3ea6b5e034771f074f6b Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 2 Oct 2005 00:20:45 -0700
Subject: Customize git command for installations that lack certain commands.
When the platform lacks certain git subcommands, omit them from the
list of subcommands that are available from "git" wrapper.
Noticed by Geert Bosch.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 7 ++++++-
git.sh | 55 ++++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 50 insertions(+), 12 deletions(-)
diff --git a/Makefile b/Makefile
index 79cafd7171..4d721f2ace 100644
--- a/Makefile
+++ b/Makefile
@@ -122,6 +122,8 @@ PROGRAMS = \
# Backward compatibility -- to be removed after 1.0
PROGRAMS += git-ssh-pull git-ssh-push
+GIT_LIST_TWEAK =
+
PYMODULES = \
gitMergeCommon.py
@@ -131,6 +133,8 @@ endif
ifdef WITH_SEND_EMAIL
SCRIPT_PERL += git-send-email.perl
+else
+ GIT_LIST_TWEAK += -e '/^send-email$$/d'
endif
LIB_FILE=libgit.a
@@ -282,7 +286,8 @@ all:
git: git.sh Makefile
rm -f $@+ $@
sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \
- -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' <$@.sh >$@+
+ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+ $(GIT_LIST_TWEAK) <$@.sh >$@+
chmod +x $@+
mv $@+ $@
diff --git a/git.sh b/git.sh
index 178d0f0c09..dc383eddea 100755
--- a/git.sh
+++ b/git.sh
@@ -16,17 +16,50 @@ esac
echo "Usage: git COMMAND [OPTIONS] [TARGET]"
if [ -n "$cmd" ]; then
- echo " git command '$cmd' not found: commands are:"
-else
- echo " git commands are:"
+ echo "git command '$cmd' not found."
fi
+echo "git commands are:"
-cat <<\EOF
- add apply archimport bisect branch checkout cherry clone
- commit count-objects cvsimport diff fetch format-patch
- fsck-cache get-tar-commit-id init-db log ls-remote octopus
- pack-objects parse-remote patch-id prune pull push rebase
- relink rename repack request-pull reset resolve revert
- send-email shortlog show-branch status tag verify-tag
- whatchanged
+fmt <<\EOF | sed -e 's/^/ /'
+add
+apply
+archimport
+bisect
+branch
+checkout
+cherry
+clone
+commit
+count-objects
+cvsimport
+diff
+fetch
+format-patch
+fsck-objects
+get-tar-commit-id
+init-db
+log
+ls-remote
+octopus
+pack-objects
+parse-remote
+patch-id
+prune
+pull
+push
+rebase
+relink
+rename
+repack
+request-pull
+reset
+resolve
+revert
+send-email
+shortlog
+show-branch
+status
+tag
+verify-tag
+whatchanged
EOF
--
cgit v1.2.1
From 7dd43575f8641c8a73b1e976715056ebdc773904 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 2 Oct 2005 00:50:16 -0700
Subject: read-tree: remove --head option.
Initially it was to allow specifying more than one remote to
allow creation of an Octopus, but it is not being used.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
read-tree.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/read-tree.c b/read-tree.c
index ca808739db..390fe2f027 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -629,11 +629,6 @@ int main(int argc, char **argv)
continue;
}
- if (!strcmp(arg, "--head")) {
- head_idx = stage - 1;
- fn = threeway_merge;
- }
-
/* "-m" stands for "merge", meaning we start in stage 1 */
if (!strcmp(arg, "-m")) {
if (stage || merge)
@@ -657,7 +652,8 @@ int main(int argc, char **argv)
}
if ((update||index_only) && !merge)
usage(read_tree_usage);
- if (merge && !fn) {
+
+ if (merge) {
if (stage < 2)
die("just how do you expect me to merge %d trees?", stage-1);
switch (stage - 1) {
@@ -674,9 +670,7 @@ int main(int argc, char **argv)
fn = threeway_merge;
break;
}
- }
- if (head_idx < 0) {
if (stage - 1 >= 3)
head_idx = stage - 2;
else
--
cgit v1.2.1
From af215114f52af0f308ef19f67e544df8ea5e4ac2 Mon Sep 17 00:00:00 2001
From: Fredrik Kuivinen <freku045@student.liu.se>
Date: Sun, 2 Oct 2005 17:43:07 +0200
Subject: [PATCH] Teach the recursive merge strategy about renames.
It will now merge cases where a file was renamed in one branch and
modified in the other branch cleanly. We also detect a couple of
conflict cases now that wasn't detected before.
Signed-off-by: Fredrik Kuivinen <freku045@student.liu.se>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
git-merge-recursive.py | 786 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 601 insertions(+), 185 deletions(-)
diff --git a/git-merge-recursive.py b/git-merge-recursive.py
index 689f91430b..b80a860357 100755
--- a/git-merge-recursive.py
+++ b/git-merge-recursive.py
@@ -7,9 +7,6 @@ from sets import Set
sys.path.append('@@GIT_PYTHON_PATH@@')
from gitMergeCommon import *
-# The actual merge code
-# ---------------------
-
originalIndexFile = os.environ.get('GIT_INDEX_FILE',
os.environ.get('GIT_DIR', '.git') + '/index')
temporaryIndexFile = os.environ.get('GIT_DIR', '.git') + \
@@ -21,11 +18,23 @@ def setupIndex(temporary):
pass
if temporary:
newIndex = temporaryIndexFile
- os.environ
else:
newIndex = originalIndexFile
os.environ['GIT_INDEX_FILE'] = newIndex
+# This is a global variable which is used in a number of places but
+# only written to in the 'merge' function.
+
+# cacheOnly == True => Don't leave any non-stage 0 entries in the cache and
+# don't update the working directory.
+# False => Leave unmerged entries in the cache and update
+# the working directory.
+
+cacheOnly = False
+
+# The entry point to the merge code
+# ---------------------------------
+
def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0):
'''Merge the commits h1 and h2, return the resulting virtual
commit object and a flag indicating the cleaness of the merge.'''
@@ -35,6 +44,7 @@ def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0):
def infoMsg(*args):
sys.stdout.write(' '*callDepth)
printList(args)
+
infoMsg('Merging:')
infoMsg(h1)
infoMsg(h2)
@@ -46,27 +56,27 @@ def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0):
infoMsg(x)
sys.stdout.flush()
- Ms = ca[0]
+ mergedCA = ca[0]
for h in ca[1:]:
- [Ms, ignore] = merge(Ms, h,
- 'Temporary shared merge branch 1',
- 'Temporary shared merge branch 2',
- graph, callDepth+1)
- assert(isinstance(Ms, Commit))
+ [mergedCA, dummy] = merge(mergedCA, h,
+ 'Temporary shared merge branch 1',
+ 'Temporary shared merge branch 2',
+ graph, callDepth+1)
+ assert(isinstance(mergedCA, Commit))
+ global cacheOnly
if callDepth == 0:
setupIndex(False)
- cleanCache = False
+ cacheOnly = False
else:
setupIndex(True)
runProgram(['git-read-tree', h1.tree()])
- cleanCache = True
+ cacheOnly = True
- [shaRes, clean] = mergeTrees(h1.tree(), h2.tree(), Ms.tree(),
- branch1Name, branch2Name,
- cleanCache)
+ [shaRes, clean] = mergeTrees(h1.tree(), h2.tree(), mergedCA.tree(),
+ branch1Name, branch2Name)
- if clean or cleanCache:
+ if clean or cacheOnly:
res = Commit(None, [h1, h2], tree=shaRes)
graph.addNode(res)
else:
@@ -89,16 +99,255 @@ def getFilesAndDirs(tree):
return [files, dirs]
+# Those two global variables are used in a number of places but only
+# written to in 'mergeTrees' and 'uniquePath'. They keep track of
+# every file and directory in the two branches that are about to be
+# merged.
+currentFileSet = None
+currentDirectorySet = None
+
+def mergeTrees(head, merge, common, branch1Name, branch2Name):
+ '''Merge the trees 'head' and 'merge' with the common ancestor
+ 'common'. The name of the head branch is 'branch1Name' and the name of
+ the merge branch is 'branch2Name'. Return a tuple (tree, cleanMerge)
+ where tree is the resulting tree and cleanMerge is True iff the
+ merge was clean.'''
+
+ assert(isSha(head) and isSha(merge) and isSha(common))
+
+ if common == merge:
+ print 'Already uptodate!'
+ return [head, True]
+
+ if cacheOnly:
+ updateArg = '-i'
+ else:
+ updateArg = '-u'
+
+ [out, code] = runProgram(['git-read-tree', updateArg, '-m',
+ common, head, merge], returnCode = True)
+ if code != 0:
+ die('git-read-tree:', out)
+
+ [tree, code] = runProgram('git-write-tree', returnCode=True)
+ tree = tree.rstrip()
+ if code != 0:
+ global currentFileSet, currentDirectorySet
+ [currentFileSet, currentDirectorySet] = getFilesAndDirs(head)
+ [filesM, dirsM] = getFilesAndDirs(merge)
+ currentFileSet.union_update(filesM)
+ currentDirectorySet.union_update(dirsM)
+
+ entries = unmergedCacheEntries()
+ renamesHead = getRenames(head, common, head, merge, entries)
+ renamesMerge = getRenames(merge, common, head, merge, entries)
+
+ cleanMerge = processRenames(renamesHead, renamesMerge,
+ branch1Name, branch2Name)
+ for entry in entries:
+ if entry.processed:
+ continue
+ if not processEntry(entry, branch1Name, branch2Name):
+ cleanMerge = False
+
+ if cleanMerge or cacheOnly:
+ tree = runProgram('git-write-tree').rstrip()
+ else:
+ tree = None
+ else:
+ cleanMerge = True
+
+ return [tree, cleanMerge]
+
+# Low level file merging, update and removal
+# ------------------------------------------
+
+def mergeFile(oPath, oSha, oMode, aPath, aSha, aMode, bPath, bSha, bMode,
+ branch1Name, branch2Name):
+
+ merge = False
+ clean = True
+
+ if stat.S_IFMT(aMode) != stat.S_IFMT(bMode):
+ clean = False
+ if stat.S_ISREG(aMode):
+ mode = aMode
+ sha = aSha
+ else:
+ mode = bMode
+ sha = bSha
+ else:
+ if aSha != oSha and bSha != oSha:
+ merge = True
+
+ if aMode == oMode:
+ mode = bMode
+ else:
+ mode = aMode
+
+ if aSha == oSha:
+ sha = bSha
+ elif bSha == oSha:
+ sha = aSha
+ elif stat.S_ISREG(aMode):
+ assert(stat.S_ISREG(bMode))
+
+ orig = runProgram(['git-unpack-file', oSha]).rstrip()
+ src1 = runProgram(['git-unpack-file', aSha]).rstrip()
+ src2 = runProgram(['git-unpack-file', bSha]).rstrip()
+ [out, code] = runProgram(['merge',
+ '-L', branch1Name + '/' + aPath,
+ '-L', 'orig/' + oPath,
+ '-L', branch2Name + '/' + bPath,
+ src1, orig, src2], returnCode=True)
+
+ sha = runProgram(['git-hash-object', '-t', 'blob', '-w',
+ src1]).rstrip()
+
+ os.unlink(orig)
+ os.unlink(src1)
+ os.unlink(src2)
+
+ clean = (code == 0)
+ else:
+ assert(stat.S_ISLNK(aMode) and stat.S_ISLNK(bMode))
+ sha = aSha
+
+ if aSha != bSha:
+ clean = False
+
+ return [sha, mode, clean, merge]
+
+def updateFile(clean, sha, mode, path):
+ updateCache = cacheOnly or clean
+ updateWd = not cacheOnly
+
+ return updateFileExt(sha, mode, path, updateCache, updateWd)
+
+def updateFileExt(sha, mode, path, updateCache, updateWd):
+ if cacheOnly:
+ updateWd = False
+
+ if updateWd:
+ pathComponents = path.split('/')
+ for x in xrange(1, len(pathComponents)):
+ p = '/'.join(pathComponents[0:x])
+
+ try:
+ createDir = not stat.S_ISDIR(os.lstat(p).st_mode)
+ except:
+ createDir = True
+
+ if createDir:
+ try:
+ os.mkdir(p)
+ except OSError, e:
+ die("Couldn't create directory", p, e.strerror)
+
+ prog = ['git-cat-file', 'blob', sha]
+ if stat.S_ISREG(mode):
+ try:
+ os.unlink(path)
+ except OSError:
+ pass
+ if mode & 0100:
+ mode = 0777
+ else:
+ mode = 0666
+ fd = os.open(path, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode)
+ proc = subprocess.Popen(prog, stdout=fd)
+ proc.wait()
+ os.close(fd)
+ elif stat.S_ISLNK(mode):
+ linkTarget = runProgram(prog)
+ os.symlink(linkTarget, path)
+ else:
+ assert(False)
+
+ if updateWd and updateCache:
+ runProgram(['git-update-index', '--add', '--', path])
+ elif updateCache:
+ runProgram(['git-update-index', '--add', '--cacheinfo',
+ '0%o' % mode, sha, path])
+
+def removeFile(clean, path):
+ updateCache = cacheOnly or clean
+ updateWd = not cacheOnly
+
+ if updateCache:
+ runProgram(['git-update-index', '--force-remove', '--', path])
+
+ if updateWd:
+ try:
+ os.unlink(path)
+ except OSError, e:
+ if e.errno != errno.ENOENT and e.errno != errno.EISDIR:
+ raise
+
+def uniquePath(path, branch):
+ def fileExists(path):
+ try:
+ os.lstat(path)
+ return True
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ return False
+ else:
+ raise
+
+ newPath = path + '_' + branch
+ suffix = 0
+ while newPath in currentFileSet or \
+ newPath in currentDirectorySet or \
+ fileExists(newPath):
+ suffix += 1
+ newPath = path + '_' + branch + '_' + str(suffix)
+ currentFileSet.add(newPath)
+ return newPath
+
+# Cache entry management
+# ----------------------
+
class CacheEntry:
def __init__(self, path):
class Stage:
def __init__(self):
self.sha1 = None
self.mode = None
+
+ # Used for debugging only
+ def __str__(self):
+ if self.mode != None:
+ m = '0%o' % self.mode
+ else:
+ m = 'None'
+
+ if self.sha1:
+ sha1 = self.sha1
+ else:
+ sha1 = 'None'
+ return 'sha1: ' + sha1 + ' mode: ' + m
- self.stages = [Stage(), Stage(), Stage()]
+ self.stages = [Stage(), Stage(), Stage(), Stage()]
self.path = path
+ self.processed = False
+
+ def __str__(self):
+ return 'path: ' + self.path + ' stages: ' + repr([str(x) for x in self.stages])
+class CacheEntryContainer:
+ def __init__(self):
+ self.entries = {}
+
+ def add(self, entry):
+ self.entries[entry.path] = entry
+
+ def get(self, path):
+ return self.entries.get(path)
+
+ def __iter__(self):
+ return self.entries.itervalues()
+
unmergedRE = re.compile(r'^([0-7]+) ([0-9a-f]{40}) ([1-3])\t(.*)$', re.S)
def unmergedCacheEntries():
'''Create a dictionary mapping file names to CacheEntry
@@ -108,155 +357,340 @@ def unmergedCacheEntries():
lines = runProgram(['git-ls-files', '-z', '--unmerged']).split('\0')
lines.pop()
- res = {}
+ res = CacheEntryContainer()
for l in lines:
m = unmergedRE.match(l)
if m:
mode = int(m.group(1), 8)
sha1 = m.group(2)
- stage = int(m.group(3)) - 1
+ stage = int(m.group(3))
path = m.group(4)
- if res.has_key(path):
- e = res[path]
- else:
+ e = res.get(path)
+ if not e:
e = CacheEntry(path)
- res[path] = e
-
+ res.add(e)
+
e.stages[stage].mode = mode
e.stages[stage].sha1 = sha1
else:
- die('Error: Merge program failed: Unexpected output from', \
+ die('Error: Merge program failed: Unexpected output from',
'git-ls-files:', l)
return res
-def mergeTrees(head, merge, common, branch1Name, branch2Name,
- cleanCache):
- '''Merge the trees 'head' and 'merge' with the common ancestor
- 'common'. The name of the head branch is 'branch1Name' and the name of
- the merge branch is 'branch2Name'. Return a tuple (tree, cleanMerge)
- where tree is the resulting tree and cleanMerge is True iff the
- merge was clean.'''
+lsTreeRE = re.compile(r'^([0-7]+) (\S+) ([0-9a-f]{40})\t(.*)\n$', re.S)
+def getCacheEntry(path, origTree, aTree, bTree):
+ '''Returns a CacheEntry object which doesn't have to correspond to
+ a real cache entry in Git's index.'''
- assert(isSha(head) and isSha(merge) and isSha(common))
+ def parse(out):
+ if out == '':
+ return [None, None]
+ else:
+ m = lsTreeRE.match(out)
+ if not m:
+ die('Unexpected output from git-ls-tree:', out)
+ elif m.group(2) == 'blob':
+ return [m.group(3), int(m.group(1), 8)]
+ else:
+ return [None, None]
- if common == merge:
- print 'Already uptodate!'
- return [head, True]
+ res = CacheEntry(path)
- if cleanCache:
- updateArg = '-i'
- else:
- updateArg = '-u'
+ [oSha, oMode] = parse(runProgram(['git-ls-tree', origTree, '--', path]))
+ [aSha, aMode] = parse(runProgram(['git-ls-tree', aTree, '--', path]))
+ [bSha, bMode] = parse(runProgram(['git-ls-tree', bTree, '--', path]))
- [out, code] = runProgram(['git-read-tree', updateArg, '-m', common, head, merge], returnCode = True)
- if code != 0:
- die('git-read-tree:', out)
+ res.stages[1].sha1 = oSha
+ res.stages[1].mode = oMode
+ res.stages[2].sha1 = aSha
+ res.stages[2].mode = aMode
+ res.stages[3].sha1 = bSha
+ res.stages[3].mode = bMode
- cleanMerge = True
+ return res
- [tree, code] = runProgram('git-write-tree', returnCode=True)
- tree = tree.rstrip()
- if code != 0:
- [files, dirs] = getFilesAndDirs(head)
- [filesM, dirsM] = getFilesAndDirs(merge)
- files.union_update(filesM)
- dirs.union_update(dirsM)
-
- cleanMerge = True
- entries = unmergedCacheEntries()
- for name in entries:
- if not processEntry(entries[name], branch1Name, branch2Name,
- files, dirs, cleanCache):
- cleanMerge = False
-
- if cleanMerge or cleanCache:
- tree = runProgram('git-write-tree').rstrip()
+# Rename detection and handling
+# -----------------------------
+
+class RenameEntry:
+ def __init__(self,
+ src, srcSha, srcMode, srcCacheEntry,
+ dst, dstSha, dstMode, dstCacheEntry,
+ score):
+ self.srcName = src
+ self.srcSha = srcSha
+ self.srcMode = srcMode
+ self.srcCacheEntry = srcCacheEntry
+ self.dstName = dst
+ self.dstSha = dstSha
+ self.dstMode = dstMode
+ self.dstCacheEntry = dstCacheEntry
+ self.score = score
+
+ self.processed = False
+
+class RenameEntryContainer:
+ def __init__(self):
+ self.entriesSrc = {}
+ self.entriesDst = {}
+
+ def add(self, entry):
+ self.entriesSrc[entry.srcName] = entry
+ self.entriesDst[entry.dstName] = entry
+
+ def getSrc(self, path):
+ return self.entriesSrc.get(path)
+
+ def getDst(self, path):
+ return self.entriesDst.get(path)
+
+ def __iter__(self):
+ return self.entriesSrc.itervalues()
+
+parseDiffRenamesRE = re.compile('^:([0-7]+) ([0-7]+) ([0-9a-f]{40}) ([0-9a-f]{40}) R([0-9]*)$')
+def getRenames(tree, oTree, aTree, bTree, cacheEntries):
+ '''Get information of all renames which occured between 'oTree' and
+ 'tree'. We need the three trees in the merge ('oTree', 'aTree' and
+ 'bTree') to be able to associate the correct cache entries with
+ the rename information. 'tree' is always equal to either aTree or bTree.'''
+
+ assert(tree == aTree or tree == bTree)
+ inp = runProgram(['git-diff-tree', '-M', '--diff-filter=R', '-r',
+ '-z', oTree, tree])
+
+ ret = RenameEntryContainer()
+ try:
+ recs = inp.split("\0")
+ recs.pop() # remove last entry (which is '')
+ it = recs.__iter__()
+ while True:
+ rec = it.next()
+ m = parseDiffRenamesRE.match(rec)
+
+ if not m:
+ die('Unexpected output from git-diff-tree:', rec)
+
+ srcMode = int(m.group(1), 8)
+ dstMode = int(m.group(2), 8)
+ srcSha = m.group(3)
+ dstSha = m.group(4)
+ score = m.group(5)
+ src = it.next()
+ dst = it.next()
+
+ srcCacheEntry = cacheEntries.get(src)
+ if not srcCacheEntry:
+ srcCacheEntry = getCacheEntry(src, oTree, aTree, bTree)
+ cacheEntries.add(srcCacheEntry)
+
+ dstCacheEntry = cacheEntries.get(dst)
+ if not dstCacheEntry:
+ dstCacheEntry = getCacheEntry(dst, oTree, aTree, bTree)
+ cacheEntries.add(dstCacheEntry)
+
+ ret.add(RenameEntry(src, srcSha, srcMode, srcCacheEntry,
+ dst, dstSha, dstMode, dstCacheEntry,
+ score))
+ except StopIteration:
+ pass
+ return ret
+
+def fmtRename(src, dst):
+ srcPath = src.split('/')
+ dstPath = dst.split('/')
+ path = []
+ endIndex = min(len(srcPath), len(dstPath)) - 1
+ for x in range(0, endIndex):
+ if srcPath[x] == dstPath[x]:
+ path.append(srcPath[x])
else:
- tree = None
+ endIndex = x
+ break
+
+ if len(path) > 0:
+ return '/'.join(path) + \
+ '/{' + '/'.join(srcPath[endIndex:]) + ' => ' + \
+ '/'.join(dstPath[endIndex:]) + '}'
else:
- cleanMerge = True
+ return src + ' => ' + dst
- return [tree, cleanMerge]
+def processRenames(renamesA, renamesB, branchNameA, branchNameB):
+ srcNames = Set()
+ for x in renamesA:
+ srcNames.add(x.srcName)
+ for x in renamesB:
+ srcNames.add(x.srcName)
-def processEntry(entry, branch1Name, branch2Name, files, dirs, cleanCache):
- '''Merge one cache entry. 'files' is a Set with the files in both of
- the heads that we are going to merge. 'dirs' contains the
- corresponding data for directories. If 'cleanCache' is True no
- non-zero stages will be left in the cache for the path
- corresponding to the entry 'entry'.'''
+ cleanMerge = True
+ for path in srcNames:
+ if renamesA.getSrc(path):
+ renames1 = renamesA
+ renames2 = renamesB
+ branchName1 = branchNameA
+ branchName2 = branchNameB
+ else:
+ renames1 = renamesB
+ renames2 = renamesA
+ branchName1 = branchNameB
+ branchName2 = branchNameA
+
+ ren1 = renames1.getSrc(path)
+ ren2 = renames2.getSrc(path)
+
+ ren1.dstCacheEntry.processed = True
+ ren1.srcCacheEntry.processed = True
+
+ if ren1.processed:
+ continue
+
+ ren1.processed = True
+ removeFile(True, ren1.srcName)
+ if ren2:
+ # Renamed in 1 and renamed in 2
+ assert(ren1.srcName == ren2.srcName)
+ ren2.dstCacheEntry.processed = True
+ ren2.processed = True
+
+ if ren1.dstName != ren2.dstName:
+ print 'CONFLICT (rename/rename): Rename', \
+ fmtRename(path, ren1.dstName), 'in branch', branchName1, \
+ 'rename', fmtRename(path, ren2.dstName), 'in', branchName2
+ cleanMerge = False
-# cleanCache == True => Don't leave any non-stage 0 entries in the cache and
-# don't update the working directory
-# False => Leave unmerged entries and update the working directory
+ if ren1.dstName in currentDirectorySet:
+ dstName1 = uniquePath(ren1.dstName, branchName1)
+ print ren1.dstName, 'is a directory in', branchName2, \
+ 'adding as', dstName1, 'instead.'
+ removeFile(False, ren1.dstName)
+ else:
+ dstName1 = ren1.dstName
-# clean == True => non-conflict case
-# False => conflict case
+ if ren2.dstName in currentDirectorySet:
+ dstName2 = uniquePath(ren2.dstName, branchName2)
+ print ren2.dstName, 'is a directory in', branchName1, \
+ 'adding as', dstName2, 'instead.'
+ removeFile(False, ren2.dstName)
+ else:
+ dstName2 = ren1.dstName
-# If cleanCache == False then the cache shouldn't be updated if clean == False
+ updateFile(False, ren1.dstSha, ren1.dstMode, dstName1)
+ updateFile(False, ren2.dstSha, ren2.dstMode, dstName2)
+ else:
+ print 'Renaming', fmtRename(path, ren1.dstName)
+ [resSha, resMode, clean, merge] = \
+ mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode,
+ ren1.dstName, ren1.dstSha, ren1.dstMode,
+ ren2.dstName, ren2.dstSha, ren2.dstMode,
+ branchName1, branchName2)
+
+ if merge:
+ print 'Auto-merging', ren1.dstName
+
+ if not clean:
+ print 'CONFLICT (content): merge conflict in', ren1.dstName
+ cleanMerge = False
+
+ if not cacheOnly:
+ updateFileExt(ren1.dstSha, ren1.dstMode, ren1.dstName,
+ updateCache=True, updateWd=False)
+ updateFile(clean, resSha, resMode, ren1.dstName)
+ else:
+ # Renamed in 1, maybe changed in 2
+ if renamesA == renames1:
+ stage = 3
+ else:
+ stage = 2
+
+ srcShaOtherBranch = ren1.srcCacheEntry.stages[stage].sha1
+ srcModeOtherBranch = ren1.srcCacheEntry.stages[stage].mode
+
+ dstShaOtherBranch = ren1.dstCacheEntry.stages[stage].sha1
+ dstModeOtherBranch = ren1.dstCacheEntry.stages[stage].mode
+
+ tryMerge = False
+
+ if ren1.dstName in currentDirectorySet:
+ newPath = uniquePath(ren1.dstName, branchName1)
+ print 'CONFLICT (rename/directory): Rename', \
+ fmtRename(ren1.srcName, ren1.dstName), 'in', branchName1,\
+ 'directory', ren1.dstName, 'added in', branchName2
+ print 'Renaming', ren1.srcName, 'to', newPath, 'instead'
+ cleanMerge = False
+ removeFile(False, ren1.dstName)
+ updateFile(False, ren1.dstSha, ren1.dstMode, newPath)
+ elif srcShaOtherBranch == None:
+ print 'CONFLICT (rename/delete): Rename', \
+ fmtRename(ren1.srcName, ren1.dstName), 'in', \
+ branchName1, 'and deleted in', branchName2
+ cleanMerge = False
+ updateFile(False, ren1.dstSha, ren1.dstMode, ren1.dstName)
+ elif dstShaOtherBranch:
+ newPath = uniquePath(ren1.dstName, branchName2)
+ print 'CONFLICT (rename/add): Rename', \
+ fmtRename(ren1.srcName, ren1.dstName), 'in', \
+ branchName1 + '.', ren1.dstName, 'added in', branchName2
+ print 'Adding as', newPath, 'instead'
+ updateFile(False, dstShaOtherBranch, dstModeOtherBranch, newPath)
+ cleanMerge = False
+ tryMerge = True
+ elif renames2.getDst(ren1.dstName):
+ dst2 = renames2.getDst(ren1.dstName)
+ newPath1 = uniquePath(ren1.dstName, branchName1)
+ newPath2 = uniquePath(dst2.dstName, branchName2)
+ print 'CONFLICT (rename/rename): Rename', \
+ fmtRename(ren1.srcName, ren1.dstName), 'in', \
+ branchName1+'. Rename', \
+ fmtRename(dst2.srcName, dst2.dstName), 'in', branchName2
+ print 'Renaming', ren1.srcName, 'to', newPath1, 'and', \
+ dst2.srcName, 'to', newPath2, 'instead'
+ removeFile(False, ren1.dstName)
+ updateFile(False, ren1.dstSha, ren1.dstMode, newPath1)
+ updateFile(False, dst2.dstSha, dst2.dstMode, newPath2)
+ dst2.processed = True
+ cleanMerge = False
+ else:
+ tryMerge = True
- def updateFile(clean, sha, mode, path, onlyWd=False):
- updateCache = not onlyWd and (cleanCache or (not cleanCache and clean))
- updateWd = onlyWd or (not cleanCache and clean)
+ if tryMerge:
+ print 'Renaming', fmtRename(ren1.srcName, ren1.dstName)
+ [resSha, resMode, clean, merge] = \
+ mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode,
+ ren1.dstName, ren1.dstSha, ren1.dstMode,
+ ren1.srcName, srcShaOtherBranch, srcModeOtherBranch,
+ branchName1, branchName2)
- if updateWd:
- prog = ['git-cat-file', 'blob', sha]
- if stat.S_ISREG(mode):
- try:
- os.unlink(path)
- except OSError:
- pass
- if mode & 0100:
- mode = 0777
- else:
- mode = 0666
- fd = os.open(path, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode)
- proc = subprocess.Popen(prog, stdout=fd)
- proc.wait()
- os.close(fd)
- elif stat.S_ISLNK(mode):
- linkTarget = runProgram(prog)
- os.symlink(linkTarget, path)
- else:
- assert(False)
+ if merge:
+ print 'Auto-merging', ren1.dstName
- if updateWd and updateCache:
- runProgram(['git-update-index', '--add', '--', path])
- elif updateCache:
- runProgram(['git-update-index', '--add', '--cacheinfo',
- '0%o' % mode, sha, path])
+ if not clean:
+ print 'CONFLICT (rename/modify): Merge conflict in', ren1.dstName
+ cleanMerge = False
- def removeFile(clean, path):
- if cleanCache or (not cleanCache and clean):
- runProgram(['git-update-index', '--force-remove', '--', path])
+ if not cacheOnly:
+ updateFileExt(ren1.dstSha, ren1.dstMode, ren1.dstName,
+ updateCache=True, updateWd=False)
+ updateFile(clean, resSha, resMode, ren1.dstName)
- if not cleanCache and clean:
- try:
- os.unlink(path)
- except OSError, e:
- if e.errno != errno.ENOENT and e.errno != errno.EISDIR:
- raise
+ return cleanMerge
- def uniquePath(path, branch):
- newPath = path + '_' + branch
- suffix = 0
- while newPath in files or newPath in dirs:
- suffix += 1
- newPath = path + '_' + branch + '_' + str(suffix)
- files.add(newPath)
- return newPath
+# Per entry merge function
+# ------------------------
- debug('processing', entry.path, 'clean cache:', cleanCache)
+def processEntry(entry, branch1Name, branch2Name):
+ '''Merge one cache entry.'''
+
+ debug('processing', entry.path, 'clean cache:', cacheOnly)
cleanMerge = True
path = entry.path
- oSha = entry.stages[0].sha1
- oMode = entry.stages[0].mode
- aSha = entry.stages[1].sha1
- aMode = entry.stages[1].mode
- bSha = entry.stages[2].sha1
- bMode = entry.stages[2].mode
+ oSha = entry.stages[1].sha1
+ oMode = entry.stages[1].mode
+ aSha = entry.stages[2].sha1
+ aMode = entry.stages[2].mode
+ bSha = entry.stages[3].sha1
+ bMode = entry.stages[3].mode
assert(oSha == None or isSha(oSha))
assert(aSha == None or isSha(aSha))
@@ -275,28 +709,26 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs, cleanCache):
(not aSha and bSha == oSha):
# Deleted in both or deleted in one and unchanged in the other
if aSha:
- print 'Removing ' + path
+ print 'Removing', path
removeFile(True, path)
else:
# Deleted in one and changed in the other
cleanMerge = False
if not aSha:
- print 'CONFLICT (del/mod): "' + path + '" deleted in', \
- branch1Name, 'and modified in', branch2Name, \
- '. Version', branch2Name, ' of "' + path + \
- '" left in tree'
+ print 'CONFLICT (delete/modify):', path, 'deleted in', \
+ branch1Name, 'and modified in', branch2Name + '.', \
+ 'Version', branch2Name, 'of', path, 'left in tree.'
mode = bMode
sha = bSha
else:
- print 'CONFLICT (mod/del): "' + path + '" deleted in', \
- branch2Name, 'and modified in', branch1Name + \
- '. Version', branch1Name, 'of "' + path + \
- '" left in tree'
+ print 'CONFLICT (modify/delete):', path, 'deleted in', \
+ branch2Name, 'and modified in', branch1Name + '.', \
+ 'Version', branch1Name, 'of', path, 'left in tree.'
mode = aMode
sha = aSha
updateFile(False, sha, mode, path)
-
+
elif (not oSha and aSha and not bSha) or \
(not oSha and not aSha and bSha):
#
@@ -307,27 +739,26 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs, cleanCache):
otherBranch = branch2Name
mode = aMode
sha = aSha
- conf = 'file/dir'
+ conf = 'file/directory'
else:
addBranch = branch2Name
otherBranch = branch1Name
mode = bMode
sha = bSha
- conf = 'dir/file'
+ conf = 'directory/file'
- if path in dirs:
+ if path in currentDirectorySet:
cleanMerge = False
newPath = uniquePath(path, addBranch)
- print 'CONFLICT (' + conf + \
- '): There is a directory with name "' + path + '" in', \
- otherBranch + '. Adding "' + path + '" as "' + newPath + '"'
+ print 'CONFLICT (' + conf + '):', \
+ 'There is a directory with name', path, 'in', \
+ otherBranch + '. Adding', path, 'as', newPath
removeFile(False, path)
- path = newPath
+ updateFile(False, sha, mode, newPath)
else:
- print 'Adding "' + path + '"'
-
- updateFile(True, sha, mode, path)
+ print 'Adding', path
+ updateFile(True, sha, mode, path)
elif not oSha and aSha and bSha:
#
@@ -336,10 +767,9 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs, cleanCache):
if aSha == bSha:
if aMode != bMode:
cleanMerge = False
- print 'CONFLICT: File "' + path + \
- '" added identically in both branches,', \
- 'but permissions conflict', '0%o' % aMode, '->', \
- '0%o' % bMode
+ print 'CONFLICT: File', path, \
+ 'added identically in both branches, but permissions', \
+ 'conflict', '0%o' % aMode, '->', '0%o' % bMode
print 'CONFLICT: adding with permission:', '0%o' % aMode
updateFile(False, aSha, aMode, path)
@@ -350,8 +780,9 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs, cleanCache):
cleanMerge = False
newPath1 = uniquePath(path, branch1Name)
newPath2 = uniquePath(path, branch2Name)
- print 'CONFLICT (add/add): File "' + path + \
- '" added non-identically in both branches.'
+ print 'CONFLICT (add/add): File', path, \
+ 'added non-identically in both branches. Adding as', \
+ newPath1, 'and', newPath2, 'instead.'
removeFile(False, path)
updateFile(False, aSha, aMode, newPath1)
updateFile(False, bSha, bMode, newPath2)
@@ -360,39 +791,24 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs, cleanCache):
#
# case D: Modified in both, but differently.
#
- print 'Auto-merging', path
- orig = runProgram(['git-unpack-file', oSha]).rstrip()
- src1 = runProgram(['git-unpack-file', aSha]).rstrip()
- src2 = runProgram(['git-unpack-file', bSha]).rstrip()
- [out, ret] = runProgram(['merge',
- '-L', branch1Name + '/' + path,
- '-L', 'orig/' + path,
- '-L', branch2Name + '/' + path,
- src1, orig, src2], returnCode=True)
-
- if aMode == oMode:
- mode = bMode
+ print 'Auto-merging', path
+ [sha, mode, clean, dummy] = \
+ mergeFile(path, oSha, oMode,
+ path, aSha, aMode,
+ path, bSha, bMode,
+ branch1Name, branch2Name)
+ if clean:
+ updateFile(True, sha, mode, path)
else:
- mode = aMode
-
- sha = runProgram(['git-hash-object', '-t', 'blob', '-w',
- src1]).rstrip()
-
- if ret != 0:
cleanMerge = False
- print 'CONFLICT (content): Merge conflict in "' + path + '".'
+ print 'CONFLICT (content): Merge conflict in', path
- if cleanCache:
+ if cacheOnly:
updateFile(False, sha, mode, path)
else:
- updateFile(True, aSha, aMode, path)
- updateFile(False, sha, mode, path, True)
- else:
- updateFile(True, sha, mode, path)
-
- os.unlink(orig)
- os.unlink(src1)
- os.unlink(src2)
+ updateFileExt(aSha, aMode, path,
+ updateCache=True, updateWd=False)
+ updateFileExt(sha, mode, path, updateCache=False, updateWd=True)
else:
die("ERROR: Fatal merge failure, shouldn't happen.")
@@ -416,7 +832,7 @@ for nextArg in xrange(1, len(sys.argv)):
try:
h1 = firstBranch = sys.argv[nextArg + 1]
h2 = secondBranch = sys.argv[nextArg + 2]
- except IndexError:
+ except IndexError:
usage()
break
@@ -428,8 +844,8 @@ try:
graph = buildGraph([h1, h2])
- [res, clean] = merge(graph.shaMap[h1], graph.shaMap[h2],
- firstBranch, secondBranch, graph)
+ [dummy, clean] = merge(graph.shaMap[h1], graph.shaMap[h2],
+ firstBranch, secondBranch, graph)
print ''
except:
--
cgit v1.2.1
From 500b97e4bb184c954a52550843e6314ecf97a208 Mon Sep 17 00:00:00 2001
From: Fredrik Kuivinen <freku045@student.liu.se>
Date: Sun, 2 Oct 2005 17:33:38 +0200
Subject: [PATCH] Teach git-ls-files about '--' to denote end of options.
Useful if you have a file whose name starts with a dash.
Signed-off-by: Fredrik Kuivinen <freku045@student.liu.se>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Documentation/git-ls-files.txt | 9 +++++-
ls-files.c | 6 +++-
t/t3002-ls-files-dashpath.sh | 69 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+), 2 deletions(-)
create mode 100755 t/t3002-ls-files-dashpath.sh
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 591f4ed462..87cc362ad8 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -13,7 +13,7 @@ SYNOPSIS
(-[c|d|o|i|s|u|k|m])\*
[-x <pattern>|--exclude=<pattern>]
[-X <file>|--exclude-from=<file>]
- [--exclude-per-directory=<file>]
+ [--exclude-per-directory=<file>] [--] [<file>]\*
DESCRIPTION
-----------
@@ -77,6 +77,13 @@ OPTIONS
K to be killed
? other
+--::
+ Do not interpret any more arguments as options.
+
+<file>::
+ Files to show. If no files are given all files which match the other
+ specified criteria are shown.
+
Output
------
show files just outputs the filename unless '--stage' is specified in
diff --git a/ls-files.c b/ls-files.c
index 956be09350..f47114a168 100644
--- a/ls-files.c
+++ b/ls-files.c
@@ -530,7 +530,7 @@ static void verify_pathspec(void)
static const char ls_files_usage[] =
"git-ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
- "[ --exclude-per-directory=<filename> ]";
+ "[ --exclude-per-directory=<filename> ] [--] [<file>]*";
int main(int argc, const char **argv)
{
@@ -544,6 +544,10 @@ int main(int argc, const char **argv)
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
+ if (!strcmp(arg, "--")) {
+ i++;
+ break;
+ }
if (!strcmp(arg, "-z")) {
line_terminator = 0;
continue;
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
new file mode 100755
index 0000000000..b42f1382bc
--- /dev/null
+++ b/t/t3002-ls-files-dashpath.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-ls-files test (-- to terminate the path list).
+
+This test runs git-ls-files --others with the following on the
+filesystem.
+
+ path0 - a file
+ -foo - a file with a funny name.
+ -- - another file with a funny name.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ setup \
+ 'echo frotz >path0 &&
+ echo frotz >./-foo &&
+ echo frotz >./--'
+
+test_expect_success \
+ 'git-ls-files without path restriction.' \
+ 'git-ls-files --others >output &&
+ diff -u output - <<EOF
+--
+-foo
+output
+path0
+EOF
+'
+
+test_expect_success \
+ 'git-ls-files with path restriction.' \
+ 'git-ls-files --others path0 >output &&
+ diff -u output - <<EOF
+path0
+EOF
+'
+
+test_expect_success \
+ 'git-ls-files with path restriction with --.' \
+ 'git-ls-files --others -- path0 >output &&
+ diff -u output - <<EOF
+path0
+EOF
+'
+
+test_expect_success \
+ 'git-ls-files with path restriction with -- --.' \
+ 'git-ls-files --others -- -- >output &&
+ diff -u output - <<EOF
+--
+EOF
+'
+
+test_expect_success \
+ 'git-ls-files with no path restriction.' \
+ 'git-ls-files --others -- >output &&
+ diff -u output - <<EOF
+--
+-foo
+output
+path0
+EOF
+'
+
+test_done
--
cgit v1.2.1
From 23822a355de674f1d1321ad7e112f0fe40124aca Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Thu, 29 Sep 2005 08:16:12 -0700
Subject: read-tree: --trivial
This adds an option --trivial to restrict 3-way 'read-tree -m -u'
to happen only if there is no file-level merging required.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
read-tree.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/read-tree.c b/read-tree.c
index 390fe2f027..5fdf58d240 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -13,6 +13,8 @@
static int merge = 0;
static int update = 0;
static int index_only = 0;
+static int nontrivial_merge = 0;
+static int trivial_merges_only = 0;
static int head_idx = -1;
static int merge_size = 0;
@@ -275,6 +277,9 @@ static int unpack_trees(merge_fn_t fn)
if (unpack_trees_rec(posns, len, "", fn, &indpos))
return -1;
+ if (trivial_merges_only && nontrivial_merge)
+ die("Merge requires file-level merging");
+
check_updates(active_cache, active_nr);
return 0;
}
@@ -460,6 +465,8 @@ static int threeway_merge(struct cache_entry **stages)
verify_uptodate(index);
}
+ nontrivial_merge = 1;
+
/* #2, #3, #4, #6, #7, #9, #11. */
count = 0;
if (!head_match || !remote_match) {
@@ -629,6 +636,11 @@ int main(int argc, char **argv)
continue;
}
+ if (!strcmp(arg, "--trivial")) {
+ trivial_merges_only = 1;
+ continue;
+ }
+
/* "-m" stands for "merge", meaning we start in stage 1 */
if (!strcmp(arg, "-m")) {
if (stage || merge)
--
cgit v1.2.1
From f9d72413bcbf33ced45f12e17ef156abd73963f6 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 2 Oct 2005 11:13:44 -0700
Subject: Handle really trivial case inside git-merge.
Using Linus' --trivial option, this handles really trivial case
inside git-merge itself, without using any strategy modules.
A 'really trivial case' is:
- we are merging one branch into the current branch;
- there is only one merge base between the branches;
- there is no file-level merge required.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
git-merge.sh | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/git-merge.sh b/git-merge.sh
index 29e86c6953..d12a2a93b1 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -123,10 +123,30 @@ case "$#,$common" in
dropsave
exit 0
;;
-1,*)
+1,?*"$LF"?*)
# We are not doing octopus and not fast forward. Need a
# real merge.
;;
+1,*)
+ # We are not doing octopus, not fast forward, and have only
+ # one common. See if it is really trivial.
+ echo "Trying really trivial in-index merge..."
+ git-update-index --refresh 2>/dev/null
+ if git-read-tree --trivial -m -u $common $head "$1" &&
+ result_tree=$(git-write-tree)
+ then
+ echo "Wonderful."
+ result_commit=$(
+ echo "$merge_msg" |
+ git-commit-tree $result_tree -p HEAD -p "$1"
+ ) || exit
+ git-update-ref HEAD $result_commit $head
+ summary $result_commit
+ dropsave
+ exit 0
+ fi
+ echo "Nope."
+ ;;
*)
# An octopus. If we can reach all the remote we are up to date.
up_to_date=t
--
cgit v1.2.1
From a2775c2a410be4bec1e29ae78bccd3247829e2be Mon Sep 17 00:00:00 2001
From: "Eric W. Biederman" <ebiederm@xmission.com>
Date: Sun, 2 Oct 2005 13:42:57 -0600
Subject: [PATCH] Update git-clone documentation
The documentation for git-clone is behind the actual command.
I have been getting tired of reading the shell script to see
what the arguments are so here is an update of the actual documentation.
Signed-off-by: Eric Biederman <ebiederman@xmission.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Documentation/git-clone.txt | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index bd53ef430d..7d713c7385 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -9,7 +9,7 @@ git-clone - Clones a repository.
SYNOPSIS
--------
-'git clone' [-l] [-u <upload-pack>] [-q] <repository> <directory>
+'git clone' [-l [-s]] [-q] [-n] [-u <upload-pack>] <repository> <directory>
DESCRIPTION
-----------
@@ -17,6 +17,7 @@ Clones a repository into a newly created directory.
OPTIONS
-------
+--local::
-l::
When the repository to clone from is on a local machine,
this flag bypasses normal "git aware" transport
@@ -25,10 +26,22 @@ OPTIONS
The files under .git/objects/ directory are hardlinked
to save space when possible.
+--shared::
+-s::
+ When the repository to clone is on the local machine,
+ instead of using hard links automatically setup
+ .git/objects/info/alternatives to share the objects
+ with the source repository
+
+--quiet::
-q::
Operate quietly. This flag is passed to "rsync" and
"git-clone-pack" commands when given.
+-n::
+ No checkout of HEAD is performed after the clone is complete.
+
+--upload-pack <upload-pack>::
-u <upload-pack>::
When given, and the repository to clone from is handled
by 'git-clone-pack', '--exec=<upload-pack>' is passed to
--
cgit v1.2.1
From 91dd674e30ba0298e89c9be2657024805170c2ac Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 2 Oct 2005 12:56:31 -0700
Subject: GIT 0.99.8
GIT already did everything I wanted it to do since mid 0.99.7,
and it has almost everything I want it to have now, except a
couple of minor tweaks and enhancements.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 2 +-
debian/changelog | 6 ++++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 4d721f2ace..92f0bda5e7 100644
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,7 @@
# DEFINES += -DUSE_STDEV
-GIT_VERSION = 0.99.7.GIT
+GIT_VERSION = 0.99.8
CFLAGS = -g -O2 -Wall
ALL_CFLAGS = $(CFLAGS) $(PLATFORM_DEFINES) $(DEFINES)
diff --git a/debian/changelog b/debian/changelog
index 128513a455..bebc1919ba 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+git-core (0.99.8-0) unstable; urgency=low
+
+ * GIT 0.99.8
+
+ -- Junio C Hamano <junkio@cox.net> Sun, 2 Oct 2005 12:54:26 -0700
+
git-core (0.99.7-0) unstable; urgency=low
* GIT 0.99.7
--
cgit v1.2.1
From baaac6a7143ad07de62926421f8740cd640c241c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 2 Oct 2005 16:37:27 -0700
Subject: Post 0.99.8 master branch Signed-off-by: Junio C Hamano
<junkio@cox.net>
---
Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 92f0bda5e7..25fd5cde86 100644
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,7 @@
# DEFINES += -DUSE_STDEV
-GIT_VERSION = 0.99.8
+GIT_VERSION = 0.99.8.GIT
CFLAGS = -g -O2 -Wall
ALL_CFLAGS = $(CFLAGS) $(PLATFORM_DEFINES) $(DEFINES)
--
cgit v1.2.1