Showing posts with label git. Show all posts
Showing posts with label git. Show all posts

Wednesday, December 09, 2009

intra-line diff

Introducing linediff, a quick ruby wrapper around diff to highlight changed words instead of changed lines.
#!/usr/bin/ruby
pre, post = $stdin.read.split("\n=======\n")
post[-1..-1] = ""

class String
  def bash
    `bash -c #{inspect.gsub("$", "\\$")}`
  end

  def binspect
    split("\n").map {|s|
      "echo #{s.inspect.gsub("$", "\\$")}"
    }.join(";")
  end

  def to_lines
    gsub("\n", "\\n").gsub(" ", "\n")
  end
  def to_lines!
    gsub!("\n", "\\n")
    gsub!(" ", "\n")
  end

  def to_words
    gsub("\n", " ").gsub("\\n", "\n")
  end
  def to_words!
    gsub!("\n", " ")
    gsub!("\\n", "\n")
  end
end

pre.to_lines!
post.to_lines!
len = pre.length+post.length

both = "diff -U#{len} <(#{pre.binspect}) <(#{post.binspect})".bash
both = both.split("\n")[3..-1]

pre = both.select {|s| s[0..0] != "+"}.join("\n")
post = both.select {|s| s[0..0] != "-"}.join("\n")

pre.gsub!(/^-/, "+")
pre.gsub!(/^ /, "")
post.gsub!(/^ /, "")

pre.to_words!
post.to_words!

puts "#{pre}\n=======\n#{post}"
I use it mostly to handle git conflicts which, in my current setup, show up more often than they should (I repeatedly sync with both a git and an svn repository, which I should not be doing according to git-svn). Here's an example conflict. Can you spot the difference?
<<<<<<< HEAD:thesis.tex
    Once we are done with binary functions, we turn to unary functions, shifting our focus from the aspect weavers to the aspects themselves. Our goal is to help programmers to build aquariums containing the aspects they need, while minimizing the number of proofs needed to achieve this goal. To this end, we provide several generic proofs that create, extend, and translate aquariums with aspects of specific forms. Note, however, that not every pair of functions that commute can be shown to do so using our proofs only, so the programmers are expected to complete our collection with proofs of their own whenever the aspects they need are not of the specific forms covered by our theorems.
    \begin{enumerate}
    \item For every associative and commutative binary function, such as those created using our unordered pair strategy, we can create an aquarium of unary functions corresponding to the elements of the algebraic datatype.
    \item Given an aquarium containing a function $f$, the aquarium can be extended with function $f^n$, for any natural number $n$. The function $f^n$ is the function which repeatedly applies, $n$ times, the function $f$. If $f$ is invertible, the theorem extends to negative integers.
=======
    Once we are done with binary functions, we turn to unary functions, shifting our focus from the aspect weavers to the aspects themselves. Our goal is to help programmers to build aquariums containing the aspects they need, while minimizing the number of proofs needed to achieve this goal. To this end, we provide several generic proofs that create, extend, and translate aquariums with aspects of specific forms. Note, however, that not every pair of functions that commute can be shown to do so using our proofs only, so the programmers are expected to complete our collection with proofs of their own whenever the aspects they need are of the specific forms covered by our theorems.
    \begin{enumerate}
    \item For every associative and commutative binary function, such as those created using our unordered pair strategy, we can create an aquarium of unary functions corresponding to the elements of the algebraic datatype.
    \item Given an aquarium containing a function $f$, the aquarium can be extended with function $f^n$, for any natural number $n$. The function $f^n$ is the function which repeatedly applies, $n$ times in total, the function $f$. If $f$ is invertible, the theorem extends to negative integers.
>>>>>>> df2a5b6eb97ded25a34412d6ff946e0c513da6f3:thesis.tex
    \item If $M$ is a functor, then applying $M$ to all the members of an aquarium transforms the aquarium into a new one, while preserving the pairwise commutativity of its elements.
    %\item We show how parametricity \cite{free_theorems} ensures that structure-manipulating aspects cannot interfere with element-manipulating aspects, thereby reducing the programmer's remaining burden of proof.
    \end{enumerate}
When I encounter such a conflict, I select the code between the "<<<<<<< ... >>>>>>>" and type "!linediff" to tell vim to filter the lines through my ruby script.
<<<<<<< HEAD:thesis.tex
Once we are done with binary functions, we turn to unary functions, shifting our focus from the aspect weavers to the aspects themselves. Our goal is to help programmers to build aquariums containing the aspects they need, while minimizing the number of proofs needed to achieve this goal. To this end, we provide several generic proofs that create, extend, and translate aquariums with aspects of specific forms. Note, however, that not every pair of functions that commute can be shown to do so using our proofs only, so the programmers are expected to complete our collection with proofs of their own whenever the aspects they need are not of the specific forms covered by our theorems. \begin{enumerate} \item For every associative and commutative binary function, such as those created using our unordered pair strategy, we can create an aquarium of unary functions corresponding to the elements of the algebraic datatype. \item Given an aquarium containing a function $f$, the aquarium can be extended with function $f^n$, for any natural number $n$. The function $f^n$ is the function which repeatedly applies, $n$ times, the function $f$. If $f$ is invertible, the theorem extends to negative integers. ======= Once we are done with binary functions, we turn to unary functions, shifting our focus from the aspect weavers to the aspects themselves. Our goal is to help programmers to build aquariums containing the aspects they need, while minimizing the number of proofs needed to achieve this goal. To this end, we provide several generic proofs that create, extend, and translate aquariums with aspects of specific forms. Note, however, that not every pair of functions that commute can be shown to do so using our proofs only, so the programmers are expected to complete our collection with proofs of their own whenever the aspects they need are of the specific forms covered by our theorems. \begin{enumerate} \item For every associative and commutative binary function, such as those created using our unordered pair strategy, we can create an aquarium of unary functions corresponding to the elements of the algebraic datatype. \item Given an aquarium containing a function $f$, the aquarium can be extended with function $f^n$, for any natural number $n$. The function $f^n$ is the function which repeatedly applies, $n$ times in total, the function $f$. If $f$ is invertible, the theorem extends to negative integers.
>>>>>>> df2a5b6eb97ded25a34412d6ff946e0c513da6f3:thesis.tex \item If $M$ is a functor, then applying $M$ to all the members of an aquarium transforms the aquarium into a new one, while preserving the pairwise commutativity of its elements. %\item We show how parametricity \cite{free_theorems} ensures that structure-manipulating aspects cannot interfere with element-manipulating aspects, thereby reducing the programmer's remaining burden of proof. \end{enumerate}
Note that my script detects the "=======" separator and diffs between the lines appearing before and after it. It should be trivial to adapt it to examine different files, like the original diff does. Anyway, the result is that the changed words get prefixed with a "+", which I highlight using vim's ":hlsearch" option.
<<<<<<< HEAD:thesis.tex
    Once we are done with binary functions, we turn to unary functions, shifting our focus from the aspect weavers to the aspects themselves. Our goal is to help programmers to build aquariums containing the aspects they need, while minimizing the number of proofs needed to achieve this goal. To this end, we provide several generic proofs that create, extend, and translate aquariums with aspects of specific forms. Note, however, that not every pair of functions that commute can be shown to do so using our proofs only, so the programmers are expected to complete our collection with proofs of their own whenever the aspects they need are +not of the specific forms covered by our theorems.
    \begin{enumerate}
    \item For every associative and commutative binary function, such as those created using our unordered pair strategy, we can create an aquarium of unary functions corresponding to the elements of the algebraic datatype.
    \item Given an aquarium containing a function $f$, the aquarium can be extended with function $f^n$, for any natural number $n$. The function $f^n$ is the function which repeatedly applies, $n$ +times, the function $f$. If $f$ is invertible, the theorem extends to negative integers.
=======
    Once we are done with binary functions, we turn to unary functions, shifting our focus from the aspect weavers to the aspects themselves. Our goal is to help programmers to build aquariums containing the aspects they need, while minimizing the number of proofs needed to achieve this goal. To this end, we provide several generic proofs that create, extend, and translate aquariums with aspects of specific forms. Note, however, that not every pair of functions that commute can be shown to do so using our proofs only, so the programmers are expected to complete our collection with proofs of their own whenever the aspects they need are of the specific forms covered by our theorems.
    \begin{enumerate}
    \item For every associative and commutative binary function, such as those created using our unordered pair strategy, we can create an aquarium of unary functions corresponding to the elements of the algebraic datatype.
    \item Given an aquarium containing a function $f$, the aquarium can be extended with function $f^n$, for any natural number $n$. The function $f^n$ is the function which repeatedly applies, $n$ +times +in +total, the function $f$. If $f$ is invertible, the theorem extends to negative integers.
>>>>>>> df2a5b6eb97ded25a34412d6ff946e0c513da6f3:thesis.tex
    \item If $M$ is a functor, then applying $M$ to all the members of an aquarium transforms the aquarium into a new one, while preserving the pairwise commutativity of its elements.
    %\item We show how parametricity \cite{free_theorems} ensures that structure-manipulating aspects cannot interfere with element-manipulating aspects, thereby reducing the programmer's remaining burden of proof.
    \end{enumerate}
Tada! Now I know which words have changed since my last unfortunate sync-and-fix-conflicts cycle.

Sunday, October 19, 2008

Rant against merge conflicts (and a solution strategy)

Version control used to be all about versioning. I never used RCS, but I bet the killer command was something like "rcs commit", to create a new version.

Projects soon grew bigger and version control began to be all about collaboration. The killer command was then "cvs checkout", to get the source and begin collaborating.

Then projects grew even more complicated and it was no longer sufficient just to version text, it was now important to version binaries and directories and symlinks and permissions and ancestry. As version control began to resemble filesystems, the magic occurred in commands like "svn add".

More recently, different distributed version control made forking much easier, and the killer command is now "git branch". But the future of version control, I believe, lies in the one command which never worked quite right: "git merge".

Because of merge conflicts, merging is the only version control command which requires human supervision. Removing this constraint would allow other programs to perform merges, a seemingly small advantage which, in the long run, could actually lead to some very useful and unexpected applications... I'm thinking about unionfs extensions, but there could be many others. Never underestimate the creativity of a million strangers!

I happen to be somewhat of an expert on conflicts, or at least I wish I was. For my master's thesis, I'm currently tackling weaving conflicts in aspect-oriented applications. My conclusion so far is that conflicts are inevitable, unless we work with very restricted structures which are guaranteed to merge properly. I'm trying to find suitably-restricted structures which would be expressive enough to program with.

I think that weaving and merging are very closely related operations. The first attempts to combine code, while the second attempts to combine filesystems. In both cases, unfortunately, the parts to be combined could turn out to be utterly incompatible. Current aspect-oriented languages tend to proceed with weaving anyway, postponing the disaster until runtime. Current version control systems take the opposite approach, refusing to merge until the user has fixed all of the conflicts. But I think it should be the other way around! Incorrect programs ought to fail as early as possible, possibly during the compilation or weaving phase. And for scripting purposes, as I've mentioned, I'd rather have merging succeed all the time.

For my aspects-related research, I've found several restricted structures which combine fine all the time, but filesystems can't be among them. Thus if we do insist on combining filesystems, I claim that conflicts are inevitable. That's why I don't insist on combining filesystems. In fact, you might remember how a few months back, I wished for a framework customizing the relationship between the git repository and the filesystem. This is precisely what I'm arguing for once again! Let's use one of my magic restricted structures inside the repository, so that merges succeed all the time. Then if users want to use the repository to track their filesystem, their logs, their cats, or whatever, let them write a translation between the repository and the thing they want to track. Now that would be doing one thing and doing it right, rather than letting filesystem concerns creep into the repository's code base.

What do you think? I am being more crazy than usual?

Wednesday, June 04, 2008

Rant against .gitkeep (and solution)

Git claims to be miles ahead of Subversion, looking down on it as "only" fixing CVS's flaws. Well, one annoying CVS flaw is its failure to archive the existence of directories on the server side, and it seems that git is still behind Subversion in this respect.

Of course, it would be incorrect to state that CVS doesn't track directories at all. It tracks changes to individual files, where a "file" is really a path from the root CVS checkout. On the server side, a CVS archive is simply a working copy where each file is replaced by a "<file>,v" version, archiving diffs in RCS format. So each RCS file on the server side is located in a directory which corresponds to a directory in the working copy.

In addition to the text-based changes which current diff formats convey, RCS can also distinguish between an empty file and a deleted file. It would have been really simple to use a "<dir>,v" file to distinguish between empty and deleted directories, but for some reason, CVS didn't. And it's really annoying.

When you check out a CVS project, the client will create all the directories it knows of, including the ones which were once needed a long time ago to store some obsolete file in the project. To avoid polluting your working copy in this manner, the CVS client offers a "-P" option, which skips the creation of directories whose files are deleted. But this is annoying too (albeit less so), since this will skip the "bin" directory and the build process will fail. Assuming you weren't foolish enough to archive your binaries into CVS, that is.

The standard fix is to archive a "bin/.keepme" file whose sole purpose is to exist whenever "bin" is supposed to exist. It's very similar to the "<dir>,v" idea I suggested above, except that it's called "<dir>/.keepme,v", and that it's visible to users. So it's still annoying, but vanishingly so.

The Subversion team decided that they could get rid of the last few specks of annoyingness in the obvious way, by keeping track of the directory proper.

The git team decided to stick with the CVS way of doing things. They even have an FAQ advising to add the ".keepme" files (which they call ".gitignore").

Anyway, I wrote these wrapper scripts to hide those ".keepme" files from the users. I name them ".gitkeep", and they're still slightly visible in that you have to create and add them manually, but once you commit them, they're gone. And my version of git-status reminds you to add them, instead of pretending that it added everything when in fact it ignored empty directories.

Monday, May 12, 2008

Rant against sidebars (and solution)

I don't browse fullscreen.

Let me repeat that, in case it isn't absolutely clear. My internet browser is never maximized. I have a few windows sprinkled about my screen, the more the merrier, until I run out of space to put them. Thanks to you, web designers (but do continue reading even if you're not a web designer as I have a goodie for you at the end), this occurs as soon as I launch firefox.

But it's not your fault. I know how deeply you care about your audience. I know you are worried about the resolution of your website, that's why you gather statistics about our screen resolutions before exclaiming: "thank god nobody runs at 640x480 anymore, but I still need to cater to those poor saps who are still stuck with 800x600". So you build a website spanning those entire 800 pixels and I need to maximize my browser to see it and I get unhappy and I double my resolution, possibly buying a new screen to accommodate the change. And now your statistics tell you that even less people are stuck at 800x600 and you build an even wider website and I buy an even wider screen and I want to slap you silly for misunderstanding my contribution to your statistics.

Your assumption that your audience is browsing fullscreen is wrong. But assume for a moment that you realized your awful mistake and began measuring the size of the browser window instead of measuring the resolution of the screen. Then your statistics would still confirm that browsers are usually larger than what your site needs, and you will still greedily increase its size. This is because I came to your website looking for a piece of information, which I will continue to desire despite the size of your website. Your statistics will report the sizes at which your audience was reluctantly browsing your site, not the sizes at which they would prefer to browse it.

Now suppose that, in your infinite kindness, you decided to redesign your site so that it would scale automatically to fit each viewer's browser size. Would this make me happy at last? I would have hoped so, but most websites today do scale to fit my browser's size, and I'm still not happy.

The source of my annoyance is sidebars. Their width is usually small and fixed, leaving the bulk of the space to the actual contents. The problem here is that "small" is measured according to the web designer's standards, and the web designer is browsing fullscreen on a very large screen. This means that in my browser, those annoying sidebars will take up two thirds of the space and the contents will be so squished that I will need to resize my browser despite the website's struggling efforts to accommodate my size choices. At least when the websites had a fixed width I could scroll those sidebars out of the way.

Anyway, Gmail uses two sidebars, and I wrote a script to get rid of the right-hand one. That's the "goodie", "solution", constructive part of this rant.

The sidebar I'm removing contains mostly text ads, but that's not the reason I'm removing it. In fact, if you came here looking for a way to remove those adds, I'd advise you not to. Well, sure, go ahead and remove the ads if they bother you, but if the sidebar doesn't bother you as well then don't get rid of an entire bar just to get rid of a few ads. The sidebar does contain a few useful things; I've never used its "New window", "Print all", nor "Expand all" buttons, but the bar does occasionally contain golden suggestions. It once parsed the text of the message I was reading, discovered that it was an invitation, and provided me with a button to add the event to my calendar. It even got the time, place, and event description right! I bet that Google will find more cool ways to understand our messages and provide us with smart buttons, but I'll miss out on them if I remove this sidebar. Oh well, I guess my hatred is greater than my enthusiasm.

Here is the script: wider gmail. It's a bookmarklet, so don't bother clicking on it now, it won't lead you anywhere. But if you put it in your toolbar, and click it once you're in Gmail's conversation mode, it will toggle the right-hand sidebar on and off. And if you'd rather have the bar hiden by default, here is a Greasemonkey script which should do the trick.