diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 47ba9a403a..b5d9763594 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -36,6 +36,9 @@ Turn off colored diff, even when the configuration file gives the default to color output. +--color-words:: + Show colored word diff, i.e. color words which have changed. + --no-renames:: Turn off rename detection, even when the configuration file gives the default to do so. diff --git a/Documentation/howto/setup-git-server-over-http.txt b/Documentation/howto/setup-git-server-over-http.txt new file mode 100644 index 0000000000..ba191569af --- /dev/null +++ b/Documentation/howto/setup-git-server-over-http.txt @@ -0,0 +1,256 @@ +From: Rutger Nijlunsing +Subject: Setting up a git repository which can be pushed into and pulled from over HTTP. +Date: Thu, 10 Aug 2006 22:00:26 +0200 + +Since Apache is one of those packages people like to compile +themselves while others prefer the bureaucrat's dream Debian, it is +impossible to give guidelines which will work for everyone. Just send +some feedback to the mailing list at git@vger.kernel.org to get this +document tailored to your favorite distro. + + +What's needed: + +- Have an Apache web-server + + On Debian: + $ apt-get install apache2 + To get apache2 by default started, + edit /etc/default/apache2 and set NO_START=0 + +- can edit the configuration of it. + + This could be found under /etc/httpd, or refer to your Apache documentation. + + On Debian: this means being able to edit files under /etc/apache2 + +- can restart it. + + 'apachectl --graceful' might do. If it doesn't, just stop and + restart apache. Be warning that active connections to your server + might be aborted by this. + + On Debian: + $ /etc/init.d/apache2 restart + or + $ /etc/init.d/apache2 force-reload + (which seems to do the same) + This adds symlinks from the /etc/apache2/mods-enabled to + /etc/apache2/mods-available. + +- have permissions to chown a directory + +- have git installed at the server _and_ client + +In effect, this probably means you're going to be root. + + +Step 1: setup a bare GIT repository +----------------------------------- + +At the time of writing, git-http-push cannot remotely create a GIT +repository. So we have to do that at the server side with git. Another +option would be to generate an empty repository at the client and copy +it to the server with WebDAV. But then you're probably the first to +try that out :) + +Create the directory under the DocumentRoot of the directories served +by Apache. As an example we take /usr/local/apache2, but try "grep +DocumentRoot /where/ever/httpd.conf" to find your root: + + $ cd /usr/local/apache/htdocs + $ mkdir my-new-repo.git + + On Debian: + + $ cd /var/www + $ mkdir my-new-repo.git + + +Initialize a bare repository + + $ cd my-new-repo.git + $ git --bare init-db + + +Change the ownership to your web-server's credentials. Use "grep ^User +httpd.conf" and "grep ^Group httpd.conf" to find out: + + $ chown -R www.www . + + On Debian: + + $ chown -R www-data.www-data . + + +If you do not know which user Apache runs as, you can alternatively do +a "chmod -R a+w .", inspect the files which are created later on, and +set the permissions appropriately. + +Restart apache2, and check whether http://server/my-new-repo.git gives +a directory listing. If not, check whether apache started up +successfully. + + +Step 2: enable DAV on this repository +------------------------------------- + +First make sure the dav_module is loaded. For this, insert in httpd.conf: + + LoadModule dav_module libexec/httpd/libdav.so + AddModule mod_dav.c + +Also make sure that this line exists which is the file used for +locking DAV operations: + + DAVLockDB "/usr/local/apache2/temp/DAV.lock" + + On Debian these steps can be performed with: + + Enable the dav and dav_fs modules of apache: + $ a2enmod dav_fs + (just to be sure. dav_fs might be unneeded, I don't know) + $ a2enmod dav + The DAV lock is located in /etc/apache2/mods-available/dav_fs.conf: + DAVLockDB /var/lock/apache2/DAVLock + +Of course, it can point somewhere else, but the string is actually just a +prefix in some Apache configurations, and therefore the _directory_ has to +be writable by the user Apache runs as. + +Then, add something like this to your httpd.conf + + + DAV on + AuthType Basic + AuthName "Git" + AuthUserFile /usr/local/apache2/conf/passwd.git + Require valid-user + + + On Debian: + Create (or add to) /etc/apache2/conf.d/git.conf : + + + DAV on + AuthType Basic + AuthName "Git" + AuthUserFile /etc/apache2/passwd.git + Require valid-user + + + Debian automatically reads all files under /etc/apach2/conf.d. + +The password file can be somewhere else, but it has to be readable by +Apache and preferably not readable by the world. + +Create this file by + $ htpasswd -c /usr/local/apache2/conf/passwd.git + + On Debian: + $ htpasswd -c /etc/apache2/passwd.git + +You will be asked a password, and the file is created. Subsequent calls +to htpasswd should omit the '-c' option, since you want to append to the +existing file. + +You need to restart Apache. + +Now go to http://@/my-new-repo.git in your +browser to check whether it asks for a password and accepts the right +password. + +On Debian: + + To test the WebDAV part, do: + + $ apt-get install litmus + $ litmus http:///my-new-repo.git + + Most tests should pass. + +A command line tool to test WebDAV is cadaver. + +If you're into Windows, from XP onwards Internet Explorer supports +WebDAV. For this, do Internet Explorer -> Open Location -> +http:///my-new-repo.git [x] Open as webfolder -> login . + + +Step 3: setup the client +------------------------ + +Make sure that you have HTTP support, i.e. your git was built with curl. +The easiest way to check is to look for the executable 'git-http-push'. + +Then, add the following to your $HOME/.netrc (you can do without, but will be +asked to input your password a _lot_ of times): + + machine + login + password + +...and set permissions: + chmod 600 ~/.netrc + +If you want to access the web-server by its IP, you have to type that in, +instead of the server name. + +To check whether all is OK, do: + + curl --netrc --location -v http://@/my-new-repo.git/ + +...this should give a directory listing in HTML of /var/www/my-new-repo.git . + + +Now, add the remote in your existing repository which contains the project +you want to export: + + $ git-repo-config remote.upload.url \ + http://@/my-new-repo.git/ + +It is important to put the last '/'; Without it, the server will send +a redirect which git-http-push does not (yet) understand, and git-http-push +will repeat the request infinitely. + + +Step 4: make the initial push +----------------------------- + +From your client repository, do + + $ git push upload master + +This pushes branch 'master' (which is assumed to be the branch you +want to export) to repository called 'upload', which we previously +defined with git-repo-config. + + +Troubleshooting: +---------------- + +If git-http-push says + + Error: no DAV locking support on remote repo http://... + +then it means the web-server did not accept your authentication. Make sure +that the user name and password matches in httpd.conf, .netrc and the URL +you are uploading to. + +If git-http-push shows you an error (22/502) when trying to MOVE a blob, +it means that your web-server somehow does not recognize its name in the +request; This can happen when you start Apache, but then disable the +network interface. A simple restart of Apache helps. + +Errors like (22/502) are of format (curl error code/http error +code). So (22/404) means something like 'not found' at the server. + +Reading /usr/local/apache2/logs/error_log is often helpful. + + On Debian: Read /var/log/apache2/error.log instead. + + +Debian References: http://www.debian-administration.org/articles/285 + +Authors + Johannes Schindelin + Rutger Nijlunsing diff --git a/diff.c b/diff.c index 0ff9ea2ba7..8861b853e7 100644 --- a/diff.c +++ b/diff.c @@ -358,12 +358,152 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) return 0; } +struct diff_words_buffer { + mmfile_t text; + long alloc; + long current; /* output pointer */ + int suppressed_newline; +}; + +static void diff_words_append(char *line, unsigned long len, + struct diff_words_buffer *buffer) +{ + if (buffer->text.size + len > buffer->alloc) { + buffer->alloc = (buffer->text.size + len) * 3 / 2; + buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc); + } + line++; + len--; + memcpy(buffer->text.ptr + buffer->text.size, line, len); + buffer->text.size += len; +} + +struct diff_words_data { + struct xdiff_emit_state xm; + struct diff_words_buffer minus, plus; +}; + +static void print_word(struct diff_words_buffer *buffer, int len, int color, + int suppress_newline) +{ + const char *ptr; + int eol = 0; + + if (len == 0) + return; + + ptr = buffer->text.ptr + buffer->current; + buffer->current += len; + + if (ptr[len - 1] == '\n') { + eol = 1; + len--; + } + + fputs(diff_get_color(1, color), stdout); + fwrite(ptr, len, 1, stdout); + fputs(diff_get_color(1, DIFF_RESET), stdout); + + if (eol) { + if (suppress_newline) + buffer->suppressed_newline = 1; + else + putchar('\n'); + } +} + +static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) +{ + struct diff_words_data *diff_words = priv; + + if (diff_words->minus.suppressed_newline) { + if (line[0] != '+') + putchar('\n'); + diff_words->minus.suppressed_newline = 0; + } + + len--; + switch (line[0]) { + case '-': + print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1); + break; + case '+': + print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0); + break; + case ' ': + print_word(&diff_words->plus, len, DIFF_PLAIN, 0); + diff_words->minus.current += len; + break; + } +} + +/* this executes the word diff on the accumulated buffers */ +static void diff_words_show(struct diff_words_data *diff_words) +{ + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + mmfile_t minus, plus; + int i; + + minus.size = diff_words->minus.text.size; + minus.ptr = xmalloc(minus.size); + memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size); + for (i = 0; i < minus.size; i++) + if (isspace(minus.ptr[i])) + minus.ptr[i] = '\n'; + diff_words->minus.current = 0; + + plus.size = diff_words->plus.text.size; + plus.ptr = xmalloc(plus.size); + memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size); + for (i = 0; i < plus.size; i++) + if (isspace(plus.ptr[i])) + plus.ptr[i] = '\n'; + diff_words->plus.current = 0; + + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; + xecfg.flags = 0; + ecb.outf = xdiff_outf; + ecb.priv = diff_words; + diff_words->xm.consume = fn_out_diff_words_aux; + xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb); + + free(minus.ptr); + free(plus.ptr); + diff_words->minus.text.size = diff_words->plus.text.size = 0; + + if (diff_words->minus.suppressed_newline) { + putchar('\n'); + diff_words->minus.suppressed_newline = 0; + } +} + struct emit_callback { struct xdiff_emit_state xm; int nparents, color_diff; const char **label_path; + struct diff_words_data *diff_words; }; +static void free_diff_words_data(struct emit_callback *ecbdata) +{ + if (ecbdata->diff_words) { + /* flush buffers */ + if (ecbdata->diff_words->minus.text.size || + ecbdata->diff_words->plus.text.size) + diff_words_show(ecbdata->diff_words); + + if (ecbdata->diff_words->minus.text.ptr) + free (ecbdata->diff_words->minus.text.ptr); + if (ecbdata->diff_words->plus.text.ptr) + free (ecbdata->diff_words->plus.text.ptr); + free(ecbdata->diff_words); + ecbdata->diff_words = NULL; + } +} + const char *diff_get_color(int diff_use_color, enum color_diff ix) { if (diff_use_color) @@ -398,12 +538,31 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) else { int nparents = ecbdata->nparents; int color = DIFF_PLAIN; - for (i = 0; i < nparents && len; i++) { - if (line[i] == '-') - color = DIFF_FILE_OLD; - else if (line[i] == '+') - color = DIFF_FILE_NEW; - } + if (ecbdata->diff_words && nparents != 1) + /* fall back to normal diff */ + free_diff_words_data(ecbdata); + if (ecbdata->diff_words) { + if (line[0] == '-') { + diff_words_append(line, len, + &ecbdata->diff_words->minus); + return; + } else if (line[0] == '+') { + diff_words_append(line, len, + &ecbdata->diff_words->plus); + return; + } + if (ecbdata->diff_words->minus.text.size || + ecbdata->diff_words->plus.text.size) + diff_words_show(ecbdata->diff_words); + line++; + len--; + } else + for (i = 0; i < nparents && len; i++) { + if (line[i] == '-') + color = DIFF_FILE_OLD; + else if (line[i] == '+') + color = DIFF_FILE_NEW; + } set = diff_get_color(ecbdata->color_diff, color); } if (len > 0 && line[len-1] == '\n') @@ -836,7 +995,12 @@ static void builtin_diff(const char *name_a, ecb.outf = xdiff_outf; ecb.priv = &ecbdata; ecbdata.xm.consume = fn_out_consume; + if (o->color_diff_words) + ecbdata.diff_words = + xcalloc(1, sizeof(struct diff_words_data)); xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + if (o->color_diff_words) + free_diff_words_data(&ecbdata); } free_ab_and_return: @@ -1710,6 +1874,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->xdl_opts |= XDF_IGNORE_WHITESPACE; else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; + else if (!strcmp(arg, "--color-words")) + options->color_diff = options->color_diff_words = 1; else if (!strcmp(arg, "--no-renames")) options->detect_rename = 0; else diff --git a/diff.h b/diff.h index 2cced530fa..b007240a5d 100644 --- a/diff.h +++ b/diff.h @@ -46,7 +46,8 @@ struct diff_options { full_index:1, silent_on_remove:1, find_copies_harder:1, - color_diff:1; + color_diff:1, + color_diff_words:1; int context; int break_opt; int detect_rename;