Revision e7cb0b4455c85b53aeba40f88ffddcf6d4002498 authored by Johannes Schindelin on 11 May 2018, 14:03:54 UTC, committed by Jeff King on 22 May 2018, 03:50:11 UTC
When we started to catch NTFS short names that clash with .git, we only looked for GIT~1. This is sufficient because we only ever clone into an empty directory, so .git is guaranteed to be the first subdirectory or file in that directory. However, even with a fresh clone, .gitmodules is *not* necessarily the first file to be written that would want the NTFS short name GITMOD~1: a malicious repository can add .gitmodul0000 and friends, which sorts before `.gitmodules` and is therefore checked out *first*. For that reason, we have to test not only for ~1 short names, but for others, too. It's hard to just adapt the existing checks in is_ntfs_dotgit(): since Windows 2000 (i.e., in all Windows versions still supported by Git), NTFS short names are only generated in the <prefix>~<number> form up to number 4. After that, a *different* prefix is used, calculated from the long file name using an undocumented, but stable algorithm. For example, the short name of .gitmodules would be GITMOD~1, but if it is taken, and all of ~2, ~3 and ~4 are taken, too, the short name GI7EBA~1 will be used. From there, collisions are handled by incrementing the number, shortening the prefix as needed (until ~9999999 is reached, in which case NTFS will not allow the file to be created). We'd also want to handle .gitignore and .gitattributes, which suffer from a similar problem, using the fall-back short names GI250A~1 and GI7D29~1, respectively. To accommodate for that, we could reimplement the hashing algorithm, but it is just safer and simpler to provide the known prefixes. This algorithm has been reverse-engineered and described at https://usn.pw/blog/gen/2015/06/09/filenames/, which is defunct but still available via https://web.archive.org/. These can be recomputed by running the following Perl script: -- snip -- use warnings; use strict; sub compute_short_name_hash ($) { my $checksum = 0; foreach (split('', $_[0])) { $checksum = ($checksum * 0x25 + ord($_)) & 0xffff; } $checksum = ($checksum * 314159269) & 0xffffffff; $checksum = 1 + (~$checksum & 0x7fffffff) if ($checksum & 0x80000000); $checksum -= (($checksum * 1152921497) >> 60) * 1000000007; return scalar reverse sprintf("%x", $checksum & 0xffff); } print compute_short_name_hash($ARGV[0]); -- snap -- E.g., running that with the argument ".gitignore" will result in "250a" (which then becomes "gi250a" in the code). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Jeff King <peff@peff.net>
1 parent 0fc333b
graph.h
#ifndef GRAPH_H
#define GRAPH_H
#include "diff.h"
/* A graph is a pointer to this opaque structure */
struct git_graph;
/*
* Called to setup global display of line_prefix diff option.
*
* Passed a diff_options structure which indicates the line_prefix and the
* file to output the prefix to. This is sort of a hack used so that the
* line_prefix will be honored by all flows which also honor "--graph"
* regardless of whether a graph has actually been setup. The normal graph
* flow will honor the exact diff_options passed, but a NULL graph will cause
* display of a line_prefix to stdout.
*/
void graph_setup_line_prefix(struct diff_options *diffopt);
/*
* Set up a custom scheme for column colors.
*
* The default column color scheme inserts ANSI color escapes to colorize
* the graph. The various color escapes are stored in an array of strings
* where each entry corresponds to a color, except for the last entry,
* which denotes the escape for resetting the color back to the default.
* When generating the graph, strings from this array are inserted before
* and after the various column characters.
*
* This function allows you to enable a custom array of color escapes.
* The 'colors_max' argument is the index of the last "reset" entry.
*
* This functions must be called BEFORE graph_init() is called.
*
* NOTE: This function isn't used in Git outside graph.c but it is used
* by CGit (http://git.zx2c4.com/cgit/) to use HTML for colors.
*/
void graph_set_column_colors(const char **colors, unsigned short colors_max);
/*
* Create a new struct git_graph.
*/
struct git_graph *graph_init(struct rev_info *opt);
/*
* Update a git_graph with a new commit.
* This will cause the graph to begin outputting lines for the new commit
* the next time graph_next_line() is called.
*
* If graph_update() is called before graph_is_commit_finished() returns 1,
* the next call to graph_next_line() will output an ellipsis ("...")
* to indicate that a portion of the graph is missing.
*/
void graph_update(struct git_graph *graph, struct commit *commit);
/*
* Determine if a graph has finished outputting lines for the current
* commit.
*
* Returns 1 if graph_next_line() needs to be called again before
* graph_update() should be called. Returns 0 if no more lines are needed
* for this commit. If 0 is returned, graph_next_line() may still be
* called without calling graph_update(), and it will merely output
* appropriate "vertical padding" in the graph.
*/
int graph_is_commit_finished(struct git_graph const *graph);
/*
* Output the next line for a graph.
* This formats the next graph line into the specified strbuf. It is not
* terminated with a newline.
*
* Returns 1 if the line includes the current commit, and 0 otherwise.
* graph_next_line() will return 1 exactly once for each time
* graph_update() is called.
*
* NOTE: This function isn't used in Git outside graph.c but it is used
* by CGit (http://git.zx2c4.com/cgit/) to wrap HTML around graph lines.
*/
int graph_next_line(struct git_graph *graph, struct strbuf *sb);
/*
* Return current width of the graph in on-screen characters.
*/
int graph_width(struct git_graph *graph);
/*
* graph_show_*: helper functions for printing to stdout
*/
/*
* If the graph is non-NULL, print the history graph to stdout,
* up to and including the line containing this commit.
* Does not print a terminating newline on the last line.
*/
void graph_show_commit(struct git_graph *graph);
/*
* If the graph is non-NULL, print one line of the history graph to stdout.
* Does not print a terminating newline on the last line.
*/
void graph_show_oneline(struct git_graph *graph);
/*
* If the graph is non-NULL, print one line of vertical graph padding to
* stdout. Does not print a terminating newline on the last line.
*/
void graph_show_padding(struct git_graph *graph);
/*
* If the graph is non-NULL, print the rest of the history graph for this
* commit to stdout. Does not print a terminating newline on the last line.
*/
int graph_show_remainder(struct git_graph *graph);
/*
* Print a commit message strbuf and the remainder of the graph to stdout.
*
* This is similar to graph_show_strbuf(), but it always prints the
* remainder of the graph.
*
* If the strbuf ends with a newline, the output printed by
* graph_show_commit_msg() will end with a newline. If the strbuf is
* missing a terminating newline (including if it is empty), the output
* printed by graph_show_commit_msg() will also be missing a terminating
* newline.
*
* Note that unlike some other graph display functions, you must pass the file
* handle directly. It is assumed that this is the same file handle as the
* file specified by the graph diff options. This is necessary so that
* graph_show_commit_msg can be called even with a NULL graph.
*/
void graph_show_commit_msg(struct git_graph *graph,
FILE *file,
struct strbuf const *sb);
#endif /* GRAPH_H */
Computing file changes ...