* Re: [RFH] zlib gurus out there?
From: Junio C Hamano @ 2006-03-08 1:22 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0603071658300.32577@g5.osdl.org>
Linus Torvalds <torvalds@osdl.org> writes:
> On Tue, 7 Mar 2006, Junio C Hamano wrote:
>>
>> However, I am stuck with the first step, which is to do a full
>> flush after the header. An obvious change to the code quoted
>> above writes out a corrupt object:
>>
>> /* First header.. */
>> stream.next_in = hdr;
>> stream.avail_in = hdrlen;
>> - while (deflate(&stream, 0) == Z_OK)
>> + while (deflate(&stream, Z_FULL_FLUSH) == Z_OK)
>> /* nothing */;
>
> No, I don't think that's good. You're only doing a partial deflate, you
> can't ask for a Z_FULL_FLUSH. That only works if you give it the whole
> buffer, and you don't.
So, in short there is no way to create:
hdr part deflated.
flush.
data part deflated independently.
and have the current sha1_read_file() not to notice that flush,
while I can inspect the deflated stream to find the "flush", and
copy only the defalted data part into a pack? Bummer... I was
really shooting for full backward compatibility.
^ permalink raw reply
* Re: [RFH] zlib gurus out there?
From: Linus Torvalds @ 2006-03-08 0:59 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Nicolas Pitre
In-Reply-To: <7vzmk1izpa.fsf_-_@assigned-by-dhcp.cox.net>
On Tue, 7 Mar 2006, Junio C Hamano wrote:
>
> However, I am stuck with the first step, which is to do a full
> flush after the header. An obvious change to the code quoted
> above writes out a corrupt object:
>
> /* First header.. */
> stream.next_in = hdr;
> stream.avail_in = hdrlen;
> - while (deflate(&stream, 0) == Z_OK)
> + while (deflate(&stream, Z_FULL_FLUSH) == Z_OK)
> /* nothing */;
No, I don't think that's good. You're only doing a partial deflate, you
can't ask for a Z_FULL_FLUSH. That only works if you give it the whole
buffer, and you don't.
iirc, wtf, wdik, and ianal.
Linus
^ permalink raw reply
* [PATCH] Update http-push functionality
From: Nick Hengeveld @ 2006-03-08 0:13 UTC (permalink / raw)
To: git
This brings http-push functionality more in line with the ssh/git version,
by borrowing bits from send-pack and rev-list to process refspecs and
revision history in more standard ways. Also, the status of remote objects
is determined using PROPFIND requests for the object directory rather than
HEAD requests for each object - while it may be less efficient for small
numbers of objects, this approach is able to get the status of all remote
loose objects in a maximum of 256 requests.
---
I've tested this by running "git push --all --tags remote" into a newly
initialized repo, and by rewinding one or multiple overlapping heads and
pushing combinations of them.
This also lays a bit of groundwork for updating remote info/refs.
Getting my head around the underlying object/refspec/revision stuff made
it hurt, and I've got to say that you guys rule.
Makefile | 2
http-push.c | 1060 ++++++++++++++++++++++++++++++++++++++++-------------------
2 files changed, 727 insertions(+), 335 deletions(-)
9d2903e7bf846eeefd3f108207d7c212139ff97c
diff --git a/Makefile b/Makefile
index b6d8804..1c555da 100644
--- a/Makefile
+++ b/Makefile
@@ -526,7 +526,7 @@ git-http-fetch$X: fetch.o http.o http-fe
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL)
-git-http-push$X: http.o http-push.o $(LIB_FILE)
+git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
diff --git a/http-push.c b/http-push.c
index fe92560..970cbfe 100644
--- a/http-push.c
+++ b/http-push.c
@@ -5,6 +5,8 @@
#include "tag.h"
#include "blob.h"
#include "http.h"
+#include "refs.h"
+#include "revision.h"
#include <expat.h>
@@ -42,14 +44,24 @@ enum XML_Status {
#define DAV_ACTIVELOCK_OWNER ".prop.lockdiscovery.activelock.owner.href"
#define DAV_ACTIVELOCK_TIMEOUT ".prop.lockdiscovery.activelock.timeout"
#define DAV_ACTIVELOCK_TOKEN ".prop.lockdiscovery.activelock.locktoken.href"
+#define DAV_PROPFIND_RESP ".multistatus.response"
+#define DAV_PROPFIND_NAME ".multistatus.response.href"
+#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"
/* DAV request body templates */
-#define PROPFIND_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>"
+#define PROPFIND_SUPPORTEDLOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>"
+#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"
#define LOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:lockinfo xmlns:D=\"DAV:\">\n<D:lockscope><D:exclusive/></D:lockscope>\n<D:locktype><D:write/></D:locktype>\n<D:owner>\n<D:href>mailto:%s</D:href>\n</D:owner>\n</D:lockinfo>"
#define LOCK_TIME 600
#define LOCK_REFRESH 30
+/* bits #0-4 in revision.h */
+
+#define LOCAL (1u << 5)
+#define REMOTE (1u << 6)
+#define PUSHING (1u << 7)
+
static int pushing = 0;
static int aborted = 0;
static char remote_dir_exists[256];
@@ -61,17 +73,19 @@ static int push_verbosely = 0;
static int push_all = 0;
static int force_all = 0;
+static struct object_list *objects = NULL;
+
struct repo
{
char *url;
+ int path_len;
struct packed_git *packs;
};
static struct repo *remote = NULL;
+static struct remote_lock *remote_locks = NULL;
enum transfer_state {
- NEED_CHECK,
- RUN_HEAD,
NEED_PUSH,
RUN_MKCOL,
RUN_PUT,
@@ -82,10 +96,10 @@ enum transfer_state {
struct transfer_request
{
- unsigned char sha1[20];
+ struct object *obj;
char *url;
char *dest;
- struct active_lock *lock;
+ struct remote_lock *lock;
struct curl_slist *headers;
struct buffer buffer;
char filename[PATH_MAX];
@@ -114,14 +128,23 @@ struct xml_ctx
void *userData;
};
-struct active_lock
+struct remote_lock
{
char *url;
char *owner;
char *token;
time_t start_time;
long timeout;
+ int active;
int refreshing;
+ struct remote_lock *next;
+};
+
+struct remote_dentry
+{
+ char *base;
+ char *name;
+ int is_dir;
};
static void finish_request(struct transfer_request *request);
@@ -134,42 +157,9 @@ static void process_response(void *callb
finish_request(request);
}
-static void start_check(struct transfer_request *request)
-{
- char *hex = sha1_to_hex(request->sha1);
- struct active_request_slot *slot;
- char *posn;
-
- request->url = xmalloc(strlen(remote->url) + 55);
- strcpy(request->url, remote->url);
- posn = request->url + strlen(remote->url);
- strcpy(posn, "objects/");
- posn += 8;
- memcpy(posn, hex, 2);
- posn += 2;
- *(posn++) = '/';
- strcpy(posn, hex + 2);
-
- slot = get_active_slot();
- slot->callback_func = process_response;
- slot->callback_data = request;
- curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
- curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
- curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
-
- if (start_active_slot(slot)) {
- request->slot = slot;
- request->state = RUN_HEAD;
- } else {
- request->state = ABORTED;
- free(request->url);
- request->url = NULL;
- }
-}
-
static void start_mkcol(struct transfer_request *request)
{
- char *hex = sha1_to_hex(request->sha1);
+ char *hex = sha1_to_hex(request->obj->sha1);
struct active_request_slot *slot;
char *posn;
@@ -203,7 +193,7 @@ static void start_mkcol(struct transfer_
static void start_put(struct transfer_request *request)
{
- char *hex = sha1_to_hex(request->sha1);
+ char *hex = sha1_to_hex(request->obj->sha1);
struct active_request_slot *slot;
char *posn;
char type[20];
@@ -214,7 +204,7 @@ static void start_put(struct transfer_re
ssize_t size;
z_stream stream;
- unpacked = read_sha1_file(request->sha1, type, &len);
+ unpacked = read_sha1_file(request->obj->sha1, type, &len);
hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
/* Set it up */
@@ -309,64 +299,90 @@ static void start_move(struct transfer_r
}
}
-static int refresh_lock(struct active_lock *lock)
+static int refresh_lock(struct remote_lock *check_lock)
{
struct active_request_slot *slot;
char *if_header;
char timeout_header[25];
struct curl_slist *dav_headers = NULL;
- int rc = 0;
+ struct remote_lock *lock;
+ int time_remaining;
+ time_t current_time;
- lock->refreshing = 1;
+ /* Refresh all active locks if they're close to expiring */
+ for (lock = remote_locks; lock; lock = lock->next) {
+ if (!lock->active)
+ continue;
- if_header = xmalloc(strlen(lock->token) + 25);
- sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
- sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout);
- dav_headers = curl_slist_append(dav_headers, if_header);
- dav_headers = curl_slist_append(dav_headers, timeout_header);
+ current_time = time(NULL);
+ time_remaining = lock->start_time + lock->timeout
+ - current_time;
+ if (time_remaining > LOCK_REFRESH)
+ continue;
- slot = get_active_slot();
- curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
- curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
- curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
- curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
- curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+ lock->refreshing = 1;
- if (start_active_slot(slot)) {
- run_active_slot(slot);
- if (slot->curl_result != CURLE_OK) {
- fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
- } else {
- lock->start_time = time(NULL);
- rc = 1;
+ if_header = xmalloc(strlen(lock->token) + 25);
+ sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
+ sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout);
+ dav_headers = curl_slist_append(dav_headers, if_header);
+ dav_headers = curl_slist_append(dav_headers, timeout_header);
+
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
+ curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result != CURLE_OK) {
+ fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
+ lock->active = 0;
+ } else {
+ lock->active = 1;
+ lock->start_time = time(NULL);
+ }
}
+
+ lock->refreshing = 0;
+ curl_slist_free_all(dav_headers);
+ free(if_header);
}
- lock->refreshing = 0;
- curl_slist_free_all(dav_headers);
- free(if_header);
+ if (check_lock)
+ return check_lock->active;
+ else
+ return 0;
+}
- return rc;
+static void release_request(struct transfer_request *request)
+{
+ struct transfer_request *entry = request_queue_head;
+
+ if (request == request_queue_head) {
+ request_queue_head = request->next;
+ } else {
+ while (entry->next != NULL && entry->next != request)
+ entry = entry->next;
+ if (entry->next == request)
+ entry->next = entry->next->next;
+ }
+
+ if (request->url != NULL)
+ free(request->url);
+ free(request);
}
static void finish_request(struct transfer_request *request)
{
- time_t current_time = time(NULL);
- int time_remaining;
-
request->curl_result = request->slot->curl_result;
request->http_code = request->slot->http_code;
request->slot = NULL;
- /* Refresh the lock if it is close to timing out */
- time_remaining = request->lock->start_time + request->lock->timeout
- - current_time;
- if (time_remaining < LOCK_REFRESH && !request->lock->refreshing) {
- if (!refresh_lock(request->lock)) {
- fprintf(stderr, "Unable to refresh remote lock\n");
- aborted = 1;
- }
- }
+ /* Keep locks active */
+ refresh_lock(request->lock);
if (request->headers != NULL)
curl_slist_free_all(request->headers);
@@ -377,27 +393,14 @@ static void finish_request(struct transf
request->url = NULL;
}
- if (request->state == RUN_HEAD) {
- if (request->http_code == 404) {
- request->state = NEED_PUSH;
- } else if (request->curl_result == CURLE_OK) {
- remote_dir_exists[request->sha1[0]] = 1;
- request->state = COMPLETE;
- } else {
- fprintf(stderr, "HEAD %s failed, aborting (%d/%ld)\n",
- sha1_to_hex(request->sha1),
- request->curl_result, request->http_code);
- request->state = ABORTED;
- aborted = 1;
- }
- } else if (request->state == RUN_MKCOL) {
+ if (request->state == RUN_MKCOL) {
if (request->curl_result == CURLE_OK ||
request->http_code == 405) {
- remote_dir_exists[request->sha1[0]] = 1;
+ remote_dir_exists[request->obj->sha1[0]] = 1;
start_put(request);
} else {
fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",
- sha1_to_hex(request->sha1),
+ sha1_to_hex(request->obj->sha1),
request->curl_result, request->http_code);
request->state = ABORTED;
aborted = 1;
@@ -407,21 +410,21 @@ static void finish_request(struct transf
start_move(request);
} else {
fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n",
- sha1_to_hex(request->sha1),
+ sha1_to_hex(request->obj->sha1),
request->curl_result, request->http_code);
request->state = ABORTED;
aborted = 1;
}
} else if (request->state == RUN_MOVE) {
if (request->curl_result == CURLE_OK) {
- if (push_verbosely)
- fprintf(stderr,
- "sent %s\n",
- sha1_to_hex(request->sha1));
+ fprintf(stderr, " sent %s\n",
+ sha1_to_hex(request->obj->sha1));
request->state = COMPLETE;
+ request->obj->flags |= REMOTE;
+ release_request(request);
} else {
fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n",
- sha1_to_hex(request->sha1),
+ sha1_to_hex(request->obj->sha1),
request->curl_result, request->http_code);
request->state = ABORTED;
aborted = 1;
@@ -429,24 +432,6 @@ static void finish_request(struct transf
}
}
-static void release_request(struct transfer_request *request)
-{
- struct transfer_request *entry = request_queue_head;
-
- if (request == request_queue_head) {
- request_queue_head = request->next;
- } else {
- while (entry->next != NULL && entry->next != request)
- entry = entry->next;
- if (entry->next == request)
- entry->next = entry->next->next;
- }
-
- if (request->url != NULL)
- free(request->url);
- free(request);
-}
-
void fill_active_slots(void)
{
struct transfer_request *request = request_queue_head;
@@ -457,14 +442,12 @@ void fill_active_slots(void)
return;
while (active_requests < max_requests && request != NULL) {
- if (!pushing && request->state == NEED_CHECK) {
- start_check(request);
- curl_multi_perform(curlm, &num_transfers);
- } else if (pushing && request->state == NEED_PUSH) {
- if (remote_dir_exists[request->sha1[0]])
+ if (pushing && request->state == NEED_PUSH) {
+ if (remote_dir_exists[request->obj->sha1[0]] == 1) {
start_put(request);
- else
+ } else {
start_mkcol(request);
+ }
curl_multi_perform(curlm, &num_transfers);
}
request = request->next;
@@ -479,26 +462,34 @@ void fill_active_slots(void)
}
}
-static void add_request(unsigned char *sha1, struct active_lock *lock)
+static void get_remote_object_list(unsigned char parent);
+
+static void add_request(struct object *obj, struct remote_lock *lock)
{
struct transfer_request *request = request_queue_head;
struct packed_git *target;
-
- while (request != NULL && memcmp(request->sha1, sha1, 20))
- request = request->next;
- if (request != NULL)
- return;
- target = find_sha1_pack(sha1, remote->packs);
- if (target)
+ /*
+ * Don't push the object if it's known to exist on the remote
+ * or is already in the request queue
+ */
+ if (remote_dir_exists[obj->sha1[0]] == -1)
+ get_remote_object_list(obj->sha1[0]);
+ if (obj->flags & (REMOTE | PUSHING))
return;
+ target = find_sha1_pack(obj->sha1, remote->packs);
+ if (target) {
+ obj->flags |= REMOTE;
+ return;
+ }
+ obj->flags |= PUSHING;
request = xmalloc(sizeof(*request));
- memcpy(request->sha1, sha1, 20);
+ request->obj = obj;
request->url = NULL;
request->lock = lock;
request->headers = NULL;
- request->state = NEED_CHECK;
+ request->state = NEED_PUSH;
request->next = request_queue_head;
request_queue_head = request;
@@ -698,14 +689,13 @@ static char *quote_ref_url(const char *b
int len, baselen, ch;
baselen = strlen(base);
- len = baselen + 12; /* "refs/heads/" + NUL */
+ len = baselen + 1;
for (cp = ref; (ch = *cp) != 0; cp++, len++)
if (needs_quote(ch))
len += 2; /* extra two hex plus replacement % */
qref = xmalloc(len);
memcpy(qref, base, baselen);
- memcpy(qref + baselen, "refs/heads/", 11);
- for (cp = ref, dp = qref + baselen + 11; (ch = *cp) != 0; cp++) {
+ for (cp = ref, dp = qref + baselen; (ch = *cp) != 0; cp++) {
if (needs_quote(ch)) {
*dp++ = '%';
*dp++ = hex((ch >> 4) & 0xF);
@@ -751,6 +741,27 @@ int fetch_ref(char *ref, unsigned char *
return 0;
}
+static void one_remote_object(const char *hex)
+{
+ unsigned char sha1[20];
+ struct object *obj;
+
+ if (get_sha1_hex(hex, sha1) != 0)
+ return;
+
+ obj = lookup_object(sha1);
+ if (!obj)
+ obj = parse_object(sha1);
+
+ /* Ignore remote objects that don't exist locally */
+ if (!obj)
+ return;
+
+ obj->flags |= REMOTE;
+ if (!object_list_contains(objects, obj))
+ add_object(obj, &objects, NULL, "");
+}
+
static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
{
int *lock_flags = (int *)ctx->userData;
@@ -772,7 +783,7 @@ static void handle_lockprop_ctx(struct x
static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
{
- struct active_lock *lock = (struct active_lock *)ctx->userData;
+ struct remote_lock *lock = (struct remote_lock *)ctx->userData;
if (tag_closed && ctx->cdata) {
if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
@@ -791,6 +802,57 @@ static void handle_new_lock_ctx(struct x
}
}
+static void one_remote_ref(char *refname);
+static void crawl_remote_refs(char *path);
+
+static void handle_crawl_ref_ctx(struct xml_ctx *ctx, int tag_closed)
+{
+ struct remote_dentry *dentry = (struct remote_dentry *)ctx->userData;
+
+
+ if (tag_closed) {
+ if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && dentry->name) {
+ if (dentry->is_dir) {
+ if (strcmp(dentry->name, dentry->base)) {
+ crawl_remote_refs(dentry->name);
+ }
+ } else {
+ one_remote_ref(dentry->name);
+ }
+ } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
+ dentry->name = xmalloc(strlen(ctx->cdata) -
+ remote->path_len + 1);
+ strcpy(dentry->name,
+ ctx->cdata + remote->path_len);
+ } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
+ dentry->is_dir = 1;
+ }
+ } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
+ dentry->name = NULL;
+ dentry->is_dir = 0;
+ }
+}
+
+static void handle_remote_object_list_ctx(struct xml_ctx *ctx, int tag_closed)
+{
+ char *path;
+ char *obj_hex;
+
+ if (tag_closed) {
+ if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
+ path = ctx->cdata + remote->path_len;
+ if (strlen(path) != 50)
+ return;
+ path += 9;
+ obj_hex = xmalloc(strlen(path));
+ strncpy(obj_hex, path, 2);
+ strcpy(obj_hex + 2, path + 3);
+ one_remote_object(obj_hex);
+ free(obj_hex);
+ }
+ }
+}
+
static void
xml_start_tag(void *userData, const char *name, const char **atts)
{
@@ -848,7 +910,7 @@ xml_cdata(void *userData, const XML_Char
strncpy(ctx->cdata, s, len);
}
-static struct active_lock *lock_remote(char *file, long timeout)
+static struct remote_lock *lock_remote(char *path, long timeout)
{
struct active_request_slot *slot;
struct buffer out_buffer;
@@ -858,14 +920,26 @@ static struct active_lock *lock_remote(c
char *url;
char *ep;
char timeout_header[25];
- struct active_lock *new_lock = NULL;
+ struct remote_lock *lock = remote_locks;
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
struct curl_slist *dav_headers = NULL;
struct xml_ctx ctx;
- url = xmalloc(strlen(remote->url) + strlen(file) + 1);
- sprintf(url, "%s%s", remote->url, file);
+ url = xmalloc(strlen(remote->url) + strlen(path) + 1);
+ sprintf(url, "%s%s", remote->url, path);
+
+ /* Make sure the url is not already locked */
+ while (lock && strcmp(lock->url, url)) {
+ lock = lock->next;
+ }
+ if (lock) {
+ free(url);
+ if (refresh_lock(lock))
+ return lock;
+ else
+ return NULL;
+ }
/* Make sure leading directories exist for the remote ref */
ep = strchr(url + strlen(remote->url) + 11, '/');
@@ -921,11 +995,11 @@ static struct active_lock *lock_remote(c
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
- new_lock = xcalloc(1, sizeof(*new_lock));
- new_lock->owner = NULL;
- new_lock->token = NULL;
- new_lock->timeout = -1;
- new_lock->refreshing = 0;
+ lock = xcalloc(1, sizeof(*lock));
+ lock->owner = NULL;
+ lock->token = NULL;
+ lock->timeout = -1;
+ lock->refreshing = 0;
if (start_active_slot(slot)) {
run_active_slot(slot);
@@ -934,7 +1008,7 @@ static struct active_lock *lock_remote(c
ctx.len = 0;
ctx.cdata = NULL;
ctx.userFunc = handle_new_lock_ctx;
- ctx.userData = new_lock;
+ ctx.userData = lock;
XML_SetUserData(parser, &ctx);
XML_SetElementHandler(parser, xml_start_tag,
xml_end_tag);
@@ -946,7 +1020,7 @@ static struct active_lock *lock_remote(c
fprintf(stderr, "XML error: %s\n",
XML_ErrorString(
XML_GetErrorCode(parser)));
- new_lock->timeout = -1;
+ lock->timeout = -1;
}
}
} else {
@@ -957,23 +1031,26 @@ static struct active_lock *lock_remote(c
free(out_data);
free(in_data);
- if (new_lock->token == NULL || new_lock->timeout <= 0) {
- if (new_lock->token != NULL)
- free(new_lock->token);
- if (new_lock->owner != NULL)
- free(new_lock->owner);
+ if (lock->token == NULL || lock->timeout <= 0) {
+ if (lock->token != NULL)
+ free(lock->token);
+ if (lock->owner != NULL)
+ free(lock->owner);
free(url);
- free(new_lock);
- new_lock = NULL;
+ free(lock);
+ lock = NULL;
} else {
- new_lock->url = url;
- new_lock->start_time = time(NULL);
+ lock->url = url;
+ lock->active = 1;
+ lock->start_time = time(NULL);
+ lock->next = remote_locks;
+ remote_locks = lock;
}
- return new_lock;
+ return lock;
}
-static int unlock_remote(struct active_lock *lock)
+static int unlock_remote(struct remote_lock *lock)
{
struct active_request_slot *slot;
char *lock_token_header;
@@ -1005,15 +1082,171 @@ static int unlock_remote(struct active_l
curl_slist_free_all(dav_headers);
free(lock_token_header);
- if (lock->owner != NULL)
- free(lock->owner);
- free(lock->url);
- free(lock->token);
- free(lock);
+ lock->active = 0;
return rc;
}
+static void crawl_remote_refs(char *path)
+{
+ char *url;
+ struct active_request_slot *slot;
+ struct buffer in_buffer;
+ struct buffer out_buffer;
+ char *in_data;
+ char *out_data;
+ XML_Parser parser = XML_ParserCreate(NULL);
+ enum XML_Status result;
+ struct curl_slist *dav_headers = NULL;
+ struct xml_ctx ctx;
+ struct remote_dentry dentry;
+
+ fprintf(stderr, " %s\n", path);
+
+ dentry.base = path;
+ dentry.name = NULL;
+ dentry.is_dir = 0;
+
+ url = xmalloc(strlen(remote->url) + strlen(path) + 1);
+ sprintf(url, "%s%s", remote->url, path);
+
+ out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
+ out_data = xmalloc(out_buffer.size + 1);
+ snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
+ out_buffer.posn = 0;
+ out_buffer.buffer = out_data;
+
+ in_buffer.size = 4096;
+ in_data = xmalloc(in_buffer.size);
+ in_buffer.posn = 0;
+ in_buffer.buffer = in_data;
+
+ dav_headers = curl_slist_append(dav_headers, "Depth: 1");
+ dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
+
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
+ curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result == CURLE_OK) {
+ ctx.name = xcalloc(10, 1);
+ ctx.len = 0;
+ ctx.cdata = NULL;
+ ctx.userFunc = handle_crawl_ref_ctx;
+ ctx.userData = &dentry;
+ XML_SetUserData(parser, &ctx);
+ XML_SetElementHandler(parser, xml_start_tag,
+ xml_end_tag);
+ XML_SetCharacterDataHandler(parser, xml_cdata);
+ result = XML_Parse(parser, in_buffer.buffer,
+ in_buffer.posn, 1);
+ free(ctx.name);
+
+ if (result != XML_STATUS_OK) {
+ fprintf(stderr, "XML error: %s\n",
+ XML_ErrorString(
+ XML_GetErrorCode(parser)));
+ }
+ }
+ } else {
+ fprintf(stderr, "Unable to start request\n");
+ }
+
+ free(url);
+ free(out_data);
+ free(in_buffer.buffer);
+ curl_slist_free_all(dav_headers);
+}
+
+static void get_remote_object_list(unsigned char parent)
+{
+ char *url;
+ struct active_request_slot *slot;
+ struct buffer in_buffer;
+ struct buffer out_buffer;
+ char *in_data;
+ char *out_data;
+ XML_Parser parser = XML_ParserCreate(NULL);
+ enum XML_Status result;
+ struct curl_slist *dav_headers = NULL;
+ struct xml_ctx ctx;
+ char path[] = "/objects/XX/";
+ static const char hex[] = "0123456789abcdef";
+ unsigned int val = parent;
+
+ path[9] = hex[val >> 4];
+ path[10] = hex[val & 0xf];
+ url = xmalloc(strlen(remote->url) + strlen(path) + 1);
+ sprintf(url, "%s%s", remote->url, path);
+
+ out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
+ out_data = xmalloc(out_buffer.size + 1);
+ snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
+ out_buffer.posn = 0;
+ out_buffer.buffer = out_data;
+
+ in_buffer.size = 4096;
+ in_data = xmalloc(in_buffer.size);
+ in_buffer.posn = 0;
+ in_buffer.buffer = in_data;
+
+ dav_headers = curl_slist_append(dav_headers, "Depth: 1");
+ dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
+
+ slot = get_active_slot();
+ curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
+ curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+ if (start_active_slot(slot)) {
+ run_active_slot(slot);
+ if (slot->curl_result == CURLE_OK) {
+ remote_dir_exists[parent] = 1;
+ ctx.name = xcalloc(10, 1);
+ ctx.len = 0;
+ ctx.cdata = NULL;
+ ctx.userFunc = handle_remote_object_list_ctx;
+ XML_SetUserData(parser, &ctx);
+ XML_SetElementHandler(parser, xml_start_tag,
+ xml_end_tag);
+ XML_SetCharacterDataHandler(parser, xml_cdata);
+ result = XML_Parse(parser, in_buffer.buffer,
+ in_buffer.posn, 1);
+ free(ctx.name);
+
+ if (result != XML_STATUS_OK) {
+ fprintf(stderr, "XML error: %s\n",
+ XML_ErrorString(
+ XML_GetErrorCode(parser)));
+ }
+ } else {
+ remote_dir_exists[parent] = 0;
+ }
+ } else {
+ fprintf(stderr, "Unable to start request\n");
+ }
+
+ free(url);
+ free(out_data);
+ free(in_buffer.buffer);
+ curl_slist_free_all(dav_headers);
+}
+
static int locking_available(void)
{
struct active_request_slot *slot;
@@ -1027,9 +1260,12 @@ static int locking_available(void)
struct xml_ctx ctx;
int lock_flags = 0;
- out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2;
+ out_buffer.size =
+ strlen(PROPFIND_SUPPORTEDLOCK_REQUEST) +
+ strlen(remote->url) - 2;
out_data = xmalloc(out_buffer.size + 1);
- snprintf(out_data, out_buffer.size + 1, PROPFIND_REQUEST, remote->url);
+ snprintf(out_data, out_buffer.size + 1,
+ PROPFIND_SUPPORTEDLOCK_REQUEST, remote->url);
out_buffer.posn = 0;
out_buffer.buffer = out_data;
@@ -1085,85 +1321,99 @@ static int locking_available(void)
return lock_flags;
}
-static int is_ancestor(unsigned char *sha1, struct commit *commit)
+static struct object_list **process_blob(struct blob *blob,
+ struct object_list **p,
+ struct name_path *path,
+ const char *name)
{
- struct commit_list *parents;
+ struct object *obj = &blob->object;
- if (parse_commit(commit))
- return 0;
- parents = commit->parents;
- for (; parents; parents = parents->next) {
- if (!memcmp(sha1, parents->item->object.sha1, 20)) {
- return 1;
- } else if (parents->item->object.type == commit_type) {
- if (is_ancestor(
- sha1,
- (struct commit *)&parents->item->object
- ))
- return 1;
- }
+ obj->flags |= LOCAL;
+
+ if (obj->flags & (UNINTERESTING | SEEN))
+ return p;
+
+ obj->flags |= SEEN;
+ return add_object(obj, p, path, name);
+}
+
+static struct object_list **process_tree(struct tree *tree,
+ struct object_list **p,
+ struct name_path *path,
+ const char *name)
+{
+ struct object *obj = &tree->object;
+ struct tree_entry_list *entry;
+ struct name_path me;
+
+ obj->flags |= LOCAL;
+
+ if (obj->flags & (UNINTERESTING | SEEN))
+ return p;
+ if (parse_tree(tree) < 0)
+ die("bad tree object %s", sha1_to_hex(obj->sha1));
+
+ obj->flags |= SEEN;
+ p = add_object(obj, p, NULL, name);
+ me.up = path;
+ me.elem = name;
+ me.elem_len = strlen(name);
+ entry = tree->entries;
+ tree->entries = NULL;
+ while (entry) {
+ struct tree_entry_list *next = entry->next;
+ if (entry->directory)
+ p = process_tree(entry->item.tree, p, &me, entry->name);
+ else
+ p = process_blob(entry->item.blob, p, &me, entry->name);
+ free(entry);
+ entry = next;
}
- return 0;
+ return p;
}
-static void get_delta(unsigned char *sha1, struct object *obj,
- struct active_lock *lock)
+static void get_delta(struct rev_info *revs, struct remote_lock *lock)
{
struct commit *commit;
- struct commit_list *parents;
- struct tree *tree;
- struct tree_entry_list *entry;
+ struct object_list **p = &objects, *pending;
- if (sha1 && !memcmp(sha1, obj->sha1, 20))
- return;
+ while ((commit = get_revision(revs)) != NULL) {
+ p = process_tree(commit->tree, p, NULL, "");
+ commit->object.flags |= LOCAL;
+ if (!(commit->object.flags & UNINTERESTING))
+ add_request(&commit->object, lock);
+ }
- if (aborted)
- return;
+ for (pending = revs->pending_objects; pending; pending = pending->next) {
+ struct object *obj = pending->item;
+ const char *name = pending->name;
- if (obj->type == commit_type) {
- if (push_verbosely)
- fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
- add_request(obj->sha1, lock);
- commit = (struct commit *)obj;
- if (parse_commit(commit)) {
- fprintf(stderr, "Error parsing commit %s\n",
- sha1_to_hex(obj->sha1));
- aborted = 1;
- return;
+ if (obj->flags & (UNINTERESTING | SEEN))
+ continue;
+ if (obj->type == tag_type) {
+ obj->flags |= SEEN;
+ p = add_object(obj, p, NULL, name);
+ continue;
}
- parents = commit->parents;
- for (; parents; parents = parents->next)
- if (sha1 == NULL ||
- memcmp(sha1, parents->item->object.sha1, 20))
- get_delta(sha1, &parents->item->object,
- lock);
- get_delta(sha1, &commit->tree->object, lock);
- } else if (obj->type == tree_type) {
- if (push_verbosely)
- fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
- add_request(obj->sha1, lock);
- tree = (struct tree *)obj;
- if (parse_tree(tree)) {
- fprintf(stderr, "Error parsing tree %s\n",
- sha1_to_hex(obj->sha1));
- aborted = 1;
- return;
+ if (obj->type == tree_type) {
+ p = process_tree((struct tree *)obj, p, NULL, name);
+ continue;
}
- entry = tree->entries;
- tree->entries = NULL;
- while (entry) {
- struct tree_entry_list *next = entry->next;
- get_delta(sha1, entry->item.any, lock);
- free(entry->name);
- free(entry);
- entry = next;
+ if (obj->type == blob_type) {
+ p = process_blob((struct blob *)obj, p, NULL, name);
+ continue;
}
- } else if (obj->type == blob_type || obj->type == tag_type) {
- add_request(obj->sha1, lock);
+ die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
+ }
+
+ while (objects) {
+ if (!(objects->item->flags & UNINTERESTING))
+ add_request(objects->item, lock);
+ objects = objects->next;
}
}
-static int update_remote(unsigned char *sha1, struct active_lock *lock)
+static int update_remote(unsigned char *sha1, struct remote_lock *lock)
{
struct active_request_slot *slot;
char *out_data;
@@ -1218,22 +1468,146 @@ static int update_remote(unsigned char *
return 1;
}
+static struct ref *local_refs, **local_tail;
+static struct ref *remote_refs, **remote_tail;
+
+static int one_local_ref(const char *refname, const unsigned char *sha1)
+{
+ struct ref *ref;
+ int len = strlen(refname) + 1;
+ ref = xcalloc(1, sizeof(*ref) + len);
+ memcpy(ref->new_sha1, sha1, 20);
+ memcpy(ref->name, refname, len);
+ *local_tail = ref;
+ local_tail = &ref->next;
+ return 0;
+}
+
+static void one_remote_ref(char *refname)
+{
+ struct ref *ref;
+ unsigned char remote_sha1[20];
+
+ if (fetch_ref(refname, remote_sha1) != 0) {
+ fprintf(stderr,
+ "Unable to fetch ref %s from %s\n",
+ refname, remote->url);
+ return;
+ }
+
+ int len = strlen(refname) + 1;
+ ref = xcalloc(1, sizeof(*ref) + len);
+ memcpy(ref->old_sha1, remote_sha1, 20);
+ memcpy(ref->name, refname, len);
+ *remote_tail = ref;
+ remote_tail = &ref->next;
+}
+
+static void get_local_heads(void)
+{
+ local_tail = &local_refs;
+ for_each_ref(one_local_ref);
+}
+
+static void get_dav_remote_heads(void)
+{
+ remote_tail = &remote_refs;
+ crawl_remote_refs("refs/");
+}
+
+static int is_zero_sha1(const unsigned char *sha1)
+{
+ int i;
+
+ for (i = 0; i < 20; i++) {
+ if (*sha1++)
+ return 0;
+ }
+ return 1;
+}
+
+static void unmark_and_free(struct commit_list *list, unsigned int mark)
+{
+ while (list) {
+ struct commit_list *temp = list;
+ temp->item->object.flags &= ~mark;
+ list = temp->next;
+ free(temp);
+ }
+}
+
+static int ref_newer(const unsigned char *new_sha1,
+ const unsigned char *old_sha1)
+{
+ struct object *o;
+ struct commit *old, *new;
+ struct commit_list *list, *used;
+ int found = 0;
+
+ /* Both new and old must be commit-ish and new is descendant of
+ * old. Otherwise we require --force.
+ */
+ o = deref_tag(parse_object(old_sha1), NULL, 0);
+ if (!o || o->type != commit_type)
+ return 0;
+ old = (struct commit *) o;
+
+ o = deref_tag(parse_object(new_sha1), NULL, 0);
+ if (!o || o->type != commit_type)
+ return 0;
+ new = (struct commit *) o;
+
+ if (parse_commit(new) < 0)
+ return 0;
+
+ used = list = NULL;
+ commit_list_insert(new, &list);
+ while (list) {
+ new = pop_most_recent_commit(&list, TMP_MARK);
+ commit_list_insert(new, &used);
+ if (new == old) {
+ found = 1;
+ break;
+ }
+ }
+ unmark_and_free(list, TMP_MARK);
+ unmark_and_free(used, TMP_MARK);
+ return found;
+}
+
+static void mark_edge_parents_uninteresting(struct commit *commit)
+{
+ struct commit_list *parents;
+
+ for (parents = commit->parents; parents; parents = parents->next) {
+ struct commit *parent = parents->item;
+ if (!(parent->object.flags & UNINTERESTING))
+ continue;
+ mark_tree_uninteresting(parent->tree);
+ }
+}
+
+static void mark_edges_uninteresting(struct commit_list *list)
+{
+ for ( ; list; list = list->next) {
+ struct commit *commit = list->item;
+
+ if (commit->object.flags & UNINTERESTING) {
+ mark_tree_uninteresting(commit->tree);
+ continue;
+ }
+ mark_edge_parents_uninteresting(commit);
+ }
+}
+
int main(int argc, char **argv)
{
struct transfer_request *request;
struct transfer_request *next_request;
int nr_refspec = 0;
char **refspec = NULL;
- int do_remote_update;
- int new_branch;
- int force_this;
- char *local_ref;
- unsigned char local_sha1[20];
- struct object *local_object = NULL;
- char *remote_ref = NULL;
- unsigned char remote_sha1[20];
- struct active_lock *remote_lock;
- char *remote_path = NULL;
+ struct remote_lock *ref_lock;
+ struct rev_info revs;
int rc = 0;
int i;
@@ -1242,6 +1616,7 @@ int main(int argc, char **argv)
remote = xmalloc(sizeof(*remote));
remote->url = NULL;
+ remote->path_len = 0;
remote->packs = NULL;
argv++;
@@ -1249,7 +1624,7 @@ int main(int argc, char **argv)
char *arg = *argv;
if (*arg == '-') {
- if (!strcmp(arg, "--complete")) {
+ if (!strcmp(arg, "--all")) {
push_all = 1;
continue;
}
@@ -1265,6 +1640,12 @@ int main(int argc, char **argv)
}
if (!remote->url) {
remote->url = arg;
+ char *path = strstr(arg, "//");
+ if (path) {
+ path = index(path+2, '/');
+ if (path)
+ remote->path_len = strlen(path);
+ }
continue;
}
refspec = argv;
@@ -1275,7 +1656,7 @@ int main(int argc, char **argv)
if (!remote->url)
usage(http_push_usage);
- memset(remote_dir_exists, 0, 256);
+ memset(remote_dir_exists, -1, 256);
http_init();
@@ -1293,92 +1674,113 @@ int main(int argc, char **argv)
goto cleanup;
}
- /* Process each refspec */
- for (i = 0; i < nr_refspec; i++) {
- char *ep;
- force_this = 0;
- do_remote_update = 0;
- new_branch = 0;
- local_ref = refspec[i];
- if (*local_ref == '+') {
- force_this = 1;
- local_ref++;
- }
- ep = strchr(local_ref, ':');
- if (ep) {
- remote_ref = ep + 1;
- *ep = 0;
+ /* Get a list of all local and remote heads to validate refspecs */
+ get_local_heads();
+ fprintf(stderr, "Fetching remote heads...\n");
+ get_dav_remote_heads();
+
+ /* match them up */
+ if (!remote_tail)
+ remote_tail = &remote_refs;
+ if (match_refs(local_refs, remote_refs, &remote_tail,
+ nr_refspec, refspec, push_all))
+ return -1;
+ if (!remote_refs) {
+ fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
+ return 0;
+ }
+
+ int ret = 0;
+ int new_refs = 0;
+ struct ref *ref;
+ for (ref = remote_refs; ref; ref = ref->next) {
+ char old_hex[60], *new_hex;
+ if (!ref->peer_ref)
+ continue;
+ if (!memcmp(ref->old_sha1, ref->peer_ref->new_sha1, 20)) {
+ if (push_verbosely || 1)
+ fprintf(stderr, "'%s': up-to-date\n", ref->name);
+ continue;
}
- else
- remote_ref = local_ref;
+
+ if (!force_all &&
+ !is_zero_sha1(ref->old_sha1) &&
+ !ref->force) {
+ if (!has_sha1_file(ref->old_sha1) ||
+ !ref_newer(ref->peer_ref->new_sha1,
+ ref->old_sha1)) {
+ /* We do not have the remote ref, or
+ * we know that the remote ref is not
+ * an ancestor of what we are trying to
+ * push. Either way this can be losing
+ * commits at the remote end and likely
+ * we were not up to date to begin with.
+ */
+ error("remote '%s' is not a strict "
+ "subset of local ref '%s'. "
+ "maybe you are not up-to-date and "
+ "need to pull first?",
+ ref->name,
+ ref->peer_ref->name);
+ ret = -2;
+ continue;
+ }
+ }
+ memcpy(ref->new_sha1, ref->peer_ref->new_sha1, 20);
+ if (is_zero_sha1(ref->new_sha1)) {
+ error("cannot happen anymore");
+ ret = -3;
+ continue;
+ }
+ new_refs++;
+ strcpy(old_hex, sha1_to_hex(ref->old_sha1));
+ new_hex = sha1_to_hex(ref->new_sha1);
+
+ fprintf(stderr, "updating '%s'", ref->name);
+ if (strcmp(ref->name, ref->peer_ref->name))
+ fprintf(stderr, " using '%s'", ref->peer_ref->name);
+ fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex);
+
/* Lock remote branch ref */
- if (remote_path)
- free(remote_path);
- remote_path = xmalloc(strlen(remote_ref) + 12);
- sprintf(remote_path, "refs/heads/%s", remote_ref);
- remote_lock = lock_remote(remote_path, LOCK_TIME);
- if (remote_lock == NULL) {
+ ref_lock = lock_remote(ref->name, LOCK_TIME);
+ if (ref_lock == NULL) {
fprintf(stderr, "Unable to lock remote branch %s\n",
- remote_ref);
+ ref->name);
rc = 1;
continue;
}
- /* Resolve local and remote refs */
- if (fetch_ref(remote_ref, remote_sha1) != 0) {
- fprintf(stderr,
- "Remote branch %s does not exist on %s\n",
- remote_ref, remote->url);
- new_branch = 1;
- }
- if (get_sha1(local_ref, local_sha1) != 0) {
- fprintf(stderr, "Error resolving local branch %s\n",
- local_ref);
- rc = 1;
- goto unlock;
- }
-
- /* Find relationship between local and remote */
- local_object = parse_object(local_sha1);
- if (!local_object) {
- fprintf(stderr, "Unable to parse local object %s\n",
- sha1_to_hex(local_sha1));
- rc = 1;
- goto unlock;
- } else if (new_branch) {
- do_remote_update = 1;
- } else {
- if (!memcmp(local_sha1, remote_sha1, 20)) {
- fprintf(stderr,
- "* %s: same as branch '%s' of %s\n",
- local_ref, remote_ref, remote->url);
- } else if (is_ancestor(remote_sha1,
- (struct commit *)local_object)) {
- fprintf(stderr,
- "Remote %s will fast-forward to local %s\n",
- remote_ref, local_ref);
- do_remote_update = 1;
- } else if (force_all || force_this) {
- fprintf(stderr,
- "* %s on %s does not fast forward to local branch '%s', overwriting\n",
- remote_ref, remote->url, local_ref);
- do_remote_update = 1;
- } else {
- fprintf(stderr,
- "* %s on %s does not fast forward to local branch '%s'\n",
- remote_ref, remote->url, local_ref);
- rc = 1;
- goto unlock;
- }
+ /* Set up revision info for this refspec */
+ const char *commit_argv[3];
+ int commit_argc = 2;
+ char *new_sha1_hex = strdup(sha1_to_hex(ref->new_sha1));
+ char *old_sha1_hex = NULL;
+ commit_argv[1] = new_sha1_hex;
+ if (!push_all && !is_zero_sha1(ref->old_sha1)) {
+ old_sha1_hex = xmalloc(42);
+ sprintf(old_sha1_hex, "^%s",
+ sha1_to_hex(ref->old_sha1));
+ commit_argv[2] = old_sha1_hex;
+ commit_argc++;
+ }
+ revs.commits = NULL;
+ setup_revisions(commit_argc, commit_argv, &revs, NULL);
+ revs.tag_objects = 1;
+ revs.tree_objects = 1;
+ revs.blob_objects = 1;
+ free(new_sha1_hex);
+ if (old_sha1_hex) {
+ free(old_sha1_hex);
+ commit_argv[1] = NULL;
}
- /* Generate and check list of required objects */
+ /* Generate a list of objects that need to be pushed */
pushing = 0;
- if (do_remote_update || push_all)
- fetch_indices();
- get_delta(push_all ? NULL : remote_sha1,
- local_object, remote_lock);
+ prepare_revision_walk(&revs);
+ mark_edges_uninteresting(revs.commits);
+ fetch_indices();
+ get_delta(&revs, ref_lock);
finish_all_active_slots();
/* Push missing objects to remote, this would be a
@@ -1388,25 +1790,15 @@ int main(int argc, char **argv)
finish_all_active_slots();
/* Update the remote branch if all went well */
- if (do_remote_update) {
- if (!aborted && update_remote(local_sha1,
- remote_lock)) {
- fprintf(stderr, "%s remote branch %s\n",
- new_branch ? "Created" : "Updated",
- remote_ref);
- } else {
- fprintf(stderr,
- "Unable to %s remote branch %s\n",
- new_branch ? "create" : "update",
- remote_ref);
- rc = 1;
- goto unlock;
- }
+ if (aborted || !update_remote(ref->new_sha1, ref_lock)) {
+ rc = 1;
+ goto unlock;
}
unlock:
- unlock_remote(remote_lock);
- free(remote_path);
+ if (!rc)
+ fprintf(stderr, " done\n");
+ unlock_remote(ref_lock);
}
cleanup:
--
1.2.4.gb477-dirty
^ permalink raw reply related
* [RFH] zlib gurus out there?
From: Junio C Hamano @ 2006-03-07 23:48 UTC (permalink / raw)
To: git; +Cc: Nicolas Pitre, Linus Torvalds
In-Reply-To: <Pine.LNX.4.64.0602250012230.31162@localhost.localdomain>
I've been staring at reusing existing data while packing, and
this occurred to me...
During packing, suppose that we chose to store an object in
base form, undeltified. And also suppose we have that object
loose in .git/objects/??/ directory. We already have it in
deflated form, but with its own header. I started wondering if
we can somehow reuse this.
A short object format brush-up lesson is in order here.
* An undeltified object in a pack is represented like this:
(1) the header is a dense variable size binary data, that
encodes type and inflated length;
(2) deflated data immediately follows the header.
* On the other hand, a loose object is represented like this:
(1) the header looks like sprintf("%s %lu%c", type, len, 0);
(2) concatenate the data to the header;
(3) SHA1 checksum of the above becomes the object name.
(4) deflate the header and data using the same z_stream, in two
steps, like this (sha1_file.c::write_sha1_file):
/* Compress it */
stream.next_out = compressed;
stream.avail_out = size;
/* First header.. */
stream.next_in = hdr;
stream.avail_in = hdrlen;
while (deflate(&stream, 0) == Z_OK)
/* nothing */;
/* Then the data itself.. */
stream.next_in = buf;
stream.avail_in = len;
while (deflate(&stream, Z_FINISH) == Z_OK)
/* nothing */;
deflateEnd(&stream);
size = stream.total_out;
So I thought... if we cause a full flush after the header part,
I can find the flush boundaries from a loose object file and
copy the rest into a packfile I am generating, after placing the
binary encoded header. If this works, we do not have to inflate
loose object to read it and deflate it to store that in the
pack. We will get a better packing as well, since we deflate
loose objects with Z_BEST_COMPRESSION, while packs are done with
Z_DEFAULT_COMPRESSION. While pack-objects read from a loose
object, if we can detect that there is no full flush after the
header, we would do the traditional inflate-deflate cycle, so
this would be backward compatible.
However, I am stuck with the first step, which is to do a full
flush after the header. An obvious change to the code quoted
above writes out a corrupt object:
/* First header.. */
stream.next_in = hdr;
stream.avail_in = hdrlen;
- while (deflate(&stream, 0) == Z_OK)
+ while (deflate(&stream, Z_FULL_FLUSH) == Z_OK)
/* nothing */;
git-fsck-objects complains that sha1 does not match. It appears
that the sha1_file.c::unpack_sha1_rest() somehow barfs upon
seeing the full flush, but I haven't dug into it yet.
Would anybody with more experience with zlib want to help?
^ permalink raw reply
* git-svn, tree moves, and --no-stop-on-copy
From: Yann Dirson @ 2006-03-07 22:08 UTC (permalink / raw)
To: Eric Wong; +Cc: GIT list
It looks that the --no-stop-on-copy flag has been dropped by error
during the "options cleanup" commit a couple of days ago. This
trivial patch appears at first sight to address the problem:
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index 3c860e4..91a0d3b 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -36,7 +36,9 @@ my %fc_opts = ( 'no-ignore-externals' =>
'authors-file|A=s' => \$_authors );
my %cmd = (
fetch => [ \&fetch, "Download new revisions from SVN",
- { 'revision|r=s' => \$_revision, %fc_opts } ],
+ { 'revision|r=s' => \$_revision,
+ 'no-stop-on-copy' => \$_no_stop_copy,
+ %fc_opts } ],
init => [ \&init, "Initialize and fetch (import)", { } ],
commit => [ \&commit, "Commit git revisions to SVN",
{ 'stdin|' => \$_stdin,
But then, "git-svn fetch" fails when trying to deal with the renaming:
$ git-svn.perl init https://svn.sourceforge.net/svnroot/ufoai/ufoai/trunk/src
$ git-svn.perl fetch --no-stop-on-copy
[...]
Updated to revision 163.
r163 = 68da9a01b2d5a2372262ada33a401e03aef41e97
svn: REPORT request failed on '/svnroot/ufoai/!svn/vcc/default'
svn: Cannot replace a directory from within
256 at /export/work/yann/git/git/contrib/git-svn/git-svn.perl line 812
main::sys('svn', 'up', '--ignore-externals', '-r166') called at /export/work/yann/git/git/contrib/git-svn/git-svn.perl line 243
main::fetch() called at /export/work/yann/git/git/contrib/git-svn/git-svn.perl line 83
The impacted revision in svn is:
------------------------------------------------------------------------
r166 | ydirson | 2006-03-02 01:36:55 +0100 (Thu, 02 Mar 2006) | 1 line
Changed paths:
D /trunk
A /ufoai/trunk (from /trunk:165)
Adding an ufoai toplevel module
------------------------------------------------------------------------
Obviously "svn update" does not like it at all.
"svn switch --relocate" does not seem to be of any help. Switching
manually .git/git-svn/tree/ to the new repository location does not
help either, since I must obviously update to r166 in that case, and
then a further "git-svn fetch" fails because it does not find
.git/git-svn/revs/166 aleady imported.
Any idea as to how to get the work done ?
I could surely import in another branch not using --no-stop-on-copy,
and then use a graft, but if we could make it work in a more
streamlined manner, it would surely be useful.
Before I find out in the doc about --no-stop-on-copy, I did a coupld
of experimentation. Among them, was using a peg-revision in the URL
passed to "git-svn init":
$ GIT_SVN_ID=git-oldsvn git-svn init https://svn.sourceforge.net/svnroot/ufoai/trunk/src@165
That succeeds, but then "git-svn fetch" will fail with:
svn: REPORT request failed on '/svnroot/ufoai/!svn/bc/190/trunk/src@165'
svn: '/svnroot/ufoai/!svn/bc/190/trunk/src@165' path not found
256 at /export/work/yann/git/git/contrib/git-svn/git-svn.perl line 783
main::svn_log_raw('https://svn.sourceforge.net/svnroot/ufoai/trunk/src@165', '-r0:HEAD', '--stop-on-copy') called at /export/work/yann/git/git/contrib/git-svn/git-svn.perl line 219
main::fetch() called at /export/work/yann/git/git/contrib/git-svn/git-svn.perl line 81
Maybe git-svn could also be guarded against peg-revisions on init
command-line, since that appears to confuse it quite a bit :)
Additionally, it may be worth poiting out in the doc at least one
valid use of the --no-stop-on-copy flag that is friendly to the user's
sanity: when the svn repository has undergone a reorg, such that the
URL passed to "init" indeed moved - at least, when/if it is made to
work :)
--
Yann Dirson <ydirson@altern.org> |
Debian-related: <dirson@debian.org> | Support Debian GNU/Linux:
| Freedom, Power, Stability, Gratis
http://ydirson.free.fr/ | Check <http://www.debian.org/>
^ permalink raw reply related
* Re: What's in git.git
From: Francis Daly @ 2006-03-07 22:23 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
> I'd like asciidoc tweaks in "next" by Francis Daly tested by
> people who have access to different vintages of docbook-xsl by
> trying to build manpages. Look for displayed examples, such as
> the one in git-branch documentation. I've tried it with v1.68
> and getting far better results than before, and Francis says
> v1.69 works fine with or without the change. IOW this is a
> workaround for a problem in v1.68.
For completeness / comparison:
I have docbook-xsl v1.68 (strictly, 1.68.1-0.1 as packaged in debian
stable) and docbook-xsl v1.69 (1.69.1 freshly downloaded with the
manpages/ChangeLog listing the last change on 2005-08-11).
I have asciidoc 7.1.1 (local install) and xmlto version 0.0.18 as packaged
in debian stable.
Also:
$ xsltproc -V
Using libxml 20616, libxslt 10112 and libexslt 810
xsltproc was compiled against libxml 20616, libxslt 10112 and libexslt 810
libxslt 10112 was compiled against libxml 20616
libexslt 810 was compiled against libxml 20616
Of that lot, I believe only the docbook-xsl version actually matters
for this test, but I'm happy to learn otherwise.
I run
asciidoc -b docbook -d manpage -f asciidoc.conf git-branch.txt
xmlto man git-branch.xml
asciidoc -b xhtml11 -d manpage -f asciidoc.conf git-branch.txt
I vary the docbook-xsl version being used and I vary the asciidoc.conf
file to include or exclude the "literallayout" change.
The four html files produced differ only in the "Last updated" time.
The two man pages produced with v1.69 are identical.
The two man pages produced with v1.68 differ in the expected and intended
ways, replacing ".IP" with a ".nf/.fi" pair in two places.
And the differences between 1.68 and 1.69 with the old configuration, and
1.68 and 1.69 with the new configuration, are also confined to that area.
Repeating the tests on git-clone.txt (which also has a multiline example)
gets the same result.
And doing the test on git-mv.txt (with no multiline example) shows
that the asciidoc.conf change has no effect on pages like that. No real
surprise there.
So I'm happy that the (fixed) change improves something and breaks
nothing, at least for those versions of the tools.
f
--
Francis Daly francis@daoine.org
^ permalink raw reply
* Re: [PATCH] gitk : fix missing lines when displaying diff.
From: Paul Mackerras @ 2006-03-07 22:17 UTC (permalink / raw)
To: Robert Fitzsimons; +Cc: Jacob Kroon, git
In-Reply-To: <20060307204648.GA13620@localhost>
Robert Fitzsimons writes:
> Lines which are added (removed) and begin with +++ (---) are not shown
> correctly when displaying in the diff panel.
>
> The +++ and --- lines are part of the diff header, with the +++
> indicating the end of the header.
Thanks for pointing out the easy fix. I had been going to do
something much more complicated. :)
However, your patch didn't apply due to the whitespace being munged.
I committed an equivalent fix, but I took the opportunity to change
the [regexp] to a couple of [string compare]s as well.
Paul.
^ permalink raw reply
* [PATCH] gitk : fix missing lines when displaying diff.
From: Robert Fitzsimons @ 2006-03-07 20:46 UTC (permalink / raw)
To: Jacob Kroon; +Cc: paulus, git
In-Reply-To: <440DE262.7020007@gmail.com>
Lines which are added (removed) and begin with +++ (---) are not shown
correctly when displaying in the diff panel.
The +++ and --- lines are part of the diff header, with the +++
indicating the end of the header.
Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
---
gitk | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
d0da9f04cf565df64a954e1bfd4c5c0913b83322
diff --git a/gitk b/gitk
index f4c6624..5e3c34c 100755
--- a/gitk
+++ b/gitk
@@ -2595,8 +2595,10 @@ proc getblobdiffline {bdf ids} {
set pad [string range "----------------------------------------" 1 $l]
$ctext insert end "$pad $header $pad\n" filesep
set diffinhdr 1
- } elseif {[regexp {^(---|\+\+\+)} $line]} {
- set diffinhdr 0
+ } elseif {$diffinhdr == 1 && [regexp {^(---|\+\+\+) } $line]} {
+ if {[regexp {^\+\+\+ } $line]} {
+ set diffinhdr 0
+ }
} elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
$line match f1l f1c f2l f2c rest]} {
$ctext insert end "$line\n" hunksep
--
1.2.3.g20d2
^ permalink raw reply related
* Re: [PATCH] Allow git-repack to optionally run git-prune-packed.
From: Alex Riesen @ 2006-03-07 21:29 UTC (permalink / raw)
To: Martin Atukunda; +Cc: git
In-Reply-To: <11417445722524-git-send-email-matlads@dsmagic.com>
Martin Atukunda, Tue, Mar 07, 2006 16:16:12 +0100:
> +-p::
> + Run `git-prune-packed` after packing, see
> + gitlink:git-prune-packed[1]
> +
Maybe just make "-d" work? I.e. "git repack -a -d" repacks and prunes
everything, and "git repack -d" prunes just what was packed
incrementally.
Something like this:
diff --git a/git-repack.sh b/git-repack.sh
index 3d6fec1..be6c7ab 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -74,6 +74,8 @@ then
esac
done
)
+ else
+ git-prune-packed
fi
fi
^ permalink raw reply related
* [PATCH] ls-files: add --abbrev[=<n>] option
From: Eric Wong @ 2006-03-07 19:59 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20060307195826.GA26870@localdomain>
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
Documentation/git-ls-files.txt | 7 ++++++-
ls-files.c | 19 +++++++++++++++++--
2 files changed, 23 insertions(+), 3 deletions(-)
1aa70bab7a4af7b79db3c86b9f88884c1dfad6ee
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index e813f84..68d32a8 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -16,7 +16,7 @@ SYNOPSIS
[-X <file>|--exclude-from=<file>]
[--exclude-per-directory=<file>]
[--error-unmatch]
- [--full-name] [--] [<file>]\*
+ [--full-name] [--abbrev] [--] [<file>]\*
DESCRIPTION
-----------
@@ -98,6 +98,11 @@ OPTIONS
option forces paths to be output relative to the project
top directory.
+--abbrev[=<n>]::
+ Instead of showing the full 40-byte hexadecimal object
+ lines, show only handful hexdigits prefix.
+ Non default number of digits can be specified with --abbrev=<n>.
+
--::
Do not interpret any more arguments as options.
diff --git a/ls-files.c b/ls-files.c
index df25c8c..585f6a7 100644
--- a/ls-files.c
+++ b/ls-files.c
@@ -11,6 +11,7 @@
#include "cache.h"
#include "quote.h"
+static int abbrev = 0;
static int show_deleted = 0;
static int show_cached = 0;
static int show_others = 0;
@@ -488,7 +489,8 @@ static void show_ce_entry(const char *ta
printf("%s%06o %s %d\t",
tag,
ntohl(ce->ce_mode),
- sha1_to_hex(ce->sha1),
+ abbrev ? find_unique_abbrev(ce->sha1,abbrev)
+ : sha1_to_hex(ce->sha1),
ce_stage(ce));
write_name_quoted("", 0, ce->name + offset,
line_terminator, stdout);
@@ -629,7 +631,8 @@ static void verify_pathspec(void)
static const char ls_files_usage[] =
"git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
- "[ --exclude-per-directory=<filename> ] [--full-name] [--] [<file>]*";
+ "[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
+ "[--] [<file>]*";
int main(int argc, const char **argv)
{
@@ -736,6 +739,18 @@ int main(int argc, const char **argv)
error_unmatch = 1;
continue;
}
+ if (!strncmp(arg, "--abbrev=", 9)) {
+ abbrev = strtoul(arg+9, NULL, 10);
+ if (abbrev && abbrev < MINIMUM_ABBREV)
+ abbrev = MINIMUM_ABBREV;
+ else if (abbrev > 40)
+ abbrev = 40;
+ continue;
+ }
+ if (!strcmp(arg, "--abbrev")) {
+ abbrev = DEFAULT_ABBREV;
+ continue;
+ }
if (*arg == '-')
usage(ls_files_usage);
break;
--
1.2.4.gc279
^ permalink raw reply related
* Re: [PATCH] ls-tree: add --abbrev[=<n>] option
From: Eric Wong @ 2006-03-07 19:58 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vzmk2jfzr.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> wrote:
> Output from ls-tree and ls-files -s are primarily for script
> consumption. diff-raw output which already has abbrev is also
> primarily for scripts, but we have an excuse there that human
> eyes can spot differences easier in abbreviated form and two
> values on the same row being different is the only information
> we are conveying in "git whatchanged" default output, and not
> abbreviating the object names makes pathnames pushed to far
> right of the display which will be chopped by "less -S".
>
> So I am not quite sure if I should take this, and a subsequent
> one you would probably send for ls-files ;-).
I've never used ls-files with -s, but it's a simple patch on its way :>
> Care to point out a use case I might have missed that this is
> useful? I've never done ls-tree to be read by _me_ (not by
> script) unless I am debugging ls-tree itself.
I'm a weirdo and I use vim as a repository browser:
(ref: http://www.gelato.unsw.edu.au/archives/git/0511/13088.html ,
I've since renamed my 'git-show' script to 'git-cat-any').
My usual buffer width is 80 columns. So when I want to see a file from
a certain tree that's not checked out: I'll run git-ls-tree -r <tree>
and output it to my vim buffer, find the corresponding sha1 for the
file, and hit my ',G' macro with my cursor over the blob sha1 to display
its contents. Abbreviating the sha1 is very useful for seeing longer
file paths on an 80 column buffer.
--
Eric Wong
^ permalink raw reply
* gitk : Lines not showing up in diff
From: Jacob Kroon @ 2006-03-07 19:43 UTC (permalink / raw)
To: paulus; +Cc: git
I "think" this is a bug in gitk:
If I have a textfile in which I remove or add a line that looks like
"-----------...", it won't show up in the diff gitk produces. "cg-log"
shows the line correctly being added or deleted.
I'm using gitk 1.2.4 (From Fedora, gitk-1.2.4-1.fc4)
//Jacob
^ permalink raw reply
* Re: git-status too verbose?
From: Carl Worth @ 2006-03-07 18:26 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Andreas Ericsson, Junio C Hamano, Eric Jaffe, git
In-Reply-To: <Pine.LNX.4.64.0603071020530.3573@g5.osdl.org>
[-- Attachment #1: Type: text/plain, Size: 732 bytes --]
On Tue, 7 Mar 2006 10:22:28 -0800 (PST), Linus Torvalds wrote:
>
> What I want to know is "what is committed", and "what is not".
Agreed. Me too.
> That table makes it really really hard to see what you are committing, if
> you have a hundred files changed that are _not_ being committed. The
> actual committed information will be interspersed in the files you're not
> interested in, and vice versa.
The current sorting and grouping should not be changed.
I just want to be able to know if I'm looking at a section of
"committed" vs. "not committed" files when the headers have scrolled
off.
I think it's really just that one bit of information needed. Maybe a
'*' before the word 'modified' (or 'M' or whatever)?
-Carl
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: git-status too verbose?
From: Linus Torvalds @ 2006-03-07 18:22 UTC (permalink / raw)
To: Andreas Ericsson; +Cc: Junio C Hamano, Eric Jaffe, Carl Worth, git
In-Reply-To: <440D503E.8090007@op5.se>
On Tue, 7 Mar 2006, Andreas Ericsson wrote:
> >
> > I agree that it would be useful if we had a tool that showed the
> > two status that matter for each file, grouped together on one
> > line, e.g.
> >
> > HEAD->index index->files
> > ------------------------------------------------
> > hello.c unmodified modified
> > world.c modified unmodified
> > frotz.c new unmodified
> > ...
> > garbage.c~ ??? n/a
> >
> > for the current index file and the current HEAD commit.
> >
>
> Could we have 'same' or some such instead of 'unmodified'? It's a bit close to
> 'modified' for the eye to find it quickly.
I really _really_ hate that table anyway.
What I want to know is "what is committed", and "what is not".
That table makes it really really hard to see what you are committing, if
you have a hundred files changed that are _not_ being committed. The
actual committed information will be interspersed in the files you're not
interested in, and vice versa.
The current commit message is a million times superior, even if it might
not be as _pretty_.
Linus
^ permalink raw reply
* Re: Pulling tags from git.git
From: Junio C Hamano @ 2006-03-07 18:10 UTC (permalink / raw)
To: Andreas Ericsson; +Cc: gitzilla, git
In-Reply-To: <440DA82D.3060909@op5.se>
Andreas Ericsson <ae@op5.se> writes:
> Imagine Linus, getting his "please pull" emails and doing so only to
> find dozens of temporary tags fetched by the pull. Junio's patch (if I
> read it correctly) unconditionally fetches *ALL* tags reachable from
> the top of the commit-chain, which means there is no longer any way to
> keep temporary tags in a repo from which someone else will pull.
I thought we made fetch made by such a promiscous pull not to
follow tags, so that wouldn't be a problem. Tag following is
only to happen when you track other's branches. That is:
$ git pull git://git.kernel.org/pub/scm/git/git.git master
would store my "master" branch tip only in your .git/FETCH_HEAD and
you merge it immediately, without following my tags, while:
$ git pull git://git.kernel.org/pub/scm/git/git.git master:origin
(which is what you get by "git pull" without arguments) would
follow my tags, because you are storing the branch head into
your local branch "origin".
> I for one riddle my repos with temporary tags whenever I'm trying
> something I'm not so sure of, or find an interesting bug or a design
> decision I'm not 100% sure of. Perhaps I should rather do this with
> branches, but imo branches are for doing work, whereas tags just mark
> a spot in the development so I easily can find them with gitk or some
> such.
I also have many throwaway unannotated tags. Whenever I have a
WIP that I want to split up or reorder, I tag the tip of that
topic branch with "git tag anchor-blah" tag, rewind the tip to
the commit before the one I want to redo, and then do this
repeatedly:
$ git diff -R anchor-blah >P.diff
$ ... edit P.diff to keep the part I want to apply first
$ git apply --index P.diff
$ ... maybe edit a bit further
$ git commit
until there is no difference between the rewound-and-redone tip
and anchor-blah other than whatever clean-ups I do during the
above cycle. Propagating such throw-away tags is not very
useful. And I think it is reasonable to say that throw-away
tags tend to be unannotated.
^ permalink raw reply
* Re: [PATCH] ls-tree: add --abbrev[=<n>] option
From: Junio C Hamano @ 2006-03-07 17:56 UTC (permalink / raw)
To: Eric Wong; +Cc: git
In-Reply-To: <20060307135202.GA17891@localdomain>
Output from ls-tree and ls-files -s are primarily for script
consumption. diff-raw output which already has abbrev is also
primarily for scripts, but we have an excuse there that human
eyes can spot differences easier in abbreviated form and two
values on the same row being different is the only information
we are conveying in "git whatchanged" default output, and not
abbreviating the object names makes pathnames pushed to far
right of the display which will be chopped by "less -S".
So I am not quite sure if I should take this, and a subsequent
one you would probably send for ls-files ;-).
Care to point out a use case I might have missed that this is
useful? I've never done ls-tree to be read by _me_ (not by
script) unless I am debugging ls-tree itself.
Although I doubt this adds much practical value, this might make
things consistent, which by itself might be a reason to do this,
though.
^ permalink raw reply
* Re: [PATCH] git-blame: Make the output human readable
From: Fredrik Kuivinen @ 2006-03-07 16:34 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Fredrik Kuivinen, git, Johannes Schindelin, Ryan Anderson
In-Reply-To: <7vveuseg2u.fsf@assigned-by-dhcp.cox.net>
On Sun, Mar 05, 2006 at 01:28:57PM -0800, Junio C Hamano wrote:
> Fredrik Kuivinen <freku045@student.liu.se> writes:
>
> > I find the first format easier to read since everything is aligned (we
> > always output 15 characters for the committer's name padded with
> > spaces if necessary and the line numbers are padded appropriately). It
> > also takes up less space on screen since it doesn't use tabs.
>
> Careful. The convention for names is to encode them in UTF-8,
> so if you mean 15 "characters" that might be OK but if it can
> truncate in the middle of a multibyte sequence of UTF-8 encoded
> single character it is a no-no.
>
> We are talking about "casual" aligning, so I would not bring up
> "proportional fonts", but even on a monospace terminal and
> line-printer output, there still is the issue of byte count not
> matching columns. In an i18nized environment, aligning columns
> by counting bytes is a lost battle.
>
> In any case, please make sure you do not chop a character in the
> middle at least [*1*].
>
I hadn't thought of that. The code as it is now will occasionally do
the wrong thing when encountered with multibyte per code point
encodings. If we can assume that the name is in UTF-8 then the code
posted by "linux@horizon.com" to the list should do the trick.
However, can we assume that the name is in UTF-8? Do
i18n.commitencoding only apply to the commit message, or is it
intended to apply to the author/committer information too?
I am beginning to think that it might be best to, by default, store
the author, committer and commit messages in UTF-8. And then
automatically convert from/to the users locale on input/output. (I
just read the old "Rss produced by git is not valid xml?" thread where
this issue was discussed quite a bit. However, I didn't see the 'store
in UTF-8 by default and convert from/to users locale' method anywhere
in that thread.)
- Fredrik
^ permalink raw reply
* Re: git clone does not work with GIT_OBJECT_DIRECTORY set
From: Benjamin LaHaise @ 2006-03-07 16:13 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vu0aa4vj4.fsf@assigned-by-dhcp.cox.net>
On Mon, Mar 06, 2006 at 10:29:51PM -0800, Junio C Hamano wrote:
> I think the recommended way these days to set up multiple
> repositories that work on related projects is to set up a single
> clone from external source (e.g. linux-2.6.git), and make a set
> of local "-l -s" clones out of it, and then fetch forked
> upstreams into them. It would go something like this:
I had been doing that, but it's gotten too painful to keep everything in
sync. Sigh.
-ben
--
"Time is of no importance, Mr. President, only life is important."
Don't Email: <dont@kvack.org>.
^ permalink raw reply
* Re: Pulling tags from git.git
From: David Ho @ 2006-03-07 16:12 UTC (permalink / raw)
To: Andreas Ericsson; +Cc: git
In-Reply-To: <440D5285.3050401@op5.se>
Andreas,
After get it working, I did an experiment where I hard-resetted my
local repo to v1.2.3, git-pruned and removed the v1.2.4 tag. A git
pull with the git protocol does indeed pick up the tag.
Thanks for explaining what's going on under the hood. Everything now
makes sense to me!
David
On 3/7/06, Andreas Ericsson <ae@op5.se> wrote:
> With the git or git+ssh protocol, tags will be autofollowed when you do
> a pull (only signed tags, I think). The auto-following is done by
> detecting tags that are fetched, so when you did a pull using rsync the
> tags weren't auto-followed but you got all the commits. Next, when you
> changed protocol to git:// you already had all the commit objects, so
> there was nothing to auto-detect tags on. Since you're using git:// now
> it should work as advertised in the future though.
^ permalink raw reply
* Re: Libify puzzle
From: Fredrik Kuivinen @ 2006-03-07 15:40 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0603071151520.14200@wbgn013.biozentrum.uni-wuerzburg.de>
On Tue, Mar 07, 2006 at 12:00:02PM +0100, Johannes Schindelin wrote:
> Hi,
>
> I was just thinking a bit about teaching git-blame about renames, and hit
> a problem: When rev-list stops because none of the parents has the file of
> interest, the program should look if the parents contained a similar file
> which got deleted. But the commit's parents were explicitely culled!
>
> The problem seems to affect more programs when we try to libify them: What
> used to be a pipe between two programs, can no longer just set
> save_commit_buffer = 0 in the first stage, since the second might depend
> on the buffer.
>
> Would the correct solution be something like reparse_commit(commit)?
>
I have started on the rename support for git-blame but it isn't
working code yet.
My idea is to change the revision.h interface a bit. Instead having
the pathname pruning hard-coded in try_to_simplify_commit as it is
today we could have a pointer to a function in the rev_info structure
which is called the same way as try_to_simplify_commit is called
now. Then users of the revision walking interface could populate the
rev_info structure with their own try_to_simplify_commit-like
function. In the case of git-blame that function could then do the
appropriate rename detection.
Thoughts/comments?
- Fredrik
^ permalink raw reply
* Re: Pulling tags from git.git
From: Andreas Ericsson @ 2006-03-07 15:35 UTC (permalink / raw)
To: gitzilla; +Cc: Junio C Hamano, git
In-Reply-To: <440D9AB0.4070305@gmail.com>
A Large Angry SCM wrote:
> Andreas Ericsson wrote:
>
>> Junio C Hamano wrote:
>>
>>> Andreas Ericsson <ae@op5.se> writes:
>>>
>>>
>>>> With the git or git+ssh protocol, tags will be autofollowed
>>>> when you do a pull (only signed tags, I think). The
>>>> auto-following is done by detecting tags that are fetched,
>>>
>>>
>>>
>>> Ah, you are correct. We do not follow lightweight tags; I am
>>> not sure if we should.
>>>
>>
>> I'm fairly sure we shouldn't. The default update-hook prevents them
>> (if enabled), and I can't for the life of me think of why anyone would
>> want to distribute such tags.
>>
>> OTOH, preventing unannotated tags from being pushed seems like a
>> better way than to not have the ability to auto-follow those same
>> tags. After all, it's better to discourage than to disallow.
>>
>
> Before you do this, please explain why unannotated tags are not useful,
> and so should not be allowed to be pushed.
Imagine Linus, getting his "please pull" emails and doing so only to
find dozens of temporary tags fetched by the pull. Junio's patch (if I
read it correctly) unconditionally fetches *ALL* tags reachable from the
top of the commit-chain, which means there is no longer any way to keep
temporary tags in a repo from which someone else will pull.
I for one riddle my repos with temporary tags whenever I'm trying
something I'm not so sure of, or find an interesting bug or a design
decision I'm not 100% sure of. Perhaps I should rather do this with
branches, but imo branches are for doing work, whereas tags just mark a
spot in the development so I easily can find them with gitk or some such.
I may be biased by the way we do things at work. In our workflow, all
tags meant to be distributed have a short note in them which explains
the rationale of the tag. For example, new versions have a very brief
changelog that sales-people get on email (a blessing, that, since we
devs no longer have to update feature-lists and such).
Tags not meant to be distributed are unannotated, and unannotated tags
are kept out of published repos which are always stored at a central
server. Everybody synchronize to those central repos, so nobody pulls
from each other. Perhaps this is how the kernel devs work too, but if it
ever changes the update hook will no longer be able to safeguard from it
and the, in my eyes, temporary tags will be distributed in a
criss-crossing mesh so no-one will ever know where it came from or who
created it or why. I.e. a Bad Thing.
--
Andreas Ericsson andreas.ericsson@op5.se
OP5 AB www.op5.se
Tel: +46 8-230225 Fax: +46 8-230231
^ permalink raw reply
* [PATCH] Allow git-repack to optionally run git-prune-packed.
From: Martin Atukunda @ 2006-03-07 15:16 UTC (permalink / raw)
To: git; +Cc: Martin Atukunda
Signed-off-by: Martin Atukunda <matlads@dsmagic.com>
---
Documentation/git-repack.txt | 6 +++++-
git-repack.sh | 9 +++++++--
2 files changed, 12 insertions(+), 3 deletions(-)
84104979482df25e6c70e17f8bf2307cdad1faff
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index d2f9a44..835e698 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -9,7 +9,7 @@ objects into pack files.
SYNOPSIS
--------
-'git-repack' [-a] [-d] [-f] [-l] [-n] [-q]
+'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [-p]
DESCRIPTION
-----------
@@ -55,6 +55,10 @@ OPTIONS
Do not update the server information with
`git update-server-info`.
+-p::
+ Run `git-prune-packed` after packing, see
+ gitlink:git-prune-packed[1]
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/git-repack.sh b/git-repack.sh
index 3d6fec1..970f4ca 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -3,11 +3,11 @@
# Copyright (c) 2005 Linus Torvalds
#
-USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
+USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [-p]'
. git-sh-setup
no_update_info= all_into_one= remove_redundant=
-local= quiet= no_reuse_delta=
+local= quiet= no_reuse_delta= run_prune=
while case "$#" in 0) break ;; esac
do
case "$1" in
@@ -17,6 +17,7 @@ do
-q) quiet=-q ;;
-f) no_reuse_delta=--no-reuse-delta ;;
-l) local=--local ;;
+ -p) run_prune=t;;
*) usage ;;
esac
shift
@@ -77,6 +78,10 @@ then
fi
fi
+case "$run_prune" in
+t) git-prune-packed;;
+esac
+
case "$no_update_info" in
t) : ;;
*) git-update-server-info ;;
--
1.2.4.g84104
^ permalink raw reply related
* Re: Pulling tags from git.git
From: A Large Angry SCM @ 2006-03-07 14:37 UTC (permalink / raw)
To: Andreas Ericsson; +Cc: Junio C Hamano, git
In-Reply-To: <440D7A7D.8070507@op5.se>
Andreas Ericsson wrote:
> Junio C Hamano wrote:
>> Andreas Ericsson <ae@op5.se> writes:
>>
>>
>>> With the git or git+ssh protocol, tags will be autofollowed
>>> when you do a pull (only signed tags, I think). The
>>> auto-following is done by detecting tags that are fetched,
>>
>>
>> Ah, you are correct. We do not follow lightweight tags; I am
>> not sure if we should.
>>
>
> I'm fairly sure we shouldn't. The default update-hook prevents them (if
> enabled), and I can't for the life of me think of why anyone would want
> to distribute such tags.
>
> OTOH, preventing unannotated tags from being pushed seems like a better
> way than to not have the ability to auto-follow those same tags. After
> all, it's better to discourage than to disallow.
>
Before you do this, please explain why unannotated tags are not useful,
and so should not be allowed to be pushed.
^ permalink raw reply
* [PATCH] ls-tree: add --abbrev[=<n>] option
From: Eric Wong @ 2006-03-07 13:52 UTC (permalink / raw)
To: Junio C Hamano, git
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
Documentation/git-ls-tree.txt | 9 ++++++++-
ls-tree.c | 19 +++++++++++++++++--
2 files changed, 25 insertions(+), 3 deletions(-)
24fa229085e3090eea54adbb18e906fed8a63a40
diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
index b92a8b2..b30b766 100644
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -8,7 +8,9 @@ git-ls-tree - Lists the contents of a tr
SYNOPSIS
--------
-'git-ls-tree' [-d] [-r] [-t] [-z] [--name-only] [--name-status] <tree-ish> [paths...]
+'git-ls-tree' [-d] [-r] [-t] [-z]
+ [--name-only] [--name-status] [--full-name] [--abbrev=[<n>]]
+ <tree-ish> [paths...]
DESCRIPTION
-----------
@@ -40,6 +42,11 @@ OPTIONS
--name-status::
List only filenames (instead of the "long" output), one per line.
+--abbrev[=<n>]::
+ Instead of showing the full 40-byte hexadecimal object
+ lines, show only handful hexdigits prefix.
+ Non default number of digits can be specified with --abbrev=<n>.
+
paths::
When paths are given, show them (note that this isn't really raw
pathnames, but rather a list of patterns to match). Otherwise
diff --git a/ls-tree.c b/ls-tree.c
index d005643..97f09bd 100644
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -13,13 +13,14 @@ static int line_termination = '\n';
#define LS_TREE_ONLY 2
#define LS_SHOW_TREES 4
#define LS_NAME_ONLY 8
+static int abbrev = 0;
static int ls_options = 0;
const char **pathspec;
static int chomp_prefix = 0;
static const char *prefix;
static const char ls_tree_usage[] =
- "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] <tree-ish> [path...]";
+ "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
static int show_recursive(const char *base, int baselen, const char *pathname)
{
@@ -73,7 +74,9 @@ static int show_tree(unsigned char *sha1
return 0;
if (!(ls_options & LS_NAME_ONLY))
- printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1));
+ printf("%06o %s %s\t", mode, type,
+ abbrev ? find_unique_abbrev(sha1,abbrev)
+ : sha1_to_hex(sha1));
write_name_quoted(base + chomp_prefix, baselen - chomp_prefix,
pathname,
line_termination, stdout);
@@ -113,6 +116,18 @@ int main(int argc, const char **argv)
chomp_prefix = 0;
break;
}
+ if (!strncmp(argv[1]+2, "abbrev=",7)) {
+ abbrev = strtoul(argv[1]+9, NULL, 10);
+ if (abbrev && abbrev < MINIMUM_ABBREV)
+ abbrev = MINIMUM_ABBREV;
+ else if (abbrev > 40)
+ abbrev = 40;
+ break;
+ }
+ if (!strcmp(argv[1]+2, "abbrev")) {
+ abbrev = DEFAULT_ABBREV;
+ break;
+ }
/* otherwise fallthru */
default:
usage(ls_tree_usage);
--
1.2.4.g76f4
^ permalink raw reply related
* Re: [PATCH] cvsimport: Remove master-updating code
From: Rajkumar S @ 2006-03-07 13:42 UTC (permalink / raw)
To: smurf; +Cc: Junio C Hamano, git
In-Reply-To: <20060307090834.GX13888@smurf.noris.de>
smurf@smurf.noris.de wrote:
> Junio C Hamano:
>
>>I do not understand what cvsimport is trying to do here; I
>>_suspect_ the part that updates the "master" branch head might
>>be a bug.
>>
>
> This should fix it.
It's working fine for me. Thanks!
raj
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox