monotone

monotone Mtn Source Tree

Root/git_change.cc

1// Copyright (C) 2009 Derek Scherger <derek@echologic.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include "git_change.hh"
12#include "parallel_iter.hh"
13#include "roster.hh"
14
15#include <stack>
16
17using std::make_pair;
18using std::map;
19using std::stack;
20using std::string;
21using std::vector;
22
23void
24get_change(roster_t const & left, roster_t const & right,
25 git_change & change)
26{
27 typedef attr_map_t::const_iterator attr_iterator;
28 static attr_key exe_attr("mtn:execute");
29
30 parallel::iter<node_map> i(left.all_nodes(), right.all_nodes());
31 while (i.next())
32 {
33 MM(i);
34 switch (i.state())
35 {
36 case parallel::invalid:
37 I(false);
38
39 case parallel::in_left:
40 // deleted
41 if (is_file_t(i.left_data()))
42 {
43 file_path path;
44 left.get_name(i.left_key(), path);
45 change.deletions.push_back(path);
46 }
47 break;
48
49 case parallel::in_right:
50 // added
51 if (is_file_t(i.right_data()))
52 {
53 file_t file = downcast_to_file_t(i.right_data());
54
55 attr_iterator exe = file->attrs.find(exe_attr);
56
57 string mode = "100644";
58 if (exe != file->attrs.end() &&
59 exe->second.first && // live attr
60 exe->second.second() == "true")
61 mode = "100755";
62
63 file_path path;
64 right.get_name(i.right_key(), path);
65 change.additions.push_back(git_add(path,
66 file->content,
67 mode));
68 }
69 break;
70
71 case parallel::in_both:
72 // moved/renamed/patched/attribute changes
73 if (is_file_t(i.left_data()))
74 {
75 file_t left_file = downcast_to_file_t(i.left_data());
76 file_t right_file = downcast_to_file_t(i.right_data());
77
78 attr_iterator left_attr = left_file->attrs.find(exe_attr);
79 attr_iterator right_attr = right_file->attrs.find(exe_attr);
80
81 string left_mode = "100644";
82 string right_mode = "100644";
83
84 if (left_attr != left_file->attrs.end() &&
85 left_attr->second.first && // live attr
86 left_attr->second.second() == "true")
87 left_mode = "100755";
88
89 if (right_attr != right_file->attrs.end() &&
90 right_attr->second.first && // live attr
91 right_attr->second.second() == "true")
92 right_mode = "100755";
93
94 file_path left_path, right_path;
95 left.get_name(i.left_key(), left_path);
96 right.get_name(i.right_key(), right_path);
97
98 if (left_path != right_path)
99 change.renames.push_back(make_pair(left_path,
100 right_path));
101
102 // git handles content changes as additions
103 if (left_file->content != right_file->content ||
104 left_mode != right_mode)
105 change.additions.push_back(git_add(right_path,
106 right_file->content,
107 right_mode));
108 }
109 break;
110 }
111 }
112}
113
114// re-order renames so that they occur in the correct order
115// i.e. rename a->b + rename b->c will be re-ordered as
116// rename b->c + rename a->b
117// this will also insert temporary names to resolve circular
118// renames and name swaps:
119// i.e. rename a->b + rename b->a will be re-ordered as
120// rename a->tmp + rename b->a + rename tmp->b
121void
122reorder_renames(vector<git_rename> const & renames,
123 vector<git_rename> & reordered_renames)
124{
125 typedef map<file_path, file_path> map_type;
126
127 map_type rename_map;
128
129 for (rename_iterator i = renames.begin(); i != renames.end(); ++i)
130 rename_map.insert(*i);
131
132 while (!rename_map.empty())
133 {
134 map_type::iterator i = rename_map.begin();
135 I(i != rename_map.end());
136 git_rename base(*i);
137 rename_map.erase(i);
138
139 map_type::iterator next = rename_map.find(base.second);
140 stack<git_rename> rename_stack;
141
142 // stack renames so their order can be reversed
143 while (next != rename_map.end())
144 {
145 git_rename rename(*next);
146 rename_stack.push(rename);
147 rename_map.erase(next);
148 next = rename_map.find(rename.second);
149 }
150
151 // break rename loops
152 if (!rename_stack.empty())
153 {
154 git_rename const & top = rename_stack.top();
155 // if there is a loop push another rename onto the stack that
156 // renames the old base to a temporary and adjust the base
157 // rename to account for this
158 if (base.first == top.second)
159 {
160 // the temporary path introduced here is pretty weak in
161 // terms of random filenames but should suffice for the
162 // already rare situations where any of this is required.
163 string path = top.second.as_internal();
164 path += ".tmp.break-rename-loop";
165 file_path tmp = file_path_internal(path);
166 rename_stack.push(git_rename(base.first, tmp));
167 base.first = tmp;
168 }
169 }
170
171 // insert the stacked renames in reverse order
172 while (!rename_stack.empty())
173 {
174 git_rename rename = rename_stack.top();
175 rename_stack.pop();
176 reordered_renames.push_back(rename);
177 }
178
179 reordered_renames.push_back(base);
180 }
181}
182
183// Local Variables:
184// mode: C++
185// fill-column: 76
186// c-file-style: "gnu"
187// indent-tabs-mode: nil
188// End:
189// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status